this repo has no description
1#include <gtest/gtest.h>
2#include <string_view>
3
4#include "http.hpp"
5
6TEST(HttpRequestParserTest, SimpleGetRequest)
7{
8 constexpr std::string_view http_request = "GET /hello/world HTTP/1.1\r\n"
9 "Host: example.com\r\n"
10 "User-Agent: TestClient/1.0\r\n"
11 "\r\n";
12
13 HttpRequestParser parser;
14 auto result = parser.parse(
15 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request.data()), http_request.size()));
16
17 ASSERT_TRUE(result.has_value());
18 RequestList requests;
19 parser.get_completed_requests(requests);
20 ASSERT_EQ(requests.size(), 1);
21
22 const HttpRequest &request = requests.front();
23
24 EXPECT_EQ(request.method, "GET");
25 EXPECT_EQ(request.url, "/hello/world");
26 EXPECT_EQ(request.http_major, 1);
27 EXPECT_EQ(request.http_minor, 1);
28 ASSERT_EQ(request.headers.size(), 2);
29 EXPECT_EQ(request.headers[0].first, "Host");
30 EXPECT_EQ(request.headers[0].second, "example.com");
31 EXPECT_EQ(request.headers[1].first, "User-Agent");
32 EXPECT_EQ(request.headers[1].second, "TestClient/1.0");
33 EXPECT_TRUE(request.body.empty());
34}
35
36TEST(HttpRequestParserTest, SimpleGetRequest2)
37{
38 constexpr std::string_view http_request =
39 "GET / HTTP/1.1\r\n"
40 "Host: localhost:8080\r\n"
41 "Connection: keep-alive\r\n"
42 "Cache-Control: max-age=0\r\n"
43 "sec-ch-ua: \"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"\r\n"
44 "sec-ch-ua-mobile: ?0\r\n"
45 "sec-ch-ua-platform: \"Linux\"\r\n"
46 "Upgrade-Insecure-Requests: 1\r\n"
47 "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 "
48 "Safari/537.36\r\n"
49 "Accept: "
50 "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/"
51 "signed-exchange;v=b3;q=0.7\r\n"
52 "Sec-Fetch-Site: none\r\n"
53 "Sec-Fetch-Mode: navigate\r\n"
54 "Sec-Fetch-User: ?1\r\n"
55 "Sec-Fetch-Dest: document\r\n"
56 "Accept-Encoding: gzip, deflate, br, zstd\r\n"
57 "Accept-Language: en-US,en;q=0.9,am;q=0.8\r\n"
58 "\r\n";
59
60 HttpRequestParser parser;
61 auto input = std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request.data()), http_request.size());
62 auto result = parser.parse(input);
63 ASSERT_TRUE(result.has_value());
64 RequestList requests;
65 parser.get_completed_requests(requests);
66 ASSERT_EQ(requests.size(), 1);
67 const HttpRequest &request = requests.front();
68 EXPECT_EQ(request.method, "GET");
69 EXPECT_EQ(request.url, "/");
70 EXPECT_EQ(request.http_major, 1);
71 EXPECT_EQ(request.http_minor, 1);
72 ASSERT_EQ(request.headers.size(), 15);
73 EXPECT_EQ(request.headers[0].first, "Host");
74 EXPECT_EQ(request.headers[0].second, "localhost:8080");
75 EXPECT_EQ(request.headers[1].first, "Connection");
76 EXPECT_EQ(request.headers[1].second, "keep-alive");
77 EXPECT_EQ(request.headers[2].first, "Cache-Control");
78 EXPECT_EQ(request.headers[2].second, "max-age=0");
79 EXPECT_EQ(request.headers[3].first, "sec-ch-ua");
80 EXPECT_EQ(request.headers[3].second,
81 "\"Chromium\";v=\"142\", \"Google Chrome\";v=\"142\", \"Not_A Brand\";v=\"99\"");
82 EXPECT_EQ(request.headers[4].first, "sec-ch-ua-mobile");
83 EXPECT_EQ(request.headers[4].second, "?0");
84 EXPECT_EQ(request.headers[5].first, "sec-ch-ua-platform");
85 EXPECT_EQ(request.headers[5].second, "\"Linux\"");
86 EXPECT_EQ(request.headers[6].first, "Upgrade-Insecure-Requests");
87 EXPECT_EQ(request.headers[6].second, "1");
88 EXPECT_EQ(request.headers[7].first, "User-Agent");
89 EXPECT_EQ(request.headers[7].second,
90 "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 "
91 "Safari/537.36");
92 EXPECT_EQ(request.headers[8].first, "Accept");
93 EXPECT_EQ(request.headers[8].second,
94 "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,"
95 "application/signed-exchange;v=b3;q=0.7");
96 EXPECT_EQ(request.headers[9].first, "Sec-Fetch-Site");
97 EXPECT_EQ(request.headers[9].second, "none");
98 EXPECT_EQ(request.headers[10].first, "Sec-Fetch-Mode");
99 EXPECT_EQ(request.headers[10].second, "navigate");
100 EXPECT_EQ(request.headers[11].first, "Sec-Fetch-User");
101 EXPECT_EQ(request.headers[11].second, "?1");
102 EXPECT_EQ(request.headers[12].first, "Sec-Fetch-Dest");
103 EXPECT_EQ(request.headers[12].second, "document");
104 EXPECT_EQ(request.headers[13].first, "Accept-Encoding");
105 EXPECT_EQ(request.headers[13].second, "gzip, deflate, br, zstd");
106 EXPECT_EQ(request.headers[14].first, "Accept-Language");
107 EXPECT_EQ(request.headers[14].second, "en-US,en;q=0.9,am;q=0.8");
108 EXPECT_TRUE(request.body.empty());
109}
110
111TEST(HttpRequestParserTest, PartialInput)
112{
113 constexpr std::string_view http_request =
114 "POST /submit HTTP/1.1\r\n"
115 "Host: example.com\r\n"
116 "Content-Type: text/html; charset=UTF-8\r\n"
117 "Content-Length: 81\r\n"
118 "Connection: close\r\n"
119 "\r\n"
120 "<html><body><h1>Hello World</h1><p>This is a test HTML payload.</p></body></html>";
121 constexpr std::string_view part1 = http_request.substr(0, 50);
122 constexpr std::string_view part2 = http_request.substr(50);
123
124 HttpRequestParser parser;
125 auto result1 =
126 parser.parse(std::span<const std::byte>(std::bit_cast<const std::byte *>(part1.data()), part1.size()));
127 ASSERT_TRUE(result1.has_value());
128 RequestList requests;
129 parser.get_completed_requests(requests);
130 EXPECT_TRUE(requests.empty()); // No complete requests yet
131
132 auto result2 =
133 parser.parse(std::span<const std::byte>(std::bit_cast<const std::byte *>(part2.data()), part2.size()));
134 ASSERT_TRUE(result2.has_value());
135 parser.get_completed_requests(requests);
136 ASSERT_EQ(requests.size(), 1);
137 const HttpRequest &request = requests.front();
138 EXPECT_EQ(request.method, "POST");
139 EXPECT_EQ(request.url, "/submit");
140 EXPECT_EQ(request.http_major, 1);
141 EXPECT_EQ(request.http_minor, 1);
142 ASSERT_EQ(request.headers.size(), 4);
143 EXPECT_EQ(request.headers[0].first, "Host");
144 EXPECT_EQ(request.headers[0].second, "example.com");
145 EXPECT_EQ(request.headers[1].first, "Content-Type");
146 EXPECT_EQ(request.headers[1].second, "text/html; charset=UTF-8");
147 EXPECT_EQ(request.headers[2].first, "Content-Length");
148 EXPECT_EQ(request.headers[2].second, "81");
149 EXPECT_EQ(request.headers[3].first, "Connection");
150 EXPECT_EQ(request.headers[3].second, "close");
151 EXPECT_EQ(request.body.size(), 81);
152 const std::string expected_body =
153 "<html><body><h1>Hello World</h1><p>This is a test HTML payload.</p></body></html>";
154 EXPECT_EQ(std::string_view(reinterpret_cast<const char *>(request.body.data()), request.body.size()),
155 expected_body);
156}
157
158TEST(HttpRequestParserTest, MultipleCompleteRequests)
159{
160 constexpr std::string_view http_requests = "GET /first HTTP/1.1\r\n"
161 "Host: example.com\r\n"
162 "\r\n"
163 "POST /second HTTP/1.1\r\n"
164 "Host: example.com\r\n"
165 "Content-Length: 11\r\n"
166 "\r\n"
167 "Hello World";
168
169 HttpRequestParser parser;
170 auto result = parser.parse(
171 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_requests.data()), http_requests.size()));
172
173 ASSERT_TRUE(result.has_value());
174
175 RequestList requests;
176 parser.get_completed_requests(requests);
177 ASSERT_EQ(requests.size(), 2);
178
179 const HttpRequest &first_request = requests[0];
180 EXPECT_EQ(first_request.method, "GET");
181 EXPECT_EQ(first_request.url, "/first");
182 EXPECT_EQ(first_request.http_major, 1);
183 EXPECT_EQ(first_request.http_minor, 1);
184 ASSERT_EQ(first_request.headers.size(), 1);
185 EXPECT_EQ(first_request.headers[0].first, "Host");
186 EXPECT_EQ(first_request.headers[0].second, "example.com");
187 EXPECT_TRUE(first_request.body.empty());
188
189 const HttpRequest &second_request = requests[1];
190 EXPECT_EQ(second_request.method, "POST");
191 EXPECT_EQ(second_request.url, "/second");
192 EXPECT_EQ(second_request.http_major, 1);
193 EXPECT_EQ(second_request.http_minor, 1);
194 ASSERT_EQ(second_request.headers.size(), 2);
195 EXPECT_EQ(second_request.headers[0].first, "Host");
196 EXPECT_EQ(second_request.headers[0].second, "example.com");
197 EXPECT_EQ(second_request.headers[1].first, "Content-Length");
198 EXPECT_EQ(second_request.headers[1].second, "11");
199 ASSERT_EQ(second_request.body.size(), 11);
200 EXPECT_EQ(std::string_view(reinterpret_cast<const char *>(second_request.body.data()), second_request.body.size()),
201 "Hello World");
202}
203
204TEST(HttpRequestParserTest, EmptyInput)
205{
206 HttpRequestParser parser;
207 auto result = parser.parse(std::span<const std::byte>());
208
209 ASSERT_TRUE(result.has_value());
210 RequestList requests;
211 parser.get_completed_requests(requests);
212 EXPECT_TRUE(requests.empty());
213}
214
215TEST(HttpRequestParserTest, IncompleteHeaders)
216{
217 constexpr std::string_view http_request = "GET /incomplete HTTP/1.1\r\n"
218 "Host: example.com\r\n"
219 "User-Agent: TestClient/1.0"; // Missing final CRLF
220
221 HttpRequestParser parser;
222 auto result = parser.parse(
223 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request.data()), http_request.size()));
224
225 ASSERT_TRUE(result.has_value());
226 RequestList requests;
227 parser.get_completed_requests(requests);
228 EXPECT_TRUE(requests.empty()); // No complete requests yet
229}
230
231TEST(HttpRequestParserTest, IncompleteBody)
232{
233 constexpr std::string_view http_request = "POST /submit HTTP/1.1\r\n"
234 "Host: example.com\r\n"
235 "Content-Length: 20\r\n"
236 "\r\n"
237 "Partial body data"; // Only 17 bytes instead of 20
238
239 HttpRequestParser parser;
240 auto result = parser.parse(
241 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request.data()), http_request.size()));
242
243 ASSERT_TRUE(result.has_value());
244 RequestList requests;
245 parser.get_completed_requests(requests);
246 EXPECT_TRUE(requests.empty()); // No complete requests yet
247
248 // Now provide the remaining body data
249 constexpr std::string_view remaining_body = "123"; // 3 more bytes to complete
250 auto result2 = parser.parse(
251 std::span<const std::byte>(std::bit_cast<const std::byte *>(remaining_body.data()), remaining_body.size()));
252 ASSERT_TRUE(result2.has_value());
253 parser.get_completed_requests(requests);
254 ASSERT_EQ(requests.size(), 1);
255 const HttpRequest &request = requests.front();
256 EXPECT_EQ(request.method, "POST");
257 EXPECT_EQ(request.url, "/submit");
258 EXPECT_EQ(request.http_major, 1);
259 EXPECT_EQ(request.http_minor, 1);
260 ASSERT_EQ(request.headers.size(), 2);
261 EXPECT_EQ(request.headers[0].first, "Host");
262 EXPECT_EQ(request.headers[0].second, "example.com");
263 EXPECT_EQ(request.headers[1].first, "Content-Length");
264 EXPECT_EQ(request.headers[1].second, "20");
265 ASSERT_EQ(request.body.size(), 20);
266 EXPECT_EQ(std::string_view(reinterpret_cast<const char *>(request.body.data()), request.body.size()),
267 "Partial body data123");
268}
269
270TEST(HttpRequestParserTest, MalformedRequest)
271{
272 constexpr std::string_view http_request = "GET /incomplete HTTP/1.1\r\n"
273 "Host: example.com\r\n"
274 "Content-Length: abc\r\n" // Invalid Content-Length
275 "\r\n";
276
277 HttpRequestParser parser;
278 auto result = parser.parse(
279 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request.data()), http_request.size()));
280
281 ASSERT_FALSE(result.has_value());
282 EXPECT_EQ(result.error(), "Invalid character in Content-Length");
283}
284
285TEST(HttpRequestParserTest, FeedCharByChar)
286{
287 constexpr std::string_view http_request = "GET /charbychar HTTP/1.1\r\n"
288 "Host: example.com\r\n"
289 "\r\n";
290
291 HttpRequestParser parser;
292 constexpr std::string_view view = http_request.substr(0, http_request.size() - 1);
293 for (char ch : view)
294 {
295 auto result = parser.parse(std::span<const std::byte>(std::bit_cast<const std::byte *>(&ch), 1));
296 // Should not produce a complete request until the final character
297 ASSERT_TRUE(result.has_value());
298 RequestList requests;
299 parser.get_completed_requests(requests);
300 EXPECT_TRUE(requests.empty());
301 }
302 // Now feed the final character
303 char final_ch = http_request.back();
304 auto result = parser.parse(std::span<const std::byte>(std::bit_cast<const std::byte *>(&final_ch), 1));
305 ASSERT_TRUE(result.has_value());
306 RequestList requests;
307 parser.get_completed_requests(requests);
308 ASSERT_EQ(requests.size(), 1);
309 const HttpRequest &request = requests.front();
310 EXPECT_EQ(request.method, "GET");
311 EXPECT_EQ(request.url, "/charbychar");
312 EXPECT_EQ(request.http_major, 1);
313 EXPECT_EQ(request.http_minor, 1);
314 ASSERT_EQ(request.headers.size(), 1);
315 EXPECT_EQ(request.headers[0].first, "Host");
316 EXPECT_EQ(request.headers[0].second, "example.com");
317 EXPECT_TRUE(request.body.empty());
318}
319
320TEST(HttpRequestParserTest, ResetParser)
321{
322 constexpr std::string_view http_request1 = "GET /first HTTP/1.1\r\n"
323 "Host: example.com\r\n"
324 "\r\n";
325
326 HttpRequestParser parser;
327 auto result1 = parser.parse(
328 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request1.data()), http_request1.size()));
329
330 ASSERT_TRUE(result1.has_value());
331 RequestList requests1;
332 parser.get_completed_requests(requests1);
333 ASSERT_EQ(requests1.size(), 1);
334
335 parser.reset();
336
337 constexpr std::string_view http_request2 = "POST /second HTTP/1.1\r\n"
338 "Host: example.com\r\n"
339 "Content-Length: 5\r\n"
340 "\r\n"
341 "Hello";
342
343 auto result2 = parser.parse(
344 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_request2.data()), http_request2.size()));
345
346 ASSERT_TRUE(result2.has_value());
347 RequestList requests2;
348 parser.get_completed_requests(requests2);
349 ASSERT_EQ(requests2.size(), 1);
350
351 const HttpRequest &request2 = requests2.front();
352 EXPECT_EQ(request2.method, "POST");
353 EXPECT_EQ(request2.url, "/second");
354 EXPECT_EQ(request2.http_major, 1);
355 EXPECT_EQ(request2.http_minor, 1);
356 ASSERT_EQ(request2.headers.size(), 2);
357 EXPECT_EQ(request2.headers[0].first, "Host");
358 EXPECT_EQ(request2.headers[0].second, "example.com");
359 EXPECT_EQ(request2.headers[1].first, "Content-Length");
360 EXPECT_EQ(request2.headers[1].second, "5");
361 ASSERT_EQ(request2.body.size(), 5);
362 EXPECT_EQ(std::string_view(reinterpret_cast<const char *>(request2.body.data()), request2.body.size()), "Hello");
363}
364
365TEST(HttpRequestParserTest, MultipleRequestsWithLastOnePartial)
366{
367 constexpr std::string_view http_requests = "GET /first HTTP/1.1\r\n"
368 "Host: example.com\r\n"
369 "\r\n"
370 "GET /first HTTP/1.1\r\n"
371 "Host: example.com\r\n"
372 "\r\n"
373 "GET /first HTTP/1.1\r\n"
374 "Host: example.com\r\n"
375 "\r\n"
376 "POST /second HTTP/1.1\r\n"
377 "Host: example.com\r\n"
378 "Content-Length: 11\r\n"
379 "\r\n"
380 "Hello"; // Incomplete body
381
382 HttpRequestParser parser;
383 auto result = parser.parse(
384 std::span<const std::byte>(std::bit_cast<const std::byte *>(http_requests.data()), http_requests.size()));
385
386 ASSERT_TRUE(result.has_value());
387
388 RequestList requests;
389 parser.get_completed_requests(requests);
390 ASSERT_EQ(requests.size(), 3); // Only 3 complete requests
391 for (int i = 0; i < 3; ++i)
392 {
393 const HttpRequest &request = requests[i];
394 EXPECT_EQ(request.method, "GET");
395 EXPECT_EQ(request.url, "/first");
396 EXPECT_EQ(request.http_major, 1);
397 EXPECT_EQ(request.http_minor, 1);
398 ASSERT_EQ(request.headers.size(), 1);
399 EXPECT_EQ(request.headers[0].first, "Host");
400 EXPECT_EQ(request.headers[0].second, "example.com");
401 EXPECT_TRUE(request.body.empty());
402 }
403
404 // Now provide the remaining body data
405 constexpr std::string_view remaining_body = " World"; // 6 more bytes to complete
406 auto result2 = parser.parse(
407 std::span<const std::byte>(std::bit_cast<const std::byte *>(remaining_body.data()), remaining_body.size()));
408 ASSERT_TRUE(result2.has_value());
409 RequestList requests2;
410 parser.get_completed_requests(requests2);
411 ASSERT_EQ(requests2.size(), 1);
412 const HttpRequest &request = requests2.front();
413 EXPECT_EQ(request.method, "POST");
414 EXPECT_EQ(request.url, "/second");
415 EXPECT_EQ(request.http_major, 1);
416 EXPECT_EQ(request.http_minor, 1);
417 ASSERT_EQ(request.headers.size(), 2);
418 EXPECT_EQ(request.headers[0].first, "Host");
419 EXPECT_EQ(request.headers[0].second, "example.com");
420 EXPECT_EQ(request.headers[1].first, "Content-Length");
421 EXPECT_EQ(request.headers[1].second, "11");
422 ASSERT_EQ(request.body.size(), 11);
423 EXPECT_EQ(std::string_view(reinterpret_cast<const char *>(request.body.data()), request.body.size()),
424 "Hello World");
425}