Baike.dev
All toolsAI codingTrendingOpen sourceNewsSubmit
Log in
Back to tool/Back to issues
#1539·hertz

URI.Update misparses relative redirect Location whose query embeds an absolute URL (e.g. /login?redirect=https://host/), breaking client DoRedirects

Author: lRoccoonCreated Aug 24, 2026Updated Aug 25, 2026

Describe the bug

protocol.URI.Update / UpdateBytes decides "absolute uri" by searching for // anywhere in the new URI string:

go
// pkg/protocol/uri.go (v0.10.6)
func (u *URI) updateBytes(newURI, buf []byte) []byte {
	...
	n := bytes.Index(newURI, bytestr.StrSlashSlash)
	if n >= 0 {
		// absolute uri

A relative redirect Location whose query string embeds an absolute URL — the classic SSO login pattern Location: /login/redirect_to_sso?redirect=https://example.com/ — contains // inside the query, so it is misclassified as an absolute URI. The whole string is then re-parsed from scratch, producing an empty host (and the scheme downgrades from https to http).

Since client.DoRequestFollowRedirects resolves every hop through getRedirectURL → URI.UpdateBytes, Client.DoRedirects fails on the second hop for any server that answers with this very common redirect shape. Depending on the dialer, the failure surfaces as dial tcp :80: connection refused or missing required Host header in request.

Minimal reproduction (no network needed)

go
u := protocol.AcquireURI()
u.Update("https://example.com/")
u.Update("/login/redirect_to_sso?redirect=https://example.com/")
fmt.Printf("%q host=%q scheme=%q\n", u.FullURI(), u.Host(), u.Scheme())
// got:  "http:///login/redirect_to_sso?redirect=https://example.com/" host="" scheme="http"
// want: "https://example.com/login/redirect_to_sso?redirect=https://example.com/" host="example.com"

Control case (same path, no // in the query) resolves correctly:

go
u2 := protocol.AcquireURI()
u2.Update("https://example.com/")
u2.Update("/login/plain")
// "https://example.com/login/plain" host="example.com"  ✅

End-to-end: Client.DoRedirects against any server replying 302 with Location: /login?redirect=https://example.com/:

err = dial tcp :80: connect: connection refused
req.URI().String() = "http:///login/redirect_to_sso?redirect=https://example.com/"

Expected behavior

Per RFC 3986 reference resolution (and net/url.URL.ResolveReference), a new URI should only be treated as absolute when it starts with a scheme (scheme://...) or is protocol-relative (starts with //). A leading / means path-absolute relative reference; // appearing later (e.g. inside the query) must not affect classification. Something like:

go
if bytes.HasPrefix(newURI, bytestr.StrSlashSlash) || schemePrefixed(newURI) {
	// absolute
}

(the existing newURI[0] == '/' branch already handles the path-absolute case correctly once the misclassification is removed).

Environment

  • hertz v0.10.6 (also reproduces via a downstream fork that forwards to the same implementation)
  • Go 1.23, Linux

Happy to send a PR if the proposed direction looks right.

Source: cloudwego/hertz

View original on GitHubView discussion on GitHub