Skip to content

feat: add bounded HTTP redirect handling - #21

Merged
zuudevs merged 10 commits into
mainfrom
feat/redirect-handling
Sep 15, 2026
Merged

zuudevs merged 10 commits into
mainfrom
feat/redirect-handling

Conversation

@zuudevs

@zuudevs zuudevs commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Summary

Implements roadmap milestone v0.7: configurable automatic HTTP redirects for the stateful Client.

Public configuration

  • Client::set_follow_redirects(bool)
  • Client::set_max_redirects(std::size_t)
  • redirect following defaults to enabled
  • default redirect limit is 10 followed redirects

Supported statuses and method policy

  • 301 / 302: POST becomes GET and drops its body; other methods are preserved
  • 303: all methods except HEAD become GET; body is dropped
  • 307 / 308: method and body are preserved

When a body is dropped, Content-Length, Transfer-Encoding, and Content-Type are not forwarded.

Location resolution

Supports:

  • absolute http:// locations
  • scheme-relative //host/path
  • absolute paths
  • relative paths with dot-segment removal
  • query-only redirects
  • fragment-only references without sending fragments on the wire
  • explicit ports and IPv6 authorities

Redirects to HTTPS or another unsupported scheme return UnsupportedRedirectScheme.

Connection reuse integration

Redirect orchestration sits above a single-request execute_once() path, so existing v0.6 connection ownership remains unchanged:

  • same-origin redirect hops can reuse the retained keep-alive connection
  • cross-origin hops automatically replace the retained connection
  • redirect responses are fully consumed before the next hop

Cross-origin header safety

Cross-origin redirects do not forward caller-supplied:

  • Host
  • Authorization
  • Proxy-Authorization
  • Cookie

Once stripped during a redirect chain, these fields are not restored automatically.

Structured failures

  • redirect limit exceeded -> RedirectLimitExceeded
  • missing/empty Location -> MissingRedirectLocation
  • HTTPS/unsupported scheme -> UnsupportedRedirectScheme
  • malformed redirect URLs propagate the existing URL error model

Tests

Adds unit and loopback integration coverage for:

  • absolute, relative, scheme-relative, query-only, fragment, IPv6 and explicit-port resolution
  • 301/302/303/307/308 method/body policy
  • same-origin redirect reuse on one accepted socket
  • redirect following disabled
  • finite redirect limit
  • POST -> GET rewrite for 302
  • method/body preservation for 307
  • cross-origin credential stripping and Host regeneration
  • HTTPS rejection
  • missing Location

Also documents the redirect behavior in the public API contract.

@zuudevs
zuudevs merged commit adf53b3 into main Sep 15, 2026
6 checks passed
@zuudevs
zuudevs deleted the feat/redirect-handling branch September 15, 2026 00:31

@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: 7d4523bcf4

ℹ️ 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".

Comment thread src/http/redirect.cpp
Comment on lines +187 to +191
return validate_redirect_url(std::string{location});
}

if (location.rfind("//", 0) == 0) {
return validate_redirect_url(std::string{"http:"} + std::string{location});

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 Normalize absolute redirect paths before following them

When Location is an absolute or scheme-relative URI whose path contains dot segments, these branches validate it without applying remove_dot_segments. For example, Location: http://other.test/a/../final is followed as /a/../final rather than the resolved /final; servers that do not normalize the request target can therefore return a different resource. Normalize the parsed path for these reference forms just as for relative paths.

Useful? React with 👍 / 👎.

Comment thread src/http/redirect.cpp
Comment on lines +98 to +100
} else if (segment != ".") {
segments.push_back(segment);
}

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 Preserve empty path segments during dot removal

For redirect paths containing an empty segment immediately before a terminal dot segment, this segment-vector reconstruction loses a slash. For example, /a//. must resolve to /a//, but the function returns /a/ because the empty segment already leaves normalized ending in / and the trailing-slash step adds nothing. Since repeated slashes can identify distinct resources, such redirects may be sent to the wrong target.

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