Project

General

Profile

Bug #2871

HTTP 400 response for chunked GET requests

Added by yoshi 8 months ago. Updated 8 months ago.

Status:
Invalid
Priority:
Normal
Assignee:
-
Category:
-
Target version:
Start date:
2018-03-01
Due date:
% Done:

0%

Estimated time:
Missing in 1.5.x:

Description

When lighttpd 1.4.48 receives a GET request with "Transfer-Encoding: chunked", it responds with HTTP 400 Bad Request.
There is no "Content-Length" header present in the request.

2018-02-28 08:54:00: (request.c.445) fd: 8 request-len: 108 \nGET / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\naccept: */*\r\nuser-agent: curl/7.58.0\r\n\r\n 
2018-02-28 08:54:00: (request.c.1289) GET/HEAD with content-length -> 400  
2018-02-28 08:54:00: (response.c.122) Response-Header: \nHTTP/1.1 400 Bad Request\r\nContent-Type: text/html\r\nContent-Length: 349\r\nConnection: close\r\nDate: Sun, 25 Feb 2018 08:54:00 GMT\r\nServer: lighttpd/1.4.48\r\n\r\n 

I traced the issue down to request.c:1275, which is executed when "Transfer-Encoding" is "chunked":

con->request.content_length = -1;

Then at request.c:1287 lighttpd stops processing the request when con->request.content_length is non-zero:

    switch(con->request.http_method) {
    case HTTP_METHOD_GET:
    case HTTP_METHOD_HEAD:
        /* content-length is forbidden for those */
        if (con_length_set && con->request.content_length != 0) {
            /* content-length is missing */
            log_error_write(srv, __FILE__, __LINE__, "s",
                    "GET/HEAD with content-length -> 400");

            con->keep_alive = 0;
            con->http_status = 400;
            return 0;
        }
        break;
...

I could fix the issue by changing the if to:

        if (con_length_set && con->request.content_length > 0) {

History

#1

Updated by gstrauss 8 months ago

  • Status changed from New to Invalid

2018-02-28 08:54:00: (request.c.1289) GET/HEAD with content-length -> 400

Not a bug. That's intentional. Why are you sending a request-body with GET or HEAD?

https://www.ietf.org/rfc/rfc7231.txt
Section 4.3.1 GET
...
A payload within a GET request message has no defined semantics;
sending a payload body on a GET request might cause some existing
implementations to reject the request.

#2

Updated by yoshi 8 months ago

GuzzlePHP seems to set the encoding to chunked if it can't determine the length of the request body, which is the case when you try to forward the request PHP got to a different server, because then it will use stdin for the request body.

While I am aware that using a chunked encoding with GET is not really correct, I would argue though that rejecting such a request is too strict if the request body is empty.
Especially because other implementations, such as Apache HTTPD, allow such requests.

If you decide to maintain the strict implementation, you should at least adjust the error message to better reflect the true reason of rejecting the request, which is the Transfer-Encoding header, not a non-empty body.

#3

Updated by gstrauss 8 months ago

GuzzlePHP seems to set the encoding to chunked if it can't determine the length of the request body, which is the case when you try to forward the request PHP got to a different server, because then it will use stdin for the request body.

That's gross. [edit: gross for a GET request]

While I am aware that using a chunked encoding with GET is not really correct, I would argue though that rejecting such a request is too strict if the request body is empty.

The code in lighttpd does accept and discard Content-Length: 0. Sending an empty request body via 0-length chunk can still occur with a long time delay, and, as I quoted in the RFC above, lighttpd can legitimately reject the request.

If you decide to maintain the strict implementation, you should at least adjust the error message to better reflect the true reason of rejecting the request, which is the Transfer-Encoding header, not a non-empty body.

No, the request is rejected before any body is read. Ending chunk may have been sent already, but it also may not have been sent (and could be sent minutes later) or might be in a separate packet. lighttpd is not spending any additional resource on this Bad Request, and does not know if the body that will be sent by Transfer-Encoding chunked is empty or not.

Did you file a bug with GuzzlePHP? Since you did not provide the reference here, I am going to assume that you did not. Please kindly tell them to fix their code. This is most definitely not a bug in lighttpd. (If you disagree, don't bother trying to convince me. I refer you once again to the RFC specification I quoted above.)

Also available in: Atom