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