diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 1486e83c57..07e34da8e2 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -20,6 +20,7 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" + "code.gitea.io/gitea/modules/globallock" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" @@ -223,6 +224,10 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool helper.ServePackageFile(ctx, s, u, pf, opts) } +func mavenPkgNameKey(packageName string) string { + return "pkg_maven_" + packageName +} + // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. func UploadPackageFile(ctx *context.Context) { params, err := extractPathParameters(ctx) @@ -241,6 +246,14 @@ func UploadPackageFile(ctx *context.Context) { packageName := params.GroupID + "-" + params.ArtifactID + // for the same package, only one upload at a time + releaser, err := globallock.Lock(ctx, mavenPkgNameKey(packageName)) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + defer releaser() + buf, err := packages_module.CreateHashedBufferFromReader(ctx.Req.Body) if err != nil { apiError(ctx, http.StatusInternalServerError, err) diff --git a/tests/integration/api_packages_maven_test.go b/tests/integration/api_packages_maven_test.go index 0466a727b2..e54238858c 100644 --- a/tests/integration/api_packages_maven_test.go +++ b/tests/integration/api_packages_maven_test.go @@ -8,6 +8,7 @@ import ( "net/http" "strconv" "strings" + "sync" "testing" "code.gitea.io/gitea/models/db" @@ -252,3 +253,35 @@ func TestPackageMaven(t *testing.T) { assert.True(t, test.IsNormalPageCompleted(resp.Body.String())) }) } + +func TestPackageMavenConcurrent(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + groupID := "com.gitea" + artifactID := "test-project" + packageVersion := "1.0.1" + + root := fmt.Sprintf("/api/packages/%s/maven/%s/%s", user.Name, strings.ReplaceAll(groupID, ".", "/"), artifactID) + + putFile := func(t *testing.T, path, content string, expectedStatus int) { + req := NewRequestWithBody(t, "PUT", root+path, strings.NewReader(content)). + AddBasicAuth(user.Name) + MakeRequest(t, req, expectedStatus) + } + + t.Run("Concurrent Upload", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + var wg sync.WaitGroup + for i := 0; i < 10; i++ { + wg.Add(1) + go func(i int) { + putFile(t, fmt.Sprintf("/%s/%s.jar", packageVersion, strconv.Itoa(i)), "test", http.StatusCreated) + wg.Done() + }(i) + } + wg.Wait() + }) +}