Redirect request if it would match with an appended trailing slash
This commit is contained in:
		
							parent
							
								
									6c62b25bf1
								
							
						
					
					
						commit
						8a06779d41
					
				|  | @ -1,6 +1,7 @@ | |||
| package upstream | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
|  | @ -50,6 +51,8 @@ func NewProxy(upstreams options.Upstreams, sigData *options.SignatureData, write | |||
| 			return nil, fmt.Errorf("unknown scheme for upstream %q: %q", upstream.ID, u.Scheme) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	registerTrailingSlashHandler(m.serveMux) | ||||
| 	return m, nil | ||||
| } | ||||
| 
 | ||||
|  | @ -120,3 +123,25 @@ func (m *multiUpstreamProxy) registerRewriteHandler(upstream options.Upstream, h | |||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // registerTrailingSlashHandler creates a new matcher that will check if the
 | ||||
| // requested path would match if it had a trailing slash appended.
 | ||||
| // If the path matches with a trailing slash, we send back a redirect.
 | ||||
| // This allows us to be consistent with the built in go servemux implementation.
 | ||||
| func registerTrailingSlashHandler(serveMux *mux.Router) { | ||||
| 	serveMux.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool { | ||||
| 		if strings.HasSuffix(req.URL.Path, "/") { | ||||
| 			return false | ||||
| 		} | ||||
| 
 | ||||
| 		// Use a separate RouteMatch so that we can redirect to the path + /.
 | ||||
| 		// If we pass through the match then the matched backed will be served
 | ||||
| 		// instead of the redirect handler.
 | ||||
| 		m := &mux.RouteMatch{} | ||||
| 		slashReq := req.Clone(context.Background()) | ||||
| 		slashReq.URL.Path += "/" | ||||
| 		return serveMux.Match(slashReq, m) | ||||
| 	}).Handler(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { | ||||
| 		http.Redirect(rw, req, req.URL.String()+"/", http.StatusMovedPermanently) | ||||
| 	})) | ||||
| } | ||||
|  |  | |||
|  | @ -29,6 +29,7 @@ var _ = Describe("Proxy Suite", func() { | |||
| 		} | ||||
| 
 | ||||
| 		ok := http.StatusOK | ||||
| 		accepted := http.StatusAccepted | ||||
| 
 | ||||
| 		upstreams := options.Upstreams{ | ||||
| 			{ | ||||
|  | @ -47,6 +48,12 @@ var _ = Describe("Proxy Suite", func() { | |||
| 				Static:     true, | ||||
| 				StaticCode: &ok, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ID:         "static-backend-no-trailing-slash", | ||||
| 				Path:       "/static", | ||||
| 				Static:     true, | ||||
| 				StaticCode: &accepted, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ID:   "bad-http-backend", | ||||
| 				Path: "/bad-http/", | ||||
|  | @ -235,5 +242,25 @@ var _ = Describe("Proxy Suite", func() { | |||
| 			}, | ||||
| 			upstream: "backend-with-rewrite-prefix", | ||||
| 		}), | ||||
| 		Entry("with a request to a path, missing the trailing slash", &proxyTableInput{ | ||||
| 			target: "http://example.localhost/http", | ||||
| 			response: testHTTPResponse{ | ||||
| 				code: 301, | ||||
| 				header: map[string][]string{ | ||||
| 					contentType: {textHTMLUTF8}, | ||||
| 					"Location":  {"http://example.localhost/http/"}, | ||||
| 				}, | ||||
| 				raw: "<a href=\"http://example.localhost/http/\">Moved Permanently</a>.\n\n", | ||||
| 			}, | ||||
| 		}), | ||||
| 		Entry("with a request to a path, missing the trailing slash, but registered separately", &proxyTableInput{ | ||||
| 			target: "http://example.localhost/static", | ||||
| 			response: testHTTPResponse{ | ||||
| 				code:   202, | ||||
| 				header: map[string][]string{}, | ||||
| 				raw:    "Authenticated", | ||||
| 			}, | ||||
| 			upstream: "static-backend-no-trailing-slash", | ||||
| 		}), | ||||
| 	) | ||||
| }) | ||||
|  |  | |||
|  | @ -59,6 +59,7 @@ const ( | |||
| 	acceptEncoding  = "Accept-Encoding" | ||||
| 	applicationJSON = "application/json" | ||||
| 	textPlainUTF8   = "text/plain; charset=utf-8" | ||||
| 	textHTMLUTF8    = "text/html; charset=utf-8" | ||||
| 	gapAuth         = "Gap-Auth" | ||||
| 	gapSignature    = "Gap-Signature" | ||||
| ) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue