client: fix spurious keepalive connection timeouts openwrt-19.07
authorJo-Philipp Wich <jo@mein.io>
Thu, 1 Oct 2020 12:59:09 +0000 (14:59 +0200)
committerJo-Philipp Wich <jo@mein.io>
Fri, 20 Nov 2020 21:49:34 +0000 (22:49 +0100)
When an uhttpd dispatch_handler provides a data_done callback which is
synchroneously finishing the request through ops->request_done(), the
calling client_poll_post_data() procedure incorrectly resets the resulting
client state from CLIENT_STATE_INIT to CLIENT_STATE_DONE which causes the
next uh_client_read_cb() invocation to bail out since no callback is
available for the CLIENT_STATE_DONE state, effectively discarding the
just received inbound request and sending the persistent connection state
into a deadlock sitation where the http client waits for a response to
its just sent request and uhttpd for further data to read.

Fix this issue by only setting CLIENT_STATE_DONE if the data_done callback
has not modified the state in the meanwhile.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
(cherry picked from commit 0f38b0370718518e66dabeaa5522f546cb459393)

client.c

index 2a2393f66a2750335148849b97435410eaf40de9..204cebcf6089377a4d5bd5ddf221b62d0ad9334b 100644 (file)
--- a/client.c
+++ b/client.c
@@ -395,6 +395,7 @@ void client_poll_post_data(struct client *cl)
 {
        struct dispatch *d = &cl->dispatch;
        struct http_request *r = &cl->request;
+       enum client_state st;
        char *buf;
        int len;
 
@@ -459,10 +460,13 @@ void client_poll_post_data(struct client *cl)
        buf = ustream_get_read_buf(cl->us, &len);
        if (!r->content_length && !r->transfer_chunked &&
                cl->state != CLIENT_STATE_DONE) {
+               st = cl->state;
+
                if (cl->dispatch.data_done)
                        cl->dispatch.data_done(cl);
 
-               cl->state = CLIENT_STATE_DONE;
+               if (cl->state == st)
+                       cl->state = CLIENT_STATE_DONE;
        }
 }