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  }