From 43a4efb9f2da9d203386aef4d6203ce6177e07a2 Mon Sep 17 00:00:00 2001 From: lyndonxu Date: Tue, 31 Mar 2026 22:17:56 +0800 Subject: [PATCH] fix: retry on GOAWAY errors when using cached HTTP/2 connections RoundTripOnlyCachedConn bypasses the retry loop in RoundTripOpt, so GOAWAY and other retryable errors were returned directly to callers. Now we check CanRetryError before returning, allowing retryable errors to fall through and create a new connection. Changes: - Export canRetryError as CanRetryError - Check CanRetryError in roundTrip when RoundTripOnlyCachedConn fails --- internal/http2/transport.go | 4 ++-- transport.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/http2/transport.go b/internal/http2/transport.go index 3677f190..25759d86 100644 --- a/internal/http2/transport.go +++ b/internal/http2/transport.go @@ -527,7 +527,7 @@ var ( // It returns either a request to retry (either the same request, or a // modified clone), or an error if the request can't be replayed. func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) { - if !canRetryError(err) { + if !CanRetryError(err) { return nil, err } // If the Body is nil (or http.NoBody), it's safe to reuse @@ -558,7 +558,7 @@ func shouldRetryRequest(req *http.Request, err error) (*http.Request, error) { return nil, fmt.Errorf("http2: Transport: cannot retry err [%v] after Request.Body was written; define Request.GetBody to avoid this error", err) } -func canRetryError(err error) bool { +func CanRetryError(err error) bool { if err == errClientConnUnusable || err == errClientConnGotGoAway { return true } diff --git a/transport.go b/transport.go index 1c549b16..cba871b1 100644 --- a/transport.go +++ b/transport.go @@ -944,7 +944,7 @@ func (t *Transport) roundTrip(req *http.Request) (resp *http.Response, err error if scheme == "https" && t.forceHttpVersion != h1 { resp, err := t.t2.RoundTripOnlyCachedConn(req) - if err != h2internal.ErrNoCachedConn { + if err != h2internal.ErrNoCachedConn && !h2internal.CanRetryError(err) { return resp, err } req, err = rewindBody(req)