Browse Source

http_client_set_http_proxy

ithewei 4 years ago
parent
commit
0a0d6ba14c
5 changed files with 226 additions and 41 deletions
  1. 52 1
      examples/curl.cpp
  2. 37 15
      http/HttpMessage.cpp
  3. 7 2
      http/HttpMessage.h
  4. 60 23
      http/client/http_client.cpp
  5. 70 0
      http/client/http_client.h

+ 52 - 1
examples/curl.cpp

@@ -26,6 +26,11 @@ static const char* data     = NULL;
 static const char* form     = NULL;
 static const char* form     = NULL;
 static int send_count       = 1;
 static int send_count       = 1;
 
 
+static int lopt = 0;
+static const char* http_proxy   = NULL;
+static const char* https_proxy  = NULL;
+static const char* no_proxy     = NULL;
+
 static const char* options = "hVvX:H:r:d:F:n:";
 static const char* options = "hVvX:H:r:d:F:n:";
 static const struct option long_options[] = {
 static const struct option long_options[] = {
     {"help",    no_argument,        NULL,   'h'},
     {"help",    no_argument,        NULL,   'h'},
@@ -36,9 +41,14 @@ static const struct option long_options[] = {
     {"range",   required_argument,  NULL,   'r'},
     {"range",   required_argument,  NULL,   'r'},
     {"data",    required_argument,  NULL,   'd'},
     {"data",    required_argument,  NULL,   'd'},
     {"form",    required_argument,  NULL,   'F'},
     {"form",    required_argument,  NULL,   'F'},
+    {"count",   required_argument,  NULL,   'n'},
     {"http2",   no_argument,        &http_version, 2},
     {"http2",   no_argument,        &http_version, 2},
     {"grpc",    no_argument,        &grpc,  1},
     {"grpc",    no_argument,        &grpc,  1},
-    {"count",   required_argument,  NULL,   'n'},
+    \
+    {"http-proxy",  required_argument,  &lopt,  1},
+    {"https-proxy", required_argument,  &lopt,  2},
+    {"no-proxy",    required_argument,  &lopt,  3},
+    \
     {NULL,      0,                  NULL,   0}
     {NULL,      0,                  NULL,   0}
 };
 };
 static const char* help = R"(Options:
 static const char* help = R"(Options:
@@ -53,6 +63,10 @@ static const char* help = R"(Options:
     -n|--count          Send request count, used for test keep-alive
     -n|--count          Send request count, used for test keep-alive
        --http2          Use http2
        --http2          Use http2
        --grpc           Use grpc over http2
        --grpc           Use grpc over http2
+       --http-proxy     Set http proxy
+       --https-proxy    Set https proxy
+       --no-proxy       Set no proxy
+
 Examples:
 Examples:
     curl -v GET  httpbin.org/get
     curl -v GET  httpbin.org/get
     curl -v POST httpbin.org/post   user=admin pswd=123456
     curl -v POST httpbin.org/post   user=admin pswd=123456
@@ -99,6 +113,15 @@ int parse_cmdline(int argc, char* argv[]) {
         case 'd': data = optarg;    break;
         case 'd': data = optarg;    break;
         case 'F': form = optarg;    break;
         case 'F': form = optarg;    break;
         case 'n': send_count = atoi(optarg); break;
         case 'n': send_count = atoi(optarg); break;
+        case  0 :
+        {
+            switch (lopt) {
+            case  1: http_proxy = optarg;   break;
+            case  2: https_proxy = optarg;  break;
+            case  3: no_proxy = optarg;     break;
+            default: break;
+            }
+        }
         default: break;
         default: break;
         }
         }
     }
     }
@@ -283,7 +306,35 @@ int main(int argc, char* argv[]) {
     res.chunked_cb = [](const char* data, size_t size){
     res.chunked_cb = [](const char* data, size_t size){
         printf("%.*s", (int)size, data);
         printf("%.*s", (int)size, data);
     };
     };
+
     http_client_t* cli = http_client_new();
     http_client_t* cli = http_client_new();
+    // http_proxy
+    if (http_proxy) {
+        hv::StringList ss = hv::split(http_proxy, ':');
+        const char* host = ss[0].c_str();
+        int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTP_PORT;
+        printf("* http_proxy=%s:%d\n", host, port);
+        http_client_set_http_proxy(cli, host, port);
+    }
+    // https_proxy
+    if (https_proxy) {
+        hv::StringList ss = hv::split(https_proxy, ':');
+        const char* host = ss[0].c_str();
+        int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTPS_PORT;
+        printf("* https_proxy=%s:%d\n", host, port);
+        http_client_set_https_proxy(cli, host, port);
+    }
+    // no_proxy
+    if (no_proxy) {
+        hv::StringList ss = hv::split(no_proxy, ',');
+        printf("* no_proxy=");
+        for (const auto& s : ss) {
+            printf("%s,", s.c_str());
+            http_client_add_no_proxy(cli, s.c_str());
+        }
+        printf("\n");
+    }
+
 send:
 send:
     if (verbose) {
     if (verbose) {
         printf("%s\n", req.Dump(true,true).c_str());
         printf("%s\n", req.Dump(true,true).c_str());

+ 37 - 15
http/HttpMessage.cpp

@@ -510,13 +510,20 @@ void HttpRequest::ParseUrl() {
     http_parser_url_init(&parser);
     http_parser_url_init(&parser);
     http_parser_parse_url(url.c_str(), url.size(), 0, &parser);
     http_parser_parse_url(url.c_str(), url.size(), 0, &parser);
     // scheme
     // scheme
-    scheme = url.substr(parser.field_data[UF_SCHEMA].off, parser.field_data[UF_SCHEMA].len);
+    std::string scheme_ = url.substr(parser.field_data[UF_SCHEMA].off, parser.field_data[UF_SCHEMA].len);
     // host
     // host
+    std::string host_(host);
     if (parser.field_set & (1<<UF_HOST)) {
     if (parser.field_set & (1<<UF_HOST)) {
-        host = url.substr(parser.field_data[UF_HOST].off, parser.field_data[UF_HOST].len);
+        host_ = url.substr(parser.field_data[UF_HOST].off, parser.field_data[UF_HOST].len);
     }
     }
     // port
     // port
-    port = parser.port ? parser.port : strcmp(scheme.c_str(), "https") ? DEFAULT_HTTP_PORT : DEFAULT_HTTPS_PORT;
+    int port_ = parser.port ? parser.port : strcmp(scheme_.c_str(), "https") ? DEFAULT_HTTP_PORT : DEFAULT_HTTPS_PORT;
+    if (!proxy) {
+        scheme = scheme_;
+        host = host_;
+        port = port_;
+    }
+    FillHost(host_.c_str(), port_);
     // path
     // path
     if (parser.field_set & (1<<UF_PATH)) {
     if (parser.field_set & (1<<UF_PATH)) {
         const char* sp = url.c_str() + parser.field_data[UF_PATH].off;
         const char* sp = url.c_str() + parser.field_data[UF_PATH].off;
@@ -535,6 +542,31 @@ void HttpRequest::ParseUrl() {
     }
     }
 }
 }
 
 
+void HttpRequest::FillHost(const char* host, int port) {
+    if (headers.find("Host") == headers.end()) {
+        if (port == 0 ||
+            port == DEFAULT_HTTP_PORT ||
+            port == DEFAULT_HTTPS_PORT) {
+            headers["Host"] = host;
+        } else {
+            headers["Host"] = asprintf("%s:%d", host, port);
+        }
+    }
+}
+
+void HttpRequest::SetHost(const char* host, int port) {
+    this->host = host;
+    this->port = port;
+    FillHost(host, port);
+}
+
+void HttpRequest::SetProxy(const char* host, int port) {
+    this->scheme = "http";
+    this->host = host;
+    this->port = port;
+    proxy = 1;
+}
+
 std::string HttpRequest::Dump(bool is_dump_headers, bool is_dump_body) {
 std::string HttpRequest::Dump(bool is_dump_headers, bool is_dump_body) {
     ParseUrl();
     ParseUrl();
 
 
@@ -542,20 +574,10 @@ std::string HttpRequest::Dump(bool is_dump_headers, bool is_dump_body) {
     str.reserve(MAX(512, path.size() + 128));
     str.reserve(MAX(512, path.size() + 128));
     // GET / HTTP/1.1\r\n
     // GET / HTTP/1.1\r\n
     str = asprintf("%s %s HTTP/%d.%d\r\n",
     str = asprintf("%s %s HTTP/%d.%d\r\n",
-            http_method_str(method), path.c_str(),
+            http_method_str(method),
+            proxy ? url.c_str() : path.c_str(),
             (int)http_major, (int)http_minor);
             (int)http_major, (int)http_minor);
     if (is_dump_headers) {
     if (is_dump_headers) {
-        // Host:
-        if (headers.find("Host") == headers.end()) {
-            if (port == 0 ||
-                port == DEFAULT_HTTP_PORT ||
-                port == DEFAULT_HTTPS_PORT) {
-                headers["Host"] = host;
-            }
-            else {
-                headers["Host"] = asprintf("%s:%d", host.c_str(), port);
-            }
-        }
         DumpHeaders(str);
         DumpHeaders(str);
     }
     }
     str += "\r\n";
     str += "\r\n";

+ 7 - 2
http/HttpMessage.h

@@ -380,7 +380,8 @@ public:
     // client_addr
     // client_addr
     HNetAddr            client_addr;    // for http server save client addr of request
     HNetAddr            client_addr;    // for http server save client addr of request
     int                 timeout;        // for http client timeout
     int                 timeout;        // for http client timeout
-    bool                redirect;       // for http_client redirect
+    unsigned            redirect: 1;    // for http_client redirect
+    unsigned            proxy   : 1;    // for http_client proxy
 
 
     HttpRequest() : HttpMessage() {
     HttpRequest() : HttpMessage() {
         type = HTTP_REQUEST;
         type = HTTP_REQUEST;
@@ -396,7 +397,8 @@ public:
         port = DEFAULT_HTTP_PORT;
         port = DEFAULT_HTTP_PORT;
         path = "/";
         path = "/";
         timeout = 0;
         timeout = 0;
-        redirect = true;
+        redirect = 1;
+        proxy = 0;
     }
     }
 
 
     virtual void Reset() {
     virtual void Reset() {
@@ -450,6 +452,9 @@ public:
         auto iter = headers.find("Host");
         auto iter = headers.find("Host");
         return iter == headers.end() ? host : iter->second;
         return iter == headers.end() ? host : iter->second;
     }
     }
+    void FillHost(const char* host, int port = DEFAULT_HTTP_PORT);
+    void SetHost(const char* host, int port = DEFAULT_HTTP_PORT);
+    void SetProxy(const char* host, int port);
 
 
     // Range: bytes=0-4095
     // Range: bytes=0-4095
     void SetRange(long from = 0, long to = -1) {
     void SetRange(long from = 0, long to = -1) {

+ 60 - 23
http/client/http_client.cpp

@@ -24,6 +24,14 @@ struct http_client_s {
     int          https;
     int          https;
     int          timeout; // s
     int          timeout; // s
     http_headers headers;
     http_headers headers;
+    // http_proxy
+    std::string  http_proxy_host;
+    int          http_proxy_port;
+    // https_proxy
+    std::string  https_proxy_host;
+    int          https_proxy_port;
+    // no_proxy
+    StringList   no_proxy_hosts;
 //private:
 //private:
 #ifdef WITH_CURL
 #ifdef WITH_CURL
     CURL* curl;
     CURL* curl;
@@ -118,6 +126,23 @@ const char* http_client_get_header(http_client_t* cli, const char* key) {
     return NULL;
     return NULL;
 }
 }
 
 
+int http_client_set_http_proxy(http_client_t* cli, const char* host, int port) {
+    cli->http_proxy_host = host;
+    cli->http_proxy_port = port;
+    return 0;
+}
+
+int http_client_set_https_proxy(http_client_t* cli, const char* host, int port) {
+    cli->https_proxy_host = host;
+    cli->https_proxy_port = port;
+    return 0;
+}
+
+int http_client_add_no_proxy(http_client_t* cli, const char* host) {
+    cli->no_proxy_hosts.push_back(host);
+    return 0;
+}
+
 static int http_client_redirect(HttpRequest* req, HttpResponse* resp) {
 static int http_client_redirect(HttpRequest* req, HttpResponse* resp) {
     std::string location = resp->headers["Location"];
     std::string location = resp->headers["Location"];
     if (!location.empty()) {
     if (!location.empty()) {
@@ -131,25 +156,51 @@ static int http_client_redirect(HttpRequest* req, HttpResponse* resp) {
     return 0;
     return 0;
 }
 }
 
 
-int http_client_send(http_client_t* cli, HttpRequest* req, HttpResponse* resp) {
-    if (!cli || !req || !resp) return ERR_NULL_POINTER;
-
+static int http_client_make_request(http_client_t* cli, HttpRequest* req) {
     if (req->url.empty() || *req->url.c_str() == '/') {
     if (req->url.empty() || *req->url.c_str() == '/') {
         req->scheme = cli->https ? "https" : "http";
         req->scheme = cli->https ? "https" : "http";
         req->host = cli->host;
         req->host = cli->host;
         req->port = cli->port;
         req->port = cli->port;
     }
     }
 
 
+    bool https = strcmp(req->scheme.c_str(), "https") == 0 || strncmp(req->url.c_str(), "https", 5) == 0;
+    bool use_proxy = https ? (!cli->https_proxy_host.empty()) : (!cli->http_proxy_host.empty());
+    if (use_proxy) {
+        if (req->host == "127.0.0.1" || req->host == "localhost") {
+            use_proxy = false;
+        }
+    }
+    if (use_proxy) {
+        for (const auto& host : cli->no_proxy_hosts) {
+            if (req->host == host) {
+                use_proxy = false;
+                break;
+            }
+        }
+    }
+    if (use_proxy) {
+        req->SetProxy(https ? cli->https_proxy_host.c_str() : cli->http_proxy_host.c_str(),
+                      https ? cli->https_proxy_port         : cli->http_proxy_port);
+    }
+
     if (req->timeout == 0) {
     if (req->timeout == 0) {
         req->timeout = cli->timeout;
         req->timeout = cli->timeout;
     }
     }
 
 
-    for (auto& pair : cli->headers) {
+    for (const auto& pair : cli->headers) {
         if (req->headers.find(pair.first) == req->headers.end()) {
         if (req->headers.find(pair.first) == req->headers.end()) {
-            req->headers[pair.first] = pair.second;
+            req->headers.insert(pair);
         }
         }
     }
     }
 
 
+    return 0;
+}
+
+int http_client_send(http_client_t* cli, HttpRequest* req, HttpResponse* resp) {
+    if (!cli || !req || !resp) return ERR_NULL_POINTER;
+
+    http_client_make_request(cli, req);
+
     if (req->head_cb)    resp->head_cb = std::move(req->head_cb);
     if (req->head_cb)    resp->head_cb = std::move(req->head_cb);
     if (req->body_cb)    resp->body_cb = std::move(req->body_cb);
     if (req->body_cb)    resp->body_cb = std::move(req->body_cb);
     if (req->chunked_cb) resp->chunked_cb = std::move(req->chunked_cb);
     if (req->chunked_cb) resp->chunked_cb = std::move(req->chunked_cb);
@@ -353,6 +404,8 @@ static int __http_client_connect(http_client_t* cli, HttpRequest* req) {
     const char* host = req->host.c_str();
     const char* host = req->host.c_str();
     int connfd = ConnectTimeout(host, req->port, blocktime);
     int connfd = ConnectTimeout(host, req->port, blocktime);
     if (connfd < 0) {
     if (connfd < 0) {
+        fprintf(stderr, "* connect %s:%d failed!\n", host, req->port);
+        hloge("connect %s:%d failed!", host, req->port);
         return connfd;
         return connfd;
     }
     }
     tcp_nodelay(connfd, 1);
     tcp_nodelay(connfd, 1);
@@ -373,7 +426,7 @@ static int __http_client_connect(http_client_t* cli, HttpRequest* req) {
         }
         }
         int ret = hssl_connect(cli->ssl);
         int ret = hssl_connect(cli->ssl);
         if (ret != 0) {
         if (ret != 0) {
-            fprintf(stderr, "ssl handshake failed: %d\n", ret);
+            fprintf(stderr, "* ssl handshake failed: %d\n", ret);
             hloge("ssl handshake failed: %d", ret);
             hloge("ssl handshake failed: %d", ret);
             hssl_free(cli->ssl);
             hssl_free(cli->ssl);
             cli->ssl = NULL;
             cli->ssl = NULL;
@@ -507,23 +560,7 @@ static int __http_client_send_async(http_client_t* cli, HttpRequestPtr req, Http
 
 
 int http_client_send_async(http_client_t* cli, HttpRequestPtr req, HttpResponseCallback resp_cb) {
 int http_client_send_async(http_client_t* cli, HttpRequestPtr req, HttpResponseCallback resp_cb) {
     if (!cli || !req) return ERR_NULL_POINTER;
     if (!cli || !req) return ERR_NULL_POINTER;
-
-    if (req->url.empty() || *req->url.c_str() == '/') {
-        req->scheme = cli->https ? "https" : "http";
-        req->host = cli->host;
-        req->port = cli->port;
-    }
-
-    if (req->timeout == 0) {
-        req->timeout = cli->timeout;
-    }
-
-    for (auto& pair : cli->headers) {
-        if (req->headers.find(pair.first) == req->headers.end()) {
-            req->headers[pair.first] = pair.second;
-        }
-    }
-
+    http_client_make_request(cli, req.get());
     return __http_client_send_async(cli, req, resp_cb);
     return __http_client_send_async(cli, req, resp_cb);
 }
 }
 
 

+ 70 - 0
http/client/http_client.h

@@ -41,6 +41,13 @@ HV_EXPORT int http_client_set_header(http_client_t* cli, const char* key, const
 HV_EXPORT int http_client_del_header(http_client_t* cli, const char* key);
 HV_EXPORT int http_client_del_header(http_client_t* cli, const char* key);
 HV_EXPORT const char* http_client_get_header(http_client_t* cli, const char* key);
 HV_EXPORT const char* http_client_get_header(http_client_t* cli, const char* key);
 
 
+// http_proxy
+HV_EXPORT int http_client_set_http_proxy(http_client_t* cli, const char* host, int port);
+// https_proxy
+HV_EXPORT int http_client_set_https_proxy(http_client_t* cli, const char* host, int port);
+// no_proxy
+HV_EXPORT int http_client_add_no_proxy(http_client_t* cli, const char* host);
+
 // sync
 // sync
 HV_EXPORT int http_client_send(http_client_t* cli, HttpRequest* req, HttpResponse* resp);
 HV_EXPORT int http_client_send(http_client_t* cli, HttpRequest* req, HttpResponse* resp);
 
 
@@ -55,4 +62,67 @@ HV_EXPORT int http_client_send(HttpRequest* req, HttpResponse* resp);
 // http_client_send_async(&default_async_client, ...)
 // http_client_send_async(&default_async_client, ...)
 HV_EXPORT int http_client_send_async(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
 HV_EXPORT int http_client_send_async(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL);
 
 
+namespace hv {
+
+class HttpClient {
+public:
+    HttpClient(const char* host = NULL, int port = DEFAULT_HTTP_PORT, int https = 0) {
+        _client = http_client_new(host, port, https);
+    }
+
+    ~HttpClient() {
+        if (_client) {
+            http_client_del(_client);
+            _client = NULL;
+        }
+    }
+
+    // timeout: s
+    int setTimeout(int timeout) {
+        return http_client_set_timeout(_client, timeout);
+    }
+
+    // headers
+    int clearHeaders() {
+        return http_client_clear_headers(_client);
+    }
+    int setHeader(const char* key, const char* value) {
+        return http_client_set_header(_client, key, value);
+    }
+    int delHeader(const char* key) {
+        return http_client_del_header(_client, key);
+    }
+    const char* getHeader(const char* key) {
+        return http_client_get_header(_client, key);
+    }
+
+    // http_proxy
+    int setHttpProxy(const char* host, int port) {
+        return http_client_set_http_proxy(_client, host, port);
+    }
+    // https_proxy
+    int setHttpsProxy(const char* host, int port) {
+        return http_client_set_https_proxy(_client, host, port);
+    }
+    // no_proxy
+    int addNoProxy(const char* host) {
+        return http_client_add_no_proxy(_client, host);
+    }
+
+    // sync
+    int send(HttpRequest* req, HttpResponse* resp) {
+        return http_client_send(_client, req, resp);
+    }
+
+    // async
+    int sendAsync(HttpRequestPtr req, HttpResponseCallback resp_cb = NULL) {
+        return http_client_send_async(_client, req, resp_cb);
+    }
+
+private:
+    http_client_t* _client;
+};
+
+}
+
 #endif // HV_HTTP_CLIENT_H_
 #endif // HV_HTTP_CLIENT_H_