]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
http-client: Added support for using an HTTP proxy running on a unix socket.
authorStephan Bosch <stephan@rename-it.nl>
Sat, 25 Apr 2015 09:42:06 +0000 (11:42 +0200)
committerStephan Bosch <stephan@rename-it.nl>
Sat, 25 Apr 2015 09:42:06 +0000 (11:42 +0200)
src/lib-http/http-client-connection.c
src/lib-http/http-client-host.c
src/lib-http/http-client-peer.c
src/lib-http/http-client-private.h
src/lib-http/http-client-queue.c
src/lib-http/http-client-request.c
src/lib-http/http-client.h

index 798c1e6a9f5e460b8b2f4e9769642d9b063a3555..543152319d5d1a177ecddfcd819130b6f4e7464d 100644 (file)
@@ -1015,7 +1015,7 @@ static int
 http_client_connection_ssl_handshaked(const char **error_r, void *context)
 {
        struct http_client_connection *conn = context;
-       const char *error, *host = conn->peer->addr.https_name;
+       const char *error, *host = conn->peer->addr.a.tcp.https_name;
 
        if (ssl_iostream_check_cert_validity(conn->ssl_iostream, host, &error) == 0)
                http_client_connection_debug(conn, "SSL handshake successful");
@@ -1049,7 +1049,7 @@ http_client_connection_ssl_init(struct http_client_connection *conn,
                http_client_connection_debug(conn, "Starting SSL handshake");
 
        if (io_stream_create_ssl_client(conn->client->ssl_ctx,
-                                       conn->peer->addr.https_name, &ssl_set,
+                                       conn->peer->addr.a.tcp.https_name, &ssl_set,
                                        &conn->conn.input, &conn->conn.output,
                                        &conn->ssl_iostream, &error) < 0) {
                *error_r = t_strdup_printf(
@@ -1089,7 +1089,7 @@ http_client_connection_connected(struct connection *_conn, bool success)
        } else {
                conn->connected_timestamp = ioloop_timeval;
                http_client_connection_debug(conn, "Connected");
-               if (conn->peer->addr.https_name != NULL) {
+               if (http_client_peer_addr_is_https(&conn->peer->addr)) {
                        if (http_client_connection_ssl_init(conn, &error) < 0) {
                                http_client_peer_connection_failure(conn->peer, error);
                                http_client_connection_debug(conn, "%s", error);
@@ -1104,7 +1104,8 @@ http_client_connection_connected(struct connection *_conn, bool success)
 static const struct connection_settings http_client_connection_set = {
        .input_max_size = (size_t)-1,
        .output_max_size = (size_t)-1,
-       .client = TRUE
+       .client = TRUE,
+       .delayed_unix_client_connected_callback = TRUE
 };
 
 static const struct connection_vfuncs http_client_connection_vfuncs = {
@@ -1248,6 +1249,9 @@ http_client_connection_create(struct http_client_peer *peer)
        case HTTP_CLIENT_PEER_ADDR_RAW:
                conn_type = "Raw";
                break;
+       case HTTP_CLIENT_PEER_ADDR_UNIX:
+               conn_type = "Unix";
+               break;
        }
 
        conn = i_new(struct http_client_connection, 1);
@@ -1258,11 +1262,20 @@ http_client_connection_create(struct http_client_peer *peer)
        if (peer->addr.type != HTTP_CLIENT_PEER_ADDR_RAW)
                i_array_init(&conn->request_wait_list, 16);
 
-       if (peer->addr.type == HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL) {
-               http_client_connection_connect_tunnel(conn, &addr->ip, addr->port);
-       } else {
-               connection_init_client_ip
-                       (peer->client->conn_list, &conn->conn, &addr->ip, addr->port);
+       switch (peer->addr.type) {
+       case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+               http_client_connection_connect_tunnel
+                       (conn, &addr->a.tcp.ip, addr->a.tcp.port);
+               break;
+       case HTTP_CLIENT_PEER_ADDR_UNIX:
+               connection_init_client_unix(peer->client->conn_list, &conn->conn,
+                       addr->a.un.path);
+               conn->connect_initialized = TRUE;
+               http_client_connection_connect(conn);
+               break;
+       default:
+               connection_init_client_ip(peer->client->conn_list, &conn->conn,
+                       &addr->a.tcp.ip, addr->a.tcp.port);
                conn->connect_initialized = TRUE;
                http_client_connection_connect(conn);
        }
index d72044c3db1ad218bf7c5e79704003a0424a9694..6ef814b59f66b8fb8fe8d8870e25a6dadf4cfb68 100644 (file)
@@ -137,31 +137,55 @@ static void http_client_host_lookup
        }
 }
 
+static struct http_client_host *http_client_host_create
+(struct http_client *client)
+{
+       struct http_client_host *host;
+
+       // FIXME: limit the maximum number of inactive cached hosts
+       host = i_new(struct http_client_host, 1);
+       host->client = client;
+       i_array_init(&host->queues, 4);
+       DLLIST_PREPEND(&client->hosts_list, host);
+
+       return host;
+}
+
 struct http_client_host *http_client_host_get
 (struct http_client *client, const struct http_url *host_url)
 {
        struct http_client_host *host;
-       const char *hostname = host_url->host_name;
-
-       host = hash_table_lookup(client->hosts, hostname);
-       if (host == NULL) {
-               // FIXME: limit the maximum number of inactive cached hosts
-               host = i_new(struct http_client_host, 1);
-               host->client = client;
-               host->name = i_strdup(hostname);
-               i_array_init(&host->queues, 4);
-
-               hostname = host->name;
-               hash_table_insert(client->hosts, hostname, host);
-               DLLIST_PREPEND(&client->hosts_list, host);
-
-               if (host_url->have_host_ip) {
-                       host->ips_count = 1;
-                       host->ips = i_new(struct ip_addr, host->ips_count);
-                       host->ips[0] = host_url->host_ip;
+
+       if (host_url == NULL) {
+               host = client->unix_host;
+               if (host == NULL) {
+                       host = http_client_host_create(client);
+                       host->name = i_strdup("[unix]");
+                       host->unix_local = TRUE;
+
+                       client->unix_host = host;
+
+                       http_client_host_debug(host, "Unix host created");
                }
 
-               http_client_host_debug(host, "Host created");
+       } else {
+               const char *hostname = host_url->host_name;
+
+               host = hash_table_lookup(client->hosts, hostname);
+               if (host == NULL) {
+                       host = http_client_host_create(client);
+                       host->name = i_strdup(hostname);
+                       hostname = host->name;
+                       hash_table_insert(client->hosts, hostname, host);
+
+                       if (host_url->have_host_ip) {
+                               host->ips_count = 1;
+                               host->ips = i_new(struct ip_addr, host->ips_count);
+                               host->ips[0] = host_url->host_ip;
+                       }
+
+                       http_client_host_debug(host, "Host created");
+               }
        }
        return host;
 }
@@ -170,13 +194,14 @@ void http_client_host_submit_request(struct http_client_host *host,
        struct http_client_request *req)
 {
        struct http_client_queue *queue;
-       const struct http_url *host_url = req->host_url;
        struct http_client_peer_addr addr;
        const char *error;
 
        req->host = host;
 
-       if (host_url->have_ssl && host->client->ssl_ctx == NULL) {
+       http_client_request_get_peer_addr(req, &addr);
+       if (http_client_peer_addr_is_https(&addr) &&
+               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);
@@ -184,12 +209,15 @@ void http_client_host_submit_request(struct http_client_host *host,
                }
        }
 
-       http_client_request_get_peer_addr(req, &addr);
-
        /* add request to queue (grouped by tcp port) */
        queue = http_client_queue_create(host, &addr);
        http_client_queue_submit_request(queue, req);
 
+       if (host->unix_local) {
+               http_client_queue_connection_setup(queue);
+               return;
+       }
+
        /* start DNS lookup if necessary */
        if (host->ips_count == 0 && host->dns_lookup == NULL)   
                http_client_host_lookup(host);
@@ -210,7 +238,8 @@ void http_client_host_free(struct http_client_host **_host)
        http_client_host_debug(host, "Host destroy");
 
        DLLIST_REMOVE(&host->client->hosts_list, host);
-       hash_table_remove(host->client->hosts, hostname);
+       if (host != host->client->unix_host)
+               hash_table_remove(host->client->hosts, hostname);
 
        if (host->dns_lookup != NULL)
                dns_lookup_abort(&host->dns_lookup);
index be905e90640e9006cb29855c616aad6cbaf1b206..3513c44434074968402fdc2de9b029198e392c6e 100644 (file)
@@ -45,13 +45,16 @@ unsigned int http_client_peer_addr_hash
 {
        switch (peer->type) {
        case HTTP_CLIENT_PEER_ADDR_RAW:
-               return net_ip_hash(&peer->ip) + peer->port + 1;
+               return net_ip_hash(&peer->a.tcp.ip) + peer->a.tcp.port + 1;
        case HTTP_CLIENT_PEER_ADDR_HTTP:
-               return net_ip_hash(&peer->ip) + peer->port;
+               return net_ip_hash(&peer->a.tcp.ip) + peer->a.tcp.port;
        case HTTP_CLIENT_PEER_ADDR_HTTPS:
        case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
-               return net_ip_hash(&peer->ip) + peer->port +
-                       (peer->https_name == NULL ? 0 : str_hash(peer->https_name));
+               return net_ip_hash(&peer->a.tcp.ip) + peer->a.tcp.port +
+                       (peer->a.tcp.https_name == NULL ?
+                               0 : str_hash(peer->a.tcp.https_name));
+       case HTTP_CLIENT_PEER_ADDR_UNIX:
+               return str_hash(peer->a.un.path);
        }
        i_unreached();
        return 0;
@@ -65,13 +68,24 @@ int http_client_peer_addr_cmp
 
        if (peer1->type != peer2->type)
                return (peer1->type > peer2->type ? 1 : -1);
-       if ((ret=net_ip_cmp(&peer1->ip, &peer2->ip)) != 0)
-               return ret;
-       if (peer1->port != peer2->port)
-               return (peer1->port > peer2->port ? 1 : -1);
-       if (peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS)
-               return 0;
-       return null_strcmp(peer1->https_name, peer2->https_name);
+       switch (peer1->type) {
+       case HTTP_CLIENT_PEER_ADDR_RAW:
+       case HTTP_CLIENT_PEER_ADDR_HTTP:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+               if ((ret=net_ip_cmp(&peer1->a.tcp.ip, &peer2->a.tcp.ip)) != 0)
+                       return ret;
+               if (peer1->a.tcp.port != peer2->a.tcp.port)
+                       return (peer1->a.tcp.port > peer2->a.tcp.port ? 1 : -1);
+               if (peer1->type != HTTP_CLIENT_PEER_ADDR_HTTPS)
+                       return 0;
+               return null_strcmp
+                       (peer1->a.tcp.https_name, peer2->a.tcp.https_name);
+       case HTTP_CLIENT_PEER_ADDR_UNIX:
+               return null_strcmp(peer1->a.un.path, peer2->a.un.path);
+       }
+       i_unreached();
+       return 0;
 }
 
 /*
@@ -438,13 +452,25 @@ http_client_peer_create(struct http_client *client,
 {
        struct http_client_peer *peer;
 
-       i_assert(addr->https_name == NULL || client->ssl_ctx != NULL);
-
        peer = i_new(struct http_client_peer, 1);
        peer->client = client;
        peer->addr = *addr;
-       peer->https_name = i_strdup(addr->https_name);
-       peer->addr.https_name = peer->https_name;
+
+       switch (addr->type) {
+       case HTTP_CLIENT_PEER_ADDR_HTTPS:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+               i_assert(client->ssl_ctx != NULL);
+               peer->addr_name = i_strdup(addr->a.tcp.https_name);
+               peer->addr.a.tcp.https_name = peer->addr_name;
+               break;
+       case HTTP_CLIENT_PEER_ADDR_UNIX:
+               peer->addr_name = i_strdup(addr->a.un.path);
+               peer->addr.a.un.path = peer->addr_name;
+               break;
+       default:
+               break;
+       }
+
        i_array_init(&peer->queues, 16);
        i_array_init(&peer->conns, 16);
 
@@ -479,7 +505,7 @@ void http_client_peer_free(struct http_client_peer **_peer)
                (peer->client->peers, (const struct http_client_peer_addr *)&peer->addr);
        DLLIST_REMOVE(&peer->client->peers_list, peer);
 
-       i_free(peer->https_name);
+       i_free(peer->addr_name);
        i_free(peer);
        *_peer = NULL;
 }
index 840c0753a5bc2938afdd6514e859cbd473ff6ba4..d1b6a193d30bc5b9eb7bc1b5a1c297cf2f366d07 100644 (file)
@@ -38,14 +38,22 @@ enum http_client_peer_addr_type {
        HTTP_CLIENT_PEER_ADDR_HTTP = 0,
        HTTP_CLIENT_PEER_ADDR_HTTPS,
        HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL,
-       HTTP_CLIENT_PEER_ADDR_RAW
+       HTTP_CLIENT_PEER_ADDR_RAW,
+       HTTP_CLIENT_PEER_ADDR_UNIX,
 };
 
 struct http_client_peer_addr {
        enum http_client_peer_addr_type type;
-       const char *https_name; /* TLS SNI */
-       struct ip_addr ip;
-       in_port_t port;
+       union {
+               struct {
+                       const char *https_name; /* TLS SNI */
+                       struct ip_addr ip;
+                       in_port_t port;
+               } tcp;
+               struct {
+                       const char *path;
+               } un;
+       } a;
 };
 
 struct http_client_request {
@@ -59,6 +67,7 @@ struct http_client_request {
        struct http_url origin_url;
        const char *username, *password;
 
+       const char *host_socket;
        const struct http_url *host_url;
        const char *authority;
 
@@ -157,7 +166,7 @@ struct http_client_connection {
 
 struct http_client_peer {
        struct http_client_peer_addr addr;
-       char *https_name;
+       char *addr_name;
 
        struct http_client *client;
        struct http_client_peer *prev, *next;
@@ -189,7 +198,7 @@ struct http_client_queue {
        char *name;
 
        struct http_client_peer_addr addr;
-       char *https_name;
+       char *addr_name;
 
        /* current index in host->ips */
        unsigned int ips_connect_idx;
@@ -232,6 +241,8 @@ struct http_client_host {
 
        /* active DNS lookup */
        struct dns_lookup *dns_lookup;
+
+       unsigned int unix_local:1;
 };
 
 struct http_client {
@@ -249,6 +260,7 @@ struct http_client {
        struct connection_list *conn_list;
 
        HASH_TABLE_TYPE(http_client_host) hosts;
+       struct http_client_host *unix_host;
        struct http_client_host *hosts_list;
        HASH_TABLE_TYPE(http_client_peer) peers;
        struct http_client_peer *peers_list;
@@ -262,6 +274,8 @@ void http_client_request_ref(struct http_client_request *req);
 void http_client_request_unref(struct http_client_request **_req);
 int http_client_request_delay_from_response(struct http_client_request *req,
        const struct http_response *response);
+void http_client_request_get_peer_addr(const struct http_client_request *req,
+       struct http_client_peer_addr *addr);
 enum http_response_payload_type
 http_client_request_get_payload_type(struct http_client_request *req);
 int http_client_request_send(struct http_client_request *req,
@@ -378,12 +392,53 @@ void http_client_remove_request_error(struct http_client *client,
        struct http_client_request *req);
 
 
+static inline bool
+http_client_peer_addr_is_https(const struct http_client_peer_addr *addr)
+{
+       switch (addr->type) {
+       case HTTP_CLIENT_PEER_ADDR_HTTPS:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+               return TRUE;
+       default:
+               break;
+       }
+       return FALSE;
+}
+
+static inline const char *
+http_client_peer_addr_get_https_name(const struct http_client_peer_addr *addr)
+{
+       switch (addr->type) {
+       case HTTP_CLIENT_PEER_ADDR_HTTPS:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+               return addr->a.tcp.https_name;
+       default:
+               break;
+       }
+       return NULL;
+}
+
 static inline const char *
 http_client_peer_addr2str(const struct http_client_peer_addr *addr)
 {
-       if (addr->ip.family == AF_INET6)
-               return t_strdup_printf("[%s]:%u", net_ip2addr(&addr->ip), addr->port);
-       return t_strdup_printf("%s:%u", net_ip2addr(&addr->ip), addr->port);
+       switch (addr->type) {
+       case HTTP_CLIENT_PEER_ADDR_HTTP:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS:
+       case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
+       case HTTP_CLIENT_PEER_ADDR_RAW:
+               if (addr->a.tcp.ip.family == AF_INET6) {
+                       return t_strdup_printf("[%s]:%u",
+                               net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port);
+               }
+               return t_strdup_printf("%s:%u",
+                       net_ip2addr(&addr->a.tcp.ip), addr->a.tcp.port);
+       case HTTP_CLIENT_PEER_ADDR_UNIX:
+               return t_strdup_printf("unix:%s", addr->a.un.path);
+       default:
+               break;
+       }
+       i_unreached();
+       return "";
 }
 
 static inline const char *
@@ -396,29 +451,6 @@ http_client_request_label(struct http_client_request *req)
        return req->label;
 }
 
-static inline void
-http_client_request_get_peer_addr(const struct http_client_request *req,
-       struct http_client_peer_addr *addr)
-{
-       const struct http_url *host_url = req->host_url;
-       
-       memset(addr, 0, sizeof(*addr));
-       if (req->connect_direct) {
-               addr->type = HTTP_CLIENT_PEER_ADDR_RAW;
-               addr->port = (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT);
-       } else if (host_url->have_ssl) {
-               if (req->ssl_tunnel)
-                       addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL;
-               else
-                       addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS;
-               addr->https_name = host_url->host_name;
-               addr->port = (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT);
-       } else {
-               addr->type = HTTP_CLIENT_PEER_ADDR_HTTP;
-               addr->port = (host_url->have_port ? host_url->port : HTTP_DEFAULT_PORT);
-       }
-}
-
 static inline bool
 http_client_request_to_proxy(const struct http_client_request *req)
 {
index b839d99efb2368be13d91fab8fd4b413476ed2fb..6db15a4d1f6b10205832ebbb00a10322b3697703 100644 (file)
@@ -62,8 +62,7 @@ http_client_queue_find(struct http_client_host *host,
        array_foreach_modifiable(&host->queues, queue_idx) {
                struct http_client_queue *queue = *queue_idx;
 
-               if (queue->addr.type == addr->type && queue->addr.port == addr->port &&
-                   null_strcmp(queue->addr.https_name, addr->https_name) == 0)
+               if (http_client_peer_addr_cmp(&queue->addr, addr) == 0)
                        return queue;
        }
 
@@ -78,30 +77,38 @@ http_client_queue_create(struct http_client_host *host,
 
        queue = http_client_queue_find(host, addr);
        if (queue == NULL) {
-               char *name;
+               queue = i_new(struct http_client_queue, 1);
+               queue->client = host->client;
+               queue->host = host;
+               queue->addr = *addr;
 
                switch (addr->type) {
                case HTTP_CLIENT_PEER_ADDR_RAW:
-                       name = i_strdup_printf("raw://%s:%u", host->name, addr->port);
+                       queue->name =
+                               i_strdup_printf("raw://%s:%u", host->name, addr->a.tcp.port);
+                       queue->addr.a.tcp.https_name = NULL;
                        break;
                case HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL:
                case HTTP_CLIENT_PEER_ADDR_HTTPS:
-                       name = i_strdup_printf("https://%s:%u", host->name, addr->port);
+                       queue->name =
+                               i_strdup_printf("https://%s:%u", host->name, addr->a.tcp.port);
+                       queue->addr_name = i_strdup(addr->a.tcp.https_name);
+                       queue->addr.a.tcp.https_name = queue->addr_name;
                        break;
                case HTTP_CLIENT_PEER_ADDR_HTTP:
-                       name = i_strdup_printf("http://%s:%u", host->name, addr->port);
+                       queue->name =
+                               i_strdup_printf("http://%s:%u", host->name, addr->a.tcp.port);
+                       queue->addr.a.tcp.https_name = NULL;
+                       break;
+               case HTTP_CLIENT_PEER_ADDR_UNIX:
+                       queue->name = i_strdup_printf("unix:%s", addr->a.un.path);
+                       queue->addr_name = i_strdup(addr->a.un.path);
+                       queue->addr.a.un.path = queue->addr_name;
                        break;
                default:
                        i_unreached();
                }
 
-               queue = i_new(struct http_client_queue, 1);
-               queue->client = host->client;
-               queue->host = host;
-               queue->addr = *addr;
-               queue->https_name = i_strdup(addr->https_name);
-               queue->addr.https_name = queue->https_name;
-               queue->name = name;
                queue->ips_connect_idx = 0;
                i_array_init(&queue->requests, 16);
                i_array_init(&queue->queued_requests, 16);
@@ -117,7 +124,6 @@ void http_client_queue_free(struct http_client_queue *queue)
 {
        http_client_queue_fail
                (queue, HTTP_CLIENT_REQUEST_ERROR_ABORTED, "Aborted");
-       i_free(queue->https_name);
        if (array_is_created(&queue->pending_peers))
                array_free(&queue->pending_peers);
        array_free(&queue->requests);
@@ -128,6 +134,7 @@ void http_client_queue_free(struct http_client_queue *queue)
                timeout_remove(&queue->to_connect);
        if (queue->to_delayed != NULL)
                timeout_remove(&queue->to_delayed);
+       i_free(queue->addr_name);
        i_free(queue->name);
        i_free(queue);
 }
@@ -169,6 +176,7 @@ http_client_queue_is_last_connect_ip(struct http_client_queue *queue)
                &queue->client->set;
        struct http_client_host *host = queue->host;
 
+       i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX);
        i_assert(queue->ips_connect_idx < host->ips_count);
        i_assert(queue->ips_connect_start_idx < host->ips_count);
 
@@ -190,6 +198,9 @@ http_client_queue_soft_connect_timeout(struct http_client_queue *queue)
 {
        struct http_client_host *host = queue->host;
        const struct http_client_peer_addr *addr = &queue->addr;
+       const char *https_name;
+
+       i_assert(queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX);
 
        if (queue->to_connect != NULL)
                timeout_remove(&queue->to_connect);
@@ -202,10 +213,11 @@ http_client_queue_soft_connect_timeout(struct http_client_queue *queue)
        /* if our our previous connection attempt takes longer than the
           soft_connect_timeout, we start a connection attempt to the next IP in
           parallel */
+       https_name = http_client_peer_addr_get_https_name(addr);
        http_client_queue_debug(queue, "Connection to %s%s is taking a long time; "
                "starting parallel connection attempt to next IP",
-               http_client_peer_addr2str(addr), addr->https_name == NULL ? "" :
-                       t_strdup_printf(" (SSL=%s)", addr->https_name)); 
+               http_client_peer_addr2str(addr), (https_name == NULL ? "" :
+                       t_strdup_printf(" (SSL=%s)", https_name)));
 
        /* next IP */
        queue->ips_connect_idx = (queue->ips_connect_idx + 1) % host->ips_count;
@@ -222,18 +234,22 @@ void http_client_queue_connection_setup(struct http_client_queue *queue)
        unsigned int num_requests =
                array_count(&queue->queued_requests) +
                array_count(&queue->queued_urgent_requests);
+       const char *ssl = "";
 
        if (num_requests == 0)
                return;
 
        /* update our peer address */
-       i_assert(queue->ips_connect_idx < host->ips_count);
-       queue->addr.ip = host->ips[queue->ips_connect_idx];
+       if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
+               i_assert(queue->ips_connect_idx < host->ips_count);
+               queue->addr.a.tcp.ip = host->ips[queue->ips_connect_idx];
+               ssl = http_client_peer_addr_get_https_name(addr);
+               ssl = (ssl == NULL ? "" : t_strdup_printf(" (SSL=%s)", ssl));
+       }
 
        http_client_queue_debug(queue, "Setting up connection to %s%s "
-               "(%u requests pending)", http_client_peer_addr2str(addr),
-               (addr->https_name == NULL ? "" :
-                       t_strdup_printf(" (SSL=%s)", addr->https_name)), num_requests);
+               "(%u requests pending)", http_client_peer_addr2str(addr), ssl,
+               num_requests);
 
 
        /* create/get peer */
@@ -263,19 +279,20 @@ void http_client_queue_connection_setup(struct http_client_queue *queue)
                }
                if (new_peer) {
                        http_client_queue_debug(queue, "Started new connection to %s%s",
-                               http_client_peer_addr2str(addr), (addr->https_name == NULL ? "" :
-                       t_strdup_printf(" (SSL=%s)", addr->https_name)));
+                               http_client_peer_addr2str(addr), ssl);
 
                        array_append(&queue->pending_peers, &peer, 1);
                        queue->connect_attempts++;
                }
 
                /* start soft connect time-out (but only if we have another IP left) */
-               msecs = host->client->set.soft_connect_timeout_msecs;
-               if (!http_client_queue_is_last_connect_ip(queue) && msecs > 0 &&
-                       queue->to_connect == NULL) {
-                       queue->to_connect =
-                               timeout_add(msecs, http_client_queue_soft_connect_timeout, queue);
+               if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
+                       msecs = host->client->set.soft_connect_timeout_msecs;
+                       if (!http_client_queue_is_last_connect_ip(queue) && msecs > 0 &&
+                               queue->to_connect == NULL) {
+                               queue->to_connect =
+                                       timeout_add(msecs, http_client_queue_soft_connect_timeout, queue);
+                       }
                }
        }
 }
@@ -284,9 +301,11 @@ void
 http_client_queue_connection_success(struct http_client_queue *queue,
                                         const struct http_client_peer_addr *addr)
 {
-       /* we achieved at least one connection the the addr->ip */
-       queue->ips_connect_start_idx =
-               http_client_host_get_ip_idx(queue->host, &addr->ip);
+       if (queue->addr.type != HTTP_CLIENT_PEER_ADDR_UNIX) {
+               /* we achieved at least one connection the the addr->ip */
+               queue->ips_connect_start_idx =
+                       http_client_host_get_ip_idx(queue->host, &addr->a.tcp.ip);
+       }
 
        /* reset attempt counter */
        queue->connect_attempts = 0;
@@ -325,14 +344,16 @@ http_client_queue_connection_failure(struct http_client_queue *queue,
 {
        const struct http_client_settings *set =
                &queue->client->set;
+       const char *https_name = http_client_peer_addr_get_https_name(addr);
        struct http_client_host *host = queue->host;
 
-       http_client_queue_debug(queue, "Failed to set up connection to %s%s: %s "
+       http_client_queue_debug(queue,
+               "Failed to set up connection to %s%s: %s "
                "(%u peers pending, %u requests pending)",
                http_client_peer_addr2str(addr),
-               (addr->https_name == NULL ? "" :
-                       t_strdup_printf(" (SSL=%s)", addr->https_name)), reason,
-               (array_is_created(&queue->pending_peers) ?
+               (https_name == NULL ? "" :
+                       t_strdup_printf(" (SSL=%s)", https_name)),
+               reason, (array_is_created(&queue->pending_peers) ?
                        array_count(&queue->pending_peers): 0),
                array_count(&queue->requests));
 
@@ -364,6 +385,12 @@ http_client_queue_connection_failure(struct http_client_queue *queue,
        if (queue->to_connect != NULL)
                timeout_remove(&queue->to_connect);
 
+       if (queue->addr.type == HTTP_CLIENT_PEER_ADDR_UNIX) {
+               http_client_queue_fail(queue,
+                       HTTP_CLIENT_REQUEST_ERROR_CONNECT_FAILED, reason);
+               return;
+       }
+
        if (http_client_queue_is_last_connect_ip(queue)) {
                /* all IPs failed, but retry all of them again if we have more
                   connect attempts left or on the next request. */
index cfa08b428451e2c37b96d201b0b30c1f8c2f0105..96fe434fa68f7d1bf910ed9ef1c5bd07f1cc27f1 100644 (file)
@@ -442,7 +442,9 @@ static void http_client_request_do_submit(struct http_client_request *req)
 {
        struct http_client *client = req->client;
        struct http_client_host *host;
+       const char *proxy_socket_path = client->set.proxy_socket_path;
        const struct http_url *proxy_url = client->set.proxy_url;
+       bool have_proxy = (proxy_socket_path != NULL) || (proxy_url != NULL);
        const char *authority, *target;
 
        i_assert(req->state == HTTP_REQUEST_STATE_NEW);
@@ -458,16 +460,20 @@ static void http_client_request_do_submit(struct http_client_request *req)
        }
 
        /* determine what host to contact to submit this request */
-       if (proxy_url != NULL) {
+       if (have_proxy) {
                if (req->origin_url.have_ssl && !client->set.no_ssl_tunnel &&
                        !req->connect_tunnel) {
-                       req->host_url = &req->origin_url;  /* tunnel to origin server */
+                       req->host_url = &req->origin_url;           /* tunnel to origin server */
                        req->ssl_tunnel = TRUE;
+               } else if (proxy_socket_path != NULL) {
+                       req->host_socket = proxy_socket_path;       /* proxy on unix socket */
+                       req->host_url = NULL;
                } else {
-                       req->host_url = proxy_url;         /* proxy server */
+                       req->host_url = proxy_url;                  /* normal proxy server */
+                       req->host_socket = NULL;
                }
        } else {
-               req->host_url = &req->origin_url;    /* origin server */
+               req->host_url = &req->origin_url;             /* origin server */
        }
 
        /* use submission date if no date is set explicitly */
@@ -481,10 +487,10 @@ static void http_client_request_do_submit(struct http_client_request *req)
        req->label = p_strdup_printf(req->pool, "[%s %s]", req->method, target);
 
        /* update request target */
-       if (req->connect_tunnel || proxy_url != NULL)
+       if (req->connect_tunnel || have_proxy)
                req->target = p_strdup(req->pool, target);
 
-       if (proxy_url == NULL) {
+       if (!have_proxy) {
                /* if we don't have a proxy, CONNECT requests are handled by creating
                   the requested connection directly */
                req->connect_direct = req->connect_tunnel;
@@ -522,6 +528,36 @@ void http_client_request_submit(struct http_client_request *req)
        client->requests_count++;
 }
 
+void
+http_client_request_get_peer_addr(const struct http_client_request *req,
+       struct http_client_peer_addr *addr)
+{
+       const char *host_socket = req->host_socket;
+       const struct http_url *host_url = req->host_url;
+       
+       memset(addr, 0, sizeof(*addr));
+       if (host_socket != NULL) {
+               addr->type = HTTP_CLIENT_PEER_ADDR_UNIX;
+               addr->a.un.path = host_socket;          
+       } else if (req->connect_direct) {
+               addr->type = HTTP_CLIENT_PEER_ADDR_RAW;
+               addr->a.tcp.port =
+                       (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT);
+       } else if (host_url->have_ssl) {
+               if (req->ssl_tunnel)
+                       addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS_TUNNEL;
+               else
+                       addr->type = HTTP_CLIENT_PEER_ADDR_HTTPS;
+               addr->a.tcp.https_name = host_url->host_name;
+               addr->a.tcp.port =
+                       (host_url->have_port ? host_url->port : HTTPS_DEFAULT_PORT);
+       } else {
+               addr->type = HTTP_CLIENT_PEER_ADDR_HTTP;
+               addr->a.tcp.port =
+                       (host_url->have_port ? host_url->port : HTTP_DEFAULT_PORT);
+       }
+}
+
 static void
 http_client_request_finish_payload_out(struct http_client_request *req)
 {
index 064082c7e36d5ff9358287154a0f053064028501..016a05dcff0e87b195b2b92a707f695585f96f7a 100644 (file)
@@ -51,9 +51,11 @@ 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 */
+       /* proxy on unix socket */
+       const char *proxy_socket_path;
+       /* URL for normal proxy (ignored if proxy_socket_path is set) */   
        const struct http_url *proxy_url;
+       /* credentials for proxy */
        const char *proxy_username;
        const char *proxy_password;