|
|
|
@ -702,10 +702,17 @@ void http_response_upgrade_read_body_unknown(request_st * const r) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int http_response_append_buffer(request_st * const r, buffer * const mem) { |
|
|
|
|
__attribute_pure__ |
|
|
|
|
static int http_response_append_buffer_simple_accum(const request_st * const r, const off_t len) { |
|
|
|
|
/*(check to accumulate small reads in buffer before flushing to temp file)*/ |
|
|
|
|
return |
|
|
|
|
len < 32768 && r->write_queue.last && r->write_queue.last->file.is_temp; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int http_response_append_buffer(request_st * const r, buffer * const mem, const int simple_accum) { |
|
|
|
|
/* Note: this routine is separate from http_response_append_mem() to
|
|
|
|
|
* potentially avoid copying in http_chunk_append_buffer(). Otherwise this |
|
|
|
|
* would be: return http_response_append_mem(r, BUF_PTR_LEN(mem)); */ |
|
|
|
|
* potentially avoid copying buffer by using http_chunk_append_buffer(). */ |
|
|
|
|
|
|
|
|
|
if (r->resp_decode_chunked) |
|
|
|
|
return http_chunk_decode_append_buffer(r, mem); |
|
|
|
@ -713,7 +720,14 @@ static int http_response_append_buffer(request_st * const r, buffer * const mem)
|
|
|
|
|
if (r->resp_body_scratchpad > 0) { |
|
|
|
|
off_t len = (off_t)buffer_clen(mem); |
|
|
|
|
r->resp_body_scratchpad -= len; |
|
|
|
|
if (r->resp_body_scratchpad <= 0) { |
|
|
|
|
if (r->resp_body_scratchpad > 0) { |
|
|
|
|
if (simple_accum |
|
|
|
|
&& http_response_append_buffer_simple_accum(r, len)) { |
|
|
|
|
r->resp_body_scratchpad += len; |
|
|
|
|
return 0; /*(accumulate small reads in buffer)*/ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
else { /* (r->resp_body_scratchpad <= 0) */ |
|
|
|
|
r->resp_body_finished = 1; |
|
|
|
|
if (r->resp_body_scratchpad < 0) { |
|
|
|
|
/*(silently truncate if data exceeds Content-Length)*/ |
|
|
|
@ -727,6 +741,10 @@ static int http_response_append_buffer(request_st * const r, buffer * const mem)
|
|
|
|
|
/*(silently truncate if data exceeds Content-Length)*/ |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
else if (simple_accum |
|
|
|
|
&& http_response_append_buffer_simple_accum(r, buffer_clen(mem))) { |
|
|
|
|
return 0; /*(accumulate small reads in buffer)*/ |
|
|
|
|
} |
|
|
|
|
return http_chunk_append_buffer(r, mem); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -1212,6 +1230,15 @@ handler_t http_response_read(request_st * const r, http_response_opts * const op
|
|
|
|
|
handler_t rc = opts->parse(r, opts, b, (size_t)n); |
|
|
|
|
if (rc != HANDLER_GO_ON) return rc; |
|
|
|
|
} else if (0 == n) { |
|
|
|
|
if (!buffer_is_blank(b) && opts->simple_accum) { |
|
|
|
|
/*(flush small reads previously accumulated in b)*/ |
|
|
|
|
int rc = http_response_append_buffer(r, b, 0); /*(0 to flush)*/ |
|
|
|
|
if (__builtin_expect( (0 != rc), 0)) { |
|
|
|
|
/* error writing to tempfile;
|
|
|
|
|
* truncate response or send 500 if nothing sent yet */ |
|
|
|
|
return HANDLER_ERROR; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
/* note: no further data is sent to backend after read EOF on socket
|
|
|
|
|
* (not checking for half-closed TCP socket) |
|
|
|
|
* (backend should read all data desired prior to closing socket, |
|
|
|
@ -1221,16 +1248,38 @@ handler_t http_response_read(request_st * const r, http_response_opts * const op
|
|
|
|
|
/* split header from body */ |
|
|
|
|
handler_t rc = http_response_parse_headers(r, opts, b); |
|
|
|
|
if (rc != HANDLER_GO_ON) return rc; |
|
|
|
|
/* accumulate response in b until headers completed (or error) */ |
|
|
|
|
if (r->resp_body_started) buffer_clear(b); |
|
|
|
|
/* accumulate response in b until headers completed (or error)*/ |
|
|
|
|
if (r->resp_body_started) { |
|
|
|
|
buffer_clear(b); |
|
|
|
|
/* enable simple accumulation of small reads in some situations
|
|
|
|
|
* no Content-Length (will read to EOF) |
|
|
|
|
* Content-Length (will read until r->resp_body_scratchpad == 0) |
|
|
|
|
* not chunked-encoding |
|
|
|
|
* not bufmin streaming |
|
|
|
|
* (no custom parse routine set for opts->parse()) */ |
|
|
|
|
if (!r->resp_decode_chunked /* && NULL == opts->parse */ |
|
|
|
|
&& !(r->conf.stream_response_body |
|
|
|
|
& FDEVENT_STREAM_RESPONSE_BUFMIN)) |
|
|
|
|
opts->simple_accum = 1; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
int rc = http_response_append_buffer(r, b); |
|
|
|
|
/* flush b (do not accumulate small reads) if streaming and might
|
|
|
|
|
* write to client since there is a chance that r->write_queue is |
|
|
|
|
* fully written to client (no more temp files) and then we do not |
|
|
|
|
* want to hold onto buffered data in b for an indeterminate time |
|
|
|
|
* until next read of data from backend */ |
|
|
|
|
int simple_accum = opts->simple_accum |
|
|
|
|
&& (!(r->conf.stream_response_body |
|
|
|
|
& FDEVENT_STREAM_RESPONSE) |
|
|
|
|
|| !r->con->is_writable); |
|
|
|
|
int rc = http_response_append_buffer(r, b, simple_accum); |
|
|
|
|
if (__builtin_expect( (0 != rc), 0)) { |
|
|
|
|
/* error writing to tempfile;
|
|
|
|
|
* truncate response or send 500 if nothing sent yet */ |
|
|
|
|
return HANDLER_ERROR; |
|
|
|
|
} |
|
|
|
|
/*buffer_clear(b);*//*http_response_append_buffer() clears*/ |
|
|
|
|
/* small reads might accumulate in b; not necessarily cleared */ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (r->conf.stream_response_body & FDEVENT_STREAM_RESPONSE_BUFMIN) { |
|
|
|
|