mirror of
				https://github.com/go-gitea/gitea
				synced 2025-11-03 21:08:25 +00:00 
			
		
		
		
	Backport #33840 by wxiaoguang Fix #33839 --------- Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		@@ -70,14 +70,13 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
 | 
			
		||||
		g.logger.Log("json marshal error", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	url := g.server.JoinPath("objects/batch").String()
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
		headerAccept:            mimeGitLFS,
 | 
			
		||||
		headerContentType:       mimeGitLFS,
 | 
			
		||||
	}
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, g.server.JoinPath("objects/batch").String(), http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		g.logger.Log("http request error", err)
 | 
			
		||||
@@ -179,13 +178,12 @@ func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser,
 | 
			
		||||
		g.logger.Log("argument id incorrect")
 | 
			
		||||
		return nil, 0, transfer.ErrCorruptData
 | 
			
		||||
	}
 | 
			
		||||
	url := action.Href
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
		headerAccept:            mimeOctetStream,
 | 
			
		||||
	}
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodGet, headers, nil)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, toInternalLFSURL(action.Href), http.MethodGet, headers, nil)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, 0, fmt.Errorf("failed to get response: %w", err)
 | 
			
		||||
@@ -225,7 +223,6 @@ func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer
 | 
			
		||||
		g.logger.Log("argument id incorrect")
 | 
			
		||||
		return transfer.ErrCorruptData
 | 
			
		||||
	}
 | 
			
		||||
	url := action.Href
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
@@ -233,7 +230,7 @@ func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer
 | 
			
		||||
		headerContentLength:     strconv.FormatInt(size, 10),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodPut, headers, nil)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, toInternalLFSURL(action.Href), http.MethodPut, headers, nil)
 | 
			
		||||
	req.Body(r)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -274,14 +271,13 @@ func (g *GiteaBackend) Verify(oid string, size int64, args transfer.Args) (trans
 | 
			
		||||
		// the server sent no verify action
 | 
			
		||||
		return transfer.SuccessStatus(), nil
 | 
			
		||||
	}
 | 
			
		||||
	url := action.Href
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
		headerAccept:            mimeGitLFS,
 | 
			
		||||
		headerContentType:       mimeGitLFS,
 | 
			
		||||
	}
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, toInternalLFSURL(action.Href), http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return transfer.NewStatus(transfer.StatusInternalServerError), err
 | 
			
		||||
 
 | 
			
		||||
@@ -43,14 +43,13 @@ func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
 | 
			
		||||
		g.logger.Log("json marshal error", err)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	url := g.server.String()
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
		headerAccept:            mimeGitLFS,
 | 
			
		||||
		headerContentType:       mimeGitLFS,
 | 
			
		||||
	}
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, g.server.String(), http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		g.logger.Log("http request error", err)
 | 
			
		||||
@@ -95,14 +94,13 @@ func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
 | 
			
		||||
		g.logger.Log("json marshal error", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	url := g.server.JoinPath(lock.ID(), "unlock").String()
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
		headerAccept:            mimeGitLFS,
 | 
			
		||||
		headerContentType:       mimeGitLFS,
 | 
			
		||||
	}
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, g.server.JoinPath(lock.ID(), "unlock").String(), http.MethodPost, headers, bodyBytes)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		g.logger.Log("http request error", err)
 | 
			
		||||
@@ -176,16 +174,15 @@ func (g *giteaLockBackend) Range(cursor string, limit int, iter func(transfer.Lo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, error) {
 | 
			
		||||
	urlq := g.server.JoinPath() // get a copy
 | 
			
		||||
	urlq.RawQuery = v.Encode()
 | 
			
		||||
	url := urlq.String()
 | 
			
		||||
	serverURLWithQuery := g.server.JoinPath() // get a copy
 | 
			
		||||
	serverURLWithQuery.RawQuery = v.Encode()
 | 
			
		||||
	headers := map[string]string{
 | 
			
		||||
		headerAuthorization:     g.authToken,
 | 
			
		||||
		headerGiteaInternalAuth: g.internalAuth,
 | 
			
		||||
		headerAccept:            mimeGitLFS,
 | 
			
		||||
		headerContentType:       mimeGitLFS,
 | 
			
		||||
	}
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, url, http.MethodGet, headers, nil)
 | 
			
		||||
	req := newInternalRequestLFS(g.ctx, serverURLWithQuery.String(), http.MethodGet, headers, nil)
 | 
			
		||||
	resp, err := req.Response()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		g.logger.Log("http request error", err)
 | 
			
		||||
 
 | 
			
		||||
@@ -8,9 +8,13 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/httplib"
 | 
			
		||||
	"code.gitea.io/gitea/modules/private"
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/util"
 | 
			
		||||
 | 
			
		||||
	"github.com/charmbracelet/git-lfs-transfer/transfer"
 | 
			
		||||
)
 | 
			
		||||
@@ -57,8 +61,7 @@ const (
 | 
			
		||||
 | 
			
		||||
// Operations enum
 | 
			
		||||
const (
 | 
			
		||||
	opNone = iota
 | 
			
		||||
	opDownload
 | 
			
		||||
	opDownload = iota + 1
 | 
			
		||||
	opUpload
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -86,8 +89,49 @@ func statusCodeToErr(code int) error {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newInternalRequestLFS(ctx context.Context, url, method string, headers map[string]string, body any) *httplib.Request {
 | 
			
		||||
	req := private.NewInternalRequest(ctx, url, method)
 | 
			
		||||
func toInternalLFSURL(s string) string {
 | 
			
		||||
	pos1 := strings.Index(s, "://")
 | 
			
		||||
	if pos1 == -1 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	appSubURLWithSlash := setting.AppSubURL + "/"
 | 
			
		||||
	pos2 := strings.Index(s[pos1+3:], appSubURLWithSlash)
 | 
			
		||||
	if pos2 == -1 {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	routePath := s[pos1+3+pos2+len(appSubURLWithSlash):]
 | 
			
		||||
	fields := strings.SplitN(routePath, "/", 3)
 | 
			
		||||
	if len(fields) < 3 || !strings.HasPrefix(fields[2], "info/lfs") {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return setting.LocalURL + "api/internal/repo/" + routePath
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func isInternalLFSURL(s string) bool {
 | 
			
		||||
	if !strings.HasPrefix(s, setting.LocalURL) {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	u, err := url.Parse(s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	routePath := util.PathJoinRelX(u.Path)
 | 
			
		||||
	subRoutePath, cut := strings.CutPrefix(routePath, "api/internal/repo/")
 | 
			
		||||
	if !cut {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	fields := strings.SplitN(subRoutePath, "/", 3)
 | 
			
		||||
	if len(fields) < 3 || !strings.HasPrefix(fields[2], "info/lfs") {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return true
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newInternalRequestLFS(ctx context.Context, internalURL, method string, headers map[string]string, body any) *httplib.Request {
 | 
			
		||||
	if !isInternalLFSURL(internalURL) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	req := private.NewInternalRequest(ctx, internalURL, method)
 | 
			
		||||
	for k, v := range headers {
 | 
			
		||||
		req.Header(k, v)
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								modules/lfstransfer/backend/util_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								modules/lfstransfer/backend/util_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
// Copyright 2025 The Gitea Authors. All rights reserved.
 | 
			
		||||
// SPDX-License-Identifier: MIT
 | 
			
		||||
 | 
			
		||||
package backend
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"code.gitea.io/gitea/modules/setting"
 | 
			
		||||
	"code.gitea.io/gitea/modules/test"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestToInternalLFSURL(t *testing.T) {
 | 
			
		||||
	defer test.MockVariableValue(&setting.LocalURL, "http://localurl/")()
 | 
			
		||||
	defer test.MockVariableValue(&setting.AppSubURL, "/sub")()
 | 
			
		||||
	cases := []struct {
 | 
			
		||||
		url      string
 | 
			
		||||
		expected string
 | 
			
		||||
	}{
 | 
			
		||||
		{"http://appurl/any", ""},
 | 
			
		||||
		{"http://appurl/sub/any", ""},
 | 
			
		||||
		{"http://appurl/sub/owner/repo/any", ""},
 | 
			
		||||
		{"http://appurl/sub/owner/repo/info/any", ""},
 | 
			
		||||
		{"http://appurl/sub/owner/repo/info/lfs/any", "http://localurl/api/internal/repo/owner/repo/info/lfs/any"},
 | 
			
		||||
	}
 | 
			
		||||
	for _, c := range cases {
 | 
			
		||||
		assert.Equal(t, c.expected, toInternalLFSURL(c.url), c.url)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestIsInternalLFSURL(t *testing.T) {
 | 
			
		||||
	defer test.MockVariableValue(&setting.LocalURL, "http://localurl/")()
 | 
			
		||||
	defer test.MockVariableValue(&setting.InternalToken, "mock-token")()
 | 
			
		||||
	cases := []struct {
 | 
			
		||||
		url      string
 | 
			
		||||
		expected bool
 | 
			
		||||
	}{
 | 
			
		||||
		{"", false},
 | 
			
		||||
		{"http://otherurl/api/internal/repo/owner/repo/info/lfs/any", false},
 | 
			
		||||
		{"http://localurl/api/internal/repo/owner/repo/info/lfs/any", true},
 | 
			
		||||
		{"http://localurl/api/internal/repo/owner/repo/info", false},
 | 
			
		||||
		{"http://localurl/api/internal/misc/owner/repo/info/lfs/any", false},
 | 
			
		||||
		{"http://localurl/api/internal/owner/repo/info/lfs/any", false},
 | 
			
		||||
		{"http://localurl/api/internal/foo/bar", false},
 | 
			
		||||
	}
 | 
			
		||||
	for _, c := range cases {
 | 
			
		||||
		req := newInternalRequestLFS(context.Background(), c.url, "GET", nil, nil)
 | 
			
		||||
		assert.Equal(t, c.expected, req != nil, c.url)
 | 
			
		||||
		assert.Equal(t, c.expected, isInternalLFSURL(c.url), c.url)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user