Skip to content

Commit e58dfbc

Browse files
authored
Merge pull request #31 from yabjames/30-feature-more-test-coverage
30-feature-more-test-coverage
2 parents f5da2d1 + d9dffdc commit e58dfbc

3 files changed

Lines changed: 156 additions & 52 deletions

File tree

include/HttpServer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class HttpServer {
1414

1515
~HttpServer();
1616

17-
std::atomic<bool> stop_flag;
17+
std::atomic<bool> stop_flag {};
1818

1919
struct Request {
2020
std::string_view route;

src/HttpServer.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ void HttpServer::listen(int port) {
5252
struct sockaddr_storage incoming_addr {};
5353
socklen_t addr_size {sizeof(incoming_addr)};
5454

55-
int conn_file_descriptor = accept(listener_fd, (struct sockaddr*)&incoming_addr, &addr_size);
55+
int conn_file_descriptor = accept(listener_fd, reinterpret_cast<struct sockaddr *>(&incoming_addr), &addr_size);
5656
if (conn_file_descriptor == -1) {
5757
// If we're stopping, accept failures are expected; don't spam logs.
5858
if (stop_flag.load()) break;
@@ -143,12 +143,14 @@ void HttpServer::handle_client() {
143143
// std::cout << "route: " << route << '\n';
144144

145145
// get body
146-
size_t req_body_start = path.find("\r\n\r\n") + 4;
147-
if (req_body_start == std::string_view::npos) {
146+
size_t req_body_delimiter = path.find("\r\n\r\n");
147+
if (req_body_delimiter == std::string_view::npos) {
148148
close (conn_fd);
149149
std::cerr << "Invalid request formatting: the start of the request body is malformed\n";
150+
continue;
150151
}
151152

153+
size_t req_body_start = req_body_delimiter + 4;
152154
std::string_view req_body = path.substr(req_body_start, path.size() - req_body_start);
153155
// std::cout << "body: " << req_body << '\n';
154156

test/HttpServerTest.cpp

Lines changed: 150 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,40 +4,38 @@
44
#include <netinet/in.h>
55
#include <string>
66

7-
class HttpServerTest : public ::testing::Test {};
7+
class HttpServerTest : public ::testing::Test {
8+
public:
9+
static constexpr int port {8081};
10+
};
811

912
TEST(ServerTest, ConstructorDestructorTest) {
1013
HttpServer server {};
1114
}
1215

13-
TEST(HttpServerTest, ServerStartsAndAcceptsRequests) {
14-
HttpServer server {};
15-
16-
// Start server in non-blocking mode
17-
server.start_listening(8080);
18-
19-
// Send real HTTP request using curl
20-
int result = system("curl -s http://localhost:8080 > /dev/null");
21-
22-
EXPECT_EQ(result, 0);
23-
}
24-
2516
TEST(HttpServerTest, AcceptsHttpRequest) {
26-
HttpServer server;
27-
server.start_listening(8081);
17+
HttpServer server {};
18+
server.get_mapping("/", [](const HttpServer::Request&, HttpServer::Response& res) {
19+
res.body = "test";
20+
});
21+
server.start_listening(HttpServerTest::port);
2822

2923
std::this_thread::sleep_for(std::chrono::milliseconds(1));
3024

3125
int sock = socket(AF_INET, SOCK_STREAM, 0);
3226

3327
sockaddr_in addr{};
3428
addr.sin_family = AF_INET;
35-
addr.sin_port = htons(8081);
29+
addr.sin_port = htons(HttpServerTest::port);
3630
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
3731

38-
ASSERT_EQ(connect(sock, (sockaddr*)&addr, sizeof(addr)), 0);
32+
ASSERT_EQ(connect(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
3933

40-
const char* request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n";
34+
const char* request = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"
35+
"Host: localhost\r\n"
36+
"Connection: keep-alive\r\n"
37+
"Content-Length: 0\r\n"
38+
"\r\n";
4139
send(sock, request, strlen(request), 0);
4240

4341
char buffer[1024];
@@ -53,18 +51,18 @@ TEST(HttpServerTest, AcceptGetRequest) {
5351
server.get_mapping("/hello", [](const HttpServer::Request&, HttpServer::Response& res){
5452
res.body = "hello, world";
5553
});
56-
server.start_listening(8082);
54+
server.start_listening(HttpServerTest::port);
5755

5856
std::this_thread::sleep_for(std::chrono::milliseconds(1));
5957

6058
int sock = socket(AF_INET, SOCK_STREAM, 0);
6159

6260
sockaddr_in addr{};
6361
addr.sin_family = AF_INET;
64-
addr.sin_port = htons(8082);
62+
addr.sin_port = htons(HttpServerTest::port);
6563
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
6664

67-
ASSERT_EQ(connect(sock, (sockaddr*)&addr, sizeof(addr)), 0);
65+
ASSERT_EQ(connect(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
6866

6967
const char* request = "GET /hello HTTP/1.1\r\n\r\n";
7068
send(sock, request, strlen(request), 0);
@@ -84,18 +82,18 @@ TEST(HttpServerTest, IgnoreGetReqBody) {
8482
server.get_mapping("/hello", [](const HttpServer::Request& req, HttpServer::Response& res){
8583
res.body = req.body;
8684
});
87-
server.start_listening(8082);
85+
server.start_listening(HttpServerTest::port);
8886

8987
std::this_thread::sleep_for(std::chrono::milliseconds(1));
9088

9189
int sock = socket(AF_INET, SOCK_STREAM, 0);
9290

9391
sockaddr_in addr{};
9492
addr.sin_family = AF_INET;
95-
addr.sin_port = htons(8082);
93+
addr.sin_port = htons(HttpServerTest::port);
9694
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
9795

98-
ASSERT_EQ(connect(sock, (sockaddr*)&addr, sizeof(addr)), 0);
96+
ASSERT_EQ(connect(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
9997

10098
const char* request = "GET /hello HTTP/1.1\r\n\r\nhello, world";
10199
send(sock, request, strlen(request), 0);
@@ -113,36 +111,44 @@ TEST(HttpServerTest, IgnoreGetReqBody) {
113111
}
114112

115113
TEST(HttpServerTest, DoesntIgnorePostReqBody) {
116-
HttpServer server {};
117-
server.post_mapping("/post-foo", [](const HttpServer::Request& req, HttpServer::Response& res){
118-
res.body = req.body;
119-
});
120-
server.start_listening(8082);
114+
try {
115+
HttpServer server {};
116+
server.post_mapping("/foo", [](const HttpServer::Request& req, HttpServer::Response& res){
117+
res.body = req.body;
118+
});
119+
server.start_listening(HttpServerTest::port);
121120

122-
std::this_thread::sleep_for(std::chrono::milliseconds(1));
121+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
123122

124-
int sock = socket(AF_INET, SOCK_STREAM, 0);
123+
int sock = socket(AF_INET, SOCK_STREAM, 0);
125124

126-
sockaddr_in addr{};
127-
addr.sin_family = AF_INET;
128-
addr.sin_port = htons(8082);
129-
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
125+
sockaddr_in addr{};
126+
addr.sin_family = AF_INET;
127+
addr.sin_port = htons(HttpServerTest::port);
128+
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
130129

131-
ASSERT_EQ(connect(sock, (sockaddr*)&addr, sizeof(addr)), 0);
130+
ASSERT_EQ(connect(sock, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
132131

133-
const char* request = "POST /post-foo HTTP/1.1\r\n\r\nhello, world";
134-
send(sock, request, strlen(request), 0);
132+
std::string request = "POST /foo HTTP/1.1\r\n"
133+
"Host: localhost\r\n"
134+
"Connection: keep-alive\r\n"
135+
"Content-Length: 5\r\n"
136+
"\r\n"
137+
"hello";
135138

136-
char buffer[1024] {};
137-
int bytes = recv(sock, buffer, sizeof(buffer), 0);
138-
std::string result = std::string(buffer);
139+
send(sock, request.c_str(), request.size(), 0);
139140

140-
EXPECT_GT(bytes, 0);
141+
char buffer[1024] {};
142+
int bytes = recv(sock, buffer, sizeof(buffer), 0);
143+
std::string result = std::string(buffer);
141144

142-
// Should find "hello, world" as setting the request body
143-
ASSERT_TRUE(result.find("hello, world") != std::string::npos);
145+
EXPECT_GT(bytes, 0);
146+
ASSERT_TRUE(result.find("hello") != std::string::npos);
144147

145-
close(sock);
148+
close(sock);
149+
} catch (const std::exception& e) {
150+
FAIL() << "Exception occurred: " << e.what();
151+
}
146152
}
147153

148154
TEST(HttpServerTest, AllUniqueReqMethods) {
@@ -177,13 +183,13 @@ TEST(HttpServerTest, AllUniqueReqMethods) {
177183
res.body = "8";
178184
});
179185

180-
server.start_listening(8083);
186+
server.start_listening(HttpServerTest::port);
181187

182188
std::this_thread::sleep_for(std::chrono::milliseconds(1));
183189

184190
sockaddr_in addr{};
185191
addr.sin_family = AF_INET;
186-
addr.sin_port = htons(8083);
192+
addr.sin_port = htons(HttpServerTest::port);
187193
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
188194

189195
const std::string methods[9] = { "GET", "POST", "PUT", "PATCH", "OPTIONS", "HEAD", "DELETE", "CONNECT", "TRACE" };
@@ -195,7 +201,7 @@ TEST(HttpServerTest, AllUniqueReqMethods) {
195201
"\r\n";
196202

197203
int listener_fd = socket(AF_INET, SOCK_STREAM, 0);
198-
ASSERT_EQ(connect(listener_fd, (sockaddr*)&addr, sizeof(addr)), 0);
204+
ASSERT_EQ(connect(listener_fd, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
199205
send(listener_fd, request.c_str(), request.size(), 0);
200206

201207
char buffer[1024] {};
@@ -207,3 +213,99 @@ TEST(HttpServerTest, AllUniqueReqMethods) {
207213
ASSERT_TRUE(close(listener_fd) != -1);
208214
}
209215
}
216+
217+
TEST(HttpServerTest, HandleNonExistentGetRoute) {
218+
HttpServer server {};
219+
server.start_listening(HttpServerTest::port);
220+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
221+
222+
sockaddr_in addr{};
223+
addr.sin_family = AF_INET;
224+
addr.sin_port = htons(HttpServerTest::port);
225+
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
226+
227+
std::string request = "GET /foo HTTP/1.1\r\n"
228+
"Host: localhost\r\n"
229+
"Connection: keep-alive\r\n"
230+
"Content-Length: 0\r\n"
231+
"\r\n";
232+
233+
int listener_fd = socket(AF_INET, SOCK_STREAM, 0);
234+
ASSERT_EQ(connect(listener_fd, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
235+
send(listener_fd, request.c_str(), request.size(), 0);
236+
237+
char buffer[1024] {};
238+
int bytes = recv(listener_fd, buffer, sizeof(buffer), 0);
239+
std::string result = std::string(buffer);
240+
241+
EXPECT_GT(bytes, 0);
242+
ASSERT_TRUE(result.find("404 Not Found") != std::string::npos);
243+
ASSERT_TRUE(close(listener_fd) != -1);
244+
}
245+
246+
/*
247+
* This test covers a different branch than HandleNonExistentGetRoute
248+
* because POST requests can handle the request body
249+
*/
250+
TEST(HttpServerTest, HandleNonExistentPostRoute) {
251+
HttpServer server {};
252+
server.start_listening(HttpServerTest::port);
253+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
254+
255+
sockaddr_in addr{};
256+
addr.sin_family = AF_INET;
257+
addr.sin_port = htons(HttpServerTest::port);
258+
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
259+
260+
std::string request = "POST /foo HTTP/1.1\r\n"
261+
"Host: localhost\r\n"
262+
"Connection: keep-alive\r\n"
263+
"Content-Length: 0\r\n"
264+
"\r\n";
265+
266+
int listener_fd = socket(AF_INET, SOCK_STREAM, 0);
267+
ASSERT_EQ(connect(listener_fd, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
268+
send(listener_fd, request.c_str(), request.size(), 0);
269+
270+
char buffer[1024] {};
271+
int bytes = recv(listener_fd, buffer, sizeof(buffer), 0);
272+
std::string result = std::string(buffer);
273+
274+
EXPECT_GT(bytes, 0);
275+
ASSERT_TRUE(result.find("404 Not Found") != std::string::npos);
276+
ASSERT_TRUE(close(listener_fd) != -1);
277+
}
278+
279+
TEST(HttpServerTest, HandleNonExistentHttpMethod) {
280+
HttpServer server {};
281+
server.start_listening(HttpServerTest::port);
282+
std::this_thread::sleep_for(std::chrono::milliseconds(1));
283+
284+
sockaddr_in addr{};
285+
addr.sin_family = AF_INET;
286+
addr.sin_port = htons(HttpServerTest::port);
287+
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
288+
289+
std::string request = "FOO /foo HTTP/1.1\r\n"
290+
"Host: localhost\r\n"
291+
"Connection: keep-alive\r\n"
292+
"Content-Length: 0\r\n"
293+
"\r\n";
294+
295+
int listener_fd = socket(AF_INET, SOCK_STREAM, 0);
296+
ASSERT_EQ(connect(listener_fd, reinterpret_cast<sockaddr *>(&addr), sizeof(addr)), 0);
297+
send(listener_fd, request.c_str(), request.size(), 0);
298+
299+
char buffer[1024] {};
300+
int bytes = recv(listener_fd, buffer, sizeof(buffer), 0);
301+
std::string result = std::string(buffer);
302+
303+
EXPECT_GT(bytes, 0);
304+
ASSERT_TRUE(result.find("500 Error") != std::string::npos);
305+
ASSERT_TRUE(close(listener_fd) != -1);
306+
}
307+
308+
TEST(HttpServerTest, ListenThrowsIfSocketInvalid) {
309+
HttpServer server {};
310+
EXPECT_THROW(server.listen(-1), std::runtime_error);
311+
}

0 commit comments

Comments
 (0)