]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: Fixed detecting disconnection when ioloop is running only intermittently.
authorStephan Bosch <stephan@rename-it.nl>
Sat, 4 Oct 2014 14:32:48 +0000 (17:32 +0300)
committerStephan Bosch <stephan@rename-it.nl>
Sat, 4 Oct 2014 14:32:48 +0000 (17:32 +0300)
This fix only applies to ioloops created and run by lib-http itself.

src/lib-http/http-client-connection.c
src/lib-http/http-client-private.h
src/lib-http/http-client-request.c

index 6089fc58b1c500ef7b480d7f42dfa1cffeba1e15..0c69ac54f1b9c811c11b1acec953b40388599e86 100644 (file)
@@ -58,23 +58,6 @@ http_client_connection_count_pending(struct http_client_connection *conn)
        return pending_count;
 }
 
-bool http_client_connection_is_ready(struct http_client_connection *conn)
-{
-       if (conn->in_req_callback) {
-               /* this can happen when a nested ioloop is created inside request
-                  callback. we currently don't reuse connections that are occupied
-                  this way, but theoretically we could, although that would add
-                  quite a bit of complexity.
-                */
-               return FALSE;
-       }
-
-       return (conn->connected && !conn->output_locked &&
-               !conn->close_indicated && !conn->tunneling &&
-               http_client_connection_count_pending(conn) <
-                       conn->client->set.max_pipelined_requests);
-}
-
 bool http_client_connection_is_idle(struct http_client_connection *conn)
 {
        return (conn->to_idle != NULL);
@@ -174,6 +157,48 @@ http_client_connection_abort_temp_error(struct http_client_connection **_conn,
        http_client_connection_unref(_conn);
 }
 
+bool http_client_connection_is_ready(struct http_client_connection *conn)
+{
+       int ret;
+
+       if (conn->in_req_callback) {
+               /* this can happen when a nested ioloop is created inside request
+                  callback. we currently don't reuse connections that are occupied
+                  this way, but theoretically we could, although that would add
+                  quite a bit of complexity.
+                */
+               return FALSE;
+       }
+
+       if (!conn->connected || conn->output_locked ||
+               conn->close_indicated || conn->tunneling ||
+               http_client_connection_count_pending(conn) >=
+                       conn->client->set.max_pipelined_requests)
+               return FALSE;
+
+       if (conn->last_ioloop != NULL && conn->last_ioloop != current_ioloop) {
+               conn->last_ioloop = current_ioloop;
+               /* Active ioloop is different from what we saw earlier;
+                  we may have missed a disconnection event on this connection.
+                  Verify status by reading from connection. */
+               if ((ret=i_stream_read(conn->conn.input)) < 0) {
+                       int stream_errno = conn->conn.input->stream_errno;
+
+                       i_assert(ret != -2);
+                       i_assert(conn->conn.input->stream_errno != 0 || conn->conn.input->eof);
+                       http_client_connection_abort_temp_error(&conn,
+                               HTTP_CLIENT_REQUEST_ERROR_CONNECTION_LOST,
+                               t_strdup_printf("Connection lost: read(%s) failed: %s",
+                                               i_stream_get_name(conn->conn.input),
+                                               stream_errno != 0 ?
+                                               i_stream_get_error(conn->conn.input) :
+                                               "EOF"));
+                       return FALSE;
+               }
+       }
+       return TRUE;
+}
+
 static void
 http_client_connection_idle_timeout(struct http_client_connection *conn)
 {
@@ -802,6 +827,7 @@ http_client_connection_ready(struct http_client_connection *conn)
 {
        /* connected */
        conn->connected = TRUE;
+       conn->last_ioloop = current_ioloop;
        if (conn->to_connect != NULL &&
            (conn->ssl_iostream == NULL ||
             ssl_iostream_is_handshaked(conn->ssl_iostream)))
index 75be4271cf4165063e7f33822863e8dcffbf0c6d..62b517478057d2ee7d9002369833f597c277cd0e 100644 (file)
@@ -131,6 +131,7 @@ struct http_client_connection {
        struct http_client_request *pending_request;
        struct istream *incoming_payload;
        struct io *io_req_payload;
+       struct ioloop *last_ioloop;
 
        /* requests that have been sent, waiting for response */
        ARRAY_TYPE(http_client_request) request_wait_list;
index 7ba37a54e868af2db61e67ce508ec623760baa06..f33a445a224f90f953848dcd01cbe107831bd383 100644 (file)
@@ -792,21 +792,27 @@ static int http_client_request_send_real(struct http_client_request *req,
                                           o_stream_get_name(output),
                                           o_stream_get_error(output));
                ret = -1;
-       }
-
-       http_client_request_debug(req, "Sent header");
-
-       if (ret >= 0 && req->payload_output != NULL) {
-               if (!req->payload_sync) {
-                       if (http_client_request_send_more(req, error_r) < 0)
-                               ret = -1;
+       } else {
+               http_client_request_debug(req, "Sent header");
+
+               if (req->payload_output != NULL) {
+                       if (!req->payload_sync) {
+                               if (http_client_request_send_more(req, error_r) < 0)
+                                       ret = -1;
+                       } else {
+                               http_client_request_debug(req, "Waiting for 100-continue");
+                               conn->output_locked = TRUE;
+                       }
                } else {
-                       http_client_request_debug(req, "Waiting for 100-continue");
-                       conn->output_locked = TRUE;
+                       req->state = HTTP_REQUEST_STATE_WAITING;
+                       conn->output_locked = FALSE;
+               }
+               if (ret >= 0 && o_stream_flush(output) < 0) {
+                       *error_r = t_strdup_printf("flush(%s) failed: %s",
+                          o_stream_get_name(output),
+                          o_stream_get_error(output));
+                       ret = -1;
                }
-       } else {
-               req->state = HTTP_REQUEST_STATE_WAITING;
-               conn->output_locked = FALSE;
        }
        o_stream_uncork(output);
        return ret;