@@ -24,13 +24,17 @@ type http3Transport struct {
2424 h3Transport * http3.Transport
2525}
2626
27+ type http3BrokenEntry struct {
28+ until time.Time
29+ backoff time.Duration
30+ }
31+
2732type http3FallbackTransport struct {
2833 h3Transport * http3.Transport
2934 h2Fallback innerTransport
3035 fallbackDelay time.Duration
3136 brokenAccess sync.Mutex
32- brokenUntil time.Time
33- brokenBackoff time.Duration
37+ broken map [string ]http3BrokenEntry
3438}
3539
3640func newHTTP3RoundTripper (
@@ -114,6 +118,7 @@ func newHTTP3FallbackTransport(
114118 h3Transport : newHTTP3RoundTripper (rawDialer , baseTLSConfig , options ),
115119 h2Fallback : h2Fallback ,
116120 fallbackDelay : fallbackDelay ,
121+ broken : make (map [string ]http3BrokenEntry ),
117122 }, nil
118123}
119124
@@ -138,31 +143,32 @@ func (t *http3FallbackTransport) RoundTrip(request *http.Request) (*http.Respons
138143}
139144
140145func (t * http3FallbackTransport ) roundTripHTTP3 (request * http.Request ) (* http.Response , error ) {
141- if t .h3Broken () {
146+ authority := requestAuthority (request )
147+ if t .h3Broken (authority ) {
142148 return t .h2FallbackRoundTrip (request )
143149 }
144150 response , err := t .h3Transport .RoundTripOpt (request , http3.RoundTripOpt {OnlyCachedConn : true })
145151 if err == nil {
146- t .clearH3Broken ()
152+ t .clearH3Broken (authority )
147153 return response , nil
148154 }
149155 if ! errors .Is (err , http3 .ErrNoCachedConn ) {
150- t .markH3Broken ()
156+ t .markH3Broken (authority )
151157 return t .h2FallbackRoundTrip (cloneRequestForRetry (request ))
152158 }
153159 if ! requestReplayable (request ) {
154160 response , err = t .h3Transport .RoundTrip (request )
155161 if err == nil {
156- t .clearH3Broken ()
162+ t .clearH3Broken (authority )
157163 return response , nil
158164 }
159- t .markH3Broken ()
165+ t .markH3Broken (authority )
160166 return nil , err
161167 }
162- return t .roundTripHTTP3Race (request )
168+ return t .roundTripHTTP3Race (request , authority )
163169}
164170
165- func (t * http3FallbackTransport ) roundTripHTTP3Race (request * http.Request ) (* http.Response , error ) {
171+ func (t * http3FallbackTransport ) roundTripHTTP3Race (request * http.Request , authority string ) (* http.Response , error ) {
166172 ctx , cancel := context .WithCancel (request .Context ())
167173 defer cancel ()
168174 type result struct {
@@ -215,13 +221,13 @@ func (t *http3FallbackTransport) roundTripHTTP3Race(request *http.Request) (*htt
215221 received ++
216222 if raceResult .err == nil {
217223 if raceResult .h3 {
218- t .clearH3Broken ()
224+ t .clearH3Broken (authority )
219225 }
220226 drainRemaining ()
221227 return raceResult .response , nil
222228 }
223229 if raceResult .h3 {
224- t .markH3Broken ()
230+ t .markH3Broken (authority )
225231 h3Err = raceResult .err
226232 if goroutines == 1 {
227233 goroutines ++
@@ -269,29 +275,47 @@ func (t *http3FallbackTransport) Close() error {
269275 return t .h3Transport .Close ()
270276}
271277
272- func (t * http3FallbackTransport ) h3Broken () bool {
278+ func (t * http3FallbackTransport ) h3Broken (authority string ) bool {
279+ if authority == "" {
280+ return false
281+ }
273282 t .brokenAccess .Lock ()
274283 defer t .brokenAccess .Unlock ()
275- return ! t .brokenUntil .IsZero () && time .Now ().Before (t .brokenUntil )
284+ entry , found := t .broken [authority ]
285+ if ! found {
286+ return false
287+ }
288+ if entry .until .IsZero () || ! time .Now ().Before (entry .until ) {
289+ delete (t .broken , authority )
290+ return false
291+ }
292+ return true
276293}
277294
278- func (t * http3FallbackTransport ) clearH3Broken () {
295+ func (t * http3FallbackTransport ) clearH3Broken (authority string ) {
296+ if authority == "" {
297+ return
298+ }
279299 t .brokenAccess .Lock ()
280- t .brokenUntil = time.Time {}
281- t .brokenBackoff = 0
300+ delete (t .broken , authority )
282301 t .brokenAccess .Unlock ()
283302}
284303
285- func (t * http3FallbackTransport ) markH3Broken () {
304+ func (t * http3FallbackTransport ) markH3Broken (authority string ) {
305+ if authority == "" {
306+ return
307+ }
286308 t .brokenAccess .Lock ()
287309 defer t .brokenAccess .Unlock ()
288- if t .brokenBackoff == 0 {
289- t .brokenBackoff = 5 * time .Minute
310+ entry := t .broken [authority ]
311+ if entry .backoff == 0 {
312+ entry .backoff = 5 * time .Minute
290313 } else {
291- t . brokenBackoff *= 2
292- if t . brokenBackoff > 48 * time .Hour {
293- t . brokenBackoff = 48 * time .Hour
314+ entry . backoff *= 2
315+ if entry . backoff > 48 * time .Hour {
316+ entry . backoff = 48 * time .Hour
294317 }
295318 }
296- t .brokenUntil = time .Now ().Add (t .brokenBackoff )
319+ entry .until = time .Now ().Add (entry .backoff )
320+ t .broken [authority ] = entry
297321}
0 commit comments