Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7e0aabc
ws-auth: Extract zone from ID to macro
cmouse Aug 18, 2023
1929764
ws-auth.cc: Split apiZoneMetadata to GET and POST variant
cmouse Aug 18, 2023
a406b33
ws-auth.cc: Split apiZoneMetadataKind to GET, PUT and DELETE variants
cmouse Aug 18, 2023
2775cad
ws-auth.cc: Split apiServerTSIGKeys to GET and POST variant
cmouse Aug 18, 2023
ca740e4
ws-auth.cc: Split apiServerTSIGKeyDetail to GET, PUT and DELETE variant
cmouse Aug 18, 2023
241dc03
ws-auth.cc: Split apiServerAutoprimaries to GET and POST variants
cmouse Aug 18, 2023
61fb497
ws-auth: Add apiServerAutoprimaryDetailDELETE
cmouse Dec 7, 2023
fb9be46
ws-auth.cc: Split apiServerZones to GET and POST variants
cmouse Aug 18, 2023
21b4feb
ws-auth: Add NOLINTs to apiServerZonesPOST()
cmouse Dec 7, 2023
b62707f
ws-auth.cc: Split apiServerZoneDetail to GET, PATCH, PUT and DELETE v…
cmouse Aug 18, 2023
20f8dbd
ws-auth.cc: Prepare apiZoneCryptokeys for method routing
cmouse Aug 18, 2023
478e169
webserver: Allow specifying supported method
cmouse Aug 18, 2023
a0281be
ext/yahttp: Move route matching to separate function
cmouse Aug 23, 2023
a9c69ea
ws-auth.cc: Move method checking to router
cmouse Aug 18, 2023
464b264
ws-recursor.cc: Add methods to routes
cmouse Aug 23, 2023
bfb7a70
ws-recursor.cc: Split apiServerConfigACL to GET and PUT variant
cmouse Aug 23, 2023
e15a061
ws-recursor.cc: Split apiServerZones to GET and POST variant
cmouse Aug 23, 2023
3bf2df5
ws-recursor.cc: Split apiServerZoneDetail to GET, PUT, DELETE variants
cmouse Aug 23, 2023
0ac79b6
ws-recursor.cc: Remove redundant checks for method
cmouse Aug 23, 2023
72c988e
ws-api.cc: Remove redundant checks for method
cmouse Aug 23, 2023
fe92132
webserver.cc: Add resource aware OPTIONS handler
cmouse Aug 23, 2023
e9c75b7
regression-tests.api/test_Basics: Update to match new dynamic CORS ha…
cmouse Aug 23, 2023
70c1c68
ws-api: Constify some variables
cmouse Oct 22, 2023
bfaeecb
Remove unused req for prometheusMetrics()
cmouse Dec 12, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/http-api/swagger/authoritative-api-swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ paths:
required: true
description: The kind of metadata
responses:
'200':
'204':
description: OK
<<: *commonErrors

Expand Down
132 changes: 81 additions & 51 deletions ext/yahttp/yahttp/router.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
#include "router.hpp"

namespace YaHTTP {
typedef funcptr::tuple<int,int> TDelim;

// router is defined here.
YaHTTP::Router Router::router;

Expand All @@ -24,76 +22,108 @@ namespace YaHTTP {
routes.push_back(funcptr::make_tuple(method2, url, handler, name));
};

bool Router::route(Request *req, THandlerFunction& handler) {
std::map<std::string, TDelim> params;
int pos1,pos2;
bool matched = 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;

if (method.empty() == false && req->method != method) continue; // no match on method
// 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> &params) {
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();
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];
}

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;

// 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;
}
}

// ensure.
if (url[k1] != req->url.path[k2])
matched = false;
else
matched = true;
if (!matched) {
if (seen) {
return RouteNoMethod;
}
// no route
return RouteNotFound;
}

if (!matched) { return false; } // no route
req->parameters.clear();
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);

return true;
return RouteFound;
};

void Router::printRoutes(std::ostream &os) {
Expand Down
13 changes: 11 additions & 2 deletions ext/yahttp/yahttp/router.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,16 @@ namespace funcptr = boost;
#include <utility>

namespace YaHTTP {
enum RoutingResult {
RouteFound = 1,
RouteNotFound = 0,
RouteNoMethod = -1,
};

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.

Expand All @@ -44,9 +51,10 @@ is consumed but not stored. Note that only path is matched, scheme, host and url
static Router router; //<! Singleton instance of Router
public:
void map(const std::string& method, const std::string& url, THandlerFunction handler, const std::string& name); //<! Instance method for mapping urls
bool route(Request *req, THandlerFunction& handler); //<! Instance method for performing routing
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
Expand All @@ -59,7 +67,8 @@ If method is left empty, it will match any method. Name is also optional, but ne
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 Route(Request *req, THandlerFunction& handler) { return router.route(req, handler); }; //<! Performs routing based on req->url.path
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

static std::pair<std::string, std::string> URLFor(const std::string &name, const strstr_map_t& arguments) { return router.urlFor(name,arguments); }; //<! Generates url from named route and arguments. Missing arguments are assumed empty
Expand Down
Loading