fix chunked transfer encoding in keepalive mode
authorJo-Philipp Wich <jow@openwrt.org>
Wed, 7 Oct 2015 21:57:55 +0000 (23:57 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Wed, 7 Oct 2015 22:13:10 +0000 (00:13 +0200)
The two commits

  5162e3b0ee7bd1d0fd6e75e1ca7993a1834b5291
"allow request handlers to disable chunked reponses"

and

  618493e378e2239f0d30902e47adfa134e649fdc
"file: disable chunked encoding for file responses"

broke the chunked transfer encoding handling for proc responses in keep-alive
connections that followed a file response with http status 204 or 304.

The effect of this bug is that cgi responses following a 204 or 304 one where
sent neither in chunked encoding nor with a content-length header, causing
browsers to stall until the keep alive timeout was reached.

Fix the logic flaw by inverting the chunk prevention flag in the client state
and by testing the chunked encoding preconditions every time instead of
once upon client (re-)initialization.

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
client.c
file.c
uhttpd.h
utils.c

index dbdd1a3cfe7c8d08c950bc4c93457d31449d1a7f..73e0e49c3bf55bbeb40c6018db482d4c91d81950 100644 (file)
--- a/client.c
+++ b/client.c
@@ -50,7 +50,7 @@ void uh_http_header(struct client *cl, int code, const char *summary)
 
        cl->http_code = code;
 
-       if (!cl->request.respond_chunked)
+       if (!uh_use_chunked(cl))
                enc = "";
 
        if (r->connection_close)
@@ -188,8 +188,6 @@ static int client_parse_request(struct client *cl, char *data)
            !conf.http_keepalive)
                req->connection_close = true;
 
-       req->respond_chunked = uh_use_chunked(cl);
-
        return CLIENT_STATE_HEADER;
 }
 
@@ -261,7 +259,7 @@ static bool tls_redirect_check(struct client *cl)
        else if ((ptr = strchr(host, ':')) != NULL)
                *ptr = 0;
 
-       cl->request.respond_chunked = false;
+       cl->request.disable_chunked = true;
        cl->request.connection_close = true;
 
        uh_http_header(cl, 307, "Temporary Redirect");
diff --git a/file.c b/file.c
index 9a2880f8f1aacad04ef9d2f9d7e9231e6ca88f0b..9946d7cd594094a868932a5b5fb0ef326c96e091 100644 (file)
--- a/file.c
+++ b/file.c
@@ -614,7 +614,7 @@ static void uh_file_request(struct client *cl, const char *url,
                if (fd < 0)
                        goto error;
 
-               req->respond_chunked = false;
+               req->disable_chunked = true;
                cl->dispatch.file.hdr = tb;
                uh_file_data(cl, pi, fd);
                cl->dispatch.file.hdr = NULL;
index fbcb1ed7a0c968087f39e1eca4e65305752c7b8e..be9ee7ff23e9a94a384271001aeb5d65976358e6 100644 (file)
--- a/uhttpd.h
+++ b/uhttpd.h
@@ -112,7 +112,7 @@ struct http_request {
        int content_length;
        bool expect_cont;
        bool connection_close;
-       bool respond_chunked;
+       bool disable_chunked;
        uint8_t transfer_chunked;
        const struct auth_realm *realm;
 };
diff --git a/utils.c b/utils.c
index 857e326e5e1793d6498b07aefe42837f68c8967b..29e03c0b9a3fc83e9b41604a545cf52ec42b35e0 100644 (file)
--- a/utils.c
+++ b/utils.c
@@ -32,12 +32,12 @@ bool uh_use_chunked(struct client *cl)
        if (cl->http_code == 204 || cl->http_code == 304)
                return false;
 
-       return true;
+       return !cl->request.disable_chunked;
 }
 
 void uh_chunk_write(struct client *cl, const void *data, int len)
 {
-       bool chunked = cl->request.respond_chunked;
+       bool chunked = uh_use_chunked(cl);
 
        if (cl->state == CLIENT_STATE_CLEANUP)
                return;
@@ -60,7 +60,7 @@ void uh_chunk_vprintf(struct client *cl, const char *format, va_list arg)
                return;
 
        uloop_timeout_set(&cl->timeout, conf.network_timeout * 1000);
-       if (!cl->request.respond_chunked) {
+       if (!uh_use_chunked(cl)) {
                ustream_vprintf(cl->us, format, arg);
                return;
        }
@@ -88,7 +88,7 @@ void uh_chunk_printf(struct client *cl, const char *format, ...)
 
 void uh_chunk_eof(struct client *cl)
 {
-       if (!cl->request.respond_chunked)
+       if (!uh_use_chunked(cl))
                return;
 
        if (cl->state == CLIENT_STATE_CLEANUP)