Sfoglia il codice sorgente

Add HttpService::Use(middleware)

ithewei 2 anni fa
parent
commit
3ba6097091

+ 18 - 17
examples/httpd/handler.cpp

@@ -32,23 +32,7 @@ int Handler::preprocessor(HttpRequest* req, HttpResponse* resp) {
     // Unified setting response Content-Type?
     resp->content_type = APPLICATION_JSON;
 
-#if 0
-    // authentication sample code
-    if (strcmp(req->path.c_str(), "/login") != 0) {
-        string token = req->GetHeader("token");
-        if (token.empty()) {
-            response_status(resp, 10011, "Miss token");
-            return HTTP_STATUS_UNAUTHORIZED;
-        }
-        else if (strcmp(token.c_str(), "abcdefg") != 0) {
-            response_status(resp, 10012, "Token wrong");
-            return HTTP_STATUS_UNAUTHORIZED;
-        }
-        return HTTP_STATUS_UNFINISHED;
-    }
-#endif
-
-    return HTTP_STATUS_UNFINISHED;
+    return HTTP_STATUS_NEXT;
 }
 
 int Handler::postprocessor(HttpRequest* req, HttpResponse* resp) {
@@ -61,6 +45,23 @@ int Handler::errorHandler(const HttpContextPtr& ctx) {
     return response_status(ctx, error_code);
 }
 
+int Handler::Authorization(HttpRequest* req, HttpResponse* resp) {
+    // authentication sample code
+    if (strcmp(req->path.c_str(), "/login") == 0) {
+        return HTTP_STATUS_NEXT;
+    }
+    std::string token = req->GetHeader("Authorization");
+    if (token.empty()) {
+        response_status(resp, 10011, "Miss Authorization header!");
+        return HTTP_STATUS_UNAUTHORIZED;
+    }
+    else if (strcmp(token.c_str(), "abcdefg") != 0) {
+        response_status(resp, 10012, "Authorization failed!");
+        return HTTP_STATUS_UNAUTHORIZED;
+    }
+    return HTTP_STATUS_NEXT;
+}
+
 int Handler::sleep(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
     writer->WriteHeader("X-Response-tid", hv_gettid());
     unsigned long long start_ms = gettimeofday_ms();

+ 4 - 1
examples/httpd/handler.h

@@ -5,11 +5,14 @@
 
 class Handler {
 public:
-    // preprocessor => api_handlers => postprocessor
+    // preprocessor => middleware -> handlers => postprocessor
     static int preprocessor(HttpRequest* req, HttpResponse* resp);
     static int postprocessor(HttpRequest* req, HttpResponse* resp);
     static int errorHandler(const HttpContextPtr& ctx);
 
+    // middleware
+    static int Authorization(HttpRequest* req, HttpResponse* resp);
+
     static int sleep(const HttpRequestPtr& req, const HttpResponseWriterPtr& writer);
     static int setTimeout(const HttpContextPtr& ctx);
     static int query(const HttpContextPtr& ctx);

+ 4 - 1
examples/httpd/router.cpp

@@ -6,12 +6,15 @@
 #include "requests.h"   // import requests::async
 
 void Router::Register(hv::HttpService& router) {
-    // preprocessor => Handler => postprocessor
+    // preprocessor => middleware -> handlers => postprocessor
     router.preprocessor = Handler::preprocessor;
     router.postprocessor = Handler::postprocessor;
     // router.errorHandler = Handler::errorHandler;
     // router.largeFileHandler = Handler::sendLargeFile;
 
+    // middleware
+    // router.Use(Handler::Authorization);
+
     // curl -v http://ip:port/ping
     router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
         return resp->String("pong");

+ 13 - 14
http/server/HttpHandler.cpp

@@ -177,7 +177,7 @@ int HttpHandler::invokeHttpHandler(const http_handler* handler) {
     } else if (handler->async_handler) {
         // NOTE: async_handler run on hv::async threadpool
         hv::async(std::bind(handler->async_handler, req, writer));
-        status_code = HTTP_STATUS_UNFINISHED;
+        status_code = HTTP_STATUS_NEXT;
     } else if (handler->ctx_handler) {
         // NOTE: ctx_handler run on IO thread, you can easily post HttpContextPtr to your consumer thread for processing.
         status_code = handler->ctx_handler(getHttpContext());
@@ -219,8 +219,8 @@ void HttpHandler::onHeadersComplete() {
     // printf("url=%s\n", pReq->url.c_str());
     pReq->ParseUrl();
 
-    if (service->api_handlers.size() != 0) {
-        service->GetApi(pReq, &api_handler);
+    if (service->pathHandlers.size() != 0) {
+        service->GetRoute(pReq, &api_handler);
     }
     if (api_handler && api_handler->state_handler) {
         writer->onclose = [this](){
@@ -332,18 +332,17 @@ int HttpHandler::HandleHttpRequest() {
 
 preprocessor:
     state = HANDLE_BEGIN;
-    if (service->allow_cors) {
-        resp->headers["Access-Control-Allow-Origin"] = req->GetHeader("Origin", "*");
-        if (req->method == HTTP_OPTIONS) {
-            resp->headers["Access-Control-Allow-Methods"] = req->GetHeader("Access-Control-Request-Method", "OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH");
-            resp->headers["Access-Control-Allow-Headers"] = req->GetHeader("Access-Control-Request-Headers", "Content-Type");
-            status_code = HTTP_STATUS_NO_CONTENT;
+    if (service->preprocessor) {
+        status_code = customHttpHandler(service->preprocessor);
+        if (status_code != HTTP_STATUS_NEXT) {
             goto postprocessor;
         }
     }
-    if (service->preprocessor) {
-        status_code = customHttpHandler(service->preprocessor);
-        if (status_code != 0) {
+
+middleware:
+    for (const auto& middleware : service->middleware) {
+        status_code = customHttpHandler(middleware);
+        if (status_code != HTTP_STATUS_NEXT) {
             goto postprocessor;
         }
     }
@@ -378,9 +377,9 @@ postprocessor:
     }
 
     if (writer && writer->state != hv::HttpResponseWriter::SEND_BEGIN) {
-        status_code = 0;
+        status_code = HTTP_STATUS_NEXT;
     }
-    if (status_code == 0) {
+    if (status_code == HTTP_STATUS_NEXT) {
         state = HANDLE_CONTINUE;
     } else {
         state = HANDLE_END;

+ 16 - 0
http/server/HttpMiddleware.cpp

@@ -0,0 +1,16 @@
+#include "HttpMiddleware.h"
+#include "HttpService.h"
+
+BEGIN_NAMESPACE_HV
+
+int HttpMiddleware::CORS(HttpRequest* req, HttpResponse* resp) {
+    resp->headers["Access-Control-Allow-Origin"] = req->GetHeader("Origin", "*");
+    if (req->method == HTTP_OPTIONS) {
+        resp->headers["Access-Control-Allow-Methods"] = req->GetHeader("Access-Control-Request-Method", "OPTIONS, HEAD, GET, POST, PUT, DELETE, PATCH");
+        resp->headers["Access-Control-Allow-Headers"] = req->GetHeader("Access-Control-Request-Headers", "Content-Type");
+        return HTTP_STATUS_NO_CONTENT;
+    }
+    return HTTP_STATUS_NEXT;
+}
+
+END_NAMESPACE_HV

+ 16 - 0
http/server/HttpMiddleware.h

@@ -0,0 +1,16 @@
+#ifndef HV_HTTP_MIDDLEWARE_H_
+#define HV_HTTP_MIDDLEWARE_H_
+
+#include "hexport.h"
+#include "HttpContext.h"
+
+BEGIN_NAMESPACE_HV
+
+class HttpMiddleware {
+public:
+    static int CORS(HttpRequest* req, HttpResponse* resp);
+};
+
+END_NAMESPACE_HV
+
+#endif // HV_HTTP_MIDDLEWARE_H_

+ 14 - 9
http/server/HttpService.cpp

@@ -1,16 +1,17 @@
 #include "HttpService.h"
+#include "HttpMiddleware.h"
 
 #include "hbase.h" // import hv_strendswith
 
 namespace hv {
 
-void HttpService::AddApi(const char* path, http_method method, const http_handler& handler) {
+void HttpService::AddRoute(const char* path, http_method method, const http_handler& handler) {
     std::shared_ptr<http_method_handlers> method_handlers = NULL;
-    auto iter = api_handlers.find(path);
-    if (iter == api_handlers.end()) {
+    auto iter = pathHandlers.find(path);
+    if (iter == pathHandlers.end()) {
         // add path
         method_handlers = std::make_shared<http_method_handlers>();
-        api_handlers[path] = method_handlers;
+        pathHandlers[path] = method_handlers;
     }
     else {
         method_handlers = iter->second;
@@ -26,7 +27,7 @@ void HttpService::AddApi(const char* path, http_method method, const http_handle
     method_handlers->push_back(http_method_handler(method, handler));
 }
 
-int HttpService::GetApi(const char* url, http_method method, http_handler** handler) {
+int HttpService::GetRoute(const char* url, http_method method, http_handler** handler) {
     // {base_url}/path?query
     const char* s = url;
     const char* b = base_url.c_str();
@@ -38,8 +39,8 @@ int HttpService::GetApi(const char* url, http_method method, http_handler** hand
     while (*e && *e != '?') ++e;
 
     std::string path = std::string(s, e);
-    auto iter = api_handlers.find(path);
-    if (iter == api_handlers.end()) {
+    auto iter = pathHandlers.find(path);
+    if (iter == pathHandlers.end()) {
         if (handler) *handler = NULL;
         return HTTP_STATUS_NOT_FOUND;
     }
@@ -54,7 +55,7 @@ int HttpService::GetApi(const char* url, http_method method, http_handler** hand
     return HTTP_STATUS_METHOD_NOT_ALLOWED;
 }
 
-int HttpService::GetApi(HttpRequest* req, http_handler** handler) {
+int HttpService::GetRoute(HttpRequest* req, http_handler** handler) {
     // {base_url}/path?query
     const char* s = req->path.c_str();
     const char* b = base_url.c_str();
@@ -68,7 +69,7 @@ int HttpService::GetApi(HttpRequest* req, http_handler** handler) {
     std::string path = std::string(s, e);
     const char *kp, *ks, *vp, *vs;
     bool match;
-    for (auto iter = api_handlers.begin(); iter != api_handlers.end(); ++iter) {
+    for (auto iter = pathHandlers.begin(); iter != pathHandlers.end(); ++iter) {
         kp = iter->first.c_str();
         vp = path.c_str();
         match = false;
@@ -170,4 +171,8 @@ std::string HttpService::GetProxyUrl(const char* path) {
     return url;
 }
 
+void HttpService::AllowCORS() {
+    Use(HttpMiddleware::CORS);
+}
+
 }

+ 41 - 110
http/server/HttpService.h

@@ -4,6 +4,7 @@
 #include <string>
 #include <map>
 #include <unordered_map>
+#include <vector>
 #include <list>
 #include <memory>
 #include <functional>
@@ -28,9 +29,10 @@
 /*
  * @param[in]  req:  parsed structured http request
  * @param[out] resp: structured http response
- * @return  0:                  handle unfinished
+ * @return  0:                  handle next
  *          http_status_code:   handle done
  */
+#define HTTP_STATUS_NEXT        0
 #define HTTP_STATUS_UNFINISHED  0
 // NOTE: http_sync_handler run on IO thread
 typedef std::function<int(HttpRequest* req, HttpResponse* resp)>                            http_sync_handler;
@@ -87,6 +89,8 @@ struct http_handler {
     }
 };
 
+typedef std::vector<http_handler>   http_handlers;
+
 struct http_method_handler {
     http_method         method;
     http_handler        handler;
@@ -97,21 +101,22 @@ struct http_method_handler {
 
 // method => http_method_handler
 typedef std::list<http_method_handler>                                          http_method_handlers;
-// path => http_method_handlers
-typedef std::unordered_map<std::string, std::shared_ptr<http_method_handlers>>  http_api_handlers;
+// path   => http_method_handlers
+typedef std::unordered_map<std::string, std::shared_ptr<http_method_handlers>>  http_path_handlers;
 
 namespace hv {
 
 struct HV_EXPORT HttpService {
-    // preprocessor -> processor -> postprocessor
+    // preprocessor -> middleware -> processor -> postprocessor
     http_handler        preprocessor;
-    // processor: api_handlers -> staticHandler -> errorHandler
+    http_handlers       middleware;
+    // processor: pathHandlers -> staticHandler -> errorHandler
     http_handler        processor;
     http_handler        postprocessor;
 
     // api service (that is http.ApiServer)
     std::string         base_url;
-    http_api_handlers   api_handlers;
+    http_path_handlers  pathHandlers;
 
     // file service (that is http.FileServer)
     http_handler    staticHandler;
@@ -145,7 +150,6 @@ struct HV_EXPORT HttpService {
      */
     int limit_rate; // limit send rate, unit: KB/s
 
-    unsigned allow_cors             :1;
     unsigned enable_forward_proxy   :1;
 
     HttpService() {
@@ -166,23 +170,22 @@ struct HV_EXPORT HttpService {
         file_cache_expired_time = DEFAULT_FILE_CACHE_EXPIRED_TIME;
         limit_rate = -1; // unlimited
 
-        allow_cors = 0;
         enable_forward_proxy = 0;
     }
 
-    void AddApi(const char* path, http_method method, const http_handler& handler);
+    void AddRoute(const char* path, http_method method, const http_handler& handler);
     // @retval 0 OK, else HTTP_STATUS_NOT_FOUND, HTTP_STATUS_METHOD_NOT_ALLOWED
-    int  GetApi(const char* url,  http_method method, http_handler** handler);
+    int  GetRoute(const char* url,  http_method method, http_handler** handler);
     // RESTful API /:field/ => req->query_params["field"]
-    int  GetApi(HttpRequest* req, http_handler** handler);
+    int  GetRoute(HttpRequest* req, http_handler** handler);
 
     // Static("/", "/var/www/html")
     void Static(const char* path, const char* dir);
     // @retval / => /var/www/html/index.html
     std::string GetStaticFilepath(const char* path);
 
-    // CORS
-    void AllowCORS() { allow_cors = 1; }
+    // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
+    void AllowCORS();
 
     // forward proxy
     void EnableForwardProxy() { enable_forward_proxy = 1; }
@@ -194,137 +197,65 @@ struct HV_EXPORT HttpService {
 
     hv::StringList Paths() {
         hv::StringList paths;
-        for (auto& pair : api_handlers) {
+        for (auto& pair : pathHandlers) {
             paths.emplace_back(pair.first);
         }
         return paths;
     }
 
-    // github.com/gin-gonic/gin
-    void Handle(const char* httpMethod, const char* relativePath, http_sync_handler handlerFunc) {
-        AddApi(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
-    }
-    void Handle(const char* httpMethod, const char* relativePath, http_async_handler handlerFunc) {
-        AddApi(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
+    // Handler = [ http_sync_handler, http_ctx_handler ]
+    template<typename Handler>
+    void Use(Handler handlerFunc) {
+        middleware.emplace_back(handlerFunc);
     }
-    void Handle(const char* httpMethod, const char* relativePath, http_ctx_handler handlerFunc) {
-        AddApi(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
-    }
-    void Handle(const char* httpMethod, const char* relativePath, http_state_handler handlerFunc) {
-        AddApi(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
+
+    // Inspired by github.com/gin-gonic/gin
+    // Handler = [ http_sync_handler, http_async_handler, http_ctx_handler, http_state_handler ]
+    template<typename Handler>
+    void Handle(const char* httpMethod, const char* relativePath, Handler handlerFunc) {
+        AddRoute(relativePath, http_method_enum(httpMethod), http_handler(handlerFunc));
     }
 
     // HEAD
-    void HEAD(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("HEAD", relativePath, handlerFunc);
-    }
-    void HEAD(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("HEAD", relativePath, handlerFunc);
-    }
-    void HEAD(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("HEAD", relativePath, handlerFunc);
-    }
-    void HEAD(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void HEAD(const char* relativePath, Handler handlerFunc) {
         Handle("HEAD", relativePath, handlerFunc);
     }
 
     // GET
-    void GET(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("GET", relativePath, handlerFunc);
-    }
-    void GET(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("GET", relativePath, handlerFunc);
-    }
-    void GET(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("GET", relativePath, handlerFunc);
-    }
-    void GET(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void GET(const char* relativePath, Handler handlerFunc) {
         Handle("GET", relativePath, handlerFunc);
     }
 
     // POST
-    void POST(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("POST", relativePath, handlerFunc);
-    }
-    void POST(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("POST", relativePath, handlerFunc);
-    }
-    void POST(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("POST", relativePath, handlerFunc);
-    }
-    void POST(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void POST(const char* relativePath, Handler handlerFunc) {
         Handle("POST", relativePath, handlerFunc);
     }
 
     // PUT
-    void PUT(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("PUT", relativePath, handlerFunc);
-    }
-    void PUT(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("PUT", relativePath, handlerFunc);
-    }
-    void PUT(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("PUT", relativePath, handlerFunc);
-    }
-    void PUT(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void PUT(const char* relativePath, Handler handlerFunc) {
         Handle("PUT", relativePath, handlerFunc);
     }
 
     // DELETE
     // NOTE: Windows <winnt.h> #define DELETE as a macro, we have to replace DELETE with Delete.
-    void Delete(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("DELETE", relativePath, handlerFunc);
-    }
-    void Delete(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("DELETE", relativePath, handlerFunc);
-    }
-    void Delete(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("DELETE", relativePath, handlerFunc);
-    }
-    void Delete(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void Delete(const char* relativePath, Handler handlerFunc) {
         Handle("DELETE", relativePath, handlerFunc);
     }
 
     // PATCH
-    void PATCH(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("PATCH", relativePath, handlerFunc);
-    }
-    void PATCH(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("PATCH", relativePath, handlerFunc);
-    }
-    void PATCH(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("PATCH", relativePath, handlerFunc);
-    }
-    void PATCH(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void PATCH(const char* relativePath, Handler handlerFunc) {
         Handle("PATCH", relativePath, handlerFunc);
     }
 
     // Any
-    void Any(const char* relativePath, http_sync_handler handlerFunc) {
-        Handle("HEAD", relativePath, handlerFunc);
-        Handle("GET", relativePath, handlerFunc);
-        Handle("POST", relativePath, handlerFunc);
-        Handle("PUT", relativePath, handlerFunc);
-        Handle("DELETE", relativePath, handlerFunc);
-        Handle("PATCH", relativePath, handlerFunc);
-    }
-    void Any(const char* relativePath, http_async_handler handlerFunc) {
-        Handle("HEAD", relativePath, handlerFunc);
-        Handle("GET", relativePath, handlerFunc);
-        Handle("POST", relativePath, handlerFunc);
-        Handle("PUT", relativePath, handlerFunc);
-        Handle("DELETE", relativePath, handlerFunc);
-        Handle("PATCH", relativePath, handlerFunc);
-    }
-    void Any(const char* relativePath, http_ctx_handler handlerFunc) {
-        Handle("HEAD", relativePath, handlerFunc);
-        Handle("GET", relativePath, handlerFunc);
-        Handle("POST", relativePath, handlerFunc);
-        Handle("PUT", relativePath, handlerFunc);
-        Handle("DELETE", relativePath, handlerFunc);
-        Handle("PATCH", relativePath, handlerFunc);
-    }
-    void Any(const char* relativePath, http_state_handler handlerFunc) {
+    template<typename Handler>
+    void Any(const char* relativePath, Handler handlerFunc) {
         Handle("HEAD", relativePath, handlerFunc);
         Handle("GET", relativePath, handlerFunc);
         Handle("POST", relativePath, handlerFunc);