fix http parser error when it ran out of data

Tue, 17 Feb 2026 17:43:10 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Tue, 17 Feb 2026 17:43:10 +0100
changeset 678
9908159eff0e
parent 677
3b8d3b37a4d5
child 679
4885cd4c3754

fix http parser error when it ran out of data

src/server/daemon/httpparser.c file | annotate | diff | comparison | revisions
src/server/daemon/httpparser.h file | annotate | diff | comparison | revisions
src/server/test/httpparser.c file | annotate | diff | comparison | revisions
src/server/test/httpparser.h file | annotate | diff | comparison | revisions
src/server/test/main.c file | annotate | diff | comparison | revisions
--- 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)
--- 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;
 
--- 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);
+    }
+}
--- 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
--- 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);

mercurial