#include "router.hpp"
namespace YaHTTP {
- typedef funcptr::tuple<int,int> TDelim;
-
// router is defined here.
YaHTTP::Router Router::router;
routes.push_back(funcptr::make_tuple(method2, url, handler, name));
};
- RoutingResult Router::route(Request *req, THandlerFunction& handler) {
- std::map<std::string, TDelim> params;
- int pos1,pos2;
- bool matched = false;
- bool seen = false;
- std::string rname;
-
- // iterate routes
- for(TRouteList::iterator i = routes.begin(); !matched && i != routes.end(); i++) {
- int k1,k2,k3;
- std::string pname;
- std::string method, url;
- funcptr::tie(method, url, handler, rname) = *i;
- matched = false;
-
- // see if we can't match the url
- params.clear();
- // simple matcher func
- for(k1=0, k2=0; k1 < static_cast<int>(url.size()) && k2 < static_cast<int>(req->url.path.size()); ) {
- if (url[k1] == '<') {
- pos1 = k2;
- k3 = k1+1;
+ bool Router::match(const std::string& route, const URL& requrl, std::map<std::string, TDelim> ¶ms) {
+ size_t rpos = 0;
+ size_t upos = 0;
+ size_t npos = 0;
+ size_t nstart = 0;
+ size_t nend = 0;
+ std::string pname;
+ for(; rpos < route.size() && upos < requrl.path.size(); ) {
+ if (route[rpos] == '<') {
+ nstart = upos;
+ npos = rpos+1;
// start of parameter
- while(k1 < static_cast<int>(url.size()) && url[k1] != '>') k1++;
- pname = std::string(url.begin()+k3, url.begin()+k1);
+ while(rpos < route.size() && route[rpos] != '>') {
+ rpos++;
+ }
+ pname = std::string(route.begin()+static_cast<long>(npos), route.begin()+static_cast<long>(rpos));
// then we also look it on the url
- if (pname[0]=='*') {
+ if (pname[0] == '*') {
pname = pname.substr(1);
// this matches whatever comes after it, basically end of string
- pos2 = req->url.path.size();
- matched = true;
- if (pname != "")
- params[pname] = funcptr::tie(pos1,pos2);
- k1 = url.size();
- k2 = req->url.path.size();
+ nend = requrl.path.size();
+ if (!pname.empty()) {
+ params[pname] = funcptr::tie(nstart,nend);
+ }
+ rpos = route.size();
+ upos = requrl.path.size();
break;
} else {
- // match until url[k1]
- while(k2 < static_cast<int>(req->url.path.size()) && req->url.path[k2] != url[k1+1]) k2++;
- pos2 = k2;
- params[pname] = funcptr::tie(pos1,pos2);
+ // match until url[upos] or next / if pattern is at end
+ while (upos < requrl.path.size()) {
+ if (route[rpos+1] == '\0' && requrl.path[upos] == '/') {
+ break;
+ }
+ if (requrl.path[upos] == route[rpos+1]) {
+ break;
+ }
+ upos++;
+ }
+ nend = upos;
+ params[pname] = funcptr::tie(nstart, nend);
}
- k2--;
+ upos--;
}
- else if (url[k1] != req->url.path[k2]) {
+ else if (route[rpos] != requrl.path[upos]) {
break;
}
- k1++; k2++;
+ rpos++; upos++;
}
+ return route[rpos] == requrl.path[upos];
+ }
- // ensure.
- if (url[k1] != req->url.path[k2])
- matched = false;
- else
- matched = true;
+ RoutingResult Router::route(Request *req, THandlerFunction& handler) {
+ std::map<std::string, TDelim> params;
+ bool matched = false;
+ bool seen = false;
+ std::string rname;
+
+ // iterate routes
+ for (auto& route: routes) {
+ std::string method;
+ std::string url;
+ funcptr::tie(method, url, handler, rname) = route;
- if (matched && method.empty() == false && req->method != method) {
+ // see if we can't match the url
+ params.clear();
+ // simple matcher func
+ matched = match(url, req->url, params);
+
+ if (matched && !method.empty() && req->method != method) {
// method did not match, record it though so we can return correct result
matched = false;
seen = true;
continue;
}
+ if (matched) {
+ break;
+ }
}
if (!matched) {
- if (seen)
+ if (seen) {
return RouteNoMethod;
+ }
// no route
return RouteNotFound;
}
req->parameters.clear();
- for(std::map<std::string, TDelim>::iterator i = params.begin(); i != params.end(); i++) {
- int p1,p2;
- funcptr::tie(p1,p2) = i->second;
- std::string value(req->url.path.begin() + p1, req->url.path.begin() + p2);
+ for (const auto& param: params) {
+ int nstart = 0;
+ int nend = 0;
+ funcptr::tie(nstart, nend) = param.second;
+ std::string value(req->url.path.begin() + nstart, req->url.path.begin() + nend);
value = Utility::decodeURL(value);
- req->parameters[i->first] = std::move(value);
+ req->parameters[param.first] = std::move(value);
}
req->routeName = std::move(rname);
typedef funcptr::function <void(Request* req, Response* resp)> THandlerFunction; //!< Handler function pointer
typedef funcptr::tuple<std::string, std::string, THandlerFunction, std::string> TRoute; //!< Route tuple (method, urlmask, handler, name)
typedef std::vector<TRoute> TRouteList; //!< List of routes in order of evaluation
+ typedef funcptr::tuple<int,int> TDelim;
/*! Implements simple router.
RoutingResult route(Request *req, THandlerFunction& handler); //<! Instance method for performing routing
void printRoutes(std::ostream &os); //<! Instance method for printing routes
std::pair<std::string, std::string> urlFor(const std::string &name, const strstr_map_t& arguments); //<! Instance method for generating paths
+ static bool match(const std::string& route, const URL& requrl, std::map<std::string, TDelim>& params); //<! Instance method for matching a route
/*! Map an URL.
If method is left empty, it will match any method. Name is also optional, but needed if you want to find it for making URLs
static void Delete(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("DELETE", url, handler, name); }; //<! Helper for mapping DELETE
static void Any(const std::string& url, THandlerFunction handler, const std::string& name = "") { router.map("", url, handler, name); }; //<! Helper for mapping any method
+ static bool Match(const std::string& route, const URL& requrl, std::map<std::string, TDelim>& params) { return router.match(route, requrl, params); };
static RoutingResult Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //<! Performs routing based on req->url.path, returns RouteFound if route is found and method matches, RouteNoMethod if route is seen but method did match, and RouteNotFound if not found.
static void PrintRoutes(std::ostream &os) { router.printRoutes(os); }; //<! Prints all known routes to given output stream