Skip to content

Commit ba28bc7

Browse files
committed
fix
1 parent 01351cc commit ba28bc7

File tree

8 files changed

+68
-18
lines changed

8 files changed

+68
-18
lines changed

modules/setting/path.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,11 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP
204204
// * When APP_TEMP_PATH="/tmp": the managed temp directory is "/tmp/gitea-tmp"
205205
// * When APP_TEMP_PATH is not set: the managed temp directory is "/{APP_DATA_PATH}/tmp"
206206
func AppDataTempDir(sub string) *tempdir.TempDir {
207-
if appTempPathInternal != "" {
208-
return tempdir.New(appTempPathInternal, "gitea-tmp/"+sub)
207+
if appTempPathInternal == nil {
208+
panic("appTempPathInternal is not initialized")
209+
}
210+
if dir := *appTempPathInternal; dir != "" {
211+
return tempdir.New(dir, "gitea-tmp/"+sub)
209212
}
210213
if AppDataPath == "" {
211214
panic("setting.AppDataPath is not set")

modules/setting/server.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ var (
7777

7878
// appTempPathInternal is the temporary path for the app, it is only an internal variable
7979
// DO NOT use it directly, always use AppDataTempDir
80-
appTempPathInternal string
80+
appTempPathInternal *string
8181

8282
Protocol Scheme
8383
UseProxyProtocol bool
@@ -363,13 +363,17 @@ func loadServerFrom(rootCfg ConfigProvider) {
363363
}
364364
}
365365

366-
appTempPathInternal = sec.Key("APP_TEMP_PATH").String()
367-
if appTempPathInternal != "" {
368-
if _, err := os.Stat(appTempPathInternal); err != nil {
369-
log.Fatal("APP_TEMP_PATH %q is not accessible: %v", appTempPathInternal, err)
366+
appTempPathInternal = util.ToPointer(sec.Key("APP_TEMP_PATH").String())
367+
if dir := *appTempPathInternal; dir != "" {
368+
if _, err := os.Stat(dir); err != nil {
369+
log.Fatal("APP_TEMP_PATH %q is not accessible: %v", dir, err)
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) {

tests/integration/attachment_test.go

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 TestAttachments(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)