# HG changeset patch # User Olaf Wintermann # Date 1771346590 -3600 # Node ID 9908159eff0e38db527111b3ea41aae3e9bbc70b # Parent 3b8d3b37a4d58816f62e57fc7f991b74a1541a2a fix http parser error when it ran out of data diff -r 3b8d3b37a4d5 -r 9908159eff0e src/server/daemon/httpparser.c --- a/src/server/daemon/httpparser.c Tue Feb 17 13:03:39 2026 +0100 +++ b/src/server/daemon/httpparser.c Tue Feb 17 17:43:10 2026 +0100 @@ -48,7 +48,7 @@ parser->start_line.ptr = (char*)netbuf->inbuf; parser->start_line.length = 0; - parser->offset = 0; + parser->line_offset = 0; return parser; } @@ -81,7 +81,7 @@ return 2; } } - + parser->line_offset = parser->netbuf->pos; parser->state++; } case 1: { @@ -119,12 +119,12 @@ while(buf->pos < buf->cursize) { unsigned char c = buf->inbuf[buf->pos]; if(c == '\n') { - size_t lnlen = buf->pos - parser->offset + 1; + size_t lnlen = buf->pos - parser->line_offset + 1; if(lnlen <= 2) { if(lnlen == 1 || buf->inbuf[buf->pos-1] == '\r') { // skip empty line buf->pos++; - parser->offset = buf->pos; + parser->line_offset = buf->pos; return 1; } // insufficient chars for request, return error @@ -135,7 +135,7 @@ } else { parser->start_line.length = lnlen; } - parser->start_line.ptr = (char*)buf->inbuf + parser->offset; + parser->start_line.ptr = (char*)buf->inbuf + parser->line_offset; buf->pos++; return 0; } @@ -147,11 +147,6 @@ int http_parser_parse_header(HttpParser *parser) { netbuf *buf = parser->netbuf; - parser->offset = buf->pos; // line offset - parser->name.ptr = NULL; - parser->name.length = 0; - parser->value.ptr = NULL; - parser->value.length = 0; while(1) { if(buf->pos >= buf->cursize) { return 1; @@ -161,8 +156,8 @@ if(c > 32) { parser->wl = 0; if(c == ':' && parser->value.ptr == NULL) { - parser->name.ptr = (char*)buf->inbuf + parser->offset; - parser->name.length = buf->pos - parser->offset - 1; + parser->name.ptr = (char*)buf->inbuf + parser->line_offset; + parser->name.length = buf->pos - parser->line_offset - 1; } else if(parser->name.ptr != NULL && parser->value.ptr == NULL) { parser->value.ptr = (char*)buf->inbuf + buf->pos - 1; } @@ -172,7 +167,7 @@ parser->state++; return 0; } else { - parser->offset = buf->pos; + parser->line_offset = buf->pos; if(parser->name.length != 0) { if(parser->value.ptr) { parser->value.length = (buf->inbuf + buf->pos - 1) diff -r 3b8d3b37a4d5 -r 9908159eff0e src/server/daemon/httpparser.h --- a/src/server/daemon/httpparser.h Tue Feb 17 13:03:39 2026 +0100 +++ b/src/server/daemon/httpparser.h Tue Feb 17 17:43:10 2026 +0100 @@ -70,10 +70,10 @@ int status; /* local parser variables */ - int wl; /* only white space */ - int tk; /* token: 0: header name 1: header value */ - int offset; /* offset of parsed string */ - int strend; /* end position */ + int wl; /* only white space */ + int tk; /* token: 0: header name 1: header value */ + int line_offset; /* offset of parsed string */ + int strend; /* end position */ cxmutstr name; cxmutstr value; diff -r 3b8d3b37a4d5 -r 9908159eff0e src/server/test/httpparser.c --- a/src/server/test/httpparser.c Tue Feb 17 13:03:39 2026 +0100 +++ b/src/server/test/httpparser.c Tue Feb 17 17:43:10 2026 +0100 @@ -209,44 +209,78 @@ header_array_free(header); } +#define _CX_STR(s) { s, sizeof(s)-1 } + +static cxstring req0 = _CX_STR( + "GET / HTTP/1.1\r\n" + "Host: example.com\r\n" + "\r\n"); + +static cxstring req1 = _CX_STR("GET /api/data?id=42 HTTP/1.1\r\n" + "Host: api.example.com\r\n" + "User-Agent: TestClient/1.0\r\n" + "Accept: application/json\r\n" + "Accept-Language: en-US,en;q=0.9\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: no-cache\r\n" + "\r\n"); + +static cxstring req2 = _CX_STR("GET /search?q=network+testing HTTP/1.1\r\n" + "Host: www.example.com\r\n" + "User-Agent: Mozilla/5.0 (X11; Linux x86_64) TestBrowser/99.0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "Accept-Language: en-US,en;q=0.9,de;q=0.8\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: max-age=0\r\n" + "Upgrade-Insecure-Requests: 1\r\n" + "Pragma: no-cache\r\n" + "DNT: 1\r\n" + "Sec-Fetch-Dest: document\r\n" + "Sec-Fetch-Mode: navigate\r\n" + "Sec-Fetch-Site: none\r\n" + "Sec-Fetch-User: ?1\r\n" + "Referer: https://www.example.com/\r\n" + "X-Forwarded-For: 203.0.113.195\r\n" + "X-Request-ID: 123e4567-e89b-12d3-a456-426614174000\r\n" + "\r\n"); + CX_TEST(test_http_parser_process_full) { CX_TEST_DO { - cxstring req0 = cx_str( - "GET / HTTP/1.1\r\n" - "Host: example.com\r\n" - "\r\n"); - - cxstring req1 = cx_str("GET /api/data?id=42 HTTP/1.1\r\n" - "Host: api.example.com\r\n" - "User-Agent: TestClient/1.0\r\n" - "Accept: application/json\r\n" - "Accept-Language: en-US,en;q=0.9\r\n" - "Connection: keep-alive\r\n" - "Cache-Control: no-cache\r\n" - "\r\n"); - - cxstring req2 = cx_str("GET /search?q=network+testing HTTP/1.1\r\n" - "Host: www.example.com\r\n" - "User-Agent: Mozilla/5.0 (X11; Linux x86_64) TestBrowser/99.0\r\n" - "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" - "Accept-Language: en-US,en;q=0.9,de;q=0.8\r\n" - "Accept-Encoding: gzip, deflate, br\r\n" - "Connection: keep-alive\r\n" - "Cache-Control: max-age=0\r\n" - "Upgrade-Insecure-Requests: 1\r\n" - "Pragma: no-cache\r\n" - "DNT: 1\r\n" - "Sec-Fetch-Dest: document\r\n" - "Sec-Fetch-Mode: navigate\r\n" - "Sec-Fetch-Site: none\r\n" - "Sec-Fetch-User: ?1\r\n" - "Referer: https://www.example.com/\r\n" - "X-Forwarded-For: 203.0.113.195\r\n" - "X-Request-ID: 123e4567-e89b-12d3-a456-426614174000\r\n" - "\r\n"); - CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req0, req0.length); CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req1, req1.length); CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req2, req2.length); } } + +CX_TEST(test_http_parser_process_chunk_1b) { + CX_TEST_DO { + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req0, 1); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req1, 1); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req2, 1); + } +} + +CX_TEST(test_http_parser_process_chunk_2b) { + CX_TEST_DO { + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req0, 2); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req1, 2); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req2, 2); + } +} + +CX_TEST(test_http_parser_process_chunk_3b) { + CX_TEST_DO { + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req0, 3); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req1, 3); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req2, 3); + } +} + +CX_TEST(test_http_parser_process_chunk_4b) { + CX_TEST_DO { + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req0, 4); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req1, 4); + CX_TEST_CALL_SUBROUTINE(test_http_parser_process, req2, 4); + } +} diff -r 3b8d3b37a4d5 -r 9908159eff0e src/server/test/httpparser.h --- a/src/server/test/httpparser.h Tue Feb 17 13:03:39 2026 +0100 +++ b/src/server/test/httpparser.h Tue Feb 17 17:43:10 2026 +0100 @@ -38,6 +38,10 @@ CX_TEST(test_parse_request_line); CX_TEST(test_parse_response_line); CX_TEST(test_http_parser_process_full); +CX_TEST(test_http_parser_process_chunk_1b); +CX_TEST(test_http_parser_process_chunk_2b); +CX_TEST(test_http_parser_process_chunk_3b); +CX_TEST(test_http_parser_process_chunk_4b); #ifdef __cplusplus diff -r 3b8d3b37a4d5 -r 9908159eff0e src/server/test/main.c --- a/src/server/test/main.c Tue Feb 17 13:03:39 2026 +0100 +++ b/src/server/test/main.c Tue Feb 17 17:43:10 2026 +0100 @@ -91,6 +91,10 @@ cx_test_register(suite, test_parse_request_line); cx_test_register(suite, test_parse_response_line); cx_test_register(suite, test_http_parser_process_full); + cx_test_register(suite, test_http_parser_process_chunk_1b); + cx_test_register(suite, test_http_parser_process_chunk_2b); + cx_test_register(suite, test_http_parser_process_chunk_3b); + cx_test_register(suite, test_http_parser_process_chunk_4b); // event tests cx_test_register(suite, test_evhandler_create);