# HG changeset patch # User Olaf Wintermann # Date 1771414279 -3600 # Node ID db37761a84943b600caf1bf273e94cb4e0f0e7ba # Parent f4c593a992667b184f9b1c6f361528b55d9f1d7a enable buffered reader for request bodies with fixed content-length diff -r f4c593a99266 -r db37761a8494 src/server/daemon/httprequest.c --- a/src/server/daemon/httprequest.c Wed Feb 18 10:53:55 2026 +0100 +++ b/src/server/daemon/httprequest.c Wed Feb 18 12:31:19 2026 +0100 @@ -399,16 +399,41 @@ // check for request body and prepare input buffer char *ctlen_str = pblock_findkeyval(pb_key_content_length, rq->rq.headers); - if(ctlen_str) { + char *transfer_encoding = pblock_findkeyval(pb_key_transfer_encoding, rq->rq.headers); + if(ctlen_str || transfer_encoding) { + netbuf *nb = sn->netbuf; + HttpStream *net_io = (HttpStream*)sn->sn.csd; + + // a separate buffer is required for reading chunked transfer enc + sn->buffer = pool_malloc(pool, nb->maxsize); + if(!sn->buffer) { + request->status = 503; + return 1; + } + + // copy remaining bytes from inbuf to the additional buffer + if(nb->cursize - nb->pos > 0) { + memcpy(sn->buffer, nb->inbuf, nb->cursize); + } + + sn->pos = nb->pos; + sn->cursize = nb->cursize; + + // clear inbuf + nb->pos = 0; + nb->cursize = 0; + int64_t ctlen; - if(util_strtoint(ctlen_str, &ctlen)) { - netbuf *nb = sn->netbuf; - HttpStream *net_io = (HttpStream*)sn->sn.csd; + if(ctlen_str && util_strtoint(ctlen_str, &ctlen)) { net_io->read_eof = WS_FALSE; - + if(httpstream_enable_buffered_read(sn->sn.csd, sn->buffer, nb->maxsize, &sn->cursize, &sn->pos)) { + request->status = 500; // should not happen + return 1; + } + // how many bytes are already read and in the buffer int cur_input_available = nb->cursize - nb->pos; - + if(cur_input_available >= ctlen) { // we have the whole request body in the buffer and // maybe even more @@ -419,37 +444,12 @@ // read still required to get the complete request body net_io->max_read = ctlen - cur_input_available; } - //printf("request body length: %d\n", ctlen); - } // else: should we abort? - } - char *transfer_encoding = pblock_findkeyval(pb_key_transfer_encoding, rq->rq.headers); - if(transfer_encoding) { - if(!strcmp(transfer_encoding, "chunked")) { - netbuf *nb = sn->netbuf; - // a separate buffer is required for reading chunked transfer enc - sn->buffer = pool_malloc(pool, nb->maxsize); - if(!sn->buffer) { - request->status = 503; - return 1; - } - - // copy remaining bytes from inbuf to the additional buffer - if(nb->cursize - nb->pos > 0) { - memcpy(sn->buffer, nb->inbuf, nb->cursize); - } - - sn->pos = nb->pos; - sn->cursize = nb->cursize; - - // clear inbuf - nb->pos = 0; - nb->cursize = 0; - + } else if (transfer_encoding) { if(httpstream_enable_chunked_read(sn->sn.csd, sn->buffer, nb->maxsize, &sn->cursize, &sn->pos)) { request->status = 500; // should not happen return 1; } - } // else: TODO: unknown transfer encoding error + } } // diff -r f4c593a99266 -r db37761a8494 src/server/proxy/httpclient.c --- a/src/server/proxy/httpclient.c Wed Feb 18 10:53:55 2026 +0100 +++ b/src/server/proxy/httpclient.c Wed Feb 18 12:31:19 2026 +0100 @@ -42,6 +42,7 @@ static int client_send_request(HttpClient *client); static int client_send_request_body(HttpClient *client); +static int client_read_response_header(HttpClient *client); HttpClient* http_client_new(EventHandler *ev) { CxMempool *mp = cxMempoolCreate(32, CX_MEMPOOL_TYPE_PURE); @@ -274,7 +275,7 @@ char *buffer; size_t nbytes; - if(client->header_complete) { + if(client->response_header_complete) { buffer = client->buffer.inbuf; nbytes = client->buffer.maxsize; } else { @@ -286,7 +287,7 @@ ssize_t r; while((r = read(client->socketfd, buffer, nbytes)) > 0) { client->buffer.cursize += r; - if(!client->header_complete) { + if(!client->response_header_complete) { switch(http_parser_process(client->parser)) { case 0: { // finish if(!http_parser_validate(client->parser)) { @@ -295,7 +296,7 @@ } client->statuscode = client->parser->status; - client->header_complete = 1; + client->response_header_complete = 1; if(client->response_start) { cxmutstr msg = client->parser->msg; char t = msg.ptr[msg.length]; @@ -464,6 +465,55 @@ return 0; } +/* +static int client_read_response_header(HttpClient *client) { + if(client->response_header_complete) { + return 0; + } + + char *buffer = client->buffer.inbuf + client->buffer.pos; + size_t nbytes = client->buffer.maxsize - client->buffer.cursize; + + ssize_t r; + while((r = read(client->socketfd, buffer, nbytes)) > 0) { + client->buffer.cursize += r; + if(!client->response_header_complete) { + switch(http_parser_process(client->parser)) { + case 0: { // finish + if(!http_parser_validate(client->parser)) { + client->error = 1; + return 0; + } + client->statuscode = client->parser->status; + + client->response_header_complete = 1; + if(client->response_start) { + cxmutstr msg = client->parser->msg; + char t = msg.ptr[msg.length]; + msg.ptr[msg.length] = 0; + int ret = client->response_start(client, client->statuscode, msg.ptr, client->response_start_userdata); + msg.ptr[msg.length] = t; + + // TODO: check ret + } + break; + } + case 1: { // need more data + continue; + } + case 2: { // error + client->error = 1; + return 0; + } + } + } + + // header complete + + } +} +*/ + /* --------------------------------- Tests --------------------------------- */ static CX_TEST(test_http_client_send_request) { @@ -755,7 +805,6 @@ } } CX_TEST_ASSERT(req.cur_reads < req.max_reads); - //CX_TEST_ASSERT(buf.size == 1084 + 5); // verify chunks char test_request_body[1024]; @@ -776,6 +825,7 @@ } char *data = str + 4; + CX_TEST_ASSERT(data + chunklen < buf.space + buf.size); memcpy(test_request_body + pos, data, chunklen); pos += chunklen; str = data + chunklen; diff -r f4c593a99266 -r db37761a8494 src/server/proxy/httpclient.h --- a/src/server/proxy/httpclient.h Wed Feb 18 10:53:55 2026 +0100 +++ b/src/server/proxy/httpclient.h Wed Feb 18 12:31:19 2026 +0100 @@ -31,6 +31,7 @@ #include "../public/nsapi.h" #include "../daemon/httpparser.h" +#include "../util/io.h" #include #include @@ -59,6 +60,7 @@ size_t addrlen; int socketfd; + HttpStream *stream; HeaderArray *request_headers; HeaderArray *response_headers; @@ -128,7 +130,7 @@ int request_body_complete; int request_body_terminated; - int header_complete; + int response_header_complete; Event readev; Event writeev; diff -r f4c593a99266 -r db37761a8494 src/server/util/io.c --- a/src/server/util/io.c Wed Feb 18 10:53:55 2026 +0100 +++ b/src/server/util/io.c Wed Feb 18 12:31:19 2026 +0100 @@ -306,6 +306,20 @@ return 0; } +int httpstream_enable_buffered_read(IOStream *st, char *buffer, size_t bufsize, int *cursize, int *pos) { + if(st->read != (io_read_f)net_http_read) { + log_ereport(LOG_FAILURE, "%s", "httpstream_enable_chunked_read: IOStream is not an HttpStream"); + return 1; + } + st->read = (io_read_f)net_http_read_buffered; + HttpStream *http = (HttpStream*)st; + http->readbuf = buffer; + http->bufsize = bufsize; + http->buflen = cursize; + http->bufpos = pos; + return 0; +} + int httpstream_enable_chunked_write(IOStream *st) { if(st->type != IO_STREAM_TYPE_HTTP) { log_ereport(LOG_FAILURE, "%s", "httpstream_enable_chunked_write: IOStream is not an HttpStream"); @@ -525,9 +539,9 @@ #define BUF_UNNEEDED_DIFF 64 /* - * read from st->chunk_buf first, read from st->fd if perform_io is true + * read from st->readbuf first, read from st->fd if perform_io is true */ -static ssize_t net_http_read_buffered(HttpStream *st, char *buf, size_t nbytes, WSBool read_data, WSBool *perform_io) { +static ssize_t http_read_buffered(HttpStream *st, char *buf, size_t nbytes, WSBool read_data, WSBool *perform_io) { ssize_t r = 0; //memset(buf, 'x', nbytes); @@ -574,13 +588,18 @@ if(rlen > 0) { // call func again to get data from buffer (no IO will be performed) - r += net_http_read_buffered(st, buf, nbytes, read_data, perform_io); + r += http_read_buffered(st, buf, nbytes, read_data, perform_io); } } return r; } +ssize_t net_http_read_buffered(HttpStream *st, void *buf, size_t nbytes) { + WSBool perform_io = TRUE; + return http_read_buffered(st, buf, nbytes, TRUE, &perform_io); +} + /* * parses a chunk header @@ -687,7 +706,7 @@ // how many bytes are available in the current chunk size_t chunk_available = st->max_read - st->read; if(chunk_available > 0) { - ssize_t r = net_http_read_buffered(st, rbuf, rbuflen, TRUE, &perform_io); + ssize_t r = http_read_buffered(st, rbuf, rbuflen, TRUE, &perform_io); if(r == 0) { break; } @@ -705,7 +724,7 @@ return -1; } // fill st->chunk_buf - ssize_t r = net_http_read_buffered(st, &st->chunk_buf[st->chunk_buf_pos], chunkbuf_avail, FALSE, &perform_io); + ssize_t r = http_read_buffered(st, &st->chunk_buf[st->chunk_buf_pos], chunkbuf_avail, FALSE, &perform_io); if(r == 0) { break; } diff -r f4c593a99266 -r db37761a8494 src/server/util/io.h --- a/src/server/util/io.h Wed Feb 18 10:53:55 2026 +0100 +++ b/src/server/util/io.h Wed Feb 18 12:31:19 2026 +0100 @@ -201,6 +201,7 @@ IOStream* httpstream_new(pool_handle_t *pool, IOStream *fd); int httpstream_enable_chunked_read(IOStream *st, char *buffer, size_t bufsize, int *cursize, int *pos); +int httpstream_enable_buffered_read(IOStream *st, char *buffer, size_t bufsize, int *cursize, int *pos); int httpstream_enable_chunked_write(IOStream *st); int httpstream_set_max_read(IOStream *st, int64_t maxread); WSBool httpstream_eof(IOStream *st); @@ -209,6 +210,7 @@ ssize_t net_http_write(HttpStream *st, const void *buf, size_t nbytes); ssize_t net_http_writev(HttpStream *st, struct iovec *iovec, int iovcnt); ssize_t net_http_read(HttpStream *st, void *buf, size_t nbytes); +ssize_t net_http_read_buffered(HttpStream *st, void *buf, size_t nbytes); ssize_t net_http_read_chunked(HttpStream *st, void *buf, size_t nbytes); ssize_t net_http_sendfile(HttpStream *st, sendfiledata *sfd); void net_http_close(HttpStream *st);