]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: If http_client_request_submit() fails, don't immediately call the callback.
authorTimo Sirainen <tss@iki.fi>
Thu, 4 Apr 2013 11:11:10 +0000 (14:11 +0300)
committerTimo Sirainen <tss@iki.fi>
Thu, 4 Apr 2013 11:11:10 +0000 (14:11 +0300)
This simplifies the caller's error handling since there is now only one
error code path instead of two.

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

index b0fc8533d8c36d3009c8404428b555a3f35976dc..49614fd7daa393add10c772993d4abb7f5208459 100644 (file)
@@ -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);
+       }
+
 }
index 6002935ad818df4b4c5734595551085fa3a5a9e9..1d34098f82f8f0dbd76395248e41daad6c1fd27c 100644 (file)
@@ -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;
 
index 5db17fe3048638edd37ece7c58f581d2f77f230a..ffc2e93bd161cc0222da501b91101cd107985106 100644 (file)
@@ -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)