]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: http-client: Implemented proxy support.
authorStephan Bosch <stephan@rename-it.nl>
Sat, 12 Oct 2013 08:00:15 +0000 (11:00 +0300)
committerStephan Bosch <stephan@rename-it.nl>
Sat, 12 Oct 2013 08:00:15 +0000 (11:00 +0300)
src/lib-http/http-client-host.c
src/lib-http/http-client-private.h
src/lib-http/http-client-request.c
src/lib-http/http-client.c
src/lib-http/http-client.h

index 33247b3b0d91a8dc2a847097b9a775604137c13f..7716bb904a8417c97560405f0f58422a19aa81ec 100644 (file)
@@ -454,12 +454,14 @@ void http_client_host_submit_request(struct http_client_host *host,
        struct http_client_request *req)
 {
        struct http_client_host_port *hport;
-       const char *https_name = req->ssl ? req->hostname : NULL;
+       const struct http_url *host_url = req->host_url;
+       const char *https_name = http_client_request_https_name(req);
+       in_port_t port = http_client_request_port(req);
        const char *error;
 
        req->host = host;
 
-       if (req->ssl && host->client->ssl_ctx == NULL) {
+       if (host_url->have_ssl && host->client->ssl_ctx == NULL) {
                if (http_client_init_ssl_ctx(host->client, &error) < 0) {
                        http_client_request_error(req,
                                HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, error);
@@ -468,7 +470,7 @@ void http_client_host_submit_request(struct http_client_host *host,
        }
 
        /* add request to host (grouped by tcp port) */
-       hport = http_client_host_port_init(host, req->port, https_name);
+       hport = http_client_host_port_init(host, port, https_name);
        if (req->urgent)
                array_insert(&hport->request_queue, 0, &req, 1);
        else
@@ -542,9 +544,10 @@ void http_client_host_drop_request(struct http_client_host *host,
        struct http_client_request *req)
 {
        struct http_client_host_port *hport;
-       const char *https_name = req->ssl ? req->hostname : NULL;
+       const char *https_name = http_client_request_https_name(req);
+       in_port_t port = http_client_request_port(req);
 
-       hport = http_client_host_port_find(host, req->port, https_name);
+       hport = http_client_host_port_find(host, port, https_name);
        if (hport == NULL)
                return;
 
@@ -596,5 +599,4 @@ void http_client_host_switch_ioloop(struct http_client_host *host)
                (*req)->to_delayed_error =
                        io_loop_move_timeout(&(*req)->to_delayed_error);
        }
-
 }
index d17a156a3d9dfe6deed978beb7eaacd92f9c3790..4cd161addc863a1cb03ea911ca40f0a819b9d130 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "connection.h"
 
+#include "http-url.h"
 #include "http-client.h"
 
 #define HTTP_DEFAULT_PORT 80
@@ -38,9 +39,13 @@ struct http_client_peer_addr {
 struct http_client_request {
        pool_t pool;
        unsigned int refcount;
+       const char *label;
 
-       const char *method, *hostname, *target;
-       in_port_t port;
+       const char *method, *target;
+       struct http_url origin_url;
+
+       const struct http_url *host_url;
+       const char *authority;
 
        struct http_client *client;
        struct http_client_host *host;
@@ -79,7 +84,6 @@ struct http_client_request {
        unsigned int payload_sync:1;
        unsigned int payload_chunked:1;
        unsigned int payload_wait:1;
-       unsigned int ssl:1;
        unsigned int urgent:1;
        unsigned int submitted:1;
 };
@@ -210,8 +214,28 @@ http_client_peer_addr2str(const struct http_client_peer_addr *addr)
 static inline const char *
 http_client_request_label(struct http_client_request *req)
 {
-       return t_strdup_printf("[%s http%s://%s:%d%s]",
-               req->method, req->ssl ? "s" : "", req->hostname, req->port, req->target);
+       if (req->label == NULL) {
+               return t_strdup_printf("[%s %s%s]",
+                       req->method, http_url_create(&req->origin_url), req->target);
+       }
+       return req->label;
+}
+
+static inline in_port_t
+http_client_request_port(const struct http_client_request *req)
+{
+       const struct http_url *host_url = req->host_url;
+
+       return (host_url->have_port ? host_url->port :
+               (host_url->have_ssl ? HTTPS_DEFAULT_PORT : HTTP_DEFAULT_PORT));
+}
+
+static inline const char *
+http_client_request_https_name(const struct http_client_request *req)
+{
+       const struct http_url *host_url = req->host_url;
+
+       return (host_url->have_ssl ? host_url->host_name : NULL);
 }
 
 static inline const char *
index c16574a32aea84b5a3ccabb99f37ecfbb3a04b52..55a98ed1071c29167132a53041c5bb6b3c4de692 100644 (file)
@@ -52,10 +52,8 @@ http_client_request_debug(struct http_client_request *req,
  */
 static void http_client_request_remove_delayed(struct http_client_request *req);
 
-#undef http_client_request
-struct http_client_request *
-http_client_request(struct http_client *client,
-                   const char *method, const char *host, const char *target,
+static struct http_client_request *
+http_client_request_new(struct http_client *client, const char *method, 
                    http_client_request_callback_t *callback, void *context)
 {
        pool_t pool;
@@ -67,18 +65,44 @@ http_client_request(struct http_client *client,
        req->refcount = 1;
        req->client = client;
        req->method = p_strdup(pool, method);
-       req->hostname = p_strdup(pool, host);
-       req->port = HTTP_DEFAULT_PORT;
-       req->target = (target == NULL ? "/" : p_strdup(pool, target));
        req->callback = callback;
        req->context = context;
-       req->headers = str_new(default_pool, 256);
        req->date = (time_t)-1;
 
        req->state = HTTP_REQUEST_STATE_NEW;
        return req;
 }
 
+#undef http_client_request
+struct http_client_request *
+http_client_request(struct http_client *client,
+                   const char *method, const char *host, const char *target,
+                   http_client_request_callback_t *callback, void *context)
+{
+       struct http_client_request *req;
+
+       req = http_client_request_new(client, method, callback, context);
+       req->origin_url.host_name = p_strdup(req->pool, host);
+       req->target = (target == NULL ? "/" : p_strdup(req->pool, target));
+       req->headers = str_new(default_pool, 256);
+       return req;
+}
+
+#undef http_client_request_url
+struct http_client_request *
+http_client_request_url(struct http_client *client,
+                   const char *method, const struct http_url *target_url,
+                   http_client_request_callback_t *callback, void *context)
+{
+       struct http_client_request *req;
+
+       req = http_client_request_new(client, method, callback, context);
+       http_url_copy_authority(req->pool, &req->origin_url, target_url);
+       req->target = p_strdup(req->pool, http_url_create_target(target_url));
+       req->headers = str_new(default_pool, 256);
+       return req;
+}
+
 void http_client_request_ref(struct http_client_request *req)
 {
        req->refcount++;
@@ -124,21 +148,15 @@ void http_client_request_set_port(struct http_client_request *req,
        in_port_t port)
 {
        i_assert(req->state == HTTP_REQUEST_STATE_NEW);
-       req->port = port;
+       req->origin_url.port = port;
+       req->origin_url.have_port = TRUE;
 }
 
 void http_client_request_set_ssl(struct http_client_request *req,
        bool ssl)
 {
        i_assert(req->state == HTTP_REQUEST_STATE_NEW);
-       if (ssl) {
-               if (!req->ssl && req->port == HTTP_DEFAULT_PORT)
-                       req->port = HTTPS_DEFAULT_PORT;
-       } else {
-               if (req->ssl && req->port == HTTPS_DEFAULT_PORT)
-                       req->port = HTTP_DEFAULT_PORT;
-       }
-       req->ssl = ssl;
+       req->origin_url.have_ssl = ssl;
 }
 
 void http_client_request_set_urgent(struct http_client_request *req)
@@ -180,6 +198,8 @@ void http_client_request_add_header(struct http_client_request *req,
                        req->have_hdr_user_agent = TRUE;
                break;
        }
+       if (req->headers == NULL)
+               req->headers = str_new(default_pool, 256);
        str_printfa(req->headers, "%s: %s\r\n", key, value);
 }
 
@@ -223,15 +243,39 @@ http_client_request_get_state(struct http_client_request *req)
 
 static void http_client_request_do_submit(struct http_client_request *req)
 {
+       struct http_client *client = req->client;
        struct http_client_host *host;
+       const struct http_url *proxy_url = client->set.proxy_url;
+       const char *target;
 
        i_assert(req->state == HTTP_REQUEST_STATE_NEW);
 
+       target = t_strconcat
+               (http_url_create_host(&req->origin_url), req->target, NULL);
+
+       /* determine what host to contact to submit this request */
+       if (proxy_url != NULL) {
+               req->host_url = proxy_url;         /* proxy server */
+       } else {
+               req->host_url = &req->origin_url;  /* origin server */
+       }
+
        /* use submission date if no date is set explicitly */
        if (req->date == (time_t)-1)
                req->date = ioloop_time;
        
-       host = http_client_host_get(req->client, req->hostname);
+       /* prepare value for Host header */
+       req->authority =
+               p_strdup(req->pool, http_url_create_authority(req->host_url));
+
+       /* debug label */
+       req->label = p_strdup_printf(req->pool, "[%s %s]", req->method, target);
+
+       /* request target needs to be made absolute url for proxy requests */
+       if (proxy_url != NULL)
+               req->target = p_strdup(req->pool, target);
+
+       host = http_client_host_get(req->client, req->host_url->host_name);
        req->state = HTTP_REQUEST_STATE_QUEUED;
 
        http_client_host_submit_request(host, req);
@@ -239,10 +283,10 @@ static void http_client_request_do_submit(struct http_client_request *req)
 
 void http_client_request_submit(struct http_client_request *req)
 {
-       http_client_request_debug(req, "Submitted");
        req->client->pending_requests++;
 
        http_client_request_do_submit(req);
+       http_client_request_debug(req, "Submitted");
        req->submitted = TRUE;
 }
 
@@ -452,11 +496,7 @@ static int http_client_request_send_real(struct http_client_request *req,
           http_client_request_add_header() */
        if (!req->have_hdr_host) {
                str_append(rtext, "Host: ");
-               str_append(rtext, req->hostname);
-               if ((!req->ssl &&req->port != HTTP_DEFAULT_PORT) ||
-                       (req->ssl && req->port != HTTPS_DEFAULT_PORT)) {
-                       str_printfa(rtext, ":%u", req->port);
-               }
+               str_append(rtext, req->authority);
                str_append(rtext, "\r\n");
        }
        if (!req->have_hdr_date) {
@@ -487,8 +527,16 @@ static int http_client_request_send_real(struct http_client_request *req,
                req->payload_output = output;
                o_stream_ref(output);
        }
-       if (!req->have_hdr_connection)
+       if (!req->have_hdr_connection && req->host_url == &req->origin_url) {
+               /* https://tools.ietf.org/html/rfc2068
+                    Section 19.7.1:
+
+                  A client MUST NOT send the Keep-Alive connection token to a proxy
+                  server as HTTP/1.0 proxy servers do not obey the rules of HTTP/1.1
+                  for parsing the Connection header field.
+                */
                str_append(rtext, "Connection: Keep-Alive\r\n");
+       }
 
        /* request line + implicit headers */
        iov[0].iov_base = str_data(rtext);
@@ -665,8 +713,7 @@ void http_client_request_redirect(struct http_client_request *req,
        unsigned int status, const char *location)
 {
        struct http_url *url;
-       const char *error, *target;
-       unsigned int newport;
+       const char *error, *target, *origin_url;
 
        /* parse URL */
        if (http_url_parse(location, NULL, 0,
@@ -707,19 +754,25 @@ void http_client_request_redirect(struct http_client_request *req,
        if (req->payload_output != NULL)
                o_stream_unref(&req->payload_output);
 
-       newport = (url->have_port ? url->port : (url->have_ssl ? 443 : 80));
        target = http_url_create_target(url);
 
-       http_client_request_debug(req, "Redirecting to http%s://%s:%u%s",
-               (url->have_ssl ? "s" : ""), url->host_name, newport, target);
-
-       // FIXME: handle literal IP specially (avoid duplicate parsing)
+       http_url_copy(req->pool, &req->origin_url, url);
+       req->target = p_strdup(req->pool, target);
+       if (req->host_url == &req->origin_url) {
+               req->authority =
+                       p_strdup(req->pool, http_url_create_authority(req->host_url));
+       }
+       
        req->host = NULL;
        req->conn = NULL;
-       req->hostname = p_strdup(req->pool, url->host_name);
-       req->port = newport;
-       req->target = p_strdup(req->pool, target);
-       req->ssl = url->have_ssl;
+
+       origin_url = http_url_create(&req->origin_url);
+
+       http_client_request_debug(req, "Redirecting to %s%s",
+               origin_url, target);
+
+       req->label = p_strdup_printf(req->pool, "[%s %s%s]",
+               req->method, origin_url, req->target);
 
        /* https://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-21
              Section-7.4.4
index 77ed2607b5c863c048c6ef6614626165c4c7af7c..c3699fb1ddb11b093d839db042ccef6709148ffe 100644 (file)
@@ -87,6 +87,15 @@ struct http_client *http_client_init(const struct http_client_settings *set)
        client->set.ssl_cert = p_strdup(pool, set->ssl_cert);
        client->set.ssl_key = p_strdup(pool, set->ssl_key);
        client->set.ssl_key_password = p_strdup(pool, set->ssl_key_password);
+
+       if (set->proxy_socket_path != NULL && *set->proxy_socket_path != '\0') {
+               client->set.proxy_socket_path = p_strdup(pool, set->proxy_socket_path);
+       } else if (set->proxy_url != NULL) {
+               client->set.proxy_url = http_url_clone(pool, set->proxy_url);
+       }
+       client->set.proxy_username = p_strdup_empty(pool, set->proxy_username);
+       client->set.proxy_password = p_strdup_empty(pool, set->proxy_password);
+
        client->set.max_idle_time_msecs = set->max_idle_time_msecs;
        client->set.max_parallel_connections =
                (set->max_parallel_connections > 0 ? set->max_parallel_connections : 1);
index cfd3f0b10ada84ab1f81f25c7744cdd209a4f406..ce72c05d880ada4b57d11d537df83c8bd67cbe71 100644 (file)
@@ -45,6 +45,12 @@ struct http_client_settings {
        /* User-Agent: header (default: none) */
        const char *user_agent;
 
+       /* configuration for using a proxy */
+       const char *proxy_socket_path; /* FIXME: implement */
+       const struct http_url *proxy_url;
+       const char *proxy_username; /* FIXME: implement */
+       const char *proxy_password;
+
        const char *rawlog_dir;
 
        unsigned int max_idle_time_msecs;
@@ -100,6 +106,16 @@ http_client_request(struct http_client *client,
                        const struct http_response *response, typeof(context))), \
                (http_client_request_callback_t *)callback, context)
 
+struct http_client_request *
+http_client_request_url(struct http_client *client,
+                   const char *method, const struct http_url *target_url,
+                   http_client_request_callback_t *callback, void *context);
+#define http_client_request_url(client, method, target_url, callback, context) \
+       http_client_request_url(client, method, target_url + \
+               CALLBACK_TYPECHECK(callback, void (*)( \
+                       const struct http_response *response, typeof(context))), \
+               (http_client_request_callback_t *)callback, context)
+
 void http_client_request_set_port(struct http_client_request *req,
        in_port_t port);
 void http_client_request_set_ssl(struct http_client_request *req,