[core] document error edge case for HTTP/1.0

When lighttpd is not configured to stream the response body,
lighttpd sends partial content with an incorrect Content-Length
to an HTTP/1.0 client if a backend sends Transfer-Encoding: chunked
in response to lighttpd HTTP/1.1 request (to backend), and the response
from the backend ends up truncated.

lighttpd could instead send an HTTP/1.0 502 Bad Gateway, but the
current implementation chooses to send the partial content.  After all,
an HTTP/1.0 client is, well, HTTP/1.0, and so of limited intelligence.
This commit is contained in:
Glenn Strauss 2021-05-14 20:45:59 -04:00
parent 5ff9e2f6eb
commit 0532d67639
1 changed files with 38 additions and 0 deletions

View File

@ -653,6 +653,44 @@ void http_response_backend_done (request_st * const r) {
if (!r->resp_body_finished) {
if (r->http_version == HTTP_VERSION_1_1)
http_chunk_close(r);
#if 0
/* This is a lot of work to make it possible for an HTTP/1.0 client
* to detect that response is truncated (after lighttpd made an
* HTTP/1.1 request to backend, and backend gave a Transfer-Encoding
* chunked response instead of sending Content-Length, and lighttpd
* is streaming response to client). An HTTP/1.0 client is probably
* not checking for truncated response, or else client should really
* prefer HTTP/1.1 or better. If lighttpd were streaming response,
* there would be no Content-Length and lighttpd would have sent
* Connection: close. Alternatively, since not streaming (if these
* conditions are true), could send an HTTP status error instead of
* sending partial content with a bogus Content-Length. If we do
* not send an HTTP error status, then response_start hooks may add
* caching headers (e.g. mod_expire, mod_setenv). If in future we
* send HTTP error status, might special-case HEAD requests and
* clear response body so that response headers (w/o Content-Length)
* can be sent. For now, we have chosen to send partial content,
* including generating an incorrect Content-Length (later), and not
* to take any of these extra steps. */
else if (__builtin_expect( (r->http_version == HTTP_VERSION_1_0), 0)
&& r->gw_dechunk && !r->gw_dechunk->done
&& !(r->conf.stream_response_body
& (FDEVENT_STREAM_RESPONSE
|FDEVENT_STREAM_RESPONSE_BUFMIN))) {
r->keep_alive = 0; /* disable keep-alive, send bogus length */
http_header_response_set(r, HTTP_HEADER_CONTENT_LENGTH,
CONST_STR_LEN("Content-Length"),
CONST_STR_LEN("9999999999999"));
http_header_response_unset(r, HTTP_HEADER_ETAG,
CONST_STR_LEN("ETag"));
http_header_response_unset(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"));
http_header_response_unset(r, HTTP_HEADER_CACHE_CONTROL,
CONST_STR_LEN("Cache-Control"));
http_header_response_unset(r, HTTP_HEADER_EXPIRES,
CONST_STR_LEN("Expires"));
}
#endif
r->resp_body_finished = 1;
}
default: