From 347717688fbccabbc9de649f4a0be51d18ea65e8 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Thu, 03 Apr 2025 16:10:16 +0800 Subject: [PATCH] HTTP: Use git2d for the repo index --- go.mod | 1 + go.sum | 4 ++++ http_handle_repo_index.go | 147 +++++++++++++++++++++++++---------------------------- diff --git a/go.mod b/go.mod index 2ee258f25001a23f4d67598742b490de4f63663c..ab91bcd9eeb34742aeef4b6d6a2edb0a24e9b483 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ ) require ( dario.cat/mergo v1.0.1 // indirect + git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect diff --git a/go.sum b/go.sum index 593a0f6acb84feb0d0c192169fc379c45bc2ac6a..1aab91c76af3eff22b1c3eb74005cb9aa86c2dbf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,8 @@ dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +git.sr.ht/~sircmpwn/getopt v0.0.0-20191230200459-23622cc906b3/go.mod h1:wMEGFFFNuPos7vHmWXfszqImLppbc0wEhh6JBfJIUgw= +git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9 h1:Ahny8Ud1LjVMMAlt8utUFKhhxJtwBAualvsbc/Sk7cE= +git.sr.ht/~sircmpwn/go-bare v0.0.0-20210406120253-ab86bc2846d9/go.mod h1:BVJwbDfVjCjoFiKrhkei6NdGcZYpkDkdyCdg1ukytRA= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -108,6 +111,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= diff --git a/http_handle_repo_index.go b/http_handle_repo_index.go index 6852fe70d26637382a4e6ca726126201837c6ac9..6534df199f806068f43c916a963d1ddcdecf438a 100644 --- a/http_handle_repo_index.go +++ b/http_handle_repo_index.go @@ -4,109 +4,102 @@ package main import ( + "encoding/hex" + "errors" + "fmt" + "io" + "net" "net/http" "strings" - "time" - "github.com/go-git/go-git/v5" - "github.com/go-git/go-git/v5/plumbing" - "github.com/go-git/go-git/v5/plumbing/object" - "github.com/go-git/go-git/v5/plumbing/storer" + "git.sr.ht/~sircmpwn/go-bare" ) -// httpHandleRepoIndex provides the front page of a repo. -func httpHandleRepoIndex(writer http.ResponseWriter, _ *http.Request, params map[string]any) { - var repo *git.Repository - var repoName string - var groupPath []string - var refHash plumbing.Hash - var refHashSlice []byte - var err error - var commitObj *object.Commit - var tree *object.Tree - var notes []string - var branches []string - var branchesIter storer.ReferenceIter - var commits []commitDisplayOld +type commitDisplay struct { + Hash string + Author string + Email string + Date string + Message string +} - repo, repoName, groupPath = params["repo"].(*git.Repository), params["repo_name"].(string), params["group_path"].([]string) +// httpHandleRepoIndex provides the front page of a repo using git2d. +func httpHandleRepoIndex(w http.ResponseWriter, req *http.Request, params map[string]any) { + repoName := params["repo_name"].(string) + groupPath := params["group_path"].([]string) + _, repoPath, _, _, _, _, _ := getRepoInfo(req.Context(), groupPath, repoName, "") // TODO: Don't use getRepoInfo + + var notes []string if strings.Contains(repoName, "\n") || sliceContainsNewlines(groupPath) { notes = append(notes, "Path contains newlines; HTTP Git access impossible") } - refHash, err = getRefHash(repo, params["ref_type"].(string), params["ref_name"].(string)) + conn, err := net.Dial("unix", config.Git.Socket) if err != nil { - goto no_ref + errorPage500(w, params, "git2d connection failed: "+err.Error()) + return } - refHashSlice = refHash[:] + defer conn.Close() - branchesIter, err = repo.Branches() - if err == nil { - _ = branchesIter.ForEach(func(branch *plumbing.Reference) error { - branches = append(branches, branch.Name().Short()) - return nil - }) + writer := bare.NewWriter(conn) + if err := writer.WriteData([]byte(repoPath)); err != nil { + errorPage500(w, params, "sending repo path failed: "+err.Error()) + return } - params["branches"] = branches - if value, found := indexCommitsDisplayCache.Get(refHashSlice); found { - if value != nil { - commits = value - } else { - goto readme - } - } else { - start := time.Now() - commits, err = getRecentCommitsDisplay(repo, refHash, 5) - if err != nil { - commits = nil - } - cost := time.Since(start).Nanoseconds() - indexCommitsDisplayCache.Set(refHashSlice, commits, cost) - if err != nil { - goto readme - } + reader := bare.NewReader(conn) + status, err := reader.ReadUint() + if err != nil { + errorPage500(w, params, "reading status failed: "+err.Error()) + return + } + if status != 0 { + errorPage500(w, params, fmt.Sprintf("git2d error: %d", status)) + return } - params["commits"] = commits + /* README */ + readmeRaw, err := reader.ReadData() + if err != nil { + readmeRaw = nil + } + readmeFilename, readmeRendered := renderReadme(readmeRaw, "README.md") -readme: - - if value, found := treeReadmeCache.Get(refHashSlice); found { - params["files"] = value.DisplayTree - params["readme_filename"] = value.ReadmeFilename - params["readme"] = value.ReadmeRendered - } else { - start := time.Now() - if commitObj, err = repo.CommitObject(refHash); err != nil { - goto no_ref + /* Commits */ + var commits []commitDisplay + for { + id, err := reader.ReadData() + if err != nil { + if errors.Is(err, io.EOF) { + break + } + errorPage500(w, params, "error reading commit ID: "+err.Error()) + return } - if tree, err = commitObj.Tree(); err != nil { - goto no_ref - } - displayTree := makeDisplayTree(tree) - readmeFilename, readmeRendered := renderReadmeAtTree(tree) - cost := time.Since(start).Nanoseconds() - - params["files"] = displayTree - params["readme_filename"] = readmeFilename - params["readme"] = readmeRendered + title, _ := reader.ReadData() + authorName, _ := reader.ReadData() + authorEmail, _ := reader.ReadData() + authorDate, _ := reader.ReadData() - entry := treeReadmeCacheEntry{ - DisplayTree: displayTree, - ReadmeFilename: readmeFilename, - ReadmeRendered: readmeRendered, - } - treeReadmeCache.Set(refHashSlice, entry, cost) + commits = append(commits, commitDisplay{ + Hash: hex.EncodeToString(id), + Author: string(authorName), + Email: string(authorEmail), + Date: string(authorDate), + Message: string(title), + }) } -no_ref: - + params["commits"] = commits + params["readme_filename"] = readmeFilename + params["readme"] = readmeRendered params["http_clone_url"] = genHTTPRemoteURL(groupPath, repoName) params["ssh_clone_url"] = genSSHRemoteURL(groupPath, repoName) params["notes"] = notes - renderTemplate(writer, "repo_index", params) + renderTemplate(w, "repo_index", params) + + // TODO: Caching } -- 2.48.1