]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: client: Moved connection pool handling from peer to separate object.
authorStephan Bosch <stephan.bosch@dovecot.fi>
Tue, 29 Nov 2016 18:29:25 +0000 (19:29 +0100)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Thu, 7 Dec 2017 16:40:45 +0000 (18:40 +0200)
src/lib-http/http-client-connection.c
src/lib-http/http-client-peer.c
src/lib-http/http-client-private.h

index be9eadb8a275f830a39883aad73be5f17a419543..f0d314bb421520d05aad8f945edf64ff46d20fd0 100644 (file)
@@ -351,6 +351,7 @@ http_client_connection_idle_timeout(struct http_client_connection *conn)
 
 void http_client_connection_check_idle(struct http_client_connection *conn)
 {
+       struct http_client_peer_pool *ppool = conn->ppool;
        unsigned int timeout, count;
 
        if (conn->connected &&
@@ -376,11 +377,11 @@ void http_client_connection_check_idle(struct http_client_connection *conn)
                        /* instant death for (urgent) connections above limit */
                        timeout = 0;
                } else {
-                       unsigned int idle_count = http_client_peer_idle_connections(conn->peer);
+                       unsigned int idle_count = array_count(&ppool->idle_conns);
 
                        /* kill duplicate connections quicker;
                                 linearly based on the number of connections */
-                       i_assert(count >= idle_count + 1);
+                       i_assert(array_count(&ppool->conns) >= idle_count + 1);
                        timeout = (conn->client->set.max_parallel_connections - idle_count) *
                                (conn->client->set.max_idle_time_msecs /
                                        conn->client->set.max_parallel_connections);
@@ -393,12 +394,41 @@ void http_client_connection_check_idle(struct http_client_connection *conn)
                conn->to_idle =
                        timeout_add(timeout, http_client_connection_idle_timeout, conn);
 
+               array_append(&ppool->idle_conns, &conn, 1);
+
        } else {
                /* there should be no idle timeout */
                i_assert(conn->to_idle == NULL);
        }
 }
 
+static void
+http_client_connection_stop_idle(struct http_client_connection *conn)
+{
+       struct http_client_connection *const *conn_idx;
+       ARRAY_TYPE(http_client_connection) *conn_arr;
+
+       if (conn->to_idle != NULL)
+               timeout_remove(&conn->to_idle);
+
+       conn_arr = &conn->ppool->idle_conns;
+       array_foreach(conn_arr, conn_idx) {
+               if (*conn_idx == conn) {
+                       array_delete(conn_arr, array_foreach_idx(conn_arr, conn_idx), 1);
+                       break;
+               }
+       }
+}
+
+void http_client_connection_use_idle(struct http_client_connection *conn,
+       struct http_client_peer *peer)
+{
+       http_client_connection_debug(conn, "Reusing idle connection");
+
+       i_assert(peer->ppool == conn->ppool);
+       http_client_connection_stop_idle(conn);
+}
+
 static void
 http_client_connection_request_timeout(struct http_client_connection *conn)
 {
@@ -497,7 +527,7 @@ int http_client_connection_next_request(struct http_client_connection *conn)
 
        i_assert(req->state == HTTP_REQUEST_STATE_QUEUED);
 
-       timeout_remove(&conn->to_idle);
+       http_client_connection_stop_idle(conn);
 
        req->payload_sync_continue = FALSE;
        if (conn->peer->no_payload_sync)
@@ -1441,6 +1471,7 @@ struct http_client_connection *
 http_client_connection_create(struct http_client_peer *peer)
 {
        struct http_client_context *cctx = peer->client->cctx;
+       struct http_client_peer_pool *ppool = peer->ppool;
        struct http_client_connection *conn;
        static unsigned int id = 0;
        const struct http_client_peer_addr *addr = &peer->addr;
@@ -1468,6 +1499,7 @@ http_client_connection_create(struct http_client_peer *peer)
        conn->refcount = 1;
        conn->client = peer->client;
        conn->id = id++;
+       conn->ppool = ppool;
        conn->peer = peer;
        if (peer->addr.type != HTTP_CLIENT_PEER_ADDR_RAW)
                i_array_init(&conn->request_wait_list, 16);
@@ -1495,10 +1527,14 @@ http_client_connection_create(struct http_client_peer *peer)
        }
 
        array_append(&peer->conns, &conn, 1);
+       array_append(&ppool->conns, &conn, 1);
+
+       http_client_peer_pool_ref(ppool);
+       http_client_peer_ref(peer);
 
        http_client_connection_debug(conn,
                "%s connection created (%d parallel connections exist)%s",
-               conn_type, array_count(&peer->conns),
+               conn_type, array_count(&ppool->conns),
                (conn->to_input == NULL ? "" : " [broken]"));
        return conn;
 }
@@ -1513,6 +1549,7 @@ static void
 http_client_connection_disconnect(struct http_client_connection *conn)
 {
        struct http_client_peer *peer = conn->peer;
+       struct http_client_peer_pool *ppool = conn->ppool;
        ARRAY_TYPE(http_client_connection) *conn_arr;
        struct http_client_connection *const *conn_idx;
 
@@ -1551,6 +1588,13 @@ http_client_connection_disconnect(struct http_client_connection *conn)
        timeout_remove(&conn->to_response);
 
        /* remove this connection from the list */
+       conn_arr = &ppool->conns;
+       array_foreach(conn_arr, conn_idx) {
+               if (*conn_idx == conn) {
+                       array_delete(conn_arr, array_foreach_idx(conn_arr, conn_idx), 1);
+                       break;
+               }
+       }
        conn_arr = &peer->conns;
        array_foreach(conn_arr, conn_idx) {
                if (*conn_idx == conn) {
@@ -1561,11 +1605,15 @@ http_client_connection_disconnect(struct http_client_connection *conn)
 
        if (conn->connect_succeeded)
                http_client_peer_connection_lost(peer, conn->lost_prematurely);
+
+       http_client_connection_stop_idle(conn); // FIXME: needed?
 }
 
 bool http_client_connection_unref(struct http_client_connection **_conn)
 {
        struct http_client_connection *conn = *_conn;
+       struct http_client_peer *peer = conn->peer;
+       struct http_client_peer_pool *ppool = conn->ppool;
 
        i_assert(conn->refcount > 0);
 
@@ -1596,6 +1644,9 @@ bool http_client_connection_unref(struct http_client_connection **_conn)
        
        i_free(conn->label);
        i_free(conn);
+
+       http_client_peer_pool_unref(&ppool);
+       http_client_peer_unref(&peer);
        return FALSE;
 }
 
index 98d2ae8c533608f7cec49e935867d62bcad74f1b..8808bb75b0880f9b733c968438ae865dc5cd6188 100644 (file)
  * Logging
  */
 
+/* Peer pool */
+
+static inline void
+http_client_peer_pool_debug(struct http_client_peer_pool *ppool,
+       const char *format, ...) ATTR_FORMAT(2, 3);
+
+static inline void
+http_client_peer_pool_debug(struct http_client_peer_pool *ppool,
+       const char *format, ...)
+{
+       struct http_client_peer *peer = ppool->peer;
+       va_list args;
+
+       if (peer->client->cctx->set.debug) {
+               va_start(args, format);
+               i_debug("http-client: peer %s: %s",
+                       http_client_peer_label(peer), t_strdup_vprintf(format, args));
+               va_end(args);
+       }
+}
+
+/* Peer */
+
 static inline void
 http_client_peer_debug(struct http_client_peer *peer,
        const char *format, ...) ATTR_FORMAT(2, 3);
@@ -99,6 +122,77 @@ int http_client_peer_addr_cmp
        i_unreached();
 }
 
+/*
+ * Peer pool
+ */
+
+static struct http_client_peer_pool *
+http_client_peer_pool_create(struct http_client_peer *peer)
+{
+       struct http_client_peer_pool *ppool;
+
+       ppool = i_new(struct http_client_peer_pool, 1);
+       ppool->refcount = 1;
+       ppool->peer = peer;
+
+       i_array_init(&ppool->conns, 16);
+       i_array_init(&ppool->idle_conns, 16);
+
+       http_client_peer_pool_debug(ppool, "Peer pool created");
+
+       return ppool;
+}
+
+void http_client_peer_pool_ref(struct http_client_peer_pool *ppool)
+{
+       if (ppool->destroyed)
+               return;
+       ppool->refcount++;
+}
+
+void http_client_peer_pool_close(struct http_client_peer_pool **_ppool)
+{
+       struct http_client_peer_pool *ppool = *_ppool;
+       struct http_client_connection **conn;
+       ARRAY_TYPE(http_client_connection) conns;
+
+       http_client_peer_pool_ref(ppool);
+
+       /* make a copy of the connection array; freed connections modify it */
+       t_array_init(&conns, array_count(&ppool->conns));
+       array_copy(&conns.arr, 0, &ppool->conns.arr, 0, array_count(&ppool->conns));
+       array_foreach_modifiable(&conns, conn)
+               http_client_connection_unref(conn);
+       i_assert(array_count(&ppool->idle_conns) == 0);
+       i_assert(array_count(&ppool->conns) == 0);
+
+       http_client_peer_pool_unref(_ppool);
+}
+
+void http_client_peer_pool_unref(struct http_client_peer_pool **_ppool)
+{
+       struct http_client_peer_pool *ppool = *_ppool;
+
+       *_ppool = NULL;
+
+       if (ppool->destroyed)
+               return;
+
+       i_assert(ppool->refcount > 0);
+       if (--ppool->refcount > 0)
+               return;
+
+       http_client_peer_pool_debug(ppool, "Peer pool destroy");
+       ppool->destroyed = TRUE;
+
+       i_assert(array_count(&ppool->idle_conns) == 0);
+       i_assert(array_count(&ppool->conns) == 0);
+       array_free(&ppool->idle_conns);
+       array_free(&ppool->conns);
+
+       i_free(ppool);
+}
+
 /*
  * Peer
  */
@@ -127,9 +221,23 @@ static void
 http_client_peer_do_connect(struct http_client_peer *peer,
        unsigned int count)
 {
-       unsigned int i;
+       struct http_client_peer_pool *ppool = peer->ppool;
+       struct http_client_connection *const *idle_conns;
+       unsigned int i, idle_count;
+
+       if (count == 0)
+               return;
+
+       idle_conns = array_get(&ppool->idle_conns, &idle_count);
+       for (i = 0; i < count && i < idle_count; i++) {
+               http_client_connection_use_idle(idle_conns[i], peer);
 
-       for (i = 0; i < count; i++) {
+               http_client_peer_debug(peer,
+                       "Using idle connection (connections=%u)",
+                       array_count(&peer->conns));
+       }
+
+       for (; i < count; i++) {
                http_client_peer_debug(peer,
                        "Making new connection %u of %u", i+1, count);
                (void)http_client_connection_create(peer);
@@ -215,6 +323,9 @@ bool http_client_peer_is_connected(struct http_client_peer *peer)
 {
        struct http_client_connection *const *conn_idx;
 
+       if (array_count(&peer->ppool->idle_conns) > 0)
+               return TRUE;
+
        array_foreach(&peer->conns, conn_idx) {
                if ((*conn_idx)->connected)
                        return TRUE;
@@ -574,6 +685,8 @@ http_client_peer_create(struct http_client *client,
                (client->peers, (const struct http_client_peer_addr *)&peer->addr, peer);
        DLLIST_PREPEND(&client->peers_list, peer);
 
+       peer->ppool = http_client_peer_pool_create(peer);
+
        http_client_peer_debug(peer, "Peer created");
        return peer;
 }
@@ -634,6 +747,7 @@ bool http_client_peer_unref(struct http_client_peer **_peer)
        http_client_peer_debug(peer, "Peer destroy");
 
        http_client_peer_disconnect(peer);
+       http_client_peer_pool_unref(&peer->ppool);
 
        i_assert(array_count(&peer->queues) == 0);
 
@@ -642,6 +756,7 @@ bool http_client_peer_unref(struct http_client_peer **_peer)
        i_free(peer->addr_name);
        i_free(peer->label);
        i_free(peer);
+
        return FALSE;
 }
 
index 18b8602ae1ba0b261652e27b24153445b890bf74..8eec07fb09b403d631f8da83519658611fa2ad0e 100644 (file)
@@ -152,9 +152,11 @@ struct http_client_request {
 
 struct http_client_connection {
        struct connection conn;
+       unsigned int refcount;
+
+       struct http_client_peer_pool *ppool;
        struct http_client_peer *peer;
        struct http_client *client;
-       unsigned int refcount;
 
        char *label;
        unsigned int id; // DEBUG: identify parallel connections
@@ -193,6 +195,20 @@ struct http_client_connection {
        bool in_req_callback:1;     /* performing request callback (busy) */
 };
 
+struct http_client_peer_pool {
+       unsigned int refcount;
+       struct http_client_peer *peer;
+       struct http_client_peer_pool *prev, *next;
+
+       /* all connections to this peer */
+       ARRAY_TYPE(http_client_connection) conns;
+
+       /* available connections to this peer */
+       ARRAY_TYPE(http_client_connection) idle_conns;
+
+       bool destroyed:1;        /* peer pool is being destroyed */
+};
+
 struct http_client_peer {
        unsigned int refcount;
        struct http_client_peer_addr addr;
@@ -203,6 +219,8 @@ struct http_client_peer {
        struct http_client *client;
        struct http_client_peer *prev, *next;
 
+       struct http_client_peer_pool *ppool;
+
        /* queues using this peer */
        ARRAY_TYPE(http_client_queue) queues;
 
@@ -447,17 +465,30 @@ void http_client_connection_check_idle(struct http_client_connection *conn);
 void http_client_connection_switch_ioloop(struct http_client_connection *conn);
 void http_client_connection_start_tunnel(struct http_client_connection **_conn,
        struct http_client_tunnel *tunnel);
+void http_client_connection_use_idle(struct http_client_connection *conn,
+       struct http_client_peer *peer);
 
 /*
  * Peer
  */
 
+/* address */
+
 unsigned int http_client_peer_addr_hash
        (const struct http_client_peer_addr *peer) ATTR_PURE;
 int http_client_peer_addr_cmp
        (const struct http_client_peer_addr *peer1,
                const struct http_client_peer_addr *peer2) ATTR_PURE;
 
+/* connection pool */
+
+void http_client_peer_pool_ref(struct http_client_peer_pool *ppool);
+void http_client_peer_pool_unref(struct http_client_peer_pool **_ppool);
+
+void http_client_peer_pool_close(struct http_client_peer_pool **_ppool);
+
+/* peer */
+
 const char *
 http_client_peer_label(struct http_client_peer *peer);