changeset 17876:ecb0ba0ce02f

lib-http: client: Fixed problem occuring when a nested ioloop was run inside a request callback using the same client. If requests in the nested ioloop would use the same connection as the one that called the callback, the requests would (in the best scenario) all be doomed to time out.
author Stephan Bosch <stephan@rename-it.nl>
date Sat, 04 Oct 2014 17:30:54 +0300
parents dd25099bd633
children 9ce4d8d394cc
files src/lib-http/http-client-connection.c src/lib-http/http-client-private.h
diffstat 2 files changed, 16 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-http/http-client-connection.c	Sat Oct 04 00:29:30 2014 +0300
+++ b/src/lib-http/http-client-connection.c	Sat Oct 04 17:30:54 2014 +0300
@@ -53,13 +53,22 @@
 {
 	unsigned int pending_count = array_count(&conn->request_wait_list);
 
-	if (conn->pending_request != NULL)
+	if (conn->in_req_callback || conn->pending_request != NULL)
 		pending_count++;
 	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) <
@@ -184,6 +193,7 @@
 	if (conn->connected &&
 		array_is_created(&conn->request_wait_list) &&
 		array_count(&conn->request_wait_list) == 0 &&
+		!conn->in_req_callback &&
 		conn->incoming_payload == NULL &&
 		conn->client->set.max_idle_time_msecs > 0) {
 
@@ -437,6 +447,7 @@
 	struct istream *payload;
 	bool retrying;
 
+	i_assert(!conn->in_req_callback);
 	i_assert(conn->incoming_payload == NULL);
 	i_assert(conn->pending_request == NULL);
 
@@ -459,10 +470,12 @@
 		if (conn->to_requests != NULL)
 			timeout_remove(&conn->to_requests);
 	}
-
+	
+	conn->in_req_callback = TRUE;
 	http_client_connection_ref(conn);
 	retrying = !http_client_request_callback(req, response);
 	http_client_connection_unref(&conn);
+	conn->in_req_callback = FALSE;
 	if (conn == NULL) {
 		/* the callback managed to get this connection destroyed */
 		if (!retrying)
--- a/src/lib-http/http-client-private.h	Sat Oct 04 00:29:30 2014 +0300
+++ b/src/lib-http/http-client-private.h	Sat Oct 04 17:30:54 2014 +0300
@@ -142,6 +142,7 @@
 	unsigned int output_locked:1;       /* output is locked; no pipelining */
 	unsigned int payload_continue:1;    /* received 100-continue for current
 	                                        request */
+	unsigned int in_req_callback:1;  /* performin request callback (busy) */
 };
 
 struct http_client_peer {