code.gitea.io/gitea@v1.21.7/routers/common/middleware.go (about) 1 // Copyright 2021 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package common 5 6 import ( 7 "fmt" 8 "net/http" 9 "strings" 10 11 "code.gitea.io/gitea/modules/cache" 12 "code.gitea.io/gitea/modules/context" 13 "code.gitea.io/gitea/modules/process" 14 "code.gitea.io/gitea/modules/setting" 15 "code.gitea.io/gitea/modules/web/middleware" 16 "code.gitea.io/gitea/modules/web/routing" 17 18 "gitea.com/go-chi/session" 19 "github.com/chi-middleware/proxy" 20 chi "github.com/go-chi/chi/v5" 21 ) 22 23 // ProtocolMiddlewares returns HTTP protocol related middlewares, and it provides a global panic recovery 24 func ProtocolMiddlewares() (handlers []any) { 25 // first, normalize the URL path 26 handlers = append(handlers, stripSlashesMiddleware) 27 28 // prepare the ContextData and panic recovery 29 handlers = append(handlers, func(next http.Handler) http.Handler { 30 return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 31 defer func() { 32 if err := recover(); err != nil { 33 RenderPanicErrorPage(resp, req, err) // it should never panic 34 } 35 }() 36 req = req.WithContext(middleware.WithContextData(req.Context())) 37 next.ServeHTTP(resp, req) 38 }) 39 }) 40 41 handlers = append(handlers, func(next http.Handler) http.Handler { 42 return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 43 ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true) 44 defer finished() 45 next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx))) 46 }) 47 }) 48 49 if setting.ReverseProxyLimit > 0 { 50 opt := proxy.NewForwardedHeadersOptions(). 51 WithForwardLimit(setting.ReverseProxyLimit). 52 ClearTrustedProxies() 53 for _, n := range setting.ReverseProxyTrustedProxies { 54 if !strings.Contains(n, "/") { 55 opt.AddTrustedProxy(n) 56 } else { 57 opt.AddTrustedNetwork(n) 58 } 59 } 60 handlers = append(handlers, proxy.ForwardedHeaders(opt)) 61 } 62 63 if setting.IsRouteLogEnabled() { 64 handlers = append(handlers, routing.NewLoggerHandler()) 65 } 66 67 if setting.IsAccessLogEnabled() { 68 handlers = append(handlers, context.AccessLogger()) 69 } 70 71 return handlers 72 } 73 74 func stripSlashesMiddleware(next http.Handler) http.Handler { 75 return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { 76 // First of all escape the URL RawPath to ensure that all routing is done using a correctly escaped URL 77 req.URL.RawPath = req.URL.EscapedPath() 78 79 urlPath := req.URL.RawPath 80 rctx := chi.RouteContext(req.Context()) 81 if rctx != nil && rctx.RoutePath != "" { 82 urlPath = rctx.RoutePath 83 } 84 85 sanitizedPath := &strings.Builder{} 86 prevWasSlash := false 87 for _, chr := range strings.TrimRight(urlPath, "/") { 88 if chr != '/' || !prevWasSlash { 89 sanitizedPath.WriteRune(chr) 90 } 91 prevWasSlash = chr == '/' 92 } 93 94 if rctx == nil { 95 req.URL.Path = sanitizedPath.String() 96 } else { 97 rctx.RoutePath = sanitizedPath.String() 98 } 99 next.ServeHTTP(resp, req) 100 }) 101 } 102 103 func Sessioner() func(next http.Handler) http.Handler { 104 return session.Sessioner(session.Options{ 105 Provider: setting.SessionConfig.Provider, 106 ProviderConfig: setting.SessionConfig.ProviderConfig, 107 CookieName: setting.SessionConfig.CookieName, 108 CookiePath: setting.SessionConfig.CookiePath, 109 Gclifetime: setting.SessionConfig.Gclifetime, 110 Maxlifetime: setting.SessionConfig.Maxlifetime, 111 Secure: setting.SessionConfig.Secure, 112 SameSite: setting.SessionConfig.SameSite, 113 Domain: setting.SessionConfig.Domain, 114 }) 115 }