| 298 | } |
| 299 | |
| 300 | bool HTTPHeaders::Read(util::LineReader& reader) |
| 301 | { |
| 302 | // Headers https://httpwg.org/specs/rfc9110.html#rfc.section.6.3 |
| 303 | // A sequence of Field Lines https://httpwg.org/specs/rfc9110.html#rfc.section.5.2 |
| 304 | while (auto maybe_line = reader.ReadLine()) { |
| 305 | if (reader.Consumed() > MAX_HEADERS_SIZE) throw std::runtime_error("HTTP headers exceed size limit"); |
| 306 | |
| 307 | const std::string_view& line = *maybe_line; |
| 308 | |
| 309 | // An empty line indicates end of the headers section https://www.rfc-editor.org/rfc/rfc2616#section-4 |
| 310 | if (line.empty()) return true; |
| 311 | |
| 312 | // "Field values containing CR, LF, or NUL characters are invalid and dangerous" |
| 313 | // https://httpwg.org/specs/rfc9110.html#rfc.section.5.5 |
| 314 | // A sender MUST NOT generate a bare CR (a CR character not immediately followed by LF) |
| 315 | // within any protocol elements other than the content. |
| 316 | // A recipient of such a bare CR MUST consider that element to be invalid... |
| 317 | // https://httpwg.org/specs/rfc9112.html#rfc.section.2.2 |
| 318 | if (line.find_first_of("\r\n\0", 0, 3) != std::string_view::npos) throw std::runtime_error("Header contains invalid character"); |
| 319 | |
| 320 | // Header line must have at least one ":" |
| 321 | // keys are not allowed to have delimiters like ":" but values are |
| 322 | // https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2 |
| 323 | const size_t pos{line.find(':')}; |
| 324 | if (pos == std::string_view::npos) throw std::runtime_error("HTTP header missing colon (:)"); |
| 325 | |
| 326 | // Whitespace is strictly not allowed in the field-name (key) |
| 327 | // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.2 |
| 328 | std::string_view key = line.substr(0, pos); |
| 329 | if (key.find_first_of(" \t\n\r\f\v") != std::string_view::npos) throw std::runtime_error("Invalid header field-name contains whitespace"); |
| 330 | // Whitespace is optional in the value and can be trimmed |
| 331 | std::string value = util::TrimString(std::string_view(line).substr(pos + 1)); |
| 332 | |
| 333 | // Header keys are Field Names: https://httpwg.org/specs/rfc9110.html#fields.names |
| 334 | // which consist of "tokens": https://httpwg.org/specs/rfc9110.html#rfc.section.5.6.2 |
| 335 | // that can not be empty. |
| 336 | if (key.empty()) throw std::runtime_error("Empty HTTP header name"); |
| 337 | |
| 338 | Write(std::string(key), std::move(value)); |
| 339 | } |
| 340 | |
| 341 | return false; |
| 342 | } |
| 343 | |
| 344 | std::string HTTPHeaders::Stringify() const |
| 345 | { |
no test coverage detected