-
Notifications
You must be signed in to change notification settings - Fork 0
Tests: add HttpServer unit coverage #30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,104 @@ | ||||||||||||||
| #include "heidi-kernel/http.h" | ||||||||||||||
| #include <gtest/gtest.h> | ||||||||||||||
|
|
||||||||||||||
| using namespace heidi; | ||||||||||||||
|
|
||||||||||||||
| // Test fixture that creates an HttpServer instance | ||||||||||||||
| class HttpServerTest : public ::testing::Test { | ||||||||||||||
| protected: | ||||||||||||||
| HttpServer server{"127.0.0.1", 0}; // Port 0 = let OS assign | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, ParseRequest_ValidGet) { | ||||||||||||||
| std::string data = "GET /api/status HTTP/1.1\r\nHost: localhost\r\n\r\n"; | ||||||||||||||
| HttpRequest req = server.parse_request(data); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_EQ(std::string(req.method), "GET"); | ||||||||||||||
| EXPECT_EQ(std::string(req.path), "/api/status"); | ||||||||||||||
| EXPECT_TRUE(req.body.empty()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, ParseRequest_ValidPostWithBody) { | ||||||||||||||
| std::string data = "POST /submit HTTP/1.1\r\nContent-Length: 5\r\n\r\nhello"; | ||||||||||||||
| HttpRequest req = server.parse_request(data); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_EQ(std::string(req.method), "POST"); | ||||||||||||||
| EXPECT_EQ(std::string(req.path), "/submit"); | ||||||||||||||
| EXPECT_EQ(std::string(req.body), "hello"); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, ParseRequest_Incomplete) { | ||||||||||||||
| std::string data = "GET /"; // Missing CRLFs | ||||||||||||||
| HttpRequest req = server.parse_request(data); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_TRUE(req.method.empty()); | ||||||||||||||
| EXPECT_TRUE(req.path.empty()); | ||||||||||||||
| EXPECT_TRUE(req.body.empty()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, ParseRequest_Empty) { | ||||||||||||||
| std::string data = ""; | ||||||||||||||
| HttpRequest req = server.parse_request(data); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_TRUE(req.method.empty()); | ||||||||||||||
| EXPECT_TRUE(req.path.empty()); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, FormatResponse_Simple) { | ||||||||||||||
| HttpResponse resp; | ||||||||||||||
| resp.status_code = 200; | ||||||||||||||
| resp.status_text = "OK"; | ||||||||||||||
| resp.body = "hello"; | ||||||||||||||
| resp.headers["Content-Type"] = "text/plain"; | ||||||||||||||
|
|
||||||||||||||
| std::string s = server.format_response(resp); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_NE(s.find("HTTP/1.1 200 OK\r\n"), std::string::npos); | ||||||||||||||
| EXPECT_NE(s.find("Content-Type: text/plain\r\n"), std::string::npos); | ||||||||||||||
| EXPECT_NE(s.find("Content-Length: 5\r\n"), std::string::npos); | ||||||||||||||
| // Check end of headers and body | ||||||||||||||
| EXPECT_NE(s.find("\r\n\r\nhello"), std::string::npos); | ||||||||||||||
|
Comment on lines
+56
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current assertions using
Suggested change
|
||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, FormatResponse_404) { | ||||||||||||||
| HttpResponse resp; | ||||||||||||||
| resp.status_code = 404; | ||||||||||||||
| resp.status_text = "Not Found"; | ||||||||||||||
|
|
||||||||||||||
| std::string s = server.format_response(resp); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_NE(s.find("HTTP/1.1 404 Not Found\r\n"), std::string::npos); | ||||||||||||||
| EXPECT_NE(s.find("Content-Length: 0\r\n"), std::string::npos); | ||||||||||||||
|
Comment on lines
+70
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similar to the other response formatting tests, using
Suggested change
|
||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, FormatResponse_WithHeaders) { | ||||||||||||||
| HttpResponse resp; | ||||||||||||||
| resp.status_code = 201; | ||||||||||||||
| resp.status_text = "Created"; | ||||||||||||||
| resp.body = "{\"id\": 123}"; | ||||||||||||||
| resp.headers["Content-Type"] = "application/json"; | ||||||||||||||
| resp.headers["Location"] = "/items/123"; | ||||||||||||||
|
|
||||||||||||||
| std::string s = server.format_response(resp); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_NE(s.find("HTTP/1.1 201 Created\r\n"), std::string::npos); | ||||||||||||||
| EXPECT_NE(s.find("Content-Type: application/json\r\n"), std::string::npos); | ||||||||||||||
| EXPECT_NE(s.find("Location: /items/123\r\n"), std::string::npos); | ||||||||||||||
| EXPECT_NE(s.find("\r\n\r\n{\"id\": 123}"), std::string::npos); | ||||||||||||||
|
Comment on lines
+84
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These checks are not very strict. They don't verify the order of headers or the exact structure of the response. Also, the automatically added
Suggested change
|
||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, ParseRequest_PathWithQuery) { | ||||||||||||||
| std::string data = "GET /api/status?verbose=true HTTP/1.1\r\n\r\n"; | ||||||||||||||
| HttpRequest req = server.parse_request(data); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_EQ(std::string(req.method), "GET"); | ||||||||||||||
| EXPECT_EQ(std::string(req.path), "/api/status?verbose=true"); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| TEST_F(HttpServerTest, ParseRequest_MethodCasePreserved) { | ||||||||||||||
| std::string data = "post /data HTTP/1.1\r\n\r\n"; | ||||||||||||||
| HttpRequest req = server.parse_request(data); | ||||||||||||||
|
|
||||||||||||||
| EXPECT_EQ(std::string(req.method), "post"); // Lowercase preserved | ||||||||||||||
| EXPECT_EQ(std::string(req.path), "/data"); | ||||||||||||||
| } | ||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
HttpRequeststruct usesstd::string_viewfor itsmethod,path, andbodymembers, which means it does not own the underlying string data. By makingparse_requestpublic, you are exposing an API where the returnedHttpRequestobject contains pointers into thedatastring passed as an argument.If a caller passes a temporary string (e.g.,
server.parse_request(std::string(buffer))) or if the source string is destroyed while theHttpRequestis still in use, these members will become dangling pointers. This leads to a Use-After-Free (UAF) vulnerability, which can cause crashes or memory corruption.To fix this, consider changing the
HttpRequeststruct members tostd::stringso they own their data, or clearly document the lifetime requirements if performance is critical.