Skip to content

feat: add incremental HTTP/1.1 response parser - #13

Merged
zuudevs merged 10 commits into
mainfrom
feat/http-response-parser
Sep 14, 2026
Merged

zuudevs merged 10 commits into
mainfrom
feat/http-response-parser

Conversation

@zuudevs

@zuudevs zuudevs commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Summary

Adds the first incremental HTTP/1.1 response parser on top of the existing Headers, Response, and transport-oriented architecture.

Added

  • include/cpp_request/response.hpp
    • public owning Response value type
    • status code and reason access
    • owned response headers
    • owned in-memory body with zero-copy string_view access
  • src/http/response_parser.hpp / .cpp
    • incremental feed model that accepts arbitrary TCP chunk boundaries
    • strict HTTP/1.1 status-line parsing
    • owned response-header parsing with token/value validation
    • interim 1xx handling before the final response
    • Content-Length framing
    • HEAD / 101 / 204 / 205 / 304 no-body handling
    • close-delimited response bodies completed by EOF
    • Connection: close reuse semantics
    • preserves bytes beyond a completed Content-Length body for a following response
    • detects conflicting Transfer-Encoding + Content-Length
    • explicit NeedsChunkedDecoder handoff for valid Transfer-Encoding: chunked
    • upgraded 101 connections are never marked reusable as HTTP connections
  • response parser tests
    • one-byte-at-a-time feeds
    • exact body completion
    • duplicate headers
    • extra-byte preservation
    • HEAD semantics
    • close-delimited EOF completion
    • connection reuse decisions
    • chunked handoff without consuming chunk bytes
    • malformed status/header/framing failures
    • interim 100 Continue
    • premature EOF
    • protocol upgrade non-reuse

Parser model

The parser owns only bytes that must survive across read_some() calls. It does not assume TCP packet boundaries match HTTP lines or body boundaries. For length-delimited responses it consumes exactly the declared body length and leaves any following bytes pending, which is required for safe keep-alive reuse.

Chunked boundary

This PR intentionally does not decode chunked bodies yet. When a valid chunked response head is recognized, the parser returns ResponseParseProgress::NeedsChunkedDecoder and leaves all body bytes untouched in pending_bytes(). The next PR can attach the chunk decoder to this explicit state without redesigning status/header parsing.

Scope

This PR does not execute socket reads itself, decode chunked bodies, implement redirects, or add the public Client request orchestration path.

@zuudevs
zuudevs merged commit b34166a into main Sep 14, 2026
6 checks passed
@zuudevs
zuudevs deleted the feat/http-response-parser branch September 14, 2026 22:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb508a8b12

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const char* const code_last = code_first + 3;
const auto parsed_code = std::from_chars(code_first, code_last, status_code, 10);
if (parsed_code.ec != std::errc{} || parsed_code.ptr != code_last
|| status_code < 100 || status_code > 999) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject status codes above 599

When a peer sends a status line such as HTTP/1.1 600 Weird, this upper bound accepts it and exposes an invalid status as a successful response. HTTP status codes outside 100–599 are invalid, so this should return InvalidStatusLine rather than proceeding with body framing.

Useful? React with 👍 / 👎.

Comment on lines +168 to +169
if (status_line.size() > code_begin + 3) {
if (status_line[code_begin + 3] != ' ') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require the separator after the status code

When the status line ends immediately after the code, for example HTTP/1.1 204\r\n, this conditional skips the separator check and accepts the response. The HTTP/1.1 status-line grammar requires a space after the three-digit code even when the reason phrase is empty, so this malformed input should produce InvalidStatusLine.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant