]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: Fixed hangs with urgent requests.
authorTimo Sirainen <tss@iki.fi>
Sun, 10 Mar 2013 15:19:14 +0000 (17:19 +0200)
committerTimo Sirainen <tss@iki.fi>
Sun, 10 Mar 2013 15:19:14 +0000 (17:19 +0200)
src/lib-http/http-client-connection.c
src/lib-http/http-client-peer.c

index baef85985e8018257bb6b702ccd17e3c3310d874..5dbf28ba20d7d1967a6ef993f33b94757d577b46 100644 (file)
@@ -61,9 +61,13 @@ static void http_client_connection_input(struct connection *_conn);
 
 bool http_client_connection_is_ready(struct http_client_connection *conn)
 {
+       unsigned int pending_count = array_count(&conn->request_wait_list);
+
+       if (conn->pending_request != NULL)
+               pending_count++;
        return (conn->connected && !conn->output_locked &&
-               !conn->close_indicated &&       array_count(&conn->request_wait_list) <
-                       conn->client->set.max_pipelined_requests);
+               !conn->close_indicated &&
+               pending_count < conn->client->set.max_pipelined_requests);
 }
 
 bool http_client_connection_is_idle(struct http_client_connection *conn)
@@ -224,6 +228,7 @@ bool http_client_connection_next_request(struct http_client_connection *conn)
 {
        struct http_client_request *req = NULL;
        const char *error;
+       bool have_pending_requests;
 
        if (!http_client_connection_is_ready(conn)) {
                http_client_connection_debug(conn, "Not ready for next request");
@@ -231,8 +236,9 @@ bool http_client_connection_next_request(struct http_client_connection *conn)
        }
 
        /* claim request, but no urgent request can be second in line */
-       req = http_client_peer_claim_request(conn->peer,
-               array_count(&conn->request_wait_list) > 0); 
+       have_pending_requests = array_count(&conn->request_wait_list) > 0 ||
+               conn->pending_request != NULL;
+       req = http_client_peer_claim_request(conn->peer, have_pending_requests);
        if (req == NULL) {
                http_client_connection_check_idle(conn);
                return FALSE;   
@@ -324,10 +330,12 @@ http_client_payload_destroyed_timeout(struct http_client_connection *conn)
        http_client_connection_input(&conn->conn);
 }
 
-static void http_client_payload_destroyed(struct http_client_connection *conn)
+static void http_client_payload_destroyed(struct http_client_request *req)
 {
+       struct http_client_connection *conn = req->conn;
+
+       i_assert(conn->pending_request == req);
        i_assert(conn->incoming_payload != NULL);
-       i_assert(conn->pending_request != NULL);
        i_assert(conn->conn.io == NULL);
 
        http_client_connection_debug(conn, "Response payload stream destroyed");
@@ -338,7 +346,7 @@ static void http_client_payload_destroyed(struct http_client_connection *conn)
 
        conn->incoming_payload = NULL;
 
-       http_client_request_finish(&conn->pending_request);
+       http_client_request_finish(&req);
        conn->pending_request = NULL;
 
        /* input stream may have pending input. make sure input handler
@@ -368,7 +376,7 @@ http_client_connection_return_response(struct http_client_connection *conn,
                        i_stream_create_limit(response->payload, (uoff_t)-1);
                i_stream_set_destroy_callback(response->payload,
                                              http_client_payload_destroyed,
-                                             conn);
+                                             req);
                /* the callback may add its own I/O, so we need to remove
                   our one before calling it */
                io_remove(&conn->conn.io);
index 23d8c0d5ff4c8d6224e923519ff9c0794d02e1a3..904d831bb4cbf7b4870ccb34d1dcae9f04a77a59 100644 (file)
@@ -133,14 +133,15 @@ http_client_peer_next_request(struct http_client_peer *peer)
        unsigned int connecting = 0, closing = 0, min_waiting = UINT_MAX;
        unsigned int num_urgent, new_connections;
 
-       /* at this point we already know that a request for this peer is pending
-        */
-       (void)http_client_peer_requests_pending(peer, &num_urgent);
+       if (http_client_peer_requests_pending(peer, &num_urgent) == 0)
+               return FALSE;
 
        /* find the least busy connection */
        array_foreach(&peer->conns, conn_idx) {
                if (http_client_connection_is_ready(*conn_idx)) {
                        unsigned int waiting = array_count(&(*conn_idx)->request_wait_list);
+                       if ((*conn_idx)->pending_request != NULL)
+                               waiting++;
                        if (waiting < min_waiting) {
                                min_waiting = waiting;
                                conn = *conn_idx;
@@ -315,8 +316,7 @@ void http_client_peer_add_host(struct http_client_peer *peer,
 
        if (!exists)
                array_append(&peer->hosts, &host, 1);
-       if (exists || array_count(&peer->hosts) > 1)
-               (void)http_client_peer_next_request(peer);
+       http_client_peer_handle_requests(peer);
 }
 
 struct http_client_request *
@@ -340,9 +340,14 @@ void http_client_peer_connection_failure(struct http_client_peer *peer)
 {
        struct http_client_host *const *host;
 
+       i_assert(array_count(&peer->conns) > 0);
+
        http_client_peer_debug(peer, "Failed to make connection");
 
        if (array_count(&peer->conns) == 1) {
+               /* this was the only/last connection and connecting to it
+                  failed. a second connect will probably also fail, so just
+                  abort all requests. */
                array_foreach(&peer->hosts, host) {
                        http_client_host_connection_failure(*host, &peer->addr);
                }
@@ -359,12 +364,11 @@ void http_client_peer_connection_lost(struct http_client_peer *peer)
        http_client_peer_debug(peer, "Lost a connection (%d connections left)",
                array_count(&peer->conns));
 
-       if (array_count(&peer->conns) == 0) {
-               if (!http_client_peer_next_request(peer)) {
-                       if (http_client_peer_requests_pending(peer, &num_urgent) == 0)
-                               http_client_peer_free(&peer);
-               }
-       }
+       /* if there are pending requests, create a new connection for them. */
+       http_client_peer_handle_requests(peer);
+       if (array_count(&peer->conns) == 0 &&
+           http_client_peer_requests_pending(peer, &num_urgent) == 0)
+               http_client_peer_free(&peer);
 }
 
 unsigned int http_client_peer_idle_connections(struct http_client_peer *peer)