this repo has no description
at main 425 lines 19 kB view raw
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}