1
0
Эх сурвалжийг харах

show http forward/reverse proxy service

ithewei 2 жил өмнө
parent
commit
5171802359

+ 16 - 2
etc/httpd.conf

@@ -29,11 +29,25 @@ document_root = html
 home_page = index.html
 #error_page = error.html
 index_of = /downloads/
+keepalive_timeout = 75000 #ms
 limit_rate = 500 # KB/s
-cors = yes
-forward_proxy = no
+cors = true
 
 # SSL/TLS
 ssl_certificate = cert/server.crt
 ssl_privatekey = cert/server.key
 ssl_ca_certificate = cert/cacert.pem
+
+# proxy
+[proxy]
+proxy_connect_timeout   = 10000 # ms
+proxy_read_timeout      = 60000 # ms
+proxy_write_timeout     = 60000 # ms
+# forward proxy
+forward_proxy = true
+trust_proxies = *httpbin.org;*postman-echo.com;*apifox.com
+#no_proxies = *
+# reverse proxy
+/httpbin/ => http://httpbin.org/
+/postman/ => http://postman-echo.com/
+/apifox/  => https://echo.apifox.com/

+ 36 - 8
examples/http_server_test.cpp

@@ -5,6 +5,8 @@
  */
 
 #include "HttpServer.h"
+#include "hthread.h"    // import hv_gettid
+#include "hasync.h"     // import hv::async
 
 using namespace hv;
 
@@ -34,12 +36,20 @@ int main(int argc, char** argv) {
 
     HttpService router;
 
+    /* Static file service */
     // curl -v http://ip:port/
     router.Static("/", "./html");
 
-    // curl -v http://ip:port/proxy/get
-    router.Proxy("/proxy/", "http://httpbin.org/");
+    /* Forward proxy service */
+    router.EnableForwardProxy();
+    // curl -v http://httpbin.org/get --proxy http://127.0.0.1:8080
+    router.AddTrustProxy("*httpbin.org");
 
+    /* Reverse proxy service */
+    // curl -v http://ip:port/httpbin/get
+    router.Proxy("/httpbin/", "http://httpbin.org/");
+
+    /* API handlers */
     // curl -v http://ip:port/ping
     router.GET("/ping", [](HttpRequest* req, HttpResponse* resp) {
         return resp->String("pong");
@@ -57,12 +67,13 @@ int main(int argc, char** argv) {
     });
 
     // curl -v http://ip:port/get?env=1
-    router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
-        resp->json["origin"] = req->client_addr.ip;
-        resp->json["url"] = req->url;
-        resp->json["args"] = req->query_params;
-        resp->json["headers"] = req->headers;
-        return 200;
+    router.GET("/get", [](const HttpContextPtr& ctx) {
+        hv::Json resp;
+        resp["origin"] = ctx->ip();
+        resp["url"] = ctx->url();
+        resp["args"] = ctx->params();
+        resp["headers"] = ctx->headers();
+        return ctx->send(resp.dump(2));
     });
 
     // curl -v http://ip:port/echo -d "hello,world!"
@@ -77,6 +88,22 @@ int main(int argc, char** argv) {
         return ctx->send(resp.dump(2));
     });
 
+    // curl -v http://ip:port/async
+    router.GET("/async", [](const HttpRequestPtr& req, const HttpResponseWriterPtr& writer) {
+        writer->Begin();
+        writer->WriteHeader("X-Response-tid", hv_gettid());
+        writer->WriteHeader("Content-Type", "text/plain");
+        writer->WriteBody("This is an async response.\n");
+        writer->End();
+    });
+
+    // middleware
+    router.AllowCORS();
+    router.Use([](HttpRequest* req, HttpResponse* resp) {
+        resp->SetHeader("X-Request-tid", hv::to_string(hv_gettid()));
+        return HTTP_STATUS_NEXT;
+    });
+
     HttpServer server;
     server.service = &router;
     server.port = port;
@@ -102,5 +129,6 @@ int main(int argc, char** argv) {
 
     // press Enter to stop
     while (getchar() != '\n');
+    hv::async::cleanup();
     return 0;
 }

+ 51 - 3
examples/httpd/httpd.cpp

@@ -170,6 +170,11 @@ int parse_confile(const char* confile) {
     if (str.size() != 0) {
         g_http_service.index_of = str;
     }
+    // keepalive_timeout
+    str = ini.GetValue("keepalive_timeout");
+    if (str.size() != 0) {
+        g_http_service.keepalive_timeout = atoi(str.c_str());
+    }
     // limit_rate
     str = ini.GetValue("limit_rate");
     if (str.size() != 0) {
@@ -179,9 +184,6 @@ int parse_confile(const char* confile) {
     if (ini.Get<bool>("cors")) {
         g_http_service.AllowCORS();
     }
-    if (ini.Get<bool>("forward_proxy")) {
-        g_http_service.EnableForwardProxy();
-    }
     // ssl
     if (g_http_server.https_port > 0) {
         std::string crt_file = ini.GetValue("ssl_certificate");
@@ -202,6 +204,52 @@ int parse_confile(const char* confile) {
             hlogi("SSL certificate verify ok!");
         }
     }
+    // proxy
+    auto proxy_keys = ini.GetKeys("proxy");
+    for (const auto& proxy_key : proxy_keys) {
+        str = ini.GetValue(proxy_key, "proxy");
+        if (str.empty()) continue;
+        if (proxy_key[0] == '/') {
+            // reverse proxy
+            const std::string& path = proxy_key;
+            std::string proxy_url = hv::ltrim(str, "> ");
+            hlogi("reverse_proxy %s => %s", path.c_str(), proxy_url.c_str());
+            g_http_service.Proxy(path.c_str(), proxy_url.c_str());
+        }
+        else if (strcmp(proxy_key.c_str(), "proxy_connect_timeout") == 0) {
+            g_http_service.proxy_connect_timeout = atoi(str.c_str());
+        }
+        else if (strcmp(proxy_key.c_str(), "proxy_read_timeout") == 0) {
+            g_http_service.proxy_read_timeout = atoi(str.c_str());
+        }
+        else if (strcmp(proxy_key.c_str(), "proxy_write_timeout") == 0) {
+            g_http_service.proxy_write_timeout = atoi(str.c_str());
+        }
+        else if (strcmp(proxy_key.c_str(), "forward_proxy") == 0) {
+            hlogi("forward_proxy = %s", str.c_str());
+            if (hv_getboolean(str.c_str())) {
+                g_http_service.EnableForwardProxy();
+            }
+        }
+        else if (strcmp(proxy_key.c_str(), "trust_proxies") == 0) {
+            auto trust_proxies = hv::split(str, ';');
+            for (auto trust_proxy : trust_proxies) {
+                trust_proxy = hv::trim(trust_proxy);
+                if (trust_proxy.empty()) continue;
+                hlogi("trust_proxy %s", trust_proxy.c_str());
+                g_http_service.AddTrustProxy(trust_proxy.c_str());
+            }
+        }
+        else if (strcmp(proxy_key.c_str(), "no_proxies") == 0) {
+            auto no_proxies = hv::split(str, ';');
+            for (auto no_proxy : no_proxies) {
+                no_proxy = hv::trim(no_proxy);
+                if (no_proxy.empty()) continue;
+                hlogi("no_proxy %s", no_proxy.c_str());
+                g_http_service.AddNoProxy(no_proxy.c_str());
+            }
+        }
+    }
 
     hlogi("parse_confile('%s') OK", confile);
     return 0;

+ 14 - 11
examples/httpd/router.cpp

@@ -1,12 +1,14 @@
 #include "router.h"
 
 #include "handler.h"
-#include "hthread.h"
+#include "hthread.h"    // import hv_gettid
 #include "hasync.h"     // import hv::async
 #include "requests.h"   // import requests::async
 
 void Router::Register(hv::HttpService& router) {
-    // preprocessor => middleware -> handlers => postprocessor
+    /* handler chain */
+    // preprocessor -> middleware -> processor -> postprocessor
+    // processor: pathHandlers -> staticHandler -> errorHandler
     router.preprocessor = Handler::preprocessor;
     router.postprocessor = Handler::postprocessor;
     // router.errorHandler = Handler::errorHandler;
@@ -36,15 +38,6 @@ void Router::Register(hv::HttpService& router) {
         return resp->Json(router.Paths());
     });
 
-    // curl -v http://ip:port/get?env=1
-    router.GET("/get", [](HttpRequest* req, HttpResponse* resp) {
-        resp->json["origin"] = req->client_addr.ip;
-        resp->json["url"] = req->url;
-        resp->json["args"] = req->query_params;
-        resp->json["headers"] = req->headers;
-        return 200;
-    });
-
     // curl -v http://ip:port/service
     router.GET("/service", [](const HttpContextPtr& ctx) {
         ctx->setContentType("application/json");
@@ -56,6 +49,16 @@ void Router::Register(hv::HttpService& router) {
         return 200;
     });
 
+    // curl -v http://ip:port/get?env=1
+    router.GET("/get", [](const HttpContextPtr& ctx) {
+        hv::Json resp;
+        resp["origin"] = ctx->ip();
+        resp["url"] = ctx->url();
+        resp["args"] = ctx->params();
+        resp["headers"] = ctx->headers();
+        return ctx->send(resp.dump(2));
+    });
+
     // curl -v http://ip:port/echo -d "hello,world!"
     router.POST("/echo", [](const HttpContextPtr& ctx) {
         return ctx->send(ctx->body(), ctx->type());

+ 1 - 1
http/server/HttpHandler.cpp

@@ -1014,7 +1014,7 @@ int HttpHandler::connectProxy(const std::string& strUrl) {
         }
     }
 
-    if (!service || !service->IsTrustProxy(url.host.c_str())) {
+    if (forward_proxy && !service->IsTrustProxy(url.host.c_str())) {
         hlogw("Forbidden to proxy %s", url.host.c_str());
         SetError(HTTP_STATUS_FORBIDDEN, HTTP_STATUS_FORBIDDEN);
         return 0;

+ 9 - 6
http/server/HttpService.h

@@ -107,6 +107,7 @@ typedef std::unordered_map<std::string, std::shared_ptr<http_method_handlers>>
 namespace hv {
 
 struct HV_EXPORT HttpService {
+    /* handler chain */
     // preprocessor -> middleware -> processor -> postprocessor
     http_handler        preprocessor;
     http_handlers       middleware;
@@ -114,11 +115,11 @@ struct HV_EXPORT HttpService {
     http_handler        processor;
     http_handler        postprocessor;
 
-    // api service (that is http.ApiServer)
+    /* API handlers */
     std::string         base_url;
     http_path_handlers  pathHandlers;
 
-    // file service (that is http.FileServer)
+    /* Static file service */
     http_handler    staticHandler;
     http_handler    largeFileHandler;
     std::string     document_root;
@@ -126,13 +127,15 @@ struct HV_EXPORT HttpService {
     std::string     error_page;
     // nginx: location => root
     std::map<std::string, std::string, std::greater<std::string>> staticDirs;
-    // indexof service (that is http.DirectoryServer)
+    /* Indexof directory service */
     std::string     index_of;
     http_handler    errorHandler;
 
-    // proxy service (that is http.ProxyServer)
+    /* Proxy service */
+    /* Reverse proxy service */
     // nginx: location => proxy_pass
     std::map<std::string, std::string, std::greater<std::string>> proxies;
+    /* Forward proxy service */
     StringList  trustProxies;
     StringList  noProxies;
     int proxy_connect_timeout;
@@ -192,11 +195,11 @@ struct HV_EXPORT HttpService {
     void AllowCORS();
 
     // proxy
+    // forward proxy
+    void EnableForwardProxy() { enable_forward_proxy = 1; }
     void AddTrustProxy(const char* host);
     void AddNoProxy(const char* host);
     bool IsTrustProxy(const char* host);
-    // forward proxy
-    void EnableForwardProxy() { enable_forward_proxy = 1; }
     // reverse proxy
     // Proxy("/api/v1/", "http://www.httpbin.org/");
     void Proxy(const char* path, const char* url);