RoundTrip is invoked by the proxy once per outer client request, after Rewrite has applied proxy headers. For centralized requests it walks the key pool, retrying on key-specific failures until one key succeeds or the pool is exhausted. BYOK requests skip the failover loop.
(req *http.Request)
| 58 | // key-specific failures until one key succeeds or the pool is |
| 59 | // exhausted. BYOK requests skip the failover loop. |
| 60 | func (t *keyFailoverTransport) RoundTrip(req *http.Request) (*http.Response, error) { |
| 61 | if t.config.IsBYOK(req) { |
| 62 | return t.inner.RoundTrip(req) |
| 63 | } |
| 64 | |
| 65 | // Buffer once so retries can replay the body. |
| 66 | body, err := bufferBody(req) |
| 67 | if err != nil { |
| 68 | return nil, err |
| 69 | } |
| 70 | |
| 71 | // Fresh walker per request, independent of other inflight requests. |
| 72 | walker := t.config.Pool.Walker() |
| 73 | for { |
| 74 | key, keyPoolErr := walker.Next() |
| 75 | if keyPoolErr != nil { |
| 76 | resp := t.config.BuildKeyPoolResponse(keyPoolErr) |
| 77 | if resp == nil { |
| 78 | // Fallback if BuildKeyPoolResponse returns nil. |
| 79 | body := []byte(`{"error":"key pool unavailable"}`) |
| 80 | resp = utils.NewJSONErrorResponse(http.StatusBadGateway, 0, body) |
| 81 | } |
| 82 | return resp, nil |
| 83 | } |
| 84 | |
| 85 | // Clone per attempt so the original request isn't mutated. |
| 86 | outReq := req.Clone(req.Context()) |
| 87 | if body != nil { |
| 88 | outReq.Body = io.NopCloser(bytes.NewReader(body)) |
| 89 | } |
| 90 | t.config.InjectAuthKey(&outReq.Header, key.Value()) |
| 91 | |
| 92 | resp, rtErr := t.inner.RoundTrip(outReq) |
| 93 | if rtErr != nil { |
| 94 | // Transport-level error, not a key issue. |
| 95 | return resp, rtErr |
| 96 | } |
| 97 | // MarkKeyOnStatus returns true on key-specific failures (e.g. 401/403/429). |
| 98 | if MarkKeyOnStatus(req.Context(), key, resp, t.config.Logger, t.config.ProviderName) { |
| 99 | // Drain and retry with the next key. |
| 100 | _, _ = io.Copy(io.Discard, resp.Body) |
| 101 | _ = resp.Body.Close() |
| 102 | continue |
| 103 | } |
| 104 | // Success or non-key error, forward as-is. |
| 105 | return resp, nil |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | // bufferBody reads the request body fully so it can be replayed |
| 110 | // across key-failover retries. Returns nil for a nil body. |
nothing calls this directly
no test coverage detected