[core] track Content-Length from backend (fixes #3046)
track Content-Length from backend in r->resp_body_scratchpad x-ref: "Failure on second request in http proxy backend" https://redmine.lighttpd.net/issues/3046
This commit is contained in:
parent
e9309ae6e6
commit
903024d711
1
src/h2.c
1
src/h2.c
|
@ -2560,6 +2560,7 @@ h2_con_upgrade_h2c (request_st * const h2r, const buffer * const http2_settings)
|
|||
#if 0 /* expect empty request body */
|
||||
r->reqbody_length = h2r->reqbody_length; /* currently always 0 */
|
||||
r->te_chunked = h2r->te_chunked; /* must be 0 */
|
||||
r->resp_body_scratchpad = h2r->resp_body_scratchpad; /*(not started yet)*/
|
||||
swap(&r->reqbody_queue,&h2r->reqbody_queue);/*currently always empty queue*/
|
||||
#endif
|
||||
r->http_host = h2r->http_host;
|
||||
|
|
|
@ -249,6 +249,7 @@ int http_response_handle_cachable(request_st * const r, const buffer * const mti
|
|||
|
||||
void http_response_body_clear (request_st * const r, int preserve_length) {
|
||||
r->resp_send_chunked = 0;
|
||||
r->resp_body_scratchpad = -1;
|
||||
if (light_btst(r->resp_htags, HTTP_HEADER_TRANSFER_ENCODING)) {
|
||||
http_header_response_unset(r, HTTP_HEADER_TRANSFER_ENCODING,
|
||||
CONST_STR_LEN("Transfer-Encoding"));
|
||||
|
@ -283,6 +284,7 @@ static void http_response_header_clear (request_st * const r) {
|
|||
* Transfer-Encoding: chunked set, then other items need to be reset */
|
||||
r->resp_send_chunked = 0;
|
||||
r->resp_decode_chunked = 0;
|
||||
r->resp_body_scratchpad = -1;
|
||||
if (r->gw_dechunk) {
|
||||
free(r->gw_dechunk->b.ptr);
|
||||
free(r->gw_dechunk);
|
||||
|
@ -1181,8 +1183,37 @@ static int http_response_process_headers(request_st * const r, http_response_opt
|
|||
break;
|
||||
case HTTP_HEADER_CONTENT_LENGTH:
|
||||
if (*value == '+') ++value;
|
||||
if (!r->resp_decode_chunked
|
||||
&& !light_btst(r->resp_htags, HTTP_HEADER_CONTENT_LENGTH)) {
|
||||
const char *err = ns;
|
||||
if (err[-1] == '\0') --err; /*(skip one '\0', trailing whitespace)*/
|
||||
while (err > value && (err[-1] == ' ' || err[-1] == '\t')) --err;
|
||||
if (err <= value) continue; /*(might error 502 Bad Gateway)*/
|
||||
uint32_t vlen = (uint32_t)(err - value);
|
||||
r->resp_body_scratchpad =
|
||||
(off_t)li_restricted_strtoint64(value, vlen, &err);
|
||||
if (err != value + vlen) {
|
||||
/*(invalid Content-Length value from backend;
|
||||
* read from backend until backend close, hope for the best)
|
||||
*(might choose to treat this as 502 Bad Gateway) */
|
||||
r->resp_body_scratchpad = -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* ignore Content-Length if Transfer-Encoding: chunked
|
||||
* ignore subsequent (multiple) Content-Length
|
||||
* (might choose to treat this as 502 Bad Gateway) */
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case HTTP_HEADER_TRANSFER_ENCODING:
|
||||
if (light_btst(r->resp_htags, HTTP_HEADER_CONTENT_LENGTH)) {
|
||||
/* ignore Content-Length if Transfer-Encoding: chunked
|
||||
* (might choose to treat this as 502 Bad Gateway) */
|
||||
r->resp_body_scratchpad = -1;
|
||||
http_header_response_unset(r, HTTP_HEADER_CONTENT_LENGTH,
|
||||
CONST_STR_LEN("Content-Length"));
|
||||
}
|
||||
/*(assumes "Transfer-Encoding: chunked"; does not verify)*/
|
||||
r->resp_decode_chunked = 1;
|
||||
r->gw_dechunk = calloc(1, sizeof(response_dechunk));
|
||||
|
|
|
@ -483,8 +483,26 @@ int http_chunk_decode_append_buffer(request_st * const r, buffer * const mem)
|
|||
|
||||
/*(called by funcs receiving data from backends, which might be chunked)*/
|
||||
/*(separate from http_chunk_append_buffer() called by numerous others)*/
|
||||
if (!r->resp_decode_chunked)
|
||||
if (!r->resp_decode_chunked) {
|
||||
if (r->resp_body_scratchpad > 0) {
|
||||
off_t len = (off_t)buffer_string_length(mem);
|
||||
r->resp_body_scratchpad -= len;
|
||||
if (r->resp_body_scratchpad <= 0) {
|
||||
r->resp_body_finished = 1;
|
||||
if (r->resp_body_scratchpad < 0) {
|
||||
/*(silently truncate if data exceeds Content-Length)*/
|
||||
len += r->resp_body_scratchpad;
|
||||
r->resp_body_scratchpad = 0;
|
||||
buffer_string_set_length(mem, (uint32_t)len);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (0 == r->resp_body_scratchpad) {
|
||||
/*(silently truncate if data exceeds Content-Length)*/
|
||||
return 0;
|
||||
}
|
||||
return http_chunk_append_buffer(r, mem);
|
||||
}
|
||||
|
||||
/* might avoid copy by transferring buffer if buffer is all data that is
|
||||
* part of large chunked block, but choosing to *not* expand that out here*/
|
||||
|
@ -508,12 +526,28 @@ int http_chunk_decode_append_buffer(request_st * const r, buffer * const mem)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int http_chunk_decode_append_mem(request_st * const r, const char * const mem, const size_t len)
|
||||
int http_chunk_decode_append_mem(request_st * const r, const char * const mem, size_t len)
|
||||
{
|
||||
/*(called by funcs receiving data from backends, which might be chunked)*/
|
||||
/*(separate from http_chunk_append_mem() called by numerous others)*/
|
||||
if (!r->resp_decode_chunked)
|
||||
if (!r->resp_decode_chunked) {
|
||||
if (r->resp_body_scratchpad > 0) {
|
||||
r->resp_body_scratchpad -= (off_t)len;
|
||||
if (r->resp_body_scratchpad <= 0) {
|
||||
r->resp_body_finished = 1;
|
||||
if (r->resp_body_scratchpad < 0) {
|
||||
/*(silently truncate if data exceeds Content-Length)*/
|
||||
len = (size_t)(r->resp_body_scratchpad + (off_t)len);
|
||||
r->resp_body_scratchpad = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (0 == r->resp_body_scratchpad) {
|
||||
/*(silently truncate if data exceeds Content-Length)*/
|
||||
return 0;
|
||||
}
|
||||
return http_chunk_append_mem(r, mem, len);
|
||||
}
|
||||
|
||||
if (0 != http_chunk_decode_append_data(r, mem, (off_t)len))
|
||||
return -1;
|
||||
|
|
|
@ -31,6 +31,7 @@ request_init_data (request_st * const r, connection * const con, server * const
|
|||
r->loops_per_request = 0;
|
||||
r->con = con;
|
||||
r->tmp_buf = srv->tmp_buf;
|
||||
r->resp_body_scratchpad = -1;
|
||||
|
||||
/* init plugin-specific per-request structures */
|
||||
r->plugin_ctx = calloc(1, (srv->plugins.used + 1) * sizeof(void *));
|
||||
|
@ -68,6 +69,7 @@ request_reset (request_st * const r)
|
|||
r->http_host = NULL;
|
||||
r->reqbody_length = 0;
|
||||
r->te_chunked = 0;
|
||||
r->resp_body_scratchpad = -1;
|
||||
r->rqst_htags = 0;
|
||||
|
||||
r->async_callback = 0;
|
||||
|
|
|
@ -153,6 +153,7 @@ struct request_st {
|
|||
|
||||
off_t reqbody_length; /* request Content-Length */
|
||||
off_t te_chunked;
|
||||
off_t resp_body_scratchpad;
|
||||
|
||||
buffer *http_host; /* copy of array value buffer ptr; not alloc'ed */
|
||||
const buffer *server_name;
|
||||
|
|
Loading…
Reference in New Issue