routes.push_back(funcptr::make_tuple(method2, url, handler, name));
};
- bool Router::route(Request *req, THandlerFunction& handler) {
+ 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
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
+ matched = false;
+
// see if we can't match the url
params.clear();
// simple matcher func
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();
matched = false;
else
matched = true;
+
+ if (matched && method.empty() == false && req->method != method) {
+ // method did not match, record it though so we can return correct result
+ matched = false;
+ seen = true;
+ continue;
+ }
+ }
+
+ 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;
req->routeName = std::move(rname);
- return true;
+ return RouteFound;
};
void Router::printRoutes(std::ostream &os) {
#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
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 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 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
handler(static_cast<HttpRequest*>(req), static_cast<HttpResponse*>(resp));
}
-void WebServer::registerBareHandler(const string& url, const HandlerFunction& handler)
+void WebServer::registerBareHandler(const string& url, const HandlerFunction& handler, const std::string& method)
{
YaHTTP::THandlerFunction f = [=](YaHTTP::Request* req, YaHTTP::Response* resp){return bareHandlerWrapper(handler, req, resp);};
- YaHTTP::Router::Any(url, std::move(f));
+ YaHTTP::Router::Map(method, url, std::move(f));
}
static bool optionsHandler(HttpRequest* req, HttpResponse* resp) {
}
}
-void WebServer::registerApiHandler(const string& url, const HandlerFunction& handler, bool allowPassword) {
+void WebServer::registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method, bool allowPassword) {
auto f = [=](HttpRequest *req, HttpResponse* resp){apiWrapper(handler, req, resp, allowPassword);};
- registerBareHandler(url, f);
+ registerBareHandler(url, f, method);
}
void WebServer::webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp) {
handler(req, resp);
}
-void WebServer::registerWebHandler(const string& url, const HandlerFunction& handler) {
+void WebServer::registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method) {
auto f = [=](HttpRequest *req, HttpResponse *resp){webWrapper(handler, req, resp);};
- registerBareHandler(url, f);
+ registerBareHandler(url, f, method);
}
static void *WebServerConnectionThreadStart(const WebServer* webServer, std::shared_ptr<Socket> client) {
}
YaHTTP::THandlerFunction handler;
- if (!YaHTTP::Router::Route(&req, handler)) {
+ YaHTTP::RoutingResult res = YaHTTP::Router::Route(&req, handler);
+
+ if (res == YaHTTP::RouteNotFound) {
SLOG(g_log<<Logger::Debug<<req.logprefix<<"No route found for \"" << req.url.path << "\"" << endl,
log->info(Logr::Debug, "No route found"));
throw HttpNotFoundException();
}
+ if (res == YaHTTP::RouteNoMethod) {
+ throw HttpMethodNotAllowedException();
+ }
const string msg = "HTTP ISE Exception";
try {
void handleRequest(HttpRequest& request, HttpResponse& resp) const;
typedef std::function<void(HttpRequest* req, HttpResponse* resp)> HandlerFunction;
- void registerApiHandler(const string& url, const HandlerFunction& handler, bool allowPassword=false);
- void registerWebHandler(const string& url, const HandlerFunction& handler);
+ void registerApiHandler(const string& url, const HandlerFunction& handler, const std::string& method = "", bool allowPassword=false);
+ void registerWebHandler(const string& url, const HandlerFunction& handler, const std::string& method = "");
enum class LogLevel : uint8_t {
None = 0, // No logs from requests at all
#endif
protected:
- void registerBareHandler(const string& url, const HandlerFunction& handler);
+ static void registerBareHandler(const string& url, const HandlerFunction& handler, const std::string& method);
void logRequest(const HttpRequest& req, const ComboAddress& remote) const;
void logResponse(const HttpResponse& resp, const ComboAddress& remote, const string& logprefix) const;