Skip to content

Commit 64e7aa6

Browse files
author
Nils Hillmann
committed
Merge remote-tracking branch 'origin/main' into feature/oauth_userinfo
2 parents 1c36c99 + eed88dc commit 64e7aa6

File tree

5 files changed

+151
-47
lines changed

5 files changed

+151
-47
lines changed

docs/content/doc/help/faq.en-us.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,13 @@ is too small. Gitea requires that the `ROWFORMAT` for its tables is `DYNAMIC`.
324324

325325
If you are receiving an error line containing `Error 1071: Specified key was too long; max key length is 1000 bytes...`
326326
then you are attempting to run Gitea on tables which use the ISAM engine. While this may have worked by chance in previous versions of Gitea, it has never been officially supported and
327-
you must use InnoDB. You should run `ALTER TABLE table_name ENGINE=InnoDB;` for each table in the database.
327+
you must use InnoDB. You should run `ALTER TABLE table_name ENGINE=InnoDB;` for each table in the database.
328+
If you are using MySQL 5, another possible fix is
329+
```mysql
330+
SET GLOBAL innodb_file_format=Barracuda;
331+
SET GLOBAL innodb_file_per_table=1;
332+
SET GLOBAL innodb_large_prefix=1;
333+
```
328334

329335
## Why Are Emoji Broken On MySQL
330336

integrations/user_avatar_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright 2021 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package integrations
6+
7+
import (
8+
"bytes"
9+
"image/png"
10+
"io"
11+
"mime/multipart"
12+
"net/http"
13+
"net/url"
14+
"strings"
15+
"testing"
16+
17+
"code.gitea.io/gitea/models"
18+
"code.gitea.io/gitea/modules/avatar"
19+
"github.com/stretchr/testify/assert"
20+
)
21+
22+
func TestUserAvatar(t *testing.T) {
23+
onGiteaRun(t, func(t *testing.T, u *url.URL) {
24+
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo3, is an org
25+
26+
seed := user2.Email
27+
if len(seed) == 0 {
28+
seed = user2.Name
29+
}
30+
31+
img, err := avatar.RandomImage([]byte(seed))
32+
if err != nil {
33+
assert.NoError(t, err)
34+
return
35+
}
36+
37+
session := loginUser(t, "user2")
38+
csrf := GetCSRF(t, session, "/user/settings")
39+
40+
imgData := &bytes.Buffer{}
41+
42+
body := &bytes.Buffer{}
43+
44+
//Setup multi-part
45+
writer := multipart.NewWriter(body)
46+
writer.WriteField("source", "local")
47+
part, err := writer.CreateFormFile("avatar", "avatar-for-testuseravatar.png")
48+
if err != nil {
49+
assert.NoError(t, err)
50+
return
51+
}
52+
53+
if err := png.Encode(imgData, img); err != nil {
54+
assert.NoError(t, err)
55+
return
56+
}
57+
58+
if _, err := io.Copy(part, imgData); err != nil {
59+
assert.NoError(t, err)
60+
return
61+
}
62+
63+
if err := writer.Close(); err != nil {
64+
assert.NoError(t, err)
65+
return
66+
}
67+
68+
req := NewRequestWithBody(t, "POST", "/user/settings/avatar", body)
69+
req.Header.Add("X-Csrf-Token", csrf)
70+
req.Header.Add("Content-Type", writer.FormDataContentType())
71+
72+
session.MakeRequest(t, req, http.StatusFound)
73+
74+
user2 = models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User) // owner of the repo3, is an org
75+
76+
req = NewRequest(t, "GET", user2.AvatarLink())
77+
resp := session.MakeRequest(t, req, http.StatusFound)
78+
location := resp.Header().Get("Location")
79+
if !strings.HasPrefix(location, "/avatars") {
80+
assert.Fail(t, "Avatar location is not local: %s", location)
81+
}
82+
req = NewRequest(t, "GET", location)
83+
session.MakeRequest(t, req, http.StatusOK)
84+
85+
// Can't test if the response matches because the image is regened on upload but checking that this at least doesn't give a 404 should be enough.
86+
})
87+
}

modules/context/context.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ func Contexter() func(next http.Handler) http.Handler {
692692
log.Debug("Session ID: %s", ctx.Session.ID())
693693
log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"])
694694

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

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

712+
ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled()
713+
ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled()
714+
ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled()
715+
ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled()
716+
711717
ctx.Data["i18n"] = locale
712718
ctx.Data["Tr"] = i18n.Tr
713719
ctx.Data["Lang"] = locale.Language()

routers/api/v1/api.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -572,10 +572,6 @@ func Routes() *web.Route {
572572
}
573573
m.Use(context.APIContexter())
574574

575-
if setting.EnableAccessLog {
576-
m.Use(context.AccessLogger())
577-
}
578-
579575
m.Use(context.ToggleAPI(&context.ToggleOptions{
580576
SignInRequired: setting.Service.RequireSignInView,
581577
}))

routers/routes/web.go

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ func commonMiddlewares() []func(http.Handler) http.Handler {
8989
handlers = append(handlers, LoggerHandler(setting.RouterLogLevel))
9090
}
9191
}
92+
if setting.EnableAccessLog {
93+
handlers = append(handlers, context.AccessLogger())
94+
}
9295

9396
handlers = append(handlers, func(next http.Handler) http.Handler {
9497
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
@@ -128,9 +131,9 @@ func NormalRoutes() *web.Route {
128131

129132
// WebRoutes returns all web routes
130133
func WebRoutes() *web.Route {
131-
r := web.NewRoute()
134+
routes := web.NewRoute()
132135

133-
r.Use(session.Sessioner(session.Options{
136+
routes.Use(session.Sessioner(session.Options{
134137
Provider: setting.SessionConfig.Provider,
135138
ProviderConfig: setting.SessionConfig.ProviderConfig,
136139
CookieName: setting.SessionConfig.CookieName,
@@ -141,93 +144,99 @@ func WebRoutes() *web.Route {
141144
Domain: setting.SessionConfig.Domain,
142145
}))
143146

144-
r.Use(Recovery())
147+
routes.Use(Recovery())
145148

146-
r.Use(public.Custom(
149+
// TODO: we should consider if there is a way to mount these using r.Route as at present
150+
// these two handlers mean that every request has to hit these "filesystems" twice
151+
// before finally getting to the router. It allows them to override any matching router below.
152+
routes.Use(public.Custom(
147153
&public.Options{
148154
SkipLogging: setting.DisableRouterLog,
149155
},
150156
))
151-
r.Use(public.Static(
157+
routes.Use(public.Static(
152158
&public.Options{
153159
Directory: path.Join(setting.StaticRootPath, "public"),
154160
SkipLogging: setting.DisableRouterLog,
155161
Prefix: "/assets",
156162
},
157163
))
158164

159-
r.Use(storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
160-
r.Use(storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
165+
// We use r.Route here over r.Use because this prevents requests that are not for avatars having to go through this additional handler
166+
routes.Route("/avatars/*", "GET, HEAD", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
167+
routes.Route("/repo-avatars/*", "GET, HEAD", storageHandler(setting.RepoAvatar.Storage, "repo-avatars", storage.RepoAvatars))
168+
169+
// for health check - doeesn't need to be passed through gzip handler
170+
routes.Head("/", func(w http.ResponseWriter, req *http.Request) {
171+
w.WriteHeader(http.StatusOK)
172+
})
173+
174+
// this png is very likely to always be below the limit for gzip so it doesn't need to pass through gzip
175+
routes.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) {
176+
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301)
177+
})
161178

162179
gob.Register(&u2f.Challenge{})
163180

181+
common := []interface{}{}
182+
164183
if setting.EnableGzip {
165184
h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize))
166185
if err != nil {
167186
log.Fatal("GzipHandlerWithOpts failed: %v", err)
168187
}
169-
r.Use(h)
188+
common = append(common, h)
170189
}
171190

172191
mailer.InitMailRender(templates.Mailer())
173192

174193
if setting.Service.EnableCaptcha {
175-
r.Use(captcha.Captchaer(context.GetImageCaptcha()))
176-
}
177-
// Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary
178-
r.Use(context.Contexter())
179-
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
180-
r.Use(middleware.GetHead)
181-
182-
if setting.EnableAccessLog {
183-
r.Use(context.AccessLogger())
194+
// The captcha http.Handler should only fire on /captcha/* so we can just mount this on that url
195+
routes.Route("/captcha/*", "GET,HEAD", append(common, captcha.Captchaer(context.GetImageCaptcha()))...)
184196
}
185197

186-
r.Use(user.GetNotificationCount)
187-
r.Use(repo.GetActiveStopwatch)
188-
r.Use(func(ctx *context.Context) {
189-
ctx.Data["UnitWikiGlobalDisabled"] = models.UnitTypeWiki.UnitGlobalDisabled()
190-
ctx.Data["UnitIssuesGlobalDisabled"] = models.UnitTypeIssues.UnitGlobalDisabled()
191-
ctx.Data["UnitPullsGlobalDisabled"] = models.UnitTypePullRequests.UnitGlobalDisabled()
192-
ctx.Data["UnitProjectsGlobalDisabled"] = models.UnitTypeProjects.UnitGlobalDisabled()
193-
})
194-
195-
// for health check
196-
r.Head("/", func(w http.ResponseWriter, req *http.Request) {
197-
w.WriteHeader(http.StatusOK)
198-
})
199-
200198
if setting.HasRobotsTxt {
201-
r.Get("/robots.txt", func(w http.ResponseWriter, req *http.Request) {
199+
routes.Get("/robots.txt", append(common, func(w http.ResponseWriter, req *http.Request) {
202200
filePath := path.Join(setting.CustomPath, "robots.txt")
203201
fi, err := os.Stat(filePath)
204202
if err == nil && httpcache.HandleTimeCache(req, w, fi) {
205203
return
206204
}
207205
http.ServeFile(w, req, filePath)
208-
})
206+
})...)
209207
}
210208

211-
r.Get("/apple-touch-icon.png", func(w http.ResponseWriter, req *http.Request) {
212-
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "img/apple-touch-icon.png"), 301)
213-
})
214-
215-
// prometheus metrics endpoint
209+
// prometheus metrics endpoint - do not need to go through contexter
216210
if setting.Metrics.Enabled {
217211
c := metrics.NewCollector()
218212
prometheus.MustRegister(c)
219213

220-
r.Get("/metrics", routers.Metrics)
214+
routes.Get("/metrics", append(common, routers.Metrics)...)
221215
}
222216

217+
// Removed: toolbox.Toolboxer middleware will provide debug informations which seems unnecessary
218+
common = append(common, context.Contexter())
219+
220+
// GetHead allows a HEAD request redirect to GET if HEAD method is not defined for that route
221+
common = append(common, middleware.GetHead)
222+
223223
if setting.API.EnableSwagger {
224224
// Note: The route moved from apiroutes because it's in fact want to render a web page
225-
r.Get("/api/swagger", misc.Swagger) // Render V1 by default
225+
routes.Get("/api/swagger", append(common, misc.Swagger)...) // Render V1 by default
226226
}
227227

228-
RegisterRoutes(r)
228+
// TODO: These really seem like things that could be folded into Contexter or as helper functions
229+
common = append(common, user.GetNotificationCount)
230+
common = append(common, repo.GetActiveStopwatch)
229231

230-
return r
232+
others := web.NewRoute()
233+
for _, middle := range common {
234+
others.Use(middle)
235+
}
236+
237+
RegisterRoutes(others)
238+
routes.Mount("", others)
239+
return routes
231240
}
232241

233242
func goGet(ctx *context.Context) {

0 commit comments

Comments
 (0)