Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 808acb5

Browse files
committedApr 25, 2023
refactor
1 parent ab42c13 commit 808acb5

File tree

13 files changed

+253
-250
lines changed

13 files changed

+253
-250
lines changed
 

‎modules/context/context.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,13 +446,33 @@ func (ctx *Context) JSON(status int, content interface{}) {
446446
}
447447
}
448448

449+
func removeSessionCookieHeader(w http.ResponseWriter) {
450+
cookies := w.Header()["Set-Cookie"]
451+
w.Header().Del("Set-Cookie")
452+
for _, cookie := range cookies {
453+
if strings.HasPrefix(cookie, setting.SessionConfig.CookieName+"=") {
454+
continue
455+
}
456+
w.Header().Add("Set-Cookie", cookie)
457+
}
458+
}
459+
449460
// Redirect redirects the request
450461
func (ctx *Context) Redirect(location string, status ...int) {
451462
code := http.StatusSeeOther
452463
if len(status) == 1 {
453464
code = status[0]
454465
}
455466

467+
if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
468+
// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
469+
// 1. the first request to "/my-path" contains cookie
470+
// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
471+
// 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
472+
// 4. then the browser accepts the empty session, then the user is logged out
473+
// So in this case, we should remove the session cookie from the response header
474+
removeSessionCookieHeader(ctx.Resp)
475+
}
456476
http.Redirect(ctx.Resp, ctx.Req, location, code)
457477
}
458478

‎modules/context/context_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package context
5+
6+
import (
7+
"net/http"
8+
"testing"
9+
10+
"code.gitea.io/gitea/modules/setting"
11+
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
type mockResponseWriter struct {
16+
header http.Header
17+
}
18+
19+
func (m *mockResponseWriter) Header() http.Header {
20+
return m.header
21+
}
22+
23+
func (m *mockResponseWriter) Write(bytes []byte) (int, error) {
24+
panic("implement me")
25+
}
26+
27+
func (m *mockResponseWriter) WriteHeader(statusCode int) {
28+
panic("implement me")
29+
}
30+
31+
func TestRemoveSessionCookieHeader(t *testing.T) {
32+
w := &mockResponseWriter{}
33+
w.header = http.Header{}
34+
w.header.Add("Set-Cookie", (&http.Cookie{Name: setting.SessionConfig.CookieName, Value: "foo"}).String())
35+
w.header.Add("Set-Cookie", (&http.Cookie{Name: "other", Value: "bar"}).String())
36+
assert.Len(t, w.Header().Values("Set-Cookie"), 2)
37+
removeSessionCookieHeader(w)
38+
assert.Len(t, w.Header().Values("Set-Cookie"), 1)
39+
assert.Contains(t, "other=bar", w.Header().Get("Set-Cookie"))
40+
}

‎modules/web/handler.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"fmt"
99
"net/http"
1010
"reflect"
11-
"strings"
1211

1312
"code.gitea.io/gitea/modules/context"
1413
"code.gitea.io/gitea/modules/web/routing"
@@ -175,26 +174,3 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
175174
provider(nil).ServeHTTP(nil, nil) // do a pre-check to make sure all arguments and return values are supported
176175
return provider
177176
}
178-
179-
// MiddlewareWithPrefix wraps a handler function at a prefix, and make it as a middleware
180-
// TODO: this design is incorrect, the asset handler should not be a middleware
181-
func MiddlewareWithPrefix(pathPrefix string, middleware func(handler http.Handler) http.Handler, handlerFunc http.HandlerFunc) func(next http.Handler) http.Handler {
182-
funcInfo := routing.GetFuncInfo(handlerFunc)
183-
handler := http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
184-
routing.UpdateFuncInfo(req.Context(), funcInfo)
185-
handlerFunc(resp, req)
186-
})
187-
return func(next http.Handler) http.Handler {
188-
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
189-
if !strings.HasPrefix(req.URL.Path, pathPrefix) {
190-
next.ServeHTTP(resp, req)
191-
return
192-
}
193-
if middleware != nil {
194-
middleware(handler).ServeHTTP(resp, req)
195-
} else {
196-
handler.ServeHTTP(resp, req)
197-
}
198-
})
199-
}
200-
}

‎modules/web/route.go

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,23 +44,13 @@ type Route struct {
4444
// NewRoute creates a new route
4545
func NewRoute() *Route {
4646
r := chi.NewRouter()
47-
return &Route{
48-
R: r,
49-
curGroupPrefix: "",
50-
curMiddlewares: []interface{}{},
51-
}
47+
return &Route{R: r}
5248
}
5349

5450
// Use supports two middlewares
5551
func (r *Route) Use(middlewares ...interface{}) {
56-
if r.curGroupPrefix != "" {
57-
// FIXME: this behavior is incorrect, should use "With" instead
58-
r.curMiddlewares = append(r.curMiddlewares, middlewares...)
59-
} else {
60-
// FIXME: another misuse, the "Use" with empty middlewares is called after "Mount"
61-
for _, m := range middlewares {
62-
r.R.Use(toHandlerProvider(m))
63-
}
52+
for _, m := range middlewares {
53+
r.R.Use(toHandlerProvider(m))
6454
}
6555
}
6656

@@ -116,9 +106,7 @@ func (r *Route) Methods(method, pattern string, h []any) {
116106

117107
// Mount attaches another Route along ./pattern/*
118108
func (r *Route) Mount(pattern string, subR *Route) {
119-
middlewares := make([]interface{}, len(r.curMiddlewares))
120-
copy(middlewares, r.curMiddlewares)
121-
subR.Use(middlewares...)
109+
subR.Use(r.curMiddlewares...)
122110
r.R.Mount(r.getPattern(pattern), subR.R)
123111
}
124112

‎routers/common/middleware.go

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,23 @@ import (
1515
"code.gitea.io/gitea/modules/setting"
1616
"code.gitea.io/gitea/modules/web/routing"
1717

18+
"gitea.com/go-chi/session"
1819
"github.com/chi-middleware/proxy"
1920
chi "github.com/go-chi/chi/v5"
2021
)
2122

22-
// Middlewares returns common middlewares
23-
func Middlewares() []func(http.Handler) http.Handler {
24-
handlers := []func(http.Handler) http.Handler{
25-
func(next http.Handler) http.Handler {
26-
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
27-
// First of all escape the URL RawPath to ensure that all routing is done using a correctly escaped URL
28-
req.URL.RawPath = req.URL.EscapedPath()
23+
// ProtocolMiddlewares returns HTTP protocol related middlewares
24+
func ProtocolMiddlewares() (handlers []any) {
25+
handlers = append(handlers, func(next http.Handler) http.Handler {
26+
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
27+
// First of all escape the URL RawPath to ensure that all routing is done using a correctly escaped URL
28+
req.URL.RawPath = req.URL.EscapedPath()
2929

30-
ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
31-
defer finished()
32-
next.ServeHTTP(context.NewResponse(resp), req.WithContext(cache.WithCacheContext(ctx)))
33-
})
34-
},
35-
}
30+
ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
31+
defer finished()
32+
next.ServeHTTP(context.NewResponse(resp), req.WithContext(cache.WithCacheContext(ctx)))
33+
})
34+
})
3635

3736
if setting.ReverseProxyLimit > 0 {
3837
opt := proxy.NewForwardedHeadersOptions().
@@ -112,3 +111,17 @@ func stripSlashesMiddleware(next http.Handler) http.Handler {
112111
next.ServeHTTP(resp, req)
113112
})
114113
}
114+
115+
func Sessioner() func(next http.Handler) http.Handler {
116+
return session.Sessioner(session.Options{
117+
Provider: setting.SessionConfig.Provider,
118+
ProviderConfig: setting.SessionConfig.ProviderConfig,
119+
CookieName: setting.SessionConfig.CookieName,
120+
CookiePath: setting.SessionConfig.CookiePath,
121+
Gclifetime: setting.SessionConfig.Gclifetime,
122+
Maxlifetime: setting.SessionConfig.Maxlifetime,
123+
Secure: setting.SessionConfig.Secure,
124+
SameSite: setting.SessionConfig.SameSite,
125+
Domain: setting.SessionConfig.Domain,
126+
})
127+
}

‎routers/init.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,15 @@ func GlobalInitInstalled(ctx context.Context) {
177177
func NormalRoutes(ctx context.Context) *web.Route {
178178
ctx, _ = templates.HTMLRenderer(ctx)
179179
r := web.NewRoute()
180-
for _, middle := range common.Middlewares() {
181-
r.Use(middle)
182-
}
180+
r.Use(common.ProtocolMiddlewares()...)
183181

184182
r.Mount("/", web_routers.Routes(ctx))
185183
r.Mount("/api/v1", apiv1.Routes(ctx))
186184
r.Mount("/api/internal", private.Routes())
187185

188186
if setting.Packages.Enabled {
189-
// Add endpoints to match common package manager APIs
190-
191187
// This implements package support for most package managers
192188
r.Mount("/api/packages", packages_router.CommonRoutes(ctx))
193-
194189
// This implements the OCI API (Note this is not preceded by /api but is instead /v2)
195190
r.Mount("/v2", packages_router.ContainerRoutes(ctx))
196191
}

‎routers/install/routes.go

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ import (
1919
"code.gitea.io/gitea/routers/common"
2020
"code.gitea.io/gitea/routers/web/healthcheck"
2121
"code.gitea.io/gitea/services/forms"
22-
23-
"gitea.com/go-chi/session"
2422
)
2523

2624
type dataStore map[string]interface{}
@@ -30,7 +28,6 @@ func (d *dataStore) GetData() map[string]interface{} {
3028
}
3129

3230
func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
33-
_, rnd := templates.HTMLRenderer(ctx)
3431
return func(next http.Handler) http.Handler {
3532
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
3633
defer func() {
@@ -69,6 +66,7 @@ func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
6966
if !setting.IsProd {
7067
store["ErrorMsg"] = combinedErr
7168
}
69+
_, rnd := templates.HTMLRenderer(ctx)
7270
err = rnd.HTML(w, http.StatusInternalServerError, "status/500", templates.BaseVars().Merge(store))
7371
if err != nil {
7472
log.Error("%v", err)
@@ -83,34 +81,22 @@ func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
8381

8482
// Routes registers the installation routes
8583
func Routes(ctx goctx.Context) *web.Route {
86-
r := web.NewRoute()
87-
for _, middle := range common.Middlewares() {
88-
r.Use(middle)
89-
}
90-
91-
r.Use(web.MiddlewareWithPrefix("/assets/", nil, public.AssetsHandlerFunc("/assets/")))
92-
93-
r.Use(session.Sessioner(session.Options{
94-
Provider: setting.SessionConfig.Provider,
95-
ProviderConfig: setting.SessionConfig.ProviderConfig,
96-
CookieName: setting.SessionConfig.CookieName,
97-
CookiePath: setting.SessionConfig.CookiePath,
98-
Gclifetime: setting.SessionConfig.Gclifetime,
99-
Maxlifetime: setting.SessionConfig.Maxlifetime,
100-
Secure: setting.SessionConfig.Secure,
101-
SameSite: setting.SessionConfig.SameSite,
102-
Domain: setting.SessionConfig.Domain,
103-
}))
84+
base := web.NewRoute()
85+
base.Use(common.ProtocolMiddlewares()...)
86+
base.RouteMethods("/assets/*", "GET, HEAD", public.AssetsHandlerFunc("/assets/"))
10487

88+
r := web.NewRoute()
89+
r.Use(common.Sessioner())
10590
r.Use(installRecovery(ctx))
10691
r.Use(Init(ctx))
10792
r.Get("/", Install) // it must be on the root, because the "install.js" use the window.location to replace the "localhost" AppURL
10893
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
10994
r.Get("/post-install", InstallDone)
11095
r.Get("/api/healthz", healthcheck.Check)
111-
11296
r.NotFound(installNotFound)
113-
return r
97+
98+
base.Mount("", r)
99+
return base
114100
}
115101

116102
func installNotFound(w http.ResponseWriter, req *http.Request) {

‎routers/install/routes_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@ import (
1111
)
1212

1313
func TestRoutes(t *testing.T) {
14+
// TODO: this test seems not really testing the handlers
1415
ctx, cancel := context.WithCancel(context.Background())
1516
defer cancel()
16-
routes := Routes(ctx)
17-
assert.NotNil(t, routes)
18-
assert.EqualValues(t, "/", routes.R.Routes()[0].Pattern)
19-
assert.Nil(t, routes.R.Routes()[0].SubRoutes)
20-
assert.Len(t, routes.R.Routes()[0].Handlers, 2)
17+
base := Routes(ctx)
18+
assert.NotNil(t, base)
19+
r := base.R.Routes()[1]
20+
routes := r.SubRoutes.Routes()[0]
21+
assert.EqualValues(t, "/", routes.Pattern)
22+
assert.Nil(t, routes.SubRoutes)
23+
assert.Len(t, routes.Handlers, 2)
2124
}

‎routers/web/base.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,7 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
5959
return
6060
}
6161

62-
http.Redirect(
63-
w,
64-
req,
65-
u.String(),
66-
http.StatusTemporaryRedirect,
67-
)
62+
http.Redirect(w, req, u.String(), http.StatusTemporaryRedirect)
6863
})
6964
}
7065

@@ -122,9 +117,9 @@ func (d *dataStore) GetData() map[string]interface{} {
122117
return *d
123118
}
124119

125-
// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
120+
// RecoveryWith500Page returns a middleware that recovers from any panics and writes a 500 and a log if so.
126121
// This error will be created with the gitea 500 page.
127-
func Recovery(ctx goctx.Context) func(next http.Handler) http.Handler {
122+
func RecoveryWith500Page(ctx goctx.Context) func(next http.Handler) http.Handler {
128123
_, rnd := templates.HTMLRenderer(ctx)
129124
return func(next http.Handler) http.Handler {
130125
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {

‎routers/web/misc/misc.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright 2023 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package misc
5+
6+
import (
7+
"net/http"
8+
"os"
9+
"path"
10+
11+
"code.gitea.io/gitea/modules/git"
12+
"code.gitea.io/gitea/modules/httpcache"
13+
"code.gitea.io/gitea/modules/log"
14+
"code.gitea.io/gitea/modules/setting"
15+
)
16+
17+
func SSHInfo(rw http.ResponseWriter, req *http.Request) {
18+
if !git.SupportProcReceive {
19+
rw.WriteHeader(http.StatusNotFound)
20+
return
21+
}
22+
rw.Header().Set("content-type", "text/json;charset=UTF-8")
23+
_, err := rw.Write([]byte(`{"type":"gitea","version":1}`))
24+
if err != nil {
25+
log.Error("fail to write result: err: %v", err)
26+
rw.WriteHeader(http.StatusInternalServerError)
27+
return
28+
}
29+
rw.WriteHeader(http.StatusOK)
30+
}
31+
32+
func DummyOK(w http.ResponseWriter, req *http.Request) {
33+
w.WriteHeader(http.StatusOK)
34+
}
35+
36+
func RobotsTxt(w http.ResponseWriter, req *http.Request) {
37+
filePath := path.Join(setting.CustomPath, "robots.txt")
38+
fi, err := os.Stat(filePath)
39+
if err == nil && httpcache.HandleTimeCache(req, w, fi) {
40+
return
41+
}
42+
http.ServeFile(w, req, filePath)
43+
}
44+
45+
func StaticRedirect(target string) func(w http.ResponseWriter, req *http.Request) {
46+
return func(w http.ResponseWriter, req *http.Request) {
47+
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, target), http.StatusMovedPermanently)
48+
}
49+
}

‎routers/web/repo/repo.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ import (
1818
"code.gitea.io/gitea/models/unit"
1919
user_model "code.gitea.io/gitea/models/user"
2020
"code.gitea.io/gitea/modules/base"
21+
"code.gitea.io/gitea/modules/cache"
2122
"code.gitea.io/gitea/modules/context"
23+
"code.gitea.io/gitea/modules/git"
2224
"code.gitea.io/gitea/modules/log"
2325
repo_module "code.gitea.io/gitea/modules/repository"
2426
"code.gitea.io/gitea/modules/setting"
@@ -59,6 +61,22 @@ func MustBeAbleToUpload(ctx *context.Context) {
5961
}
6062
}
6163

64+
func CommitInfoCache(ctx *context.Context) {
65+
var err error
66+
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
67+
if err != nil {
68+
ctx.ServerError("GetBranchCommit", err)
69+
return
70+
}
71+
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
72+
if err != nil {
73+
ctx.ServerError("GetCommitsCount", err)
74+
return
75+
}
76+
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
77+
ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
78+
}
79+
6280
func checkContextUser(ctx *context.Context, uid int64) *user_model.User {
6381
orgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx.Doer.ID)
6482
if err != nil {

‎routers/web/user/home.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ import (
3030
"code.gitea.io/gitea/modules/markup/markdown"
3131
"code.gitea.io/gitea/modules/setting"
3232
"code.gitea.io/gitea/modules/util"
33+
"code.gitea.io/gitea/routers/web/feed"
34+
context_service "code.gitea.io/gitea/services/context"
3335
issue_service "code.gitea.io/gitea/services/issue"
3436
pull_service "code.gitea.io/gitea/services/pull"
3537

@@ -815,3 +817,51 @@ func ShowGPGKeys(ctx *context.Context) {
815817
writer.Close()
816818
ctx.PlainTextBytes(http.StatusOK, buf.Bytes())
817819
}
820+
821+
func UsernameSubRoute(ctx *context.Context) {
822+
// WORKAROUND to support usernames with "." in it
823+
// https://github.com/go-chi/chi/issues/781
824+
username := ctx.Params("username")
825+
reloadParam := func(suffix string) (success bool) {
826+
ctx.SetParams("username", strings.TrimSuffix(username, suffix))
827+
context_service.UserAssignmentWeb()(ctx)
828+
return !ctx.Written()
829+
}
830+
switch {
831+
case strings.HasSuffix(username, ".png"):
832+
if reloadParam(".png") {
833+
AvatarByUserName(ctx)
834+
}
835+
case strings.HasSuffix(username, ".keys"):
836+
if reloadParam(".keys") {
837+
ShowSSHKeys(ctx)
838+
}
839+
case strings.HasSuffix(username, ".gpg"):
840+
if reloadParam(".gpg") {
841+
ShowGPGKeys(ctx)
842+
}
843+
case strings.HasSuffix(username, ".rss"):
844+
if !setting.Other.EnableFeed {
845+
ctx.Error(http.StatusNotFound)
846+
return
847+
}
848+
if reloadParam(".rss") {
849+
context_service.UserAssignmentWeb()(ctx)
850+
feed.ShowUserFeedRSS(ctx)
851+
}
852+
case strings.HasSuffix(username, ".atom"):
853+
if !setting.Other.EnableFeed {
854+
ctx.Error(http.StatusNotFound)
855+
return
856+
}
857+
if reloadParam(".atom") {
858+
feed.ShowUserFeedAtom(ctx)
859+
}
860+
default:
861+
context_service.UserAssignmentWeb()(ctx)
862+
if !ctx.Written() {
863+
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
864+
Profile(ctx)
865+
}
866+
}
867+
}

‎routers/web/web.go

Lines changed: 25 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,10 @@ package web
66
import (
77
gocontext "context"
88
"net/http"
9-
"os"
10-
"path"
11-
"strings"
129

1310
"code.gitea.io/gitea/models/perm"
1411
"code.gitea.io/gitea/models/unit"
15-
"code.gitea.io/gitea/modules/cache"
1612
"code.gitea.io/gitea/modules/context"
17-
"code.gitea.io/gitea/modules/git"
18-
"code.gitea.io/gitea/modules/httpcache"
1913
"code.gitea.io/gitea/modules/log"
2014
"code.gitea.io/gitea/modules/metrics"
2115
"code.gitea.io/gitea/modules/public"
@@ -26,12 +20,12 @@ import (
2620
"code.gitea.io/gitea/modules/validation"
2721
"code.gitea.io/gitea/modules/web"
2822
"code.gitea.io/gitea/modules/web/routing"
23+
"code.gitea.io/gitea/routers/common"
2924
"code.gitea.io/gitea/routers/web/admin"
3025
"code.gitea.io/gitea/routers/web/auth"
3126
"code.gitea.io/gitea/routers/web/devtest"
3227
"code.gitea.io/gitea/routers/web/events"
3328
"code.gitea.io/gitea/routers/web/explore"
34-
"code.gitea.io/gitea/routers/web/feed"
3529
"code.gitea.io/gitea/routers/web/healthcheck"
3630
"code.gitea.io/gitea/routers/web/misc"
3731
"code.gitea.io/gitea/routers/web/org"
@@ -48,7 +42,6 @@ import (
4842
_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
4943

5044
"gitea.com/go-chi/captcha"
51-
"gitea.com/go-chi/session"
5245
"github.com/NYTimes/gziphandler"
5346
"github.com/go-chi/chi/v5/middleware"
5447
"github.com/go-chi/cors"
@@ -103,45 +96,18 @@ func buildAuthGroup() *auth_service.Group {
10396
func Routes(ctx gocontext.Context) *web.Route {
10497
routes := web.NewRoute()
10598

106-
routes.Use(web.MiddlewareWithPrefix("/assets/", CorsHandler(), public.AssetsHandlerFunc("/assets/")))
107-
108-
sessioner := 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-
routes.Use(sessioner)
120-
121-
ctx, _ = templates.HTMLRenderer(ctx)
122-
123-
routes.Use(Recovery(ctx))
124-
125-
// We use r.Route here over r.Use because this prevents requests that are not for avatars having to go through this additional handler
99+
routes.Head("/", misc.DummyOK) // for health check - doesn't need to be passed through gzip handler
100+
routes.RouteMethods("/assets/*", "GET, HEAD", CorsHandler(), public.AssetsHandlerFunc("/assets/"))
126101
routes.RouteMethods("/avatars/*", "GET, HEAD", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
127102
routes.RouteMethods("/repo-avatars/*", "GET, HEAD", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
103+
routes.RouteMethods("/apple-touch-icon.png", "GET, HEAD", misc.StaticRedirect("/assets/img/apple-touch-icon.png"))
104+
routes.RouteMethods("/favicon.ico", "GET, HEAD", misc.StaticRedirect("/assets/img/favicon.png"))
128105

129-
// for health check - doesn't need to be passed through gzip handler
130-
routes.Head("/", func(w http.ResponseWriter, req *http.Request) {
131-
w.WriteHeader(http.StatusOK)
132-
})
133-
134-
// this png is very likely to always be below the limit for gzip so it doesn't need to pass through gzip
135-
routes.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) {
136-
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "/assets/img/apple-touch-icon.png"), http.StatusPermanentRedirect)
137-
})
138-
139-
// redirect default favicon to the path of the custom favicon with a default as a fallback
140-
routes.Get("/favicon.ico", func(w http.ResponseWriter, req *http.Request) {
141-
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "/assets/img/favicon.png"), http.StatusMovedPermanently)
142-
})
143-
144-
common := []interface{}{}
106+
ctx, _ = templates.HTMLRenderer(ctx)
107+
common := []any{
108+
common.Sessioner(),
109+
RecoveryWith500Page(ctx),
110+
}
145111

146112
if setting.EnableGzip {
147113
h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
@@ -157,42 +123,18 @@ func Routes(ctx gocontext.Context) *web.Route {
157123
}
158124

159125
if setting.HasRobotsTxt {
160-
routes.Get("/robots.txt", append(common, func(w http.ResponseWriter, req *http.Request) {
161-
filePath := path.Join(setting.CustomPath, "robots.txt")
162-
fi, err := os.Stat(filePath)
163-
if err == nil && httpcache.HandleTimeCache(req, w, fi) {
164-
return
165-
}
166-
http.ServeFile(w, req, filePath)
167-
})...)
126+
routes.Get("/robots.txt", append(common, misc.RobotsTxt)...)
168127
}
169128

170129
// prometheus metrics endpoint - do not need to go through contexter
171130
if setting.Metrics.Enabled {
172-
c := metrics.NewCollector()
173-
prometheus.MustRegister(c)
174-
131+
prometheus.MustRegister(metrics.NewCollector())
175132
routes.Get("/metrics", append(common, Metrics)...)
176133
}
177134

178-
routes.Get("/ssh_info", func(rw http.ResponseWriter, req *http.Request) {
179-
if !git.SupportProcReceive {
180-
rw.WriteHeader(http.StatusNotFound)
181-
return
182-
}
183-
rw.Header().Set("content-type", "text/json;charset=UTF-8")
184-
_, err := rw.Write([]byte(`{"type":"gitea","version":1}`))
185-
if err != nil {
186-
log.Error("fail to write result: err: %v", err)
187-
rw.WriteHeader(http.StatusInternalServerError)
188-
return
189-
}
190-
rw.WriteHeader(http.StatusOK)
191-
})
192-
135+
routes.Get("/ssh_info", misc.SSHInfo)
193136
routes.Get("/api/healthz", healthcheck.Check)
194137

195-
// Removed: toolbox.Toolboxer middleware will provide debug information which seems unnecessary
196138
common = append(common, context.Contexter(ctx))
197139

198140
group := buildAuthGroup()
@@ -207,7 +149,7 @@ func Routes(ctx gocontext.Context) *web.Route {
207149
common = append(common, middleware.GetHead)
208150

209151
if setting.API.EnableSwagger {
210-
// Note: The route moved from apiroutes because it's in fact want to render a web page
152+
// Note: The route is here but no in API routes because it renders a web page
211153
routes.Get("/api/swagger", append(common, misc.Swagger)...) // Render V1 by default
212154
}
213155

@@ -217,17 +159,14 @@ func Routes(ctx gocontext.Context) *web.Route {
217159
common = append(common, goGet)
218160

219161
others := web.NewRoute()
220-
for _, middle := range common {
221-
others.Use(middle)
222-
}
223-
224-
RegisterRoutes(others)
162+
others.Use(common...)
163+
registerRoutes(others)
225164
routes.Mount("", others)
226165
return routes
227166
}
228167

229-
// RegisterRoutes register routes
230-
func RegisterRoutes(m *web.Route) {
168+
// registerRoutes register routes
169+
func registerRoutes(m *web.Route) {
231170
reqSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: true})
232171
ignSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: setting.Service.RequireSignInView})
233172
ignExploreSignIn := auth_service.VerifyAuthWithOptions(&auth_service.VerifyOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView})
@@ -354,8 +293,8 @@ func RegisterRoutes(m *web.Route) {
354293
m.Get("/nodeinfo", NodeInfoLinks)
355294
m.Get("/webfinger", WebfingerQuery)
356295
}, federationEnabled)
357-
m.Get("/change-password", func(w http.ResponseWriter, req *http.Request) {
358-
http.Redirect(w, req, "/user/settings/account", http.StatusTemporaryRedirect)
296+
m.Get("/change-password", func(ctx *context.Context) {
297+
ctx.Redirect(setting.AppSubURL + "/user/settings/account")
359298
})
360299
})
361300

@@ -664,53 +603,7 @@ func RegisterRoutes(m *web.Route) {
664603
// ***** END: Admin *****
665604

666605
m.Group("", func() {
667-
m.Get("/favicon.ico", func(ctx *context.Context) {
668-
ctx.SetServeHeaders(&context.ServeHeaderOptions{
669-
Filename: "favicon.png",
670-
})
671-
http.ServeFile(ctx.Resp, ctx.Req, path.Join(setting.StaticRootPath, "public/img/favicon.png"))
672-
})
673-
m.Get("/{username}", func(ctx *context.Context) {
674-
// WORKAROUND to support usernames with "." in it
675-
// https://github.com/go-chi/chi/issues/781
676-
username := ctx.Params("username")
677-
reloadParam := func(suffix string) (success bool) {
678-
ctx.SetParams("username", strings.TrimSuffix(username, suffix))
679-
context_service.UserAssignmentWeb()(ctx)
680-
return !ctx.Written()
681-
}
682-
switch {
683-
case strings.HasSuffix(username, ".png"):
684-
if reloadParam(".png") {
685-
user.AvatarByUserName(ctx)
686-
}
687-
case strings.HasSuffix(username, ".keys"):
688-
if reloadParam(".keys") {
689-
user.ShowSSHKeys(ctx)
690-
}
691-
case strings.HasSuffix(username, ".gpg"):
692-
if reloadParam(".gpg") {
693-
user.ShowGPGKeys(ctx)
694-
}
695-
case strings.HasSuffix(username, ".rss"):
696-
feedEnabled(ctx)
697-
if !ctx.Written() && reloadParam(".rss") {
698-
context_service.UserAssignmentWeb()(ctx)
699-
feed.ShowUserFeedRSS(ctx)
700-
}
701-
case strings.HasSuffix(username, ".atom"):
702-
feedEnabled(ctx)
703-
if !ctx.Written() && reloadParam(".atom") {
704-
feed.ShowUserFeedAtom(ctx)
705-
}
706-
default:
707-
context_service.UserAssignmentWeb()(ctx)
708-
if !ctx.Written() {
709-
ctx.Data["EnableFeed"] = setting.Other.EnableFeed
710-
user.Profile(ctx)
711-
}
712-
}
713-
})
606+
m.Get("/{username}", user.UsernameSubRoute)
714607
m.Get("/attachments/{uuid}", repo.GetAttachment)
715608
}, ignSignIn)
716609

@@ -1229,21 +1122,7 @@ func RegisterRoutes(m *web.Route) {
12291122
m.Group("/releases", func() {
12301123
m.Get("/edit/*", repo.EditRelease)
12311124
m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost)
1232-
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, func(ctx *context.Context) {
1233-
var err error
1234-
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
1235-
if err != nil {
1236-
ctx.ServerError("GetBranchCommit", err)
1237-
return
1238-
}
1239-
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
1240-
if err != nil {
1241-
ctx.ServerError("GetCommitsCount", err)
1242-
return
1243-
}
1244-
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
1245-
ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
1246-
})
1125+
}, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, repo.CommitInfoCache)
12471126
}, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader)
12481127

12491128
// to maintain compatibility with old attachments
@@ -1322,18 +1201,10 @@ func RegisterRoutes(m *web.Route) {
13221201
m.Group("/wiki", func() {
13231202
m.Combo("/").
13241203
Get(repo.Wiki).
1325-
Post(context.RepoMustNotBeArchived(),
1326-
reqSignIn,
1327-
reqRepoWikiWriter,
1328-
web.Bind(forms.NewWikiForm{}),
1329-
repo.WikiPost)
1204+
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
13301205
m.Combo("/*").
13311206
Get(repo.Wiki).
1332-
Post(context.RepoMustNotBeArchived(),
1333-
reqSignIn,
1334-
reqRepoWikiWriter,
1335-
web.Bind(forms.NewWikiForm{}),
1336-
repo.WikiPost)
1207+
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
13371208
m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
13381209
m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff)
13391210
}, repo.MustEnableWiki, func(ctx *context.Context) {
@@ -1461,8 +1332,7 @@ func RegisterRoutes(m *web.Route) {
14611332
m.Group("", func() {
14621333
m.Get("/forks", repo.Forks)
14631334
}, context.RepoRef(), reqRepoCodeReader)
1464-
m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}",
1465-
repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
1335+
m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
14661336
}, ignSignIn, context.RepoAssignment, context.UnitTypes())
14671337

14681338
m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit)

0 commit comments

Comments
 (0)
Please sign in to comment.