From: Stephan Bosch Date: Sat, 12 Oct 2013 08:00:15 +0000 (+0300) Subject: lib-http: http-client: Implemented proxy support. X-Git-Tag: 2.2.7~75 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4219de12b28f1936219e27501b9c4b27a4f8d53c;p=thirdparty%2Fdovecot%2Fcore.git lib-http: http-client: Implemented proxy support. --- diff --git a/src/lib-http/http-client-host.c b/src/lib-http/http-client-host.c index 33247b3b0d..7716bb904a 100644 --- a/src/lib-http/http-client-host.c +++ b/src/lib-http/http-client-host.c @@ -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); } - } diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h index d17a156a3d..4cd161addc 100644 --- a/src/lib-http/http-client-private.h +++ b/src/lib-http/http-client-private.h @@ -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 * diff --git a/src/lib-http/http-client-request.c b/src/lib-http/http-client-request.c index c16574a32a..55a98ed107 100644 --- a/src/lib-http/http-client-request.c +++ b/src/lib-http/http-client-request.c @@ -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 diff --git a/src/lib-http/http-client.c b/src/lib-http/http-client.c index 77ed2607b5..c3699fb1dd 100644 --- a/src/lib-http/http-client.c +++ b/src/lib-http/http-client.c @@ -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); diff --git a/src/lib-http/http-client.h b/src/lib-http/http-client.h index cfd3f0b10a..ce72c05d88 100644 --- a/src/lib-http/http-client.h +++ b/src/lib-http/http-client.h @@ -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,