Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT: contrib/dimfeld/httptreemux.v5: fix resource name for 301 redirects #14

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
52 changes: 43 additions & 9 deletions contrib/dimfeld/httptreemux.v5/httptreemux.go
Original file line number Diff line number Diff line change
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 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is for the other possible case where:

  • request URL: GET /api/example (without trailing slash)
  • matched handler: GET /api/example/ (with trailing slash)

// 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
}