fix: prevent HTTP/2 hpack panic with shared HTTP client#85
fix: prevent HTTP/2 hpack panic with shared HTTP client#85jeffersonrodrigues92 merged 1 commit intodevelopfrom
Conversation
Replace per-request http.Client{} (3 occurrences) with a package-level shared client using custom transport with ForceAttemptHTTP2: false. The bare http.Client{} used Go's default transport which enables HTTP/2 over HTTPS, causing hpack encoder panic under concurrent goroutine access.
X-Lerian-Ref: 0x1
WalkthroughThe change introduces a package-level shared HTTP client in the authentication middleware that consolidates HTTP client creation. Previously, 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@auth/middleware/middleware.go`:
- Around line 52-63: The custom transport for sharedHTTPClient omits important
DefaultTransport fields; update the Transport in sharedHTTPClient to set Proxy:
http.ProxyFromEnvironment and add TLSHandshakeTimeout (e.g., 10*time.Second) and
ExpectContinueTimeout (e.g., 1*time.Second) alongside the existing
ForceAttemptHTTP2/MaxIdleConns/MaxIdleConnsPerHost/IdleConnTimeout so the client
respects system proxies and uses robust TLS/expect-continue timeouts.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: ffdb6080-8e29-4286-a23f-68f597a6c3f9
📒 Files selected for processing (1)
auth/middleware/middleware.go
| // sharedHTTPClient is a package-level HTTP client with a custom transport | ||
| // that prevents HTTP/2 hpack panics under concurrent access. HTTP clients | ||
| // are safe for concurrent use and should be reused across requests. | ||
| var sharedHTTPClient = &http.Client{ | ||
| Timeout: 30 * time.Second, | ||
| Transport: &http.Transport{ | ||
| ForceAttemptHTTP2: false, | ||
| MaxIdleConns: 100, | ||
| MaxIdleConnsPerHost: 10, | ||
| IdleConnTimeout: 90 * time.Second, | ||
| }, | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding missing transport settings for robustness.
The fix correctly addresses the HTTP/2 panic by using a shared client with ForceAttemptHTTP2: false. However, the custom transport omits several settings from Go's DefaultTransport that could cause issues:
- Missing
Proxy: http.ProxyFromEnvironment– HTTP/HTTPS proxy environment variables won't be respected, which may break deployments behind corporate proxies. - Missing
TLSHandshakeTimeout– TLS handshakes could stall; the client-level timeout is a coarse fallback. - Missing
ExpectContinueTimeout– Affects POST requests withExpect: 100-continue.
♻️ Proposed enhancement to match DefaultTransport behavior
var sharedHTTPClient = &http.Client{
Timeout: 30 * time.Second,
Transport: &http.Transport{
+ Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: false,
MaxIdleConns: 100,
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
},
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // sharedHTTPClient is a package-level HTTP client with a custom transport | |
| // that prevents HTTP/2 hpack panics under concurrent access. HTTP clients | |
| // are safe for concurrent use and should be reused across requests. | |
| var sharedHTTPClient = &http.Client{ | |
| Timeout: 30 * time.Second, | |
| Transport: &http.Transport{ | |
| ForceAttemptHTTP2: false, | |
| MaxIdleConns: 100, | |
| MaxIdleConnsPerHost: 10, | |
| IdleConnTimeout: 90 * time.Second, | |
| }, | |
| } | |
| // sharedHTTPClient is a package-level HTTP client with a custom transport | |
| // that prevents HTTP/2 hpack panics under concurrent access. HTTP clients | |
| // are safe for concurrent use and should be reused across requests. | |
| var sharedHTTPClient = &http.Client{ | |
| Timeout: 30 * time.Second, | |
| Transport: &http.Transport{ | |
| Proxy: http.ProxyFromEnvironment, | |
| ForceAttemptHTTP2: false, | |
| MaxIdleConns: 100, | |
| MaxIdleConnsPerHost: 10, | |
| IdleConnTimeout: 90 * time.Second, | |
| TLSHandshakeTimeout: 10 * time.Second, | |
| ExpectContinueTimeout: 1 * time.Second, | |
| }, | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@auth/middleware/middleware.go` around lines 52 - 63, The custom transport for
sharedHTTPClient omits important DefaultTransport fields; update the Transport
in sharedHTTPClient to set Proxy: http.ProxyFromEnvironment and add
TLSHandshakeTimeout (e.g., 10*time.Second) and ExpectContinueTimeout (e.g.,
1*time.Second) alongside the existing
ForceAttemptHTTP2/MaxIdleConns/MaxIdleConnsPerHost/IdleConnTimeout so the client
respects system proxies and uses robust TLS/expect-continue timeouts.
gandalf-at-lerian
left a comment
There was a problem hiding this comment.
Fix correto. Client compartilhado com HTTP/1.1 elimina o race condition no hpack encoder e melhora performance com connection pooling. Merge seguro.
|
🎉 This PR is included in version 2.6.0-beta.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
|
🎉 This PR is included in version 2.6.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
Replace per-request
&http.Client{}(3 occurrences in auth middleware) with a package-level shared client using custom transport withForceAttemptHTTP2: false.Problem
Each auth request created a new
&http.Client{}without custom transport, using Go'shttp.DefaultTransportwhich negotiates HTTP/2 over HTTPS. Under concurrent requests, multiple goroutines share the same HTTP/2 connection whose hpack encoder is not goroutine-safe in Go 1.26.1, causing:Fix
Single shared
http.Clientwith:ForceAttemptHTTP2: false— stays on HTTP/1.1 with connection poolingMaxIdleConnsPerHost: 10— reuses connections efficientlyTimeout: 30s— prevents hanging requestsPer Go docs,
http.Clientis safe for concurrent use and should be reused.Test plan
go build ./...go vet ./...go test ./auth/middleware/...passesGODEBUG=http2client=0