Skip to content

Commit e2333b8

Browse files
committed
Document redirect helpers and tighten validation
1 parent 81c5bf1 commit e2333b8

1 file changed

Lines changed: 34 additions & 0 deletions

File tree

client/transport.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ func (l *lbClientTransport) DoDeadline(req *fasthttp.Request, resp *fasthttp.Res
153153
return l.client.DoDeadline(req, resp, deadline)
154154
}
155155

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.
156159
func (l *lbClientTransport) DoRedirects(req *fasthttp.Request, resp *fasthttp.Response, maxRedirects int) error {
157160
return doRedirectsWithClient(req, resp, maxRedirects, l.client)
158161
}
@@ -191,12 +194,16 @@ func (l *lbClientTransport) Client() any {
191194
return l.client
192195
}
193196

197+
// forEachHostClient applies fn to every host client reachable from the provided
198+
// load balancer, recursing into nested balancing clients.
194199
func forEachHostClient(lb *fasthttp.LBClient, fn func(*fasthttp.HostClient)) {
195200
for _, c := range lb.Clients {
196201
walkBalancingClient(c, fn)
197202
}
198203
}
199204

205+
// walkBalancingClient traverses nested balancing clients and invokes fn for
206+
// each host client encountered.
200207
func walkBalancingClient(client any, fn func(*fasthttp.HostClient)) {
201208
switch c := client.(type) {
202209
case *fasthttp.HostClient:
@@ -212,6 +219,9 @@ func walkBalancingClient(client any, fn func(*fasthttp.HostClient)) {
212219
}
213220
}
214221

222+
// extractTLSConfig returns the first TLS configuration discovered while walking
223+
// the provided balancing clients, enabling cached reuse across nested load
224+
// balancers.
215225
func extractTLSConfig(clients []fasthttp.BalancingClient) *tls.Config {
216226
var cfg *tls.Config
217227
for _, c := range clients {
@@ -228,6 +238,9 @@ func extractTLSConfig(clients []fasthttp.BalancingClient) *tls.Config {
228238
return cfg
229239
}
230240

241+
// extractDial returns the first dial function discovered while walking the
242+
// provided balancing clients, ensuring overrides propagate through nested
243+
// transports.
231244
func extractDial(clients []fasthttp.BalancingClient) fasthttp.DialFunc {
232245
var dial fasthttp.DialFunc
233246
for _, c := range clients {
@@ -244,6 +257,9 @@ func extractDial(clients []fasthttp.BalancingClient) fasthttp.DialFunc {
244257
return dial
245258
}
246259

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.
247263
func walkBalancingClientWithBreak(client any, fn func(*fasthttp.HostClient) bool) bool {
248264
switch c := client.(type) {
249265
case *fasthttp.HostClient:
@@ -264,10 +280,16 @@ func walkBalancingClientWithBreak(client any, fn func(*fasthttp.HostClient) bool
264280
return false
265281
}
266282

283+
// redirectClient describes the minimal surface needed by doRedirectsWithClient to
284+
// issue HTTP requests while preserving fasthttp redirect semantics.
267285
type redirectClient interface {
268286
Do(req *fasthttp.Request, resp *fasthttp.Response) error
269287
}
270288

289+
// doRedirectsWithClient mirrors fasthttp's redirect loop for transports that do
290+
// not expose DoRedirects (e.g. fasthttp.LBClient). The helper always issues the
291+
// initial request, respects zero redirect limits, falls back to the default cap
292+
// for negative values, and validates redirect targets before following them.
271293
func doRedirectsWithClient(req *fasthttp.Request, resp *fasthttp.Response, maxRedirects int, client redirectClient) error {
272294
currentURL := req.URI().String()
273295
redirects := 0
@@ -318,7 +340,15 @@ func doRedirectsWithClient(req *fasthttp.Request, resp *fasthttp.Response, maxRe
318340
}
319341
}
320342

343+
// composeRedirectURL resolves a redirect target relative to the current request
344+
// URL while rejecting suspicious payloads (e.g. control characters) and
345+
// restricting schemes to HTTP/S so caller-provided Location headers cannot
346+
// trigger arbitrary transports.
321347
func composeRedirectURL(base string, location []byte, disablePathNormalizing bool) (string, error) {
348+
if bytes.ContainsAny(location, "\r\n") {
349+
return "", fasthttp.ErrorInvalidURI
350+
}
351+
322352
uri := fasthttp.AcquireURI()
323353
defer fasthttp.ReleaseURI(uri)
324354

@@ -330,5 +360,9 @@ func composeRedirectURL(base string, location []byte, disablePathNormalizing boo
330360
return "", fasthttp.ErrorInvalidURI
331361
}
332362

363+
if len(uri.Scheme()) > 0 && len(uri.Host()) == 0 {
364+
return "", fasthttp.ErrorInvalidURI
365+
}
366+
333367
return uri.String(), nil
334368
}

0 commit comments

Comments
 (0)