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  }