Skip to content

Commit

Permalink
lib-http: client: Prevent useless and unexpected request callbacks du…
Browse files Browse the repository at this point in the history
…ring http_client_deinit().

Requests are now destroyed before queues, hosts, peers and connections.
As a side-effect, requests are now removed from the client request list at http_client_request_destroy(), so that requests with lingering references will no longer make http_client_wait() hang.
  • Loading branch information
stephanbosch committed May 26, 2016
1 parent c972eaa commit 6ab81c8
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/lib-http/http-client-private.h
Expand Up @@ -131,6 +131,7 @@ struct http_client_request {
unsigned int payload_wait:1;
unsigned int urgent:1;
unsigned int submitted:1;
unsigned int listed:1;
unsigned int connect_tunnel:1;
unsigned int connect_direct:1;
unsigned int ssl_tunnel:1;
Expand Down
42 changes: 33 additions & 9 deletions src/lib-http/http-client-request.c
Expand Up @@ -151,6 +151,32 @@ http_client_request_connect_ip(struct http_client *client,
return req;
}

static void
http_client_request_add(struct http_client_request *req)
{
struct http_client *client = req->client;

DLLIST_PREPEND(&client->requests_list, req);
client->requests_count++;
req->listed = TRUE;
}

static void
http_client_request_remove(struct http_client_request *req)
{
struct http_client *client = req->client;

if (req->listed) {
/* only decrease pending request counter if this request was submitted */
DLLIST_REMOVE(&client->requests_list, req);
client->requests_count--;
}
req->listed = FALSE;

if (client->requests_count == 0 && client->ioloop != NULL)
io_loop_stop(client->ioloop);
}

void http_client_request_ref(struct http_client_request *req)
{
i_assert(req->refcount > 0);
Expand Down Expand Up @@ -183,11 +209,7 @@ bool http_client_request_unref(struct http_client_request **_req)
req->destroy_callback = NULL;
}

/* only decrease pending request counter if this request was submitted */
if (req->submitted) {
DLLIST_REMOVE(&client->requests_list, req);
client->requests_count--;
}
http_client_request_remove(req);

if (client->requests_count == 0 && client->ioloop != NULL)
io_loop_stop(client->ioloop);
Expand All @@ -214,6 +236,10 @@ void http_client_request_destroy(struct http_client_request **_req)
http_client_request_debug(req, "Destroy (requests left=%d)",
client->requests_count);

if (req->state < HTTP_REQUEST_STATE_FINISHED)
req->state = HTTP_REQUEST_STATE_ABORTED;
req->callback = NULL;

if (req->queue != NULL)
http_client_queue_drop_request(req->queue, req);

Expand All @@ -223,6 +249,7 @@ void http_client_request_destroy(struct http_client_request **_req)
req->destroy_callback = NULL;
callback(req->destroy_context);
}
http_client_request_remove(req);
http_client_request_unref(&req);
}

Expand Down Expand Up @@ -589,16 +616,13 @@ static void http_client_request_do_submit(struct http_client_request *req)

void http_client_request_submit(struct http_client_request *req)
{
struct http_client *client = req->client;

req->submit_time = ioloop_timeval;

http_client_request_do_submit(req);
http_client_request_debug(req, "Submitted");

req->submitted = TRUE;
DLLIST_PREPEND(&client->requests_list, req);
client->requests_count++;
http_client_request_add(req);
}

void
Expand Down
24 changes: 12 additions & 12 deletions src/lib-http/http-client.c
Expand Up @@ -163,24 +163,20 @@ struct http_client *http_client_init(const struct http_client_settings *set)
void http_client_deinit(struct http_client **_client)
{
struct http_client *client = *_client;
struct http_client_request *req, *const *req_idx;
struct http_client_request *req;
struct http_client_host *host;
struct http_client_peer *peer;

*_client = NULL;

/* drop delayed failing requests */
while (array_count(&client->delayed_failing_requests) > 0) {
req_idx = array_idx(&client->delayed_failing_requests, 0);
req = *req_idx;

i_assert(req->refcount == 1);
http_client_request_error_delayed(&req);
/* destroy requests without calling callbacks */
req = client->requests_list;
while (req != NULL) {
struct http_client_request *next_req = req->next;
http_client_request_destroy(&req);
req = next_req;
}
array_free(&client->delayed_failing_requests);

if (client->to_failing_requests != NULL)
timeout_remove(&client->to_failing_requests);
i_assert(client->requests_count == 0);

/* free peers */
while (client->peers_list != NULL) {
Expand All @@ -196,6 +192,10 @@ void http_client_deinit(struct http_client **_client)
}
hash_table_destroy(&client->hosts);

array_free(&client->delayed_failing_requests);
if (client->to_failing_requests != NULL)
timeout_remove(&client->to_failing_requests);

connection_list_deinit(&client->conn_list);

if (client->ssl_ctx != NULL)
Expand Down

0 comments on commit 6ab81c8

Please sign in to comment.