RTBKit
0.9
Open-source framework to create real-time ad bidding systems.
|
00001 /* rest_request_router.cc 00002 Jeremy Barnes, 15 November 2012 00003 Copyright (c) 2012 Datacratic Inc. All rights reserved. 00004 00005 */ 00006 00007 #include "rest_request_router.h" 00008 #include "jml/utils/vector_utils.h" 00009 #include "jml/arch/exception_handler.h" 00010 #include "jml/utils/set_utils.h" 00011 00012 00013 using namespace std; 00014 00015 00016 namespace Datacratic { 00017 00018 00019 /*****************************************************************************/ 00020 /* PATH SPEC */ 00021 /*****************************************************************************/ 00022 00023 std::ostream & operator << (std::ostream & stream, const PathSpec & path) 00024 { 00025 return stream << path.path; 00026 } 00027 00028 00029 /*****************************************************************************/ 00030 /* REQUEST FILTER */ 00031 /*****************************************************************************/ 00032 00033 std::ostream & operator << (std::ostream & stream, const RequestFilter & filter) 00034 { 00035 return stream; 00036 } 00037 00038 00039 /*****************************************************************************/ 00040 /* REQUEST REQUEST PARSING CONTEXT */ 00041 /*****************************************************************************/ 00042 00043 std::ostream & operator << (std::ostream & stream, 00044 const RestRequestParsingContext & context) 00045 { 00046 return stream << context.resources << " " << context.remaining; 00047 } 00048 00049 00050 /*****************************************************************************/ 00051 /* REST REQUEST ROUTER */ 00052 /*****************************************************************************/ 00053 00054 RestRequestRouter:: 00055 RestRequestRouter() 00056 : terminal(false) 00057 { 00058 } 00059 00060 RestRequestRouter:: 00061 RestRequestRouter(const OnProcessRequest & processRequest, 00062 const std::string & description, 00063 bool terminal, 00064 const Json::Value & argHelp) 00065 : rootHandler(processRequest), 00066 description(description), 00067 terminal(terminal), 00068 argHelp(argHelp) 00069 { 00070 } 00071 00072 RestRequestRouter:: 00073 ~RestRequestRouter() 00074 { 00075 } 00076 00077 RestServiceEndpoint::OnHandleRequest 00078 RestRequestRouter:: 00079 requestHandler() const 00080 { 00081 return std::bind(&RestRequestRouter::handleRequest, 00082 this, 00083 std::placeholders::_1, 00084 std::placeholders::_2); 00085 } 00086 00087 void 00088 RestRequestRouter:: 00089 handleRequest(const RestServiceEndpoint::ConnectionId & connection, 00090 const RestRequest & request) const 00091 { 00092 //JML_TRACE_EXCEPTIONS(false); 00093 00094 RestRequestParsingContext context(request); 00095 MatchResult res = processRequest(connection, request, context); 00096 if (res == MR_NO) { 00097 connection.sendErrorResponse(404, "unknown resource " + request.resource); 00098 } 00099 } 00100 00101 RestRequestRouter:: 00102 MatchResult 00103 RestRequestRouter:: 00104 processRequest(const RestServiceEndpoint::ConnectionId & connection, 00105 const RestRequest & request, 00106 RestRequestParsingContext & context) const 00107 { 00108 bool debug = false; 00109 00110 if (debug) { 00111 cerr << "processing request " << request 00112 << " with context " << context 00113 << " against route " << description 00114 << " with " << subRoutes.size() << " subroutes" << endl; 00115 } 00116 00117 if (rootHandler && (!terminal || context.remaining.empty())) 00118 return rootHandler(connection, request, context); 00119 00120 for (auto & sr: subRoutes) { 00121 if (debug) 00122 cerr << " trying subroute " << sr.router->description << endl; 00123 try { 00124 MatchResult mr = sr.process(request, context, connection); 00125 //cerr << "returned " << mr << endl; 00126 if (mr == MR_YES || mr == MR_ERROR) 00127 return mr; 00128 } catch (const std::exception & exc) { 00129 connection.sendErrorResponse(500, ML::format("threw exception: %s", 00130 exc.what())); 00131 } catch (...) { 00132 connection.sendErrorResponse(500, "unknown exception"); 00133 } 00134 } 00135 00136 return MR_NO; 00137 //connection.sendErrorResponse(404, "invalid route for " 00138 // + request.resource); 00139 } 00140 00141 RestRequestRouter::MatchResult 00142 RestRequestRouter::Route:: 00143 process(const RestRequest & request, 00144 const RestRequestParsingContext & context, 00145 const RestServiceEndpoint::ConnectionId & connection) const 00146 { 00147 using namespace std; 00148 00149 bool debug = false; 00150 00151 if (debug) { 00152 cerr << "verb = " << request.verb << " filter.verbs = " << filter.verbs 00153 << endl; 00154 } 00155 if (!filter.verbs.empty() 00156 && !filter.verbs.count(request.verb)) 00157 return MR_NO; 00158 00159 RestRequestParsingContext matched = context; 00160 switch (path.type) { 00161 case PathSpec::STRING: { 00162 std::string::size_type pos = context.remaining.find(path.path); 00163 if (pos == 0) { 00164 using namespace std; 00165 //cerr << "matched string " << pos << endl; 00166 matched.resources.push_back(path.path); 00167 matched.remaining = string(matched.remaining, path.path.size()); 00168 break; 00169 } 00170 else return MR_NO; 00171 } 00172 case PathSpec::REGEX: { 00173 boost::smatch results; 00174 bool found 00175 = boost::regex_search(context.remaining, 00176 results, 00177 path.rex) 00178 && !results.prefix().matched; // matches from the start 00179 00180 //cerr << "matching regex " << path.path << " against " 00181 // << context.remaining << " with found " << found << endl; 00182 if (!found) 00183 return MR_NO; 00184 for (unsigned i = 0; i < results.size(); ++i) 00185 matched.resources.push_back(results[i]); 00186 matched.remaining = std::string(matched.remaining, 00187 results[0].length()); 00188 break; 00189 } 00190 case PathSpec::NONE: 00191 default: 00192 throw ML::Exception("unknown rest request type"); 00193 } 00194 00195 return router->processRequest(connection, request, matched); 00196 } 00197 00198 void 00199 RestRequestRouter:: 00200 addRoute(PathSpec path, RequestFilter filter, 00201 const std::shared_ptr<RestRequestRouter> & handler) 00202 { 00203 if (rootHandler) 00204 throw ML::Exception("can't add a sub-route to a terminal route"); 00205 00206 Route route; 00207 route.path = path; 00208 route.filter = filter; 00209 route.router = handler; 00210 00211 subRoutes.emplace_back(std::move(route)); 00212 } 00213 00214 void 00215 RestRequestRouter:: 00216 addRoute(PathSpec path, RequestFilter filter, 00217 const std::string & description, 00218 const OnProcessRequest & cb, 00219 const Json::Value & argHelp) 00220 { 00221 addRoute(path, filter, 00222 std::make_shared<RestRequestRouter>(cb, description, true, argHelp)); 00223 } 00224 00225 void 00226 RestRequestRouter:: 00227 addHelpRoute(PathSpec path, RequestFilter filter) 00228 { 00229 OnProcessRequest helpRoute 00230 = [=] (const RestServiceEndpoint::ConnectionId & connection, 00231 const RestRequest & request, 00232 const RestRequestParsingContext & context) 00233 { 00234 Json::Value help; 00235 getHelp(help, "", set<string>()); 00236 connection.sendResponse(200, help); 00237 00238 return MR_YES; 00239 }; 00240 00241 addRoute(path, filter, "Get help on the available API commands", 00242 helpRoute, Json::Value()); 00243 } 00244 00245 void 00246 RestRequestRouter:: 00247 getHelp(Json::Value & result, const std::string & currentPath, 00248 const std::set<std::string> & verbs) 00249 { 00250 auto getVerbsStr = [] (const std::set<std::string> & verbs) 00251 { 00252 string verbsStr; 00253 for (auto v: verbs) { 00254 if (!verbsStr.empty()) 00255 verbsStr += ","; 00256 else verbsStr += " "; 00257 verbsStr += v; 00258 } 00259 00260 return verbsStr; 00261 }; 00262 00263 Json::Value & v = result[currentPath + getVerbsStr(verbs)]; 00264 00265 v["description"] = description; 00266 if (!argHelp.isNull()) 00267 v["arguments"] = argHelp; 00268 00269 for (unsigned i = 0; i < subRoutes.size(); ++i) { 00270 string path = currentPath + subRoutes[i].path.getPathDesc(); 00271 Json::Value & sri = result[path + getVerbsStr(subRoutes[i].filter.verbs)]; 00272 subRoutes[i].path.getHelp(sri); 00273 subRoutes[i].filter.getHelp(sri); 00274 subRoutes[i].router->getHelp(result, path, subRoutes[i].filter.verbs); 00275 } 00276 } 00277 00278 RestRequestRouter & 00279 RestRequestRouter:: 00280 addSubRouter(PathSpec path, const std::string & description) 00281 { 00282 // TODO: check it doesn't exist 00283 Route route; 00284 route.path = path; 00285 route.router.reset(new RestRequestRouter()); 00286 route.router->description = description; 00287 00288 subRoutes.push_back(route); 00289 return *route.router; 00290 } 00291 00292 00293 } // namespace Datacratic