Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions internal/strategy/git/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import (
"github.com/block/cachew/internal/gitclone"
)

// Exports unexported symbols for use by external test packages.

func IsGitRequest(pathValue string) bool { return isGitRequest(pathValue) }

func (s *Strategy) GenerateAndUploadSnapshot(ctx context.Context, repo *gitclone.Repository) error {
return s.generateAndUploadSnapshot(ctx, repo)
}
Expand Down
31 changes: 19 additions & 12 deletions internal/strategy/git/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,16 +244,6 @@ func (s *Strategy) handleRequest(w http.ResponseWriter, r *http.Request) {
return
}

// Proxy LFS Batch API requests directly to GitHub. Cachew doesn't cache
// individual LFS objects, but it needs to handle these requests because
// git's url.*.insteadOf rewrites the LFS endpoint URL to point at cachew.
if strings.Contains(pathValue, "/info/lfs/") {
s.metrics.recordRequest(ctx, "lfs-api")
logger.DebugContext(ctx, "Proxying LFS API request to upstream", "uri", pathValue)
s.forwardToUpstream(w, r, host, pathValue)
return
}

service := r.URL.Query().Get("service")
isReceivePack := service == "git-receive-pack" || strings.HasSuffix(pathValue, "/git-receive-pack")

Expand All @@ -264,8 +254,18 @@ func (s *Strategy) handleRequest(w http.ResponseWriter, r *http.Request) {
return
}

s.metrics.recordRequest(ctx, "upload-pack")
s.handleGitRequest(w, r, host, pathValue)
// Only handle known git smart protocol operations locally (info/refs
// discovery and git-upload-pack negotiation). Everything else (LFS API
// requests, unknown paths, etc.) is forwarded to upstream so it isn't
// mistakenly treated as a clone/fetch.
if isGitRequest(pathValue) {
s.handleGitRequest(w, r, host, pathValue)
return
}

s.metrics.recordRequest(ctx, "forward")
logger.DebugContext(ctx, "Forwarding non-git request to upstream", "uri", pathValue)
s.forwardToUpstream(w, r, host, pathValue)
}

func (s *Strategy) handleGitRequest(w http.ResponseWriter, r *http.Request, host, pathValue string) {
Expand Down Expand Up @@ -457,6 +457,13 @@ func (s *Strategy) serveWithSpool(w http.ResponseWriter, r *http.Request, host,
return nil
}

// isGitRequest reports whether pathValue matches a git smart HTTP protocol
// endpoint (info/refs discovery or git-upload-pack negotiation).
func isGitRequest(pathValue string) bool {
return strings.HasSuffix(pathValue, "/info/refs") ||
strings.HasSuffix(pathValue, "/git-upload-pack")
}

func ExtractRepoPath(pathValue string) string {
repoPath := pathValue
repoPath = strings.TrimSuffix(repoPath, "/info/refs")
Expand Down
25 changes: 25 additions & 0 deletions internal/strategy/git/git_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,31 @@ func TestNew(t *testing.T) {
}
}

func TestIsGitRequest(t *testing.T) {
tests := []struct {
name string
input string
expected bool
}{
{name: "InfoRefs", input: "org/repo/info/refs", expected: true},
{name: "GitUploadPack", input: "org/repo/git-upload-pack", expected: true},
{name: "GitReceivePack", input: "org/repo/git-receive-pack", expected: false},
{name: "LFSBatchAPI", input: "org/repo.git/info/lfs/objects/batch", expected: false},
{name: "LFSObjectDownload", input: "org/repo.git/info/lfs/objects/abc123", expected: false},
{name: "PlainRepoPath", input: "org/repo", expected: false},
{name: "RandomPath", input: "org/repo/some/random/path", expected: false},
{name: "InfoRefsWithGitSuffix", input: "org/repo.git/info/refs", expected: true},
{name: "UploadPackWithGitSuffix", input: "org/repo.git/git-upload-pack", expected: true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := git.IsGitRequest(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}

func TestExtractRepoPath(t *testing.T) {
tests := []struct {
name string
Expand Down