vole
Clone
Read only, no credentials:
https://rodent.software/vole.git
Write over SSH with a registered key:
git clone ssh://git@rodent.software:2222/vole.git
A free and open-source forge that lives in your Git repo.
Bound and stream the anonymous read path
4581690d1ae9d87c272428d101a0fbe3538d7f44 · Keenan Nemetz · 13d ago · parent e05f1c48
Bound and stream the anonymous read path Every anonymous read path read a whole git object before deciding what to do with it: the code browser truncated after allocating, the overview rendered the README it had just read in full, and the site pool allocated the entire object for every request. `max_blob_bytes` allows a 100 MiB blob, so one pushed file made every concurrent request for it a 100 MiB allocation, with no account needed to ask for it. Read the header first instead. `core/git` gains `BlobSize`, `BlobSizes` (one `cat-file --batch-check` for a whole tree) and `BlobStream`, which parses the batch header before touching the payload and stops the process when a caller wants only a prefix. Then use it: `/raw` streams with the object's own `Content-Length`, the inline viewer reads a one-MiB prefix and reports the file's real size, the README is capped and named instead of rendered when it is too large, site blobs are capped at 25 MiB with a 413, and the commit and tree objects read to resolve a site path are capped separately. Metadata gets the same treatment on both sides: 4 MiB per file, 2 MiB per body, 20,000 files and 64 MiB for `issues/` and `requests/` together. The loader asks git for every size in one call and refuses the tree before reading it; the push boundary refuses the same tree, so the author learns why instead of finding that their issue list stopped loading. Counting is now per subtree, so the machine written `runs/` tree no longer eats the issue file budget.
server/web/releases.go +6−3
| @@ -6,6 +6,7 @@ package web | |||
| 6 | 6 | // with the repository, and no stored tarball that can disagree with the tag. | |
| 7 | 7 | ||
| 8 | 8 | import ( | |
| 9 | "bytes" | ||
| 9 | 10 | "fmt" | |
| 10 | 11 | "html/template" | |
| 11 | 12 | "mime" | |
| @@ -261,13 +262,15 @@ func (w *Web) releaseArchive(rw http.ResponseWriter, r *http.Request, format str | |||
| 261 | 262 | // usesLFS reports whether the tree tracks anything through Git LFS. It reads the | |
| 262 | 263 | // root .gitattributes, which is where `git lfs track` writes its patterns; a | |
| 263 | 264 | // pattern declared in a subdirectory is not detected, and the notice is worded so | |
| 264 | // that the omission cannot make it false. | ||
| 265 | // that the omission cannot make it false. Only the head of the file is read: a | ||
| 266 | // pattern lives on one line, and the file's length is not evidence of anything. | ||
| 265 | 267 | func usesLFS(g *git.Repo, commit string) bool { | |
| 266 | data, err := g.BlobAt(commit, ".gitattributes") | ||
| 268 | const maxGitattributesBytes = 64 << 10 | ||
| 269 | data, _, err := g.BlobPrefixAt(commit, ".gitattributes", maxGitattributesBytes) | ||
| 267 | 270 | if err != nil { | |
| 268 | 271 | return false | |
| 269 | 272 | } | |
| 270 | return strings.Contains(string(data), "filter=lfs") | ||
| 273 | return bytes.Contains(data, []byte("filter=lfs")) | ||
| 271 | 274 | } | |
| 272 | 275 | ||
| 273 | 276 | // releaseSafe reduces a repository or tag name to characters that are safe in a | |