Tue, 17 Feb 2026 13:03:39 +0100
add test_http_parser_process_full
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2026 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "httpparser.h" #include "../daemon/httpparser.h" #include <cx/string.h> CX_TEST(test_parse_request_line) { CX_TEST_DO { HttpParser parser; memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("GET / HTTP/1.1\r\n"); int ret1 = parse_request_line(&parser); CX_TEST_ASSERT(!ret1); CX_TEST_ASSERT(!cx_strcmp(parser.method, "GET")); CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/")); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.1")); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("POST /longer/url/test/path/ HTTP/1.0\r\n"); int ret2 = parse_request_line(&parser); CX_TEST_ASSERT(!ret2); CX_TEST_ASSERT(!cx_strcmp(parser.method, "POST")); CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/longer/url/test/path/")); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.0")); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("G / v\r\n"); int ret3 = parse_request_line(&parser); CX_TEST_ASSERT(!ret3); CX_TEST_ASSERT(!cx_strcmp(parser.method, "G")); CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/")); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "v")); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HEAD /space HTTP/0.9 \r\n"); int ret4 = parse_request_line(&parser); CX_TEST_ASSERT(!ret4); CX_TEST_ASSERT(!cx_strcmp(parser.method, "HEAD")); CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/space")); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/0.9")); // unix-style line break tests memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("PROPFIND /collection/ HTTP/1.1\n"); int uret1 = parse_request_line(&parser); CX_TEST_ASSERT(!uret1); CX_TEST_ASSERT(!cx_strcmp(parser.method, "PROPFIND")); CX_TEST_ASSERT(!cx_strcmp(parser.uri, "/collection/")); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.1")); // negative tests parser.start_line = cx_mutstr("\r\n"); int nret1 = parse_request_line(&parser); CX_TEST_ASSERT(nret1); parser.start_line = cx_mutstr("GET\r\n"); int nret2 = parse_request_line(&parser); CX_TEST_ASSERT(nret2); parser.start_line = cx_mutstr("GET /test\r\n"); int nret3 = parse_request_line(&parser); CX_TEST_ASSERT(nret3); parser.start_line = cx_mutstr("GET /test \r\n"); int nret4 = parse_request_line(&parser); CX_TEST_ASSERT(nret4); parser.start_line = cx_mutstr("/test HTTP/1.1\r\n"); int nret5 = parse_request_line(&parser); CX_TEST_ASSERT(nret5); parser.start_line = cx_mutstr("GET /uri HTTP/1.1 test\r\n"); int nret6 = parse_request_line(&parser); CX_TEST_ASSERT(nret6); parser.start_line = cx_mutstr(" /uri2 HTTP/1.1\r\n"); int nret7 = parse_request_line(&parser); CX_TEST_ASSERT(nret7); parser.start_line = cx_mutstr("GET HTTP/1.1\r\n"); int nret8 = parse_request_line(&parser); CX_TEST_ASSERT(nret8); } } CX_TEST(test_parse_response_line) { CX_TEST_DO { HttpParser parser; memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HTTP/1.1 200 OK\r\n"); parser.type = 1; int ret1 = parse_response_line(&parser); CX_TEST_ASSERT(!ret1); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.1")); CX_TEST_ASSERT(!cx_strcmp(parser.msg, "OK")); CX_TEST_ASSERT(parser.status == 200); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HTTP/1.0 201 Created\r\n"); parser.type = 1; int ret2 = parse_response_line(&parser); CX_TEST_ASSERT(!ret2); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/1.0")); CX_TEST_ASSERT(!cx_strcmp(parser.msg, "Created")); CX_TEST_ASSERT(parser.status == 201); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HTTP/0.9 204 No Content\r\n"); parser.type = 1; int ret3 = parse_response_line(&parser); CX_TEST_ASSERT(!ret3); CX_TEST_ASSERT(!cx_strcmp(parser.httpv, "HTTP/0.9")); CX_TEST_ASSERT(!cx_strcmp(parser.msg, "No Content")); CX_TEST_ASSERT(parser.status == 204); // negative tests memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HTTP/1.1 200\r\n"); parser.type = 1; int nret1 = parse_response_line(&parser); CX_TEST_ASSERT(nret1); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("\r\n"); parser.type = 1; int nret2 = parse_response_line(&parser); CX_TEST_ASSERT(nret2); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("200\r\n"); parser.type = 1; int nret3 = parse_response_line(&parser); CX_TEST_ASSERT(nret3); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HTTP/1.1 2345 Test Message\r\n"); parser.type = 1; int nret4 = parse_response_line(&parser); CX_TEST_ASSERT(nret4); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr("HTTP/1.1 xy1 OK\r\n"); parser.type = 1; int nret5 = parse_response_line(&parser); CX_TEST_ASSERT(nret5); memset(&parser, 0, sizeof(HttpParser)); parser.start_line = cx_mutstr(" 200 OK\r\n"); parser.type = 1; int nret6 = parse_response_line(&parser); CX_TEST_ASSERT(nret6); } } CX_TEST_SUBROUTINE(test_http_parser_process, cxstring request, int chunksize) { netbuf buf; memset(&buf, 0, sizeof(netbuf)); buf.inbuf = (unsigned char*)request.ptr; buf.maxsize = request.length; HeaderArray *header = header_array_create(); HttpParser *parser = http_parser_new2(0, &buf, header); while(buf.cursize < request.length) { buf.cursize += chunksize; if(buf.cursize > buf.maxsize) { buf.cursize = buf.maxsize; } int ret = http_parser_process(parser); if(buf.cursize < buf.maxsize) { CX_TEST_ASSERT(ret == 1); } else { CX_TEST_ASSERT(ret == 0); } } CX_TEST_ASSERT(http_parser_validate(parser)); http_parser_free(parser); header_array_free(header); } 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); } }