diff --git a/src/chunk.c b/src/chunk.c index 78abdd68..0cb4842f 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -461,7 +461,7 @@ static chunk *chunkqueue_get_append_tempfile(chunkqueue *cq) { static void chunkqueue_remove_empty_chunks(chunkqueue *cq); -static int chunkqueue_append_to_tempfile(server *srv, chunkqueue *dest, const char *mem, size_t len) { +int chunkqueue_append_mem_to_tempfile(server *srv, chunkqueue *dest, const char *mem, size_t len) { chunk *dst_c; ssize_t written; @@ -599,7 +599,7 @@ int chunkqueue_steal_with_tempfiles(server *srv, chunkqueue *dest, chunkqueue *s case MEM_CHUNK: /* store "use" bytes from memory chunk in tempfile */ - if (0 != chunkqueue_append_to_tempfile(srv, dest, c->mem->ptr + c->offset, use)) { + if (0 != chunkqueue_append_mem_to_tempfile(srv, dest, c->mem->ptr + c->offset, use)) { return -1; } diff --git a/src/chunk.h b/src/chunk.h index 98fad2ef..3cb1fc3e 100644 --- a/src/chunk.h +++ b/src/chunk.h @@ -57,6 +57,9 @@ void chunkqueue_append_mem(chunkqueue *cq, const char *mem, size_t len); /* copi void chunkqueue_append_buffer(chunkqueue *cq, buffer *mem); /* may reset "mem" */ void chunkqueue_prepend_buffer(chunkqueue *cq, buffer *mem); /* may reset "mem" */ +struct server; /*(declaration)*/ +int chunkqueue_append_mem_to_tempfile(struct server *srv, chunkqueue *cq, const char *mem, size_t len); + /* functions to handle buffers to read into: */ /* return a pointer to a buffer in *mem with size *len; * it should be at least min_size big, and use alloc_size if diff --git a/src/http_chunk.c b/src/http_chunk.c index 32d9eef1..d895181f 100644 --- a/src/http_chunk.c +++ b/src/http_chunk.c @@ -94,45 +94,78 @@ int http_chunk_append_file(server *srv, connection *con, buffer *fn) { return 0; } -void http_chunk_append_buffer(server *srv, connection *con, buffer *mem) { - chunkqueue *cq; +static int http_chunk_append_to_tempfile(server *srv, connection *con, const char * mem, size_t len) { + chunkqueue * const cq = con->write_queue; - force_assert(NULL != con); - - if (buffer_string_is_empty(mem)) return; + if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { + /*http_chunk_append_len(srv, con, len);*/ + buffer *b = srv->tmp_chunk_len; - cq = con->write_queue; + buffer_string_set_length(b, 0); + buffer_append_uint_hex(b, len); + buffer_append_string_len(b, CONST_STR_LEN("\r\n")); - if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { - http_chunk_append_len(srv, con, buffer_string_length(mem)); + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_BUF_LEN(b))) { + return -1; + } } - chunkqueue_append_buffer(cq, mem); + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, mem, len)) { + return -1; + } if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { - chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); + if (0 != chunkqueue_append_mem_to_tempfile(srv, cq, CONST_STR_LEN("\r\n"))) { + return -1; + } } + + return 0; } -void http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len) { - chunkqueue *cq; +static int http_chunk_append_data(server *srv, connection *con, buffer *b, const char * mem, size_t len) { - force_assert(NULL != con); - force_assert(NULL != mem || 0 == len); + chunkqueue * const cq = con->write_queue; + chunk *c = cq->last; + if (0 == len) return 0; - if (NULL == mem || 0 == len) return; + /* current usage does not append_mem or append_buffer after appending + * file, so not checking if users of this interface have appended large + * (references to) files to chunkqueue, which would not be in memory */ - cq = con->write_queue; + if ((c && c->type == FILE_CHUNK && c->file.is_temp) + || cq->bytes_in - cq->bytes_out + len > 64 * 1024) { + return http_chunk_append_to_tempfile(srv, con, b ? b->ptr : mem, len); + } + + /* not appending to prior mem chunk just in case using openssl + * and need to resubmit same args as prior call to openssl (required?)*/ if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { http_chunk_append_len(srv, con, len); } - chunkqueue_append_mem(cq, mem, len); + /*(chunkqueue_append_buffer() might steal buffer contents)*/ + b ? chunkqueue_append_buffer(cq, b) : chunkqueue_append_mem(cq, mem, len); if (con->response.transfer_encoding & HTTP_TRANSFER_ENCODING_CHUNKED) { chunkqueue_append_mem(cq, CONST_STR_LEN("\r\n")); } + + return 0; +} + +int http_chunk_append_buffer(server *srv, connection *con, buffer *mem) { + force_assert(NULL != con); + + return http_chunk_append_data(srv, con, mem, NULL, buffer_string_length(mem)); +} + +int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len) { + force_assert(NULL != con); + force_assert(NULL != mem || 0 == len); + + return http_chunk_append_data(srv, con, NULL, mem, len); } void http_chunk_close(server *srv, connection *con) { diff --git a/src/http_chunk.h b/src/http_chunk.h index 266e24ce..791b7634 100644 --- a/src/http_chunk.h +++ b/src/http_chunk.h @@ -5,8 +5,8 @@ #include "server.h" #include -void http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len); /* copies memory */ -void http_chunk_append_buffer(server *srv, connection *con, buffer *mem); /* may reset "mem" */ +int http_chunk_append_mem(server *srv, connection *con, const char * mem, size_t len); /* copies memory */ +int http_chunk_append_buffer(server *srv, connection *con, buffer *mem); /* may reset "mem" */ int http_chunk_append_file(server *srv, connection *con, buffer *fn); /* copies "fn" */ int http_chunk_append_file_range(server *srv, connection *con, buffer *fn, off_t offset, off_t len); /* copies "fn" */ void http_chunk_close(server *srv, connection *con); diff --git a/src/mod_cgi.c b/src/mod_cgi.c index 31f6fe1a..236cfc01 100644 --- a/src/mod_cgi.c +++ b/src/mod_cgi.c @@ -500,7 +500,9 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - http_chunk_append_buffer(srv, con, hctx->response_header); + if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) { + return FDEVENT_HANDLED_ERROR; + } } else { const char *bstart; size_t blen; @@ -541,14 +543,18 @@ static int cgi_demux_response(server *srv, handler_ctx *hctx) { } if (blen > 0) { - http_chunk_append_mem(srv, con, bstart, blen); + if (0 != http_chunk_append_mem(srv, con, bstart, blen)) { + return FDEVENT_HANDLED_ERROR; + } } } con->file_started = 1; } } else { - http_chunk_append_buffer(srv, con, hctx->response); + if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { + return FDEVENT_HANDLED_ERROR; + } } #if 0 @@ -756,7 +762,10 @@ static handler_t cgi_handle_fdevent(server *srv, void *ctx, int revents) { /* check if we still have a unfinished header package which is a body in reality */ if (con->file_started == 0 && !buffer_string_is_empty(hctx->response_header)) { con->file_started = 1; - http_chunk_append_buffer(srv, con, hctx->response_header); + if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) { + cgi_connection_close(srv, hctx); + return HANDLER_ERROR; + } } # if 0 diff --git a/src/mod_fastcgi.c b/src/mod_fastcgi.c index e92040a7..56f63dcc 100644 --- a/src/mod_fastcgi.c +++ b/src/mod_fastcgi.c @@ -2627,7 +2627,11 @@ static int fcgi_demux_response(server *srv, handler_ctx *hctx) { } if (hctx->send_content_body && !buffer_string_is_empty(packet.b)) { - http_chunk_append_buffer(srv, con, packet.b); + if (0 != http_chunk_append_buffer(srv, con, packet.b)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + fin = 1; + } } break; case FCGI_STDERR: diff --git a/src/mod_proxy.c b/src/mod_proxy.c index 45d98fb4..216eccff 100644 --- a/src/mod_proxy.c +++ b/src/mod_proxy.c @@ -700,11 +700,22 @@ static int proxy_demux_response(server *srv, handler_ctx *hctx) { } con->file_started = 1; - if (blen > 0) http_chunk_append_mem(srv, con, c + 4, blen); + if (blen > 0) { + if (0 != http_chunk_append_mem(srv, con, c + 4, blen)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + fin = 1; + con->file_started = 0; + } + } buffer_reset(hctx->response); } } else { - http_chunk_append_buffer(srv, con, hctx->response); + if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + fin = 1; + } buffer_reset(hctx->response); } diff --git a/src/mod_scgi.c b/src/mod_scgi.c index b47096a4..37bfd995 100644 --- a/src/mod_scgi.c +++ b/src/mod_scgi.c @@ -1968,7 +1968,11 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) { con->response.transfer_encoding = HTTP_TRANSFER_ENCODING_CHUNKED; } - http_chunk_append_buffer(srv, con, hctx->response_header); + if (0 != http_chunk_append_buffer(srv, con, hctx->response_header)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + return 1; + } } else { size_t blen = buffer_string_length(hctx->response_header) - hlen; @@ -1993,14 +1997,22 @@ static int scgi_demux_response(server *srv, handler_ctx *hctx) { } if (blen > 0) { - http_chunk_append_mem(srv, con, hctx->response_header->ptr + hlen, blen); + if (0 != http_chunk_append_mem(srv, con, hctx->response_header->ptr + hlen, blen)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + return 1; + } } } con->file_started = 1; } } else { - http_chunk_append_buffer(srv, con, hctx->response); + if (0 != http_chunk_append_buffer(srv, con, hctx->response)) { + /* error writing to tempfile; + * truncate response or send 500 if nothing sent yet */ + return 1; + } } #if 0