dovecot-2.2: lib-http: http-client: Implemented proxy support.

dovecot at dovecot.org dovecot at dovecot.org
Sat Oct 12 11:14:58 EEST 2013


details:   http://hg.dovecot.org/dovecot-2.2/rev/462ae2cb8094
changeset: 16854:462ae2cb8094
user:      Stephan Bosch <stephan at rename-it.nl>
date:      Sat Oct 12 11:00:15 2013 +0300
description:
lib-http: http-client: Implemented proxy support.

diffstat:

 src/lib-http/http-client-host.c    |   14 ++-
 src/lib-http/http-client-private.h |   34 ++++++++-
 src/lib-http/http-client-request.c |  125 ++++++++++++++++++++++++++----------
 src/lib-http/http-client.c         |    9 ++
 src/lib-http/http-client.h         |   16 ++++
 5 files changed, 151 insertions(+), 47 deletions(-)

diffs (truncated from 394 to 300 lines):

diff -r 9709839d2be7 -r 462ae2cb8094 src/lib-http/http-client-host.c
--- a/src/lib-http/http-client-host.c	Sat Oct 12 10:58:04 2013 +0300
+++ b/src/lib-http/http-client-host.c	Sat Oct 12 11:00:15 2013 +0300
@@ -454,12 +454,14 @@
 	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 @@
 	}
 
 	/* 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 @@
 	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 @@
 		(*req)->to_delayed_error =
 			io_loop_move_timeout(&(*req)->to_delayed_error);
 	}
-
 }
diff -r 9709839d2be7 -r 462ae2cb8094 src/lib-http/http-client-private.h
--- a/src/lib-http/http-client-private.h	Sat Oct 12 10:58:04 2013 +0300
+++ b/src/lib-http/http-client-private.h	Sat Oct 12 11:00:15 2013 +0300
@@ -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_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 @@
 	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 @@
 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 -r 9709839d2be7 -r 462ae2cb8094 src/lib-http/http-client-request.c
--- a/src/lib-http/http-client-request.c	Sat Oct 12 10:58:04 2013 +0300
+++ b/src/lib-http/http-client-request.c	Sat Oct 12 11:00:15 2013 +0300
@@ -52,10 +52,8 @@
  */
 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 @@
 	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 @@
 	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 @@
 			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 @@
 
 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 @@
 
 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 @@
 	   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 @@
 		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 @@
 	unsigned int status, const char *location)
 {


More information about the dovecot-cvs mailing list