Skip to content

Commit

Permalink
contrib/dimfeld/httptreemux.v5: fix resource name for 301 redirects
Browse files Browse the repository at this point in the history
The httptreemux router has a set of redirect behaviours that is invoked
when a request URL and matched route only differs in a trailing slash.
The default behaviour is to respond with a 301 (moved permanently) to
redirect the client to the exact path of the matched handler. We
previously patched one of the two scenarios where this occurs in DataDog#2332.
The changes in this commit addresses the other scenario.

We also standardize the resource name in the event that there is a
trailing slash missmatch between request URL and matched handler. We
always truncate the trailing slash in the resource name.

Fixes DataDog#2663
  • Loading branch information
devillecodes committed Apr 18, 2024
1 parent af0486a commit d5ec356
Show file tree
Hide file tree
Showing 2 changed files with 1,198 additions and 28 deletions.
52 changes: 43 additions & 9 deletions contrib/dimfeld/httptreemux.v5/httptreemux.go
Expand Up @@ -112,18 +112,26 @@ func getRoute(router *httptreemux.TreeMux, w http.ResponseWriter, req *http.Requ
if !found {
return "", false
}
routeLen := len(route)
trailingSlash := route[routeLen-1] == '/' && routeLen > 1

// Check for redirecting route due to trailing slash for parameters.
// The redirecting behaviour originates from httptreemux router.
if lr.StatusCode == http.StatusMovedPermanently && strings.HasSuffix(route, "/") {
// Retry the population of lookup result parameters.
// If the initial attempt to populate the parameters fails, clone the request and modify the URI and URL Path.
// Depending on whether the route has a trailing slash or not, it will either add or remove the trailing slash and retry the lookup.
if routerRedirectEnabled(router) && isSupportedRedirectStatus(lr.StatusCode) && lr.Params == nil {
rReq := req.Clone(req.Context())
rReq.RequestURI = strings.TrimSuffix(rReq.RequestURI, "/")
rReq.URL.Path = strings.TrimSuffix(rReq.URL.Path, "/")

lr, found = router.Lookup(w, rReq)
if !found {
return "", false
if trailingSlash {
// if the route has a trailing slash, remove it
rReq.RequestURI = strings.TrimSuffix(rReq.RequestURI, "/")
rReq.URL.Path = strings.TrimSuffix(rReq.URL.Path, "/")
} else {
// if the route does not have a trailing slash, add one
rReq.RequestURI = rReq.RequestURI + "/"
rReq.URL.Path = rReq.URL.Path + "/"
}
// no need to check found again
// we already matched a route and we are only trying to populate the lookup result params
lr, _ = router.Lookup(w, rReq)
}

for k, v := range lr.Params {
Expand All @@ -139,5 +147,31 @@ func getRoute(router *httptreemux.TreeMux, w http.ResponseWriter, req *http.Requ
newP = "/:" + k
route = strings.Replace(route, oldP, newP, 1)
}

// remove trailing slash from route to standardize returned value
// the router does not allow you to register two matching routes with the only difference being a trailing slash
// this only affects the resulting returned value and not the actual request URL set on tag http.url
if trailingSlash {
route = strings.TrimSuffix(route, "/")
}

return route, true
}

// isSupportedRedirectStatus checks if the given HTTP status code is a supported redirect status.
// It returns true if the status code is either StatusMovedPermanently, StatusTemporaryRedirect, or StatusPermanentRedirect.
// Otherwise, it returns false.
func isSupportedRedirectStatus(status int) bool {
return status == http.StatusMovedPermanently ||
status == http.StatusTemporaryRedirect ||
status == http.StatusPermanentRedirect
}

// routerRedirectEnabled checks if the redirection is enabled on the router.
// It returns true if either RedirectCleanPath or RedirectTrailingSlash is enabled,
// and the RedirectBehavior is not set to UseHandler. Otherwise, it returns false.
// This function is used to determine whether to perform redirections based on the router's configuration.
func routerRedirectEnabled(router *httptreemux.TreeMux) bool {
return (router.RedirectCleanPath || router.RedirectTrailingSlash) &&
router.RedirectBehavior != httptreemux.UseHandler
}

0 comments on commit d5ec356

Please sign in to comment.