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
912TEST (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-
2516TEST (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\n Host: localhost\r\n\r\n " ;
34+ const char * request = " GET / HTTP/1.1\r\n Host: 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\n hello, world" ;
10199 send (sock, request, strlen (request), 0 );
@@ -113,36 +111,44 @@ TEST(HttpServerTest, IgnoreGetReqBody) {
113111}
114112
115113TEST (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\n hello, 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
148154TEST (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