git.iko.soy/typeable/oauth2-proxy
on master help sign in
Redirect request if it would match with an appended trailing slash
commit8a06779d41e6cd23b6c5944739428e43984ba954 authorJoel Speed <joel.speed@hotmail.co.uk> dateSun 7 Mar 2021, 12:42 (6 years ago) parent6c62b25
3 files changed+53
pkg/upstream/proxy.go+25 pkg/upstream/proxy_test.go+27 pkg/upstream/upstream_suite_test.go+1
pkg/upstream/proxy.go+25
@@ -1,6 +1,7 @@
11package upstream
22
33import (
4+ "context"
45 "fmt"
56 "net/http"
67 "net/url"
@@ -50,6 +51,8 @@ func NewProxy(upstreams options.Upstreams, sigData *options.SignatureData, write
5051 return nil, fmt.Errorf("unknown scheme for upstream %q: %q", upstream.ID, u.Scheme)
5152 }
5253 }
54+
55+ registerTrailingSlashHandler(m.serveMux)
5356 return m, nil
5457}
5558
@@ -120,3 +123,25 @@ func (m *multiUpstreamProxy) registerRewriteHandler(upstream options.Upstream, h
120123
121124 return nil
122125}
126+
127+// registerTrailingSlashHandler creates a new matcher that will check if the
128+// requested path would match if it had a trailing slash appended.
129+// If the path matches with a trailing slash, we send back a redirect.
130+// This allows us to be consistent with the built in go servemux implementation.
131+func registerTrailingSlashHandler(serveMux *mux.Router) {
132+ serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
133+ if strings.HasSuffix(req.URL.Path, "/") {
134+ return false
135+ }
136+
137+ // Use a separate RouteMatch so that we can redirect to the path + /.
138+ // If we pass through the match then the matched backed will be served
139+ // instead of the redirect handler.
140+ m := &mux.RouteMatch{}
141+ slashReq := req.Clone(context.Background())
142+ slashReq.URL.Path += "/"
143+ return serveMux.Match(slashReq, m)
144+ }).Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
145+ http.Redirect(rw, req, req.URL.String()+"/", http.StatusMovedPermanently)
146+ }))
147+}
pkg/upstream/proxy_test.go+27
@@ -29,6 +29,7 @@ var _ = Describe("Proxy Suite", func() {
2929 }
3030
3131 ok := http.StatusOK
32+ accepted := http.StatusAccepted
3233
3334 upstreams := options.Upstreams{
3435 {
@@ -47,6 +48,12 @@ var _ = Describe("Proxy Suite", func() {
4748 Static: true,
4849 StaticCode: &ok,
4950 },
51+ {
52+ ID: "static-backend-no-trailing-slash",
53+ Path: "/static",
54+ Static: true,
55+ StaticCode: &accepted,
56+ },
5057 {
5158 ID: "bad-http-backend",
5259 Path: "/bad-http/",
@@ -235,5 +242,25 @@ var _ = Describe("Proxy Suite", func() {
235242 },
236243 upstream: "backend-with-rewrite-prefix",
237244 }),
245+ Entry("with a request to a path, missing the trailing slash", &proxyTableInput{
246+ target: "http://example.localhost/http",
247+ response: testHTTPResponse{
248+ code: 301,
249+ header: map[string][]string{
250+ contentType: {textHTMLUTF8},
251+ "Location": {"http://example.localhost/http/"},
252+ },
253+ raw: "<a href=\"http://example.localhost/http/\">Moved Permanently</a>.\n\n",
254+ },
255+ }),
256+ Entry("with a request to a path, missing the trailing slash, but registered separately", &proxyTableInput{
257+ target: "http://example.localhost/static",
258+ response: testHTTPResponse{
259+ code: 202,
260+ header: map[string][]string{},
261+ raw: "Authenticated",
262+ },
263+ upstream: "static-backend-no-trailing-slash",
264+ }),
238265 )
239266})
pkg/upstream/upstream_suite_test.go+1
@@ -59,6 +59,7 @@ const (
5959 acceptEncoding = "Accept-Encoding"
6060 applicationJSON = "application/json"
6161 textPlainUTF8 = "text/plain; charset=utf-8"
62+ textHTMLUTF8 = "text/html; charset=utf-8"
6263 gapAuth = "Gap-Auth"
6364 gapSignature = "Gap-Signature"
6465)