Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use route rather than use thus reducing the number of stack frames #15301

Merged
merged 11 commits into from
May 4, 2021
9 changes: 8 additions & 1 deletion cmd/web_graceful.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import (
"net"
"net/http"
"net/http/fcgi"
"strings"

"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)

func runHTTP(network, listenAddr, name string, m http.Handler) error {
Expand Down Expand Up @@ -48,7 +50,12 @@ func runFCGI(network, listenAddr, name string, m http.Handler) error {
fcgiServer := graceful.NewServer(network, listenAddr, name)

err := fcgiServer.ListenAndServe(func(listener net.Listener) error {
return fcgi.Serve(listener, m)
return fcgi.Serve(listener, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if setting.AppSubURL != "" {
req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
}
m.ServeHTTP(resp, req)
}))
})
if err != nil {
log.Fatal("Failed to start FCGI main server: %v", err)
Expand Down
6 changes: 6 additions & 0 deletions modules/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,7 @@ func Contexter() func(next http.Handler) http.Handler {
log.Debug("Session ID: %s", ctx.Session.ID())
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])

// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
ctx.Data["IsLandingPageHome"] = setting.LandingPageURL == setting.LandingPageHome
ctx.Data["IsLandingPageExplore"] = setting.LandingPageURL == setting.LandingPageExplore
ctx.Data["IsLandingPageOrganizations"] = setting.LandingPageURL == setting.LandingPageOrganizations
Expand All @@ -707,6 +708,11 @@ func Contexter() func(next http.Handler) http.Handler {

ctx.Data["ManifestData"] = setting.ManifestData

ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled()
ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled()
ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled()
ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled()

ctx.Data["i18n"] = locale
ctx.Data["Tr"] = i18n.Tr
ctx.Data["Lang"] = locale.Language()
Expand Down
4 changes: 0 additions & 4 deletions routers/api/v1/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,10 +572,6 @@ func Routes() *web.Route {
}
m.Use(context.APIContexter())

if setting.EnableAccessLog {
m.Use(context.AccessLogger())
}

m.Use(context.ToggleAPI(&context.ToggleOptions{
SignInRequired: setting.Service.RequireSignInView,
}))
Expand Down
104 changes: 49 additions & 55 deletions routers/routes/web.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import (
"code.gitea.io/gitea/routers"
"code.gitea.io/gitea/routers/admin"
apiv1 "code.gitea.io/gitea/routers/api/v1"
"code.gitea.io/gitea/routers/api/v1/misc"
"code.gitea.io/gitea/routers/dev"
"code.gitea.io/gitea/routers/events"
"code.gitea.io/gitea/routers/org"
Expand Down Expand Up @@ -89,6 +88,9 @@ func commonMiddlewares() []func(http.Handler) http.Handler {
handlers = append(handlers, LoggerHandler(setting.RouterLogLevel))
}
}
if setting.EnableAccessLog {
handlers = append(handlers, context.AccessLogger())
}

handlers = append(handlers, func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
Expand Down Expand Up @@ -128,9 +130,9 @@ func NormalRoutes() *web.Route {

// WebRoutes returns all web routes
func WebRoutes() *web.Route {
r := web.NewRoute()
routes := web.NewRoute()

r.Use(session.Sessioner(session.Options{
routes.Use(session.Sessioner(session.Options{
Provider: setting.SessionConfig.Provider,
ProviderConfig: setting.SessionConfig.ProviderConfig,
CookieName: setting.SessionConfig.CookieName,
Expand All @@ -141,101 +143,93 @@ func WebRoutes() *web.Route {
Domain: setting.SessionConfig.Domain,
}))

r.Use(Recovery())
routes.Use(Recovery())

r.Use(public.Custom(
// TODO: we should consider if there is a way to mount these using r.Route as at present
// these two handlers mean that every request has to hit these "filesystems" twice
// before finally getting to the router. It allows them to override any matching router below.
routes.Use(public.Custom(
&public.Options{
SkipLogging: setting.DisableRouterLog,
},
))
r.Use(public.Static(
routes.Use(public.Static(
&public.Options{
Directory: path.Join(setting.StaticRootPath, "public"),
SkipLogging: setting.DisableRouterLog,
},
))

r.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
r.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
// We use r.Route here over r.Use because this prevents requests that are not for avatars having to go through this additional handler
routes.Route("/avatars", "GET, HEAD", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
routes.Route("/repo-avatars", "GET, HEAD", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))

// for health check - doeesn't need to be passed through gzip handler
routes.Head("/", func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
})

// this png is very likely to always be below the limit for gzip so it doesn't need to pass through gzip
routes.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably nuke that route and make it use a <link> tag to directly link to the image. I recall this route was only there for some edge case with Firefox, but it should be favoring the SVG icon over this one nowadays.


gob.Register(&u2f.Challenge{})

common := []interface{}{}

if setting.EnableGzip {
h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
if err != nil {
log.Fatal("GzipHandlerWithOpts failed: %v", err)
}
r.Use(h)
}

if (setting.Protocol == setting.FCGI || setting.Protocol == setting.FCGIUnix) && setting.AppSubURL != "" {
r.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
req.URL.Path = strings.TrimPrefix(req.URL.Path, setting.AppSubURL)
next.ServeHTTP(resp, req)
})
})
common = append(common, h)
}

mailer.InitMailRender(templates.Mailer())

if setting.Service.EnableCaptcha {
r.Use(captcha.Captchaer(context.GetImageCaptcha()))
}
// Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary
r.Use(context.Contexter())
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
r.Use(middleware.GetHead)

if setting.EnableAccessLog {
r.Use(context.AccessLogger())
// The captcha http.Handler should only fire on /captcha/* so we can just mount this on that url
routes.Route("/captcha/*", "GET,HEAD", append(common, captcha.Captchaer(context.GetImageCaptcha()))...)
}

r.Use(user.GetNotificationCount)
r.Use(repo.GetActiveStopwatch)
r.Use(func(ctx *context.Context) {
ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled()
ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled()
ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled()
ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled()
})

// for health check
r.Head("/", func(w http.ResponseWriter, req *http.Request) {
w.WriteHeader(http.StatusOK)
})

if setting.HasRobotsTxt {
r.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) {
routes.Get("/robots.txt", append(common, func(w http.ResponseWriter, req *http.Request) {
filePath := path.Join(setting.CustomPath, "robots.txt")
fi, err := os.Stat(filePath)
if err == nil && httpcache.HandleTimeCache(req, w, fi) {
return
}
http.ServeFile(w, req, filePath)
})
})...)
}

r.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) {
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301)
})

// prometheus metrics endpoint
// prometheus metrics endpoint - do not need to go through contexter
if setting.Metrics.Enabled {
c := metrics.NewCollector()
prometheus.MustRegister(c)

r.Get("/metrics", routers.Metrics)
routes.Get("/metrics", append(common, routers.Metrics)...)
}

if setting.API.EnableSwagger {
// Note: The route moved from apiroutes because it's in fact want to render a web page
r.Get("/api/swagger", misc.Swagger) // Render V1 by default
}
// Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary
common = append(common, context.Contexter())

RegisterRoutes(r)
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
common = append(common, middleware.GetHead)

return r
// TODO: These really seem like things that could be folded into Contexter or as helper functions
common = append(common, user.GetNotificationCount)
common = append(common, repo.GetActiveStopwatch)

others := web.NewRoute()
for _, middle := range common {
others.Use(middle)
}

RegisterRoutes(others)
routes.Mount("", others)
return routes
}

func goGet(ctx *context.Context) {
Expand Down