Skip to content

Commit 338ba5a

Browse files
committed
Document redirect helpers and tighten URL validation
1 parent e2333b8 commit 338ba5a

2 files changed

Lines changed: 29 additions & 17 deletions

File tree

client/transport.go

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import (
1111
// defaultRedirectLimit mirrors fasthttp's default when callers supply a negative redirect cap.
1212
const defaultRedirectLimit = 16
1313

14+
// httpClientTransport unifies the operations exposed by the Fiber client across
15+
// the fasthttp.Client, fasthttp.HostClient, and fasthttp.LBClient adapters so
16+
// helper logic can treat the concrete transports uniformly.
1417
type httpClientTransport interface {
1518
Do(req *fasthttp.Request, resp *fasthttp.Response) error
1619
DoTimeout(req *fasthttp.Request, resp *fasthttp.Response, timeout time.Duration) error
@@ -153,9 +156,9 @@ func (l *lbClientTransport) DoDeadline(req *fasthttp.Request, resp *fasthttp.Res
153156
return l.client.DoDeadline(req, resp, deadline)
154157
}
155158

156-
// DoRedirects proxies redirect handling through doRedirectsWithClient so
157-
// lbClientTransport matches fasthttp.Client semantics even though
158-
// fasthttp.LBClient does not publish a dedicated DoRedirects helper.
159+
// DoRedirects proxies redirect handling through doRedirectsWithClient so the
160+
// load-balanced transport mirrors fasthttp.Client semantics despite
161+
// fasthttp.LBClient not exposing DoRedirects directly.
159162
func (l *lbClientTransport) DoRedirects(req *fasthttp.Request, resp *fasthttp.Response, maxRedirects int) error {
160163
return doRedirectsWithClient(req, resp, maxRedirects, l.client)
161164
}
@@ -195,15 +198,15 @@ func (l *lbClientTransport) Client() any {
195198
}
196199

197200
// forEachHostClient applies fn to every host client reachable from the provided
198-
// load balancer, recursing into nested balancing clients.
201+
// load balancer by recursively following nested balancers and wrapper types.
199202
func forEachHostClient(lb *fasthttp.LBClient, fn func(*fasthttp.HostClient)) {
200203
for _, c := range lb.Clients {
201204
walkBalancingClient(c, fn)
202205
}
203206
}
204207

205-
// walkBalancingClient traverses nested balancing clients and invokes fn for
206-
// each host client encountered.
208+
// walkBalancingClient traverses balancing clients recursively, invoking fn for
209+
// every host client discovered beneath the current node.
207210
func walkBalancingClient(client any, fn func(*fasthttp.HostClient)) {
208211
switch c := client.(type) {
209212
case *fasthttp.HostClient:
@@ -220,8 +223,8 @@ func walkBalancingClient(client any, fn func(*fasthttp.HostClient)) {
220223
}
221224

222225
// extractTLSConfig returns the first TLS configuration discovered while walking
223-
// the provided balancing clients, enabling cached reuse across nested load
224-
// balancers.
226+
// the provided balancing clients so cached settings flow through nested load
227+
// balancers without redundant traversal.
225228
func extractTLSConfig(clients []fasthttp.BalancingClient) *tls.Config {
226229
var cfg *tls.Config
227230
for _, c := range clients {
@@ -239,8 +242,7 @@ func extractTLSConfig(clients []fasthttp.BalancingClient) *tls.Config {
239242
}
240243

241244
// extractDial returns the first dial function discovered while walking the
242-
// provided balancing clients, ensuring overrides propagate through nested
243-
// transports.
245+
// provided balancing clients so overrides propagate through nested transports.
244246
func extractDial(clients []fasthttp.BalancingClient) fasthttp.DialFunc {
245247
var dial fasthttp.DialFunc
246248
for _, c := range clients {
@@ -257,9 +259,9 @@ func extractDial(clients []fasthttp.BalancingClient) fasthttp.DialFunc {
257259
return dial
258260
}
259261

260-
// walkBalancingClientWithBreak traverses balancing clients and invokes fn for
261-
// each host client encountered until fn returns true, allowing callers to stop
262-
// the traversal early when a match is found.
262+
// walkBalancingClientWithBreak traverses balancing clients recursively and
263+
// invokes fn for each host client until fn signals success, enabling early
264+
// termination once a match is found.
263265
func walkBalancingClientWithBreak(client any, fn func(*fasthttp.HostClient) bool) bool {
264266
switch c := client.(type) {
265267
case *fasthttp.HostClient:
@@ -280,8 +282,9 @@ func walkBalancingClientWithBreak(client any, fn func(*fasthttp.HostClient) bool
280282
return false
281283
}
282284

283-
// redirectClient describes the minimal surface needed by doRedirectsWithClient to
284-
// issue HTTP requests while preserving fasthttp redirect semantics.
285+
// redirectClient describes the minimal Do-capable surface needed by
286+
// doRedirectsWithClient so transports that do not expose DoRedirects (such as
287+
// fasthttp.LBClient) can participate in redirect handling.
285288
type redirectClient interface {
286289
Do(req *fasthttp.Request, resp *fasthttp.Response) error
287290
}
@@ -345,8 +348,10 @@ func doRedirectsWithClient(req *fasthttp.Request, resp *fasthttp.Response, maxRe
345348
// restricting schemes to HTTP/S so caller-provided Location headers cannot
346349
// trigger arbitrary transports.
347350
func composeRedirectURL(base string, location []byte, disablePathNormalizing bool) (string, error) {
348-
if bytes.ContainsAny(location, "\r\n") {
349-
return "", fasthttp.ErrorInvalidURI
351+
for _, b := range location {
352+
if b < 0x20 || b == 0x7f {
353+
return "", fasthttp.ErrorInvalidURI
354+
}
350355
}
351356

352357
uri := fasthttp.AcquireURI()

client/transport_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,13 @@ func TestDoRedirectsWithClientBranches(t *testing.T) {
341341
req.Header.SetMethod(fasthttp.MethodPost)
342342
req.SetRequestURI("http://example.com/start")
343343

344+
client = &stubRedirectClient{calls: []stubRedirectCall{{status: ptrInt(fasthttp.StatusFound), location: ptrString("/bad\x00path")}}}
345+
require.ErrorIs(t, doRedirectsWithClient(req, resp, 1, client), fasthttp.ErrorInvalidURI)
346+
347+
resp.Reset()
348+
req.Header.SetMethod(fasthttp.MethodPost)
349+
req.SetRequestURI("http://example.com/start")
350+
344351
client = &stubRedirectClient{calls: []stubRedirectCall{{status: ptrInt(fasthttp.StatusMovedPermanently), location: ptrString("/loop")}, {status: ptrInt(fasthttp.StatusFound), location: ptrString("/final")}, {status: ptrInt(fasthttp.StatusOK)}}}
345352
require.ErrorIs(t, doRedirectsWithClient(req, resp, 1, client), fasthttp.ErrTooManyRedirects)
346353
}

0 commit comments

Comments
 (0)