mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-01-10 15:42:16 +01:00
3f44b97b5f
- For every process that is spawned (every new non-trivial goroutine such as http requests, queues or tasks) start a [execution tracer](https://pkg.go.dev/runtime/trace). This allows very precise diagnosis of how each individual process over a time period. - It's safe and [fast](https://go.dev/blog/execution-traces-2024#low-overhead-tracing) to be run in production, hence no setting to disable this. There's only noticable overhead when tracing is actually performed and not continuous. - Proper tracing support would mean the codebase would be full of `trace.WithRegion` and `trace.Log`, which feels premature for this patch as there's no real-world usage yet to indicate which places would need this the most. So far only Git commands and SQL queries receive somewhat proper tracing support given that these are used throughout the codebase. - Make git commands a new process type. - Add tracing to diagnosis zip file.
119 lines
3.7 KiB
Go
119 lines
3.7 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package common
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"runtime/trace"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/modules/cache"
|
|
"code.gitea.io/gitea/modules/process"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
"code.gitea.io/gitea/modules/web/middleware"
|
|
"code.gitea.io/gitea/modules/web/routing"
|
|
"code.gitea.io/gitea/services/context"
|
|
|
|
"code.forgejo.org/go-chi/session"
|
|
"github.com/chi-middleware/proxy"
|
|
chi "github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
// 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)
|
|
|
|
// prepare the ContextData and panic recovery
|
|
handlers = append(handlers, func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
defer func() {
|
|
if err := recover(); err != nil {
|
|
RenderPanicErrorPage(resp, req, err) // it should never panic
|
|
}
|
|
}()
|
|
req = req.WithContext(middleware.WithContextData(req.Context()))
|
|
next.ServeHTTP(resp, req)
|
|
})
|
|
})
|
|
|
|
// wrap the request and response, use the process context and add it to the process manager
|
|
handlers = append(handlers, func(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
|
|
defer finished()
|
|
trace.Log(ctx, "method", req.Method)
|
|
trace.Log(ctx, "url", req.RequestURI)
|
|
next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx)))
|
|
})
|
|
})
|
|
|
|
if setting.ReverseProxyLimit > 0 {
|
|
opt := proxy.NewForwardedHeadersOptions().
|
|
WithForwardLimit(setting.ReverseProxyLimit).
|
|
ClearTrustedProxies()
|
|
for _, n := range setting.ReverseProxyTrustedProxies {
|
|
if !strings.Contains(n, "/") {
|
|
opt.AddTrustedProxy(n)
|
|
} else {
|
|
opt.AddTrustedNetwork(n)
|
|
}
|
|
}
|
|
handlers = append(handlers, proxy.ForwardedHeaders(opt))
|
|
}
|
|
|
|
if setting.IsRouteLogEnabled() {
|
|
handlers = append(handlers, routing.NewLoggerHandler())
|
|
}
|
|
|
|
if setting.IsAccessLogEnabled() {
|
|
handlers = append(handlers, context.AccessLogger())
|
|
}
|
|
|
|
return handlers
|
|
}
|
|
|
|
func stripSlashesMiddleware(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
|
|
req.URL.RawPath = req.URL.EscapedPath()
|
|
|
|
urlPath := req.URL.RawPath
|
|
rctx := chi.RouteContext(req.Context())
|
|
if rctx != nil && rctx.RoutePath != "" {
|
|
urlPath = rctx.RoutePath
|
|
}
|
|
|
|
sanitizedPath := &strings.Builder{}
|
|
prevWasSlash := false
|
|
for _, chr := range strings.TrimRight(urlPath, "/") {
|
|
if chr != '/' || !prevWasSlash {
|
|
sanitizedPath.WriteRune(chr)
|
|
}
|
|
prevWasSlash = chr == '/'
|
|
}
|
|
|
|
if rctx == nil {
|
|
req.URL.Path = sanitizedPath.String()
|
|
} else {
|
|
rctx.RoutePath = sanitizedPath.String()
|
|
}
|
|
next.ServeHTTP(resp, req)
|
|
})
|
|
}
|
|
|
|
func Sessioner() func(next http.Handler) http.Handler {
|
|
return session.Sessioner(session.Options{
|
|
Provider: setting.SessionConfig.Provider,
|
|
ProviderConfig: setting.SessionConfig.ProviderConfig,
|
|
CookieName: setting.SessionConfig.CookieName,
|
|
CookiePath: setting.SessionConfig.CookiePath,
|
|
Gclifetime: setting.SessionConfig.Gclifetime,
|
|
Maxlifetime: setting.SessionConfig.Maxlifetime,
|
|
Secure: setting.SessionConfig.Secure,
|
|
SameSite: setting.SessionConfig.SameSite,
|
|
Domain: setting.SessionConfig.Domain,
|
|
})
|
|
}
|