#include "test_framework.h" #include "../src/types.h" #include "../src/http.h" void test_http_parse_get_request(void) { TEST_SUITE_BEGIN("HTTP GET Request Parsing"); http_request_t req; const char *request = "GET /path/to/resource HTTP/1.1\r\nHost: example.com\r\nConnection: keep-alive\r\n\r\n"; int result = http_parse_request(request, strlen(request), &req); TEST_ASSERT_EQ(1, result, "Parse GET request"); TEST_ASSERT_STR_EQ("GET", req.method, "Method is GET"); TEST_ASSERT_STR_EQ("/path/to/resource", req.uri, "URI parsed correctly"); TEST_ASSERT_STR_EQ("HTTP/1.1", req.version, "Version is HTTP/1.1"); TEST_ASSERT_STR_EQ("example.com", req.host, "Host header parsed"); TEST_ASSERT_EQ(1, req.keep_alive, "Keep-alive is enabled"); TEST_SUITE_END(); } void test_http_parse_post_request(void) { TEST_SUITE_BEGIN("HTTP POST Request Parsing"); http_request_t req; const char *request = "POST /api/data HTTP/1.1\r\nHost: api.example.com\r\nContent-Length: 100\r\nConnection: close\r\n\r\n"; int result = http_parse_request(request, strlen(request), &req); TEST_ASSERT_EQ(1, result, "Parse POST request"); TEST_ASSERT_STR_EQ("POST", req.method, "Method is POST"); TEST_ASSERT_STR_EQ("/api/data", req.uri, "URI parsed correctly"); TEST_ASSERT_EQ(100, req.content_length, "Content-Length parsed"); TEST_ASSERT_EQ(0, req.keep_alive, "Keep-alive is disabled"); TEST_ASSERT_EQ(1, req.connection_close, "Connection close flag set"); TEST_SUITE_END(); } void test_http_parse_webdav_methods(void) { TEST_SUITE_BEGIN("HTTP WebDAV Methods Parsing"); http_request_t req; const char *propfind = "PROPFIND /folder HTTP/1.1\r\nHost: webdav.example.com\r\n\r\n"; int result = http_parse_request(propfind, strlen(propfind), &req); TEST_ASSERT_EQ(1, result, "Parse PROPFIND request"); TEST_ASSERT_STR_EQ("PROPFIND", req.method, "Method is PROPFIND"); const char *mkcol = "MKCOL /newfolder HTTP/1.1\r\nHost: webdav.example.com\r\n\r\n"; result = http_parse_request(mkcol, strlen(mkcol), &req); TEST_ASSERT_EQ(1, result, "Parse MKCOL request"); TEST_ASSERT_STR_EQ("MKCOL", req.method, "Method is MKCOL"); const char *move = "MOVE /source HTTP/1.1\r\nHost: webdav.example.com\r\n\r\n"; result = http_parse_request(move, strlen(move), &req); TEST_ASSERT_EQ(1, result, "Parse MOVE request"); TEST_ASSERT_STR_EQ("MOVE", req.method, "Method is MOVE"); const char *copy = "COPY /source HTTP/1.1\r\nHost: webdav.example.com\r\n\r\n"; result = http_parse_request(copy, strlen(copy), &req); TEST_ASSERT_EQ(1, result, "Parse COPY request"); TEST_ASSERT_STR_EQ("COPY", req.method, "Method is COPY"); const char *lock = "LOCK /resource HTTP/1.1\r\nHost: webdav.example.com\r\n\r\n"; result = http_parse_request(lock, strlen(lock), &req); TEST_ASSERT_EQ(1, result, "Parse LOCK request"); TEST_ASSERT_STR_EQ("LOCK", req.method, "Method is LOCK"); const char *unlock = "UNLOCK /resource HTTP/1.1\r\nHost: webdav.example.com\r\n\r\n"; result = http_parse_request(unlock, strlen(unlock), &req); TEST_ASSERT_EQ(1, result, "Parse UNLOCK request"); TEST_ASSERT_STR_EQ("UNLOCK", req.method, "Method is UNLOCK"); TEST_SUITE_END(); } void test_http_parse_custom_methods(void) { TEST_SUITE_BEGIN("HTTP Custom Methods Parsing"); http_request_t req; const char *custom1 = "MYMETHOD /path HTTP/1.1\r\nHost: example.com\r\n\r\n"; int result = http_parse_request(custom1, strlen(custom1), &req); TEST_ASSERT_EQ(1, result, "Parse custom MYMETHOD request"); TEST_ASSERT_STR_EQ("MYMETHOD", req.method, "Method is MYMETHOD"); const char *custom2 = "FOOBAR /path HTTP/1.1\r\nHost: example.com\r\n\r\n"; result = http_parse_request(custom2, strlen(custom2), &req); TEST_ASSERT_EQ(1, result, "Parse custom FOOBAR request"); TEST_ASSERT_STR_EQ("FOOBAR", req.method, "Method is FOOBAR"); TEST_SUITE_END(); } void test_http_parse_websocket_upgrade(void) { TEST_SUITE_BEGIN("HTTP WebSocket Upgrade Parsing"); http_request_t req; const char *request = "GET /ws HTTP/1.1\r\n" "Host: example.com\r\n" "Upgrade: websocket\r\n" "Connection: upgrade\r\n" "\r\n"; int result = http_parse_request(request, strlen(request), &req); TEST_ASSERT_EQ(1, result, "Parse WebSocket upgrade request"); TEST_ASSERT_STR_EQ("GET", req.method, "Method is GET"); TEST_ASSERT_EQ(1, req.is_websocket, "WebSocket flag is set"); TEST_SUITE_END(); } void test_http_parse_chunked_encoding(void) { TEST_SUITE_BEGIN("HTTP Chunked Encoding Parsing"); http_request_t req; const char *request = "POST /api/upload HTTP/1.1\r\n" "Host: example.com\r\n" "Transfer-Encoding: chunked\r\n" "\r\n"; int result = http_parse_request(request, strlen(request), &req); TEST_ASSERT_EQ(1, result, "Parse chunked request"); TEST_ASSERT_EQ(1, req.is_chunked, "Chunked flag is set"); TEST_SUITE_END(); } void test_http_parse_http10(void) { TEST_SUITE_BEGIN("HTTP/1.0 Request Parsing"); http_request_t req; const char *request = "GET /path HTTP/1.0\r\nHost: example.com\r\n\r\n"; int result = http_parse_request(request, strlen(request), &req); TEST_ASSERT_EQ(1, result, "Parse HTTP/1.0 request"); TEST_ASSERT_STR_EQ("HTTP/1.0", req.version, "Version is HTTP/1.0"); TEST_ASSERT_EQ(0, req.keep_alive, "Keep-alive is disabled by default for HTTP/1.0"); TEST_SUITE_END(); } void test_http_parse_host_with_port(void) { TEST_SUITE_BEGIN("HTTP Host with Port Parsing"); http_request_t req; const char *request = "GET / HTTP/1.1\r\nHost: example.com:8080\r\n\r\n"; int result = http_parse_request(request, strlen(request), &req); TEST_ASSERT_EQ(1, result, "Parse request with host:port"); TEST_ASSERT_STR_EQ("example.com", req.host, "Port stripped from host"); TEST_SUITE_END(); } void test_http_is_request_start(void) { TEST_SUITE_BEGIN("HTTP Request Start Detection"); TEST_ASSERT_EQ(1, http_is_request_start("GET / HTTP/1.1", 14), "GET is valid request start"); TEST_ASSERT_EQ(1, http_is_request_start("POST /api HTTP/1.1", 18), "POST is valid request start"); TEST_ASSERT_EQ(1, http_is_request_start("PROPFIND / HTTP/1.1", 19), "PROPFIND is valid request start"); TEST_ASSERT_EQ(1, http_is_request_start("CUSTOMMETHOD / HTTP/1.1", 23), "Custom method is valid request start"); TEST_ASSERT_EQ(0, http_is_request_start("123 invalid", 11), "Numbers at start are invalid"); TEST_ASSERT_EQ(0, http_is_request_start("abc", 3), "Too short is invalid"); TEST_SUITE_END(); } void test_http_malformed_requests(void) { TEST_SUITE_BEGIN("HTTP Malformed Request Handling"); http_request_t req; const char *no_space = "GETHTTP/1.1\r\nHost: example.com\r\n\r\n"; int result = http_parse_request(no_space, strlen(no_space), &req); TEST_ASSERT_EQ(0, result, "Reject request without space after method"); const char *no_version = "GET /path\r\nHost: example.com\r\n\r\n"; result = http_parse_request(no_version, strlen(no_version), &req); TEST_ASSERT_EQ(0, result, "Reject request without version"); TEST_SUITE_END(); } void run_http_tests(void) { test_http_parse_get_request(); test_http_parse_post_request(); test_http_parse_webdav_methods(); test_http_parse_custom_methods(); test_http_parse_websocket_upgrade(); test_http_parse_chunked_encoding(); test_http_parse_http10(); test_http_parse_host_with_port(); test_http_is_request_start(); test_http_malformed_requests(); }