| 365 | } |
| 366 | |
| 367 | bool HTTPRequest::LoadControlData(LineReader& reader) |
| 368 | { |
| 369 | auto maybe_line = reader.ReadLine(); |
| 370 | if (!maybe_line) return false; |
| 371 | const std::string_view& request_line = *maybe_line; |
| 372 | |
| 373 | // Request Line aka Control Data https://httpwg.org/specs/rfc9110.html#rfc.section.6.2 |
| 374 | // Three words separated by spaces, terminated by \n or \r\n |
| 375 | if (request_line.length() < MIN_REQUEST_LINE_LENGTH) throw std::runtime_error("HTTP request line too short"); |
| 376 | |
| 377 | // NUL is not a valid tchar and would silently truncate |
| 378 | // C-string-based parsers rather than being rejected as malformed. |
| 379 | // tchar: https://www.rfc-editor.org/info/rfc7230/#section-3.2.6 |
| 380 | if (request_line.find('\0') != std::string_view::npos) throw std::runtime_error("Invalid request line contains NUL"); |
| 381 | |
| 382 | const std::vector<std::string_view> parts{Split<std::string_view>(request_line, " ")}; |
| 383 | if (parts.size() != 3) throw std::runtime_error("HTTP request line malformed"); |
| 384 | |
| 385 | if (parts[0] == "GET") { |
| 386 | m_method = HTTPRequestMethod::GET; |
| 387 | } else if (parts[0] == "POST") { |
| 388 | m_method = HTTPRequestMethod::POST; |
| 389 | } else if (parts[0] == "HEAD") { |
| 390 | m_method = HTTPRequestMethod::HEAD; |
| 391 | } else if (parts[0] == "PUT") { |
| 392 | m_method = HTTPRequestMethod::PUT; |
| 393 | } else { |
| 394 | m_method = HTTPRequestMethod::UNKNOWN; |
| 395 | } |
| 396 | |
| 397 | m_target = parts[1]; |
| 398 | |
| 399 | if (parts[2].rfind("HTTP/") != 0) throw std::runtime_error("HTTP request line malformed"); |
| 400 | |
| 401 | // Version is exactly two decimal digits separated by a decimal point |
| 402 | // https://httpwg.org/specs/rfc9110.html#rfc.section.2.5 |
| 403 | const std::vector<std::string_view> version_parts{Split<std::string_view>(parts[2].substr(5), ".")}; |
| 404 | if (version_parts.size() != 2) throw std::runtime_error("HTTP request line malformed"); |
| 405 | if (version_parts[0].size() != 1 || version_parts[1].size() != 1) throw std::runtime_error("HTTP bad version"); |
| 406 | auto major = ToIntegral<uint8_t>(version_parts[0]); |
| 407 | auto minor = ToIntegral<uint8_t>(version_parts[1]); |
| 408 | if (!major || !minor || major != 1 || minor > 9) throw std::runtime_error("HTTP bad version"); |
| 409 | m_version.major = major.value(); |
| 410 | m_version.minor = minor.value(); |
| 411 | |
| 412 | return true; |
| 413 | } |
| 414 | |
| 415 | bool HTTPRequest::LoadHeaders(LineReader& reader) |
| 416 | { |