From: Timo Sirainen Date: Thu, 4 Apr 2013 11:11:10 +0000 (+0300) Subject: lib-http: If http_client_request_submit() fails, don't immediately call the callback. X-Git-Tag: 2.2.rc4~34 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6bc9fb43cc1ac24693d030a6cbfa43bc7cbc82cb;p=thirdparty%2Fdovecot%2Fcore.git lib-http: If http_client_request_submit() fails, don't immediately call the callback. This simplifies the caller's error handling since there is now only one error code path instead of two. --- diff --git a/src/lib-http/http-client-host.c b/src/lib-http/http-client-host.c index b0fc8533d8..49614fd7da 100644 --- a/src/lib-http/http-client-host.c +++ b/src/lib-http/http-client-host.c @@ -278,6 +278,7 @@ struct http_client_host *http_client_host_get host->client = client; host->name = i_strdup(hostname); i_array_init(&host->ports, 4); + i_array_init(&host->delayed_failing_requests, 1); hostname = host->name; hash_table_insert(client->hosts, hostname, host); @@ -397,6 +398,9 @@ void http_client_host_free(struct http_client_host **_host) } array_free(&host->ports); + i_assert(array_count(&host->delayed_failing_requests) == 0); + array_free(&host->delayed_failing_requests); + i_free(host->ips); i_free(host->name); i_free(host); @@ -404,6 +408,13 @@ void http_client_host_free(struct http_client_host **_host) void http_client_host_switch_ioloop(struct http_client_host *host) { + struct http_client_request **req; + if (host->dns_lookup != NULL) dns_lookup_switch_ioloop(host->dns_lookup); + array_foreach_modifiable(&host->delayed_failing_requests, req) { + (*req)->to_delayed_error = + io_loop_move_timeout(&(*req)->to_delayed_error); + } + } diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h index 6002935ad8..1d34098f82 100644 --- a/src/lib-http/http-client-private.h +++ b/src/lib-http/http-client-private.h @@ -50,6 +50,10 @@ struct http_client_request { unsigned int attempts; unsigned int redirects; + unsigned int delayed_error_status; + const char *delayed_error; + struct timeout *to_delayed_error; + http_client_request_callback_t *callback; void *context; @@ -63,6 +67,7 @@ struct http_client_request { unsigned int payload_wait:1; unsigned int ssl:1; unsigned int urgent:1; + unsigned int submitted:1; }; struct http_client_host_port { @@ -87,6 +92,9 @@ struct http_client_host { unsigned int ips_count; struct ip_addr *ips; + /* list of requests in this host that are waiting for ioloop */ + ARRAY(struct http_client_request *) delayed_failing_requests; + /* requests are managed on a per-port basis */ ARRAY_TYPE(http_client_host_port) ports; diff --git a/src/lib-http/http-client-request.c b/src/lib-http/http-client-request.c index 5db17fe304..ffc2e93bd1 100644 --- a/src/lib-http/http-client-request.c +++ b/src/lib-http/http-client-request.c @@ -81,6 +81,23 @@ void http_client_request_ref(struct http_client_request *req) req->refcount++; } +static void http_client_request_remove_delayed(struct http_client_request *req) +{ + struct http_client_request *const *reqs; + unsigned int i, count; + + timeout_remove(&req->to_delayed_error); + + reqs = array_get(&req->host->delayed_failing_requests, &count); + for (i = 0; i < count; i++) { + if (reqs[i] == req) { + array_delete(&req->host->delayed_failing_requests, i, 1); + return; + } + } + i_unreached(); +} + void http_client_request_unref(struct http_client_request **_req) { struct http_client_request *req = *_req; @@ -101,6 +118,8 @@ void http_client_request_unref(struct http_client_request **_req) if (client->pending_requests == 0 && client->ioloop != NULL) io_loop_stop(client->ioloop); + if (req->to_delayed_error != NULL) + http_client_request_remove_delayed(req); if (req->payload_input != NULL) i_stream_unref(&req->payload_input); if (req->payload_output != NULL) @@ -193,6 +212,7 @@ void http_client_request_submit(struct http_client_request *req) req->client->pending_requests++; http_client_request_do_submit(req); + req->submitted = TRUE; } static void @@ -454,11 +474,31 @@ http_client_request_send_error(struct http_client_request *req, } } +static void http_client_request_error_delayed(struct http_client_request *req) +{ + http_client_request_remove_delayed(req); + http_client_request_send_error(req, req->delayed_error_status, + req->delayed_error); + http_client_request_unref(&req); +} + void http_client_request_error(struct http_client_request *req, unsigned int status, const char *error) { - http_client_request_send_error(req, status, error); - http_client_request_unref(&req); + if (!req->submitted) { + /* we're still in http_client_request_submit(). delay + reporting the error, so the caller doesn't have to handle + immediate callbacks. */ + i_assert(req->delayed_error == NULL); + req->delayed_error = p_strdup(req->pool, error); + req->delayed_error_status = status; + req->to_delayed_error = timeout_add_short(0, + http_client_request_error_delayed, req); + array_append(&req->host->delayed_failing_requests, &req, 1); + } else { + http_client_request_send_error(req, status, error); + http_client_request_unref(&req); + } } void http_client_request_abort(struct http_client_request **_req)