#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();
}