1
1
mirror of https://github.com/go-gitea/gitea synced 2025-07-22 18:28:37 +00:00

Fix duplicate sub-path for avatars (#31365)

Fix #31361, and add tests

And this PR introduces an undocumented & debug-purpose-only config
option: `USE_SUB_URL_PATH`. It does nothing for end users, it only helps
the development of sub-path related problems.

And also fix #31366

Co-authored-by: @ExplodingDragon
This commit is contained in:
wxiaoguang
2024-06-15 11:43:57 +08:00
committed by GitHub
parent 23147494a7
commit 84cbb6c4d2
13 changed files with 150 additions and 40 deletions

View File

@@ -25,7 +25,7 @@ import (
// ProtocolMiddlewares returns HTTP protocol related middlewares, and it provides a global panic recovery
func ProtocolMiddlewares() (handlers []any) {
// first, normalize the URL path
handlers = append(handlers, stripSlashesMiddleware)
handlers = append(handlers, normalizeRequestPathMiddleware)
// prepare the ContextData and panic recovery
handlers = append(handlers, func(next http.Handler) http.Handler {
@@ -75,9 +75,9 @@ func ProtocolMiddlewares() (handlers []any) {
return handlers
}
func stripSlashesMiddleware(next http.Handler) http.Handler {
func normalizeRequestPathMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
// First of all escape the URL RawPath to ensure that all routing is done using a correctly escaped URL
// escape the URL RawPath to ensure that all routing is done using a correctly escaped URL
req.URL.RawPath = req.URL.EscapedPath()
urlPath := req.URL.RawPath
@@ -86,19 +86,42 @@ func stripSlashesMiddleware(next http.Handler) http.Handler {
urlPath = rctx.RoutePath
}
sanitizedPath := &strings.Builder{}
prevWasSlash := false
for _, chr := range strings.TrimRight(urlPath, "/") {
if chr != '/' || !prevWasSlash {
sanitizedPath.WriteRune(chr)
normalizedPath := strings.TrimRight(urlPath, "/")
// the following code block is a slow-path for replacing all repeated slashes "//" to one single "/"
// if the path doesn't have repeated slashes, then no need to execute it
if strings.Contains(normalizedPath, "//") {
buf := &strings.Builder{}
prevWasSlash := false
for _, chr := range normalizedPath {
if chr != '/' || !prevWasSlash {
buf.WriteRune(chr)
}
prevWasSlash = chr == '/'
}
prevWasSlash = chr == '/'
normalizedPath = buf.String()
}
if setting.UseSubURLPath {
remainingPath, ok := strings.CutPrefix(normalizedPath, setting.AppSubURL+"/")
if ok {
normalizedPath = "/" + remainingPath
} else if normalizedPath == setting.AppSubURL {
normalizedPath = "/"
} else if !strings.HasPrefix(normalizedPath+"/", "/v2/") {
// do not respond to other requests, to simulate a real sub-path environment
http.Error(resp, "404 page not found, sub-path is: "+setting.AppSubURL, http.StatusNotFound)
return
}
// TODO: it's not quite clear about how req.URL and rctx.RoutePath work together.
// Fortunately, it is only used for debug purpose, we have enough time to figure it out in the future.
req.URL.RawPath = normalizedPath
req.URL.Path = normalizedPath
}
if rctx == nil {
req.URL.Path = sanitizedPath.String()
req.URL.Path = normalizedPath
} else {
rctx.RoutePath = sanitizedPath.String()
rctx.RoutePath = normalizedPath
}
next.ServeHTTP(resp, req)
})