Skip to content

Commit 2296af5

Browse files
committed
fix
1 parent 01351cc commit 2296af5

File tree

7 files changed

+58
-11
lines changed

7 files changed

+58
-11
lines changed

modules/setting/server.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,10 @@ func loadServerFrom(rootCfg ConfigProvider) {
370370
}
371371
}
372372

373+
// TODO: GOLANG-HTTP-TMPDIR: Some Golang packages (like "http") use os.TempDir() to create temporary files when uploading files.
374+
// So ideally we should set the TMPDIR environment variable to make them use our managed temp directory.
375+
// But there is no clear place to set it currently, for example: when running "install" page, the AppDataPath is not ready yet, then AppDataTempDir won't work
376+
373377
EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
374378
EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
375379
PprofDataPath = sec.Key("PPROF_DATA_PATH").MustString(filepath.Join(AppWorkPath, "data/tmp/pprof"))

modules/web/routemock.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,15 @@ func RouterMockPoint(pointName string) func(next http.Handler) http.Handler {
4646
//
4747
// Then the mock function will be executed as a middleware at the mock point.
4848
// It only takes effect in testing mode (setting.IsInTesting == true).
49-
func RouteMock(pointName string, h any) {
49+
func RouteMock(pointName string, h any) func() {
5050
if _, ok := routeMockPoints[pointName]; !ok {
5151
panic("route mock point not found: " + pointName)
5252
}
53+
old := routeMockPoints[pointName]
5354
routeMockPoints[pointName] = toHandlerProvider(h)
55+
return func() {
56+
routeMockPoints[pointName] = old
57+
}
5458
}
5559

5660
// RouteMockReset resets all mock points (no mock anymore)

modules/web/router.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func NewRouter() *Router {
5555
// Use supports two middlewares
5656
func (r *Router) Use(middlewares ...any) {
5757
for _, m := range middlewares {
58-
if m != nil {
58+
if !isNilOrFuncNil(m) {
5959
r.chiRouter.Use(toHandlerProvider(m))
6060
}
6161
}

routers/common/middleware.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,13 @@ func RequestContextHandler() func(h http.Handler) http.Handler {
7272
req = req.WithContext(cache.WithCacheContext(ctx))
7373
ds.SetContextValue(httplib.RequestContextKey, req)
7474
ds.AddCleanUp(func() {
75-
if req.MultipartForm != nil {
76-
_ = req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
75+
// TODO: GOLANG-HTTP-TMPDIR: Golang saves the uploaded files to temp directory (TMPDIR) when parsing multipart-form.
76+
// The "req" might have changed due to the new "req.WithContext" calls
77+
// For example: in NewBaseContext, a new "req" with context is created, and the multipart-form is parsed there.
78+
// So we always use the latest "req" from the data store.
79+
ctxReq := ds.GetContextValue(httplib.RequestContextKey).(*http.Request)
80+
if ctxReq.MultipartForm != nil {
81+
_ = ctxReq.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
7782
}
7883
})
7984
next.ServeHTTP(respWriter, req)

routers/web/web.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ func ctxDataSet(args ...any) func(ctx *context.Context) {
227227
}
228228
}
229229

230+
const RouterMockPointBeforeWebRoutes = "before-web-routes"
231+
230232
// Routes returns all web routes
231233
func Routes() *web.Router {
232234
routes := web.NewRouter()
@@ -285,7 +287,7 @@ func Routes() *web.Router {
285287

286288
webRoutes := web.NewRouter()
287289
webRoutes.Use(mid...)
288-
webRoutes.Group("", func() { registerWebRoutes(webRoutes) }, common.BlockExpensive(), common.QoS())
290+
webRoutes.Group("", func() { registerWebRoutes(webRoutes) }, common.BlockExpensive(), common.QoS(), web.RouterMockPoint(RouterMockPointBeforeWebRoutes))
289291
routes.Mount("", webRoutes)
290292
return routes
291293
}

services/context/base.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,10 @@ type Base struct {
4343
Locale translation.Locale
4444
}
4545

46+
var ParseMultipartFormMaxMemory = int64(32 << 20)
47+
4648
func (b *Base) ParseMultipartForm() bool {
47-
err := b.Req.ParseMultipartForm(32 << 20)
49+
err := b.Req.ParseMultipartForm(ParseMultipartFormMaxMemory)
4850
if err != nil {
4951
// TODO: all errors caused by client side should be ignored (connection closed).
5052
if !errors.Is(err, io.EOF) && !errors.Is(err, io.ErrUnexpectedEOF) {
Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,23 @@ import (
77
"bytes"
88
"image"
99
"image/png"
10+
"io/fs"
1011
"mime/multipart"
1112
"net/http"
13+
"os"
1214
"strings"
1315
"testing"
1416

1517
repo_model "code.gitea.io/gitea/models/repo"
1618
"code.gitea.io/gitea/modules/storage"
1719
"code.gitea.io/gitea/modules/test"
20+
"code.gitea.io/gitea/modules/web"
21+
route_web "code.gitea.io/gitea/routers/web"
22+
"code.gitea.io/gitea/services/context"
1823
"code.gitea.io/gitea/tests"
1924

2025
"github.com/stretchr/testify/assert"
26+
"github.com/stretchr/testify/require"
2127
)
2228

2329
func testGeneratePngBytes() []byte {
@@ -52,14 +58,39 @@ func testCreateIssueAttachment(t *testing.T, session *TestSession, csrf, repoURL
5258
return obj["uuid"]
5359
}
5460

55-
func TestCreateAnonymousAttachment(t *testing.T) {
61+
func Test0Attachments(t *testing.T) {
5662
defer tests.PrepareTestEnv(t)()
63+
t.Run("CreateAnonymousAttachment", testCreateAnonymousAttachment)
64+
t.Run("CreateUser2IssueAttachment", testCreateUser2IssueAttachment)
65+
t.Run("UploadAttachmentDeleteTemp", testUploadAttachmentDeleteTemp)
66+
t.Run("GetAttachment", testGetAttachment)
67+
}
68+
69+
func testUploadAttachmentDeleteTemp(t *testing.T) {
70+
defer test.MockVariableValue(&context.ParseMultipartFormMaxMemory, 1)()
71+
session := loginUser(t, "user2")
72+
var uploadTempFile string
73+
defer web.RouteMock(route_web.RouterMockPointBeforeWebRoutes, func(resp http.ResponseWriter, req *http.Request) {
74+
// TODO: GOLANG-HTTP-TMPDIR: Golang saves the uploaded file to os.TempDir() when it exceeds the max memory limit.
75+
files, err := fs.Glob(os.DirFS(os.Getenv("TMPDIR")), "multipart-*")
76+
require.NoError(t, err)
77+
require.Len(t, files, 1)
78+
79+
uploadTempFile = os.Getenv("TMPDIR") + "/" + files[0]
80+
_, err = os.Stat(uploadTempFile)
81+
assert.NoError(t, err, "the temp file should exist during upload handling because size exceeds the parse form's max memory")
82+
})()
83+
_ = testCreateIssueAttachment(t, session, GetUserCSRFToken(t, session), "user2/repo1", "image.png", testGeneratePngBytes(), http.StatusOK)
84+
_, err := os.Stat(uploadTempFile)
85+
assert.ErrorIs(t, err, os.ErrNotExist, "the temp file should be deleted after upload")
86+
}
87+
88+
func testCreateAnonymousAttachment(t *testing.T) {
5789
session := emptyTestSession(t)
5890
testCreateIssueAttachment(t, session, GetAnonymousCSRFToken(t, session), "user2/repo1", "image.png", testGeneratePngBytes(), http.StatusSeeOther)
5991
}
6092

61-
func TestCreateIssueAttachment(t *testing.T) {
62-
defer tests.PrepareTestEnv(t)()
93+
func testCreateUser2IssueAttachment(t *testing.T) {
6394
const repoURL = "user2/repo1"
6495
session := loginUser(t, "user2")
6596
uuid := testCreateIssueAttachment(t, session, GetUserCSRFToken(t, session), repoURL, "image.png", testGeneratePngBytes(), http.StatusOK)
@@ -90,8 +121,7 @@ func TestCreateIssueAttachment(t *testing.T) {
90121
MakeRequest(t, req, http.StatusOK)
91122
}
92123

93-
func TestGetAttachment(t *testing.T) {
94-
defer tests.PrepareTestEnv(t)()
124+
func testGetAttachment(t *testing.T) {
95125
adminSession := loginUser(t, "user1")
96126
user2Session := loginUser(t, "user2")
97127
user8Session := loginUser(t, "user8")

0 commit comments

Comments
 (0)