From 42bd7bb7e27a1c529b908892f9ce73f52cfd2292 Mon Sep 17 00:00:00 2001 From: Scott Kidder Date: Mon, 30 Oct 2023 11:08:51 -0700 Subject: [PATCH] Allow redirect URI path to not be normalized. (#1638) * Allow redirect URI path to not be normalized. * Introduce DisableRedirectPathNormalizing field to Request * Use field name as start of comment. Co-authored-by: Erik Dubbelboer --------- Co-authored-by: Erik Dubbelboer --- client.go | 5 +-- client_test.go | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ http.go | 7 ++++ 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/client.go b/client.go index 1c1b486820..02456d313f 100644 --- a/client.go +++ b/client.go @@ -1064,16 +1064,17 @@ func doRequestFollowRedirects(req *Request, resp *Response, url string, maxRedir err = ErrMissingLocation break } - url = getRedirectURL(url, location) + url = getRedirectURL(url, location, req.DisableRedirectPathNormalizing) } return statusCode, body, err } -func getRedirectURL(baseURL string, location []byte) string { +func getRedirectURL(baseURL string, location []byte, disablePathNormalizing bool) string { u := AcquireURI() u.Update(baseURL) u.UpdateBytes(location) + u.DisablePathNormalizing = disablePathNormalizing redirectURL := u.String() ReleaseURI(u) return redirectURL diff --git a/client_test.go b/client_test.go index 9ff3bda955..e6a13588f0 100644 --- a/client_test.go +++ b/client_test.go @@ -1589,6 +1589,10 @@ func TestClientFollowRedirects(t *testing.T) { u := ctx.URI() u.Update("/bar") ctx.Redirect(u.String(), StatusFound) + case "/abc/*/123": + u := ctx.URI() + u.Update("/xyz/*/456") + ctx.Redirect(u.String(), StatusFound) default: ctx.Success("text/plain", ctx.Path()) } @@ -1710,6 +1714,31 @@ func TestClientFollowRedirects(t *testing.T) { ReleaseResponse(resp) } + for i := 0; i < 10; i++ { + req := AcquireRequest() + resp := AcquireResponse() + + req.SetRequestURI("http://xxx/abc/*/123") + req.URI().DisablePathNormalizing = true + req.DisableRedirectPathNormalizing = true + + err := c.DoRedirects(req, resp, 16) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + if statusCode := resp.StatusCode(); statusCode != StatusOK { + t.Fatalf("unexpected status code: %d", statusCode) + } + + if body := string(resp.Body()); body != "/xyz/*/456" { + t.Fatalf("unexpected response %q. Expecting %q", body, "/xyz/*/456") + } + + ReleaseRequest(req) + ReleaseResponse(resp) + } + req := AcquireRequest() resp := AcquireResponse() @@ -3306,3 +3335,60 @@ func TestClientTransportEx(t *testing.T) { t.Errorf("round trip count should be: %v", roundTripCount) } } + +func Test_getRedirectURL(t *testing.T) { + type args struct { + baseURL string + location []byte + disablePathNormalizing bool + } + tests := []struct { + name string + args args + want string + }{ + { + name: "Path normalizing enabled, no special characters in path", + args: args{ + baseURL: "http://foo.example.com/abc", + location: []byte("http://bar.example.com/def"), + disablePathNormalizing: false, + }, + want: "http://bar.example.com/def", + }, + { + name: "Path normalizing enabled, special characters in path", + args: args{ + baseURL: "http://foo.example.com/abc/*/def", + location: []byte("http://bar.example.com/123/*/456"), + disablePathNormalizing: false, + }, + want: "http://bar.example.com/123/%2A/456", + }, + { + name: "Path normalizing disabled, no special characters in path", + args: args{ + baseURL: "http://foo.example.com/abc", + location: []byte("http://bar.example.com/def"), + disablePathNormalizing: true, + }, + want: "http://bar.example.com/def", + }, + { + name: "Path normalizing disabled, special characters in path", + args: args{ + baseURL: "http://foo.example.com/abc/*/def", + location: []byte("http://bar.example.com/123/*/456"), + disablePathNormalizing: true, + }, + want: "http://bar.example.com/123/*/456", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getRedirectURL(tt.args.baseURL, tt.args.location, tt.args.disablePathNormalizing); got != tt.want { + t.Errorf("getRedirectURL() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/http.go b/http.go index 0a5b446e89..2dd906102f 100644 --- a/http.go +++ b/http.go @@ -71,6 +71,12 @@ type Request struct { // Use Host header (request.Header.SetHost) instead of the host from SetRequestURI, SetHost, or URI().SetHost UseHostHeader bool + + // DisableRedirectPathNormalizing disables redirect path normalization when used with DoRedirects. + // + // By default redirect path values are normalized, i.e. + // extra slashes are removed, special characters are encoded. + DisableRedirectPathNormalizing bool } // Response represents HTTP response. @@ -1080,6 +1086,7 @@ func (req *Request) Reset() { req.resetSkipHeader() req.timeout = 0 req.UseHostHeader = false + req.DisableRedirectPathNormalizing = false } func (req *Request) resetSkipHeader() {