MCPcopy Create free account
hub / github.com/coder/coder / RoundTrip

Method RoundTrip

aibridge/keypool/failover.go:60–107  ·  view source on GitHub ↗

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)

Source from the content-addressed store, hash-verified

58// key-specific failures until one key succeeds or the pool is
59// exhausted. BYOK requests skip the failover loop.
60func (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.

Callers

nothing calls this directly

Calls 10

NewJSONErrorResponseFunction · 0.92
bufferBodyFunction · 0.85
MarkKeyOnStatusFunction · 0.85
WalkerMethod · 0.80
NextMethod · 0.65
ContextMethod · 0.65
CopyMethod · 0.65
CloseMethod · 0.65
CloneMethod · 0.45
ValueMethod · 0.45

Tested by

no test coverage detected