all repos — elgit @ 975625f9d4d26eb56f4428f2921b9e1090a49621

fork of legit: web frontend for git, written in go

switch to atreugo

Alan Pearce
commit

975625f9d4d26eb56f4428f2921b9e1090a49621

parent

8b1540f0bd4d2e042b28506abcb3de9189399b1b

M config/config.goconfig/config.go
@@ -22,7 +22,7 @@
type Server struct { Name string `yaml:"name,omitempty"` Host string `yaml:"host"` - Port int `yaml:"port"` + Port int64 `yaml:"port"` } type Config struct {
M git/service/service.gogit/service/service.go
@@ -3,11 +3,9 @@
import ( "bytes" "context" - "errors" "fmt" "io" "log" - "net/http" "os/exec" "strings" "syscall"
@@ -21,7 +19,7 @@
type Command struct { Dir string Stdin io.Reader - Stdout http.ResponseWriter + Stdout io.Writer } func (c *Command) InfoRefs(ctx context.Context) error {
@@ -93,23 +91,11 @@ cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
stdoutPipe, _ := cmd.StdoutPipe() cmd.Stderr = cmd.Stdout - defer func() { - err2 := stdoutPipe.Close() - if err == nil { - err = errors.Join(err, err2) - } - }() stdinPipe, err := cmd.StdinPipe() if err != nil { return err } - defer func() { - err2 := stdinPipe.Close() - if err == nil { - err = errors.Join(err, err2) - } - }() if err := cmd.Start(); err != nil { log.Printf("git: failed to start git-upload-pack: %s", err)
@@ -123,11 +109,12 @@
return err } - if _, err := io.Copy(newWriteFlusher(c.Stdout), stdoutPipe); err != nil { + if _, err := io.Copy(c.Stdout, stdoutPipe); err != nil { log.Printf("git: failed to copy stdout: %s", err) return err } + if err := cmd.Wait(); err != nil { log.Printf("git: failed to wait for git-upload-pack: %s", err)
D git/service/write_flusher.go
@@ -1,26 +0,0 @@
-package service - -import ( - "io" - "net/http" -) - -func newWriteFlusher(w http.ResponseWriter) io.Writer { - return writeFlusher{w.(interface { - io.Writer - http.Flusher - })} -} - -type writeFlusher struct { - wf interface { - io.Writer - http.Flusher - } -} - -func (w writeFlusher) Write(p []byte) (int, error) { - defer w.wf.Flush() - - return w.wf.Write(p) -}
M go.modgo.mod
@@ -12,6 +12,8 @@ github.com/fsnotify/fsnotify v1.9.0
github.com/go-git/go-git/v5 v5.16.3 github.com/microcosm-cc/bluemonday v1.0.27 github.com/russross/blackfriday/v2 v2.1.0 + github.com/savsgio/atreugo/v11 v11.13.2 + github.com/valyala/fasthttp v1.68.0 golang.org/x/sys v0.38.0 gopkg.in/yaml.v3 v3.0.1 )
@@ -20,19 +22,24 @@ require (
dario.cat/mergo v1.0.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.3.0 // indirect + github.com/andybalholm/brotli v1.2.0 // indirect github.com/aymerick/douceur v0.2.0 // indirect github.com/cloudflare/circl v1.6.1 // indirect github.com/emirpasic/gods v1.18.1 // indirect + github.com/fasthttp/router v1.5.4 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/kevinburke/ssh_config v1.4.0 // indirect + github.com/klauspost/compress v1.18.1 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/pjbgf/sha1cd v0.5.0 // indirect + github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.2 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect golang.org/x/crypto v0.44.0 // indirect golang.org/x/net v0.47.0 // indirect
M go.sumgo.sum
@@ -1,5 +1,3 @@
-alin.ovh/gomponents v1.6.0 h1:ymFYykOwMSG63dQ8bQZ4U0611zkDEzVNMOpKQSwwFjE= -alin.ovh/gomponents v1.6.0/go.mod h1:RQtPwKzGB0Nt1JTBDSqRzH2w/mg/6PkuhnNvQMV0/AQ= alin.ovh/gomponents v1.8.0 h1:KE5y9yRRe/H8se4BVqcc86gNSHqVs/eyIqWDf79m9nc= alin.ovh/gomponents v1.8.0/go.mod h1:RQtPwKzGB0Nt1JTBDSqRzH2w/mg/6PkuhnNvQMV0/AQ= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
@@ -9,18 +7,20 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw= github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= +github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= +github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/atreugo/mock v0.0.0-20200601091009-13c275b330b0 h1:IVqe9WnancrkICl5HqEfGjrnkQ4+VsU5fodcuFVoG/A= +github.com/atreugo/mock v0.0.0-20200601091009-13c275b330b0/go.mod h1:HTHAc8RoZXMVTr6wZQN7Jjm3mYMnbfkqqKdnQgSoe9o= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/bluekeyes/go-gitdiff v0.8.1 h1:lL1GofKMywO17c0lgQmJYcKek5+s8X6tXVNOLxy4smI= github.com/bluekeyes/go-gitdiff v0.8.1/go.mod h1:WWAk1Mc6EgWarCrPFO+xeYlujPu98VuLW3Tu+B/85AE= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= -github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -34,6 +34,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/fasthttp/router v1.5.4 h1:oxdThbBwQgsDIYZ3wR1IavsNl6ZS9WdjKukeMikOnC8= +github.com/fasthttp/router v1.5.4/go.mod h1:3/hysWq6cky7dTfzaaEPZGdptwjwx0qzTgFCKEWRjgc= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@@ -44,8 +46,6 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM= -github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8= github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
@@ -56,10 +56,10 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= -github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ= github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= +github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= +github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -73,8 +73,6 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA= github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/pjbgf/sha1cd v0.4.0 h1:NXzbL1RvjTUi6kgYZCX3fPwwl27Q1LJndxtUDVfJGRY= -github.com/pjbgf/sha1cd v0.4.0/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0= github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -85,31 +83,34 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/savsgio/atreugo/v11 v11.13.2 h1:F1XHN2slrHKokg3ESLS6O2+hvlKqPJcBRuhJBEFJbn8= +github.com/savsgio/atreugo/v11 v11.13.2/go.mod h1:PloaYuHYZpXc7vkxXbMmqqorLlGZVAI4KFzVrynx5qo= +github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38 h1:D0vL7YNisV2yqE55+q0lFuGse6U8lxlg7fYTctlT5Gc= +github.com/savsgio/gotils v0.0.0-20240704082632-aef3928b8a38/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= -github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg= github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasthttp v1.68.0 h1:v12Nx16iepr8r9ySOwqI+5RBJ/DqTxhOy1HrHoDFnok= +github.com/valyala/fasthttp v1.68.0/go.mod h1:5EXiRfYQAoiO/khu4oU9VISC/eVY6JqmSpPJoHCKsz4= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -118,18 +119,14 @@ golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
M main.gomain.go
@@ -3,11 +3,8 @@
import ( "embed" "flag" + "io/fs" "log" - "net" - "net/http" - "strconv" - "time" "alin.ovh/elgit/config" "alin.ovh/elgit/routes"
@@ -32,13 +29,12 @@ if err := UnveilPaths([]string{c.Repo.Root}, "r"); err != nil {
log.Fatalf("unveil: %s", err) } - mux := routes.Handlers(c, staticFiles) - addr := net.JoinHostPort(c.Server.Host, strconv.FormatInt(int64(c.Server.Port), 10)) - log.Println("starting server on", addr) - srv := http.Server{ - Addr: addr, - Handler: mux, - ReadHeaderTimeout: time.Second * 5, + staticFiles, err := fs.Sub(staticFiles, "static") + if err != nil { + log.Fatal(err) } + + srv := routes.Handlers(c, staticFiles) + log.Fatal(srv.ListenAndServe()) }
M nix/gomod2nix.tomlnix/gomod2nix.toml
@@ -13,6 +13,9 @@ hash = "sha256-tVNWDUMILZbJvarcl/E7tpSnkn7urqgSHa2Eaka5vSU="
[mod."github.com/ProtonMail/go-crypto"] version = "v1.3.0" hash = "sha256-TUG+C4MyeWglOmiwiW2/NUVurFHXLgEPRd3X9uQ1NGI=" + [mod."github.com/andybalholm/brotli"] + version = "v1.2.0" + hash = "sha256-EwPR3o6o8dUzsAeHki26rYm/s1uDuEcpV3ljmaHiNSg=" [mod."github.com/aymerick/douceur"] version = "v0.2.0" hash = "sha256-NiBX8EfOvLXNiK3pJaZX4N73YgfzdrzRXdiBFe3X3sE="
@@ -34,6 +37,9 @@ hash = "sha256-yuvxYYngpfVkUg9yAmG99IUVmADTQA0tMbBXe0Fq0Mc="
[mod."github.com/emirpasic/gods"] version = "v1.18.1" hash = "sha256-hGDKddjLj+5dn2woHtXKUdd49/3xdsqnhx7VEdCu1m4=" + [mod."github.com/fasthttp/router"] + version = "v1.5.4" + hash = "sha256-b3+ED1/d70MoyNbKMQBYpmE+fTy+v+rejtLMJYEW7Os=" [mod."github.com/fsnotify/fsnotify"] version = "v1.9.0" hash = "sha256-WtpE1N6dpHwEvIub7Xp/CrWm0fd6PX7MKA4PV44rp2g="
@@ -58,6 +64,9 @@ hash = "sha256-VANNCWNNpARH/ILQV9sCQsBWgyL2iFT+4AHZREpxIWE="
[mod."github.com/kevinburke/ssh_config"] version = "v1.4.0" hash = "sha256-UclxB7Ll1FZCgU2SrGkiGdr4CoSRJ127MNnZtxKTsvg=" + [mod."github.com/klauspost/compress"] + version = "v1.18.1" + hash = "sha256-nkL7v2/C66brSfYRPN8FXLtUXN2I7VZ+Obvyc6be+GA=" [mod."github.com/klauspost/cpuid/v2"] version = "v2.3.0" hash = "sha256-50JhbQyT67BK38HIdJihPtjV7orYp96HknI2VP7A9Yc="
@@ -70,12 +79,24 @@ hash = "sha256-11XBkhdciQGsQ7jEMZ6PgphRKjruTSc7ZxfOwDuPCr8="
[mod."github.com/russross/blackfriday/v2"] version = "v2.1.0" hash = "sha256-R+84l1si8az5yDqd5CYcFrTyNZ1eSYlpXKq6nFt4OTQ=" + [mod."github.com/savsgio/atreugo/v11"] + version = "v11.13.2" + hash = "sha256-XnbKbpDx8LT4Nn8xp8Ty1bxD9P26CGlEecaNGlQz/rw=" + [mod."github.com/savsgio/gotils"] + version = "v0.0.0-20240704082632-aef3928b8a38" + hash = "sha256-lJrQB5moZ3buPX3WvX+zfCf59Rf8T4D5CbELzwnWfWg=" [mod."github.com/sergi/go-diff"] version = "v1.4.0" hash = "sha256-rs9NKpv/qcQEMRg7CmxGdP4HGuFdBxlpWf9LbA9wS4k=" [mod."github.com/skeema/knownhosts"] version = "v1.3.2" hash = "sha256-fEGeX/V9fC3MVjY34yuL8vn00brqIoTXLAX0x4ecBzg=" + [mod."github.com/valyala/bytebufferpool"] + version = "v1.0.0" + hash = "sha256-I9FPZ3kCNRB+o0dpMwBnwZ35Fj9+ThvITn8a3Jr8mAY=" + [mod."github.com/valyala/fasthttp"] + version = "v1.68.0" + hash = "sha256-GIT3Bg2TIZ6v98tjxWsWPC6SVFWHsv6C4llEfATQMx8=" [mod."github.com/xanzy/ssh-agent"] version = "v0.3.3" hash = "sha256-l3pGB6IdzcPA/HLk93sSN6NM2pKPy+bVOoacR5RC2+c="
M routes/git.goroutes/git.go
@@ -1,87 +1,83 @@
package routes import ( + "bytes" "compress/gzip" "log" "net/http" "path" "alin.ovh/elgit/git/service" + "github.com/savsgio/atreugo/v11" + "github.com/valyala/fasthttp" ) -func (d *deps) InfoRefs(w http.ResponseWriter, r *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) +func (d *deps) InfoRefs(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - repo, err := d.GetCleanPath(name) + repo, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound + } + + svc := rc.QueryArgs().Peek("service") + if string(svc) == "git-receive-pack" { + return rc.TextResponse("no pushing allowed!", fasthttp.StatusBadRequest) } - w.Header().Set("content-type", "application/x-git-upload-pack-advertisement") - w.Header().Set("cache-control", "no-cache") - w.WriteHeader(http.StatusOK) + rc.SetStatusCode(http.StatusOK) + rc.Response.Header.Set("content-type", "application/x-git-upload-pack-advertisement") + rc.Response.Header.Set("cache-control", "no-cache") cmd := service.Command{ Dir: repo, - Stdout: w, + Stdout: rc, } - if err := cmd.InfoRefs(r.Context()); err != nil { - http.Error(w, err.Error(), 500) - log.Printf("git: failed to execute git-upload-pack (info/refs) %s", err) - - return - } + return cmd.InfoRefs(rc) } -func (d *deps) UploadPack(w http.ResponseWriter, r *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) +func (d *deps) UploadPack(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - repo, err := d.GetCleanPath(name) + repo, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return - } - - w.Header().Set("content-type", "application/x-git-upload-pack-result") - w.Header().Set("Connection", "Keep-Alive") - w.Header().Set("Transfer-Encoding", "chunked") - w.Header().Set("cache-control", "no-cache") - w.WriteHeader(http.StatusOK) - - cmd := service.Command{ - Dir: repo, - Stdout: w, + return ErrNotFound } - var reader = r.Body + rc.SetStatusCode(http.StatusOK) + rc.SetContentType("application/x-git-upload-pack-result") + rc.Response.Header.Set("Connection", "Keep-Alive") + rc.Response.Header.Set("Transfer-Encoding", "chunked") + rc.Response.Header.Set("Cache-Control", "no-cache") - if r.Header.Get("Content-Encoding") == "gzip" { - reader, err = gzip.NewReader(r.Body) + var reader = rc.Request.BodyStream() + if bytes.Contains(rc.Request.Header.ContentEncoding(), []byte("gzip")) { + reader, err = gzip.NewReader(reader) if err != nil { - http.Error(w, err.Error(), 500) - log.Printf("git: failed to create gzip reader: %s", err) - - return + return rc.ErrorResponse(err, 500) } defer func() { - err := reader.Close() + err := rc.Request.CloseBodyStream() if err != nil { log.Printf("git: failed to close gzip reader: %s", err) } }() } - cmd.Stdin = reader - if err := cmd.UploadPack(r.Context()); err != nil { - http.Error(w, err.Error(), 500) - log.Printf("git: failed to execute git-upload-pack %s", err) - - return + cmd := service.Command{ + Dir: repo, + Stdout: rc, + Stdin: reader, } + + return cmd.UploadPack(rc) }
M routes/handler.goroutes/handler.go
@@ -1,79 +1,24 @@
package routes import ( + "fmt" "io/fs" "log" - "net/http" - "path" + "net" "path/filepath" + "strconv" + "time" - "github.com/dimfeld/httptreemux/v5" "github.com/fsnotify/fsnotify" + "github.com/savsgio/atreugo/v11" "alin.ovh/elgit/config" - "alin.ovh/elgit/templates" ) -var ErrNotAllowed = templates.Error{ - Code: http.StatusMethodNotAllowed, - Message: "that's not possible.", -} - -// Checks for gitprotocol-http(5) specific smells; if found, passes -// the request on to the git http service, else render the web frontend. -func (d *deps) Multiplex(w http.ResponseWriter, r *http.Request, params map[string]string) { - rest := params["rest"] - switch params["name"] { - case "info", "git-upload-pack": - rest = path.Join(params["name"], params["rest"]) - params["name"] = params["category"] - params["category"] = "" - } +var ErrMethodNotAllowed = fmt.Errorf("that's not possible") +var ErrNotFound = fmt.Errorf("nothing like that here") - if d.isNotAllowed(path.Join(params["category"], params["name"])) { - d.Write404(w) - - return - } - - if r.URL.RawQuery == "service=git-receive-pack" { - w.WriteHeader(http.StatusBadRequest) - _, err := w.Write([]byte("no pushing allowed!")) - if err != nil { - log.Println(err) - d.Write500(w) - - return - } - - return - } - - switch r.Method { - case http.MethodGet: - switch { - case rest == "info/refs" && - r.URL.RawQuery == "service=git-upload-pack": - d.InfoRefs(w, r, params) - default: - d.RepoIndex(w, r, params) - } - - case http.MethodPost: - switch rest { - case "git-upload-pack": - d.UploadPack(w, r, params) - default: - d.WriteError(w, ErrNotAllowed) - } - default: - d.WriteError(w, ErrNotAllowed) - } -} - -func Handlers(c *config.Config, staticFiles fs.FS) *httptreemux.TreeMux { - mux := httptreemux.New() - +func Handlers(c *config.Config, staticFiles fs.FS) *atreugo.Atreugo { plist := filepath.Join(c.Repo.Root, "projects.list") projects, err := ReadProjectsList(plist) if err != nil {
@@ -84,6 +29,27 @@ d := deps{
c, projects, } + + addr := net.JoinHostPort(c.Server.Host, strconv.FormatInt(c.Server.Port, 10)) + srv := atreugo.New(atreugo.Config{ + Addr: addr, + Name: c.Server.Name, + Network: "tcp6", + Prefork: false, + Reuseport: false, + GracefulShutdown: true, + Compress: true, + Logger: log.Default(), + NotFoundView: d.NotFound, + MethodNotAllowedView: nil, + ErrorView: d.Error, + // PanicView: d.Panic, + ReadTimeout: 5 * time.Second, + DisablePreParseMultipartForm: true, + KeepHijackedConns: false, + CloseOnShutdown: true, + StreamRequestBody: true, + }) watcher, err := fsnotify.NewWatcher() if err != nil {
@@ -117,32 +83,25 @@ }
} }() - mux.NotFoundHandler = func(w http.ResponseWriter, _ *http.Request) { - d.Write404(w) - } - - mux.GET("/", d.Index) - mux.NewGroup("/static/"). - UsingContext(). - Handler("GET", "/*", http.FileServerFS(staticFiles)) + d.mountRepoPaths(srv.NewGroupPath("/{name}")) + d.mountRepoPaths(srv.NewGroupPath("/{category}/{name}")) - d.mountRepoPaths(mux.NewGroup("/:name")) - d.mountRepoPaths(mux.NewGroup("/:category/:name")) + srv.StaticFS("/static/", staticFiles) + srv.GET("/", d.Index) - return mux + return srv } -func (d deps) mountRepoPaths(group *httptreemux.Group) { - group.GET("/tree/:ref/*rest", d.RepoTree) - group.GET("/blob/:ref/*rest", d.FileContent) - group.GET("/tree/:ref/", d.RepoTree) - group.GET("/log/:ref/", d.Log) - group.GET("/archive/:file", d.Archive) - group.GET("/commit/:ref", d.Diff) - group.GET("/commit/:ref/*file", d.FileDiff) - group.GET("/refs/", d.Refs) - group.GET("", d.Multiplex) - group.POST("", d.Multiplex) - group.GET("/*rest", d.Multiplex) - group.POST("/*rest", d.Multiplex) +func (d deps) mountRepoPaths(router *atreugo.Router) { + router.GET("/tree/{ref}/{rest:*}", d.RepoTree) + router.GET("/blob/{ref}/{rest:*}", d.FileContent) + router.GET("/tree/{ref}/", d.RepoTree) + router.GET("/log/{ref}/", d.Log) + router.GET("/archive/{file}", d.Archive) + router.GET("/commit/{ref}", d.Diff) + router.GET("/commit/{ref}/{file:*}", d.FileDiff) + router.GET("/refs/", d.Refs) + router.GET("/info/refs", d.InfoRefs) + router.POST("/git-upload-pack", d.UploadPack) + router.GET("/", d.RepoIndex) }
M routes/routes.goroutes/routes.go
@@ -4,7 +4,6 @@ import (
"compress/gzip" "fmt" "log" - "net/http" "path" "path/filepath" "strconv"
@@ -15,6 +14,8 @@ "alin.ovh/elgit/git"
"alin.ovh/elgit/templates" "github.com/microcosm-cc/bluemonday" "github.com/russross/blackfriday/v2" + "github.com/savsgio/atreugo/v11" + "github.com/valyala/fasthttp" ) type deps struct {
@@ -22,49 +23,41 @@ c *config.Config
projects []string } -func (d *deps) Index(w http.ResponseWriter, _ *http.Request, _ map[string]string) { +func (d *deps) Index(rc *atreugo.RequestCtx) error { repos := d.getAllRepos() pageData := templates.PageData{ Meta: d.c.Meta, } - if err := templates.IndexPage(pageData, repos).Render(w); err != nil { - log.Println(err) + rc.SetContentType("text/html; charset=utf-8") - return - } + return templates.IndexPage(pageData, repos).Render(rc) } -func (d *deps) RepoIndex(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) +func (d *deps) RepoIndex(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - if d.isNotAllowed(name) { - d.Write404(w) - - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } gr, err := git.Open(path, "") if err != nil { - d.Write404(w) - - return + return ErrNotFound } commits, err := gr.Commits() if err != nil { - d.Write500(w) - log.Println(err) - - return + return err } var readmeContent string
@@ -91,10 +84,7 @@ }
mainBranch, err := gr.FindMainBranch(d.c.Repo.MainBranch) if err != nil { - d.Write500(w) - log.Println(err) - - return + return err } if len(commits) >= 3 {
@@ -103,151 +93,144 @@ }
pageData := templates.PageData{ Meta: d.c.Meta, - Name: name, - DisplayName: getDisplayName(name), + Name: repoName, + DisplayName: getDisplayName(repoName), Ref: mainBranch, Description: getDescription(path), Servername: d.c.Server.Name, Gomod: isGoModule(gr), } - if err := templates.RepoPage(pageData, commits, readmeContent).Render(w); err != nil { - log.Println(err) - } + rc.SetContentType("text/html; charset=utf-8") + + return templates.RepoPage(pageData, commits, readmeContent).Render(rc) } -func (d *deps) RepoTree(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) +func (d *deps) RepoTree(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - treePath := strings.TrimSuffix(params["rest"], "/") - ref := params["ref"] + + rest, _ := rc.UserValue("rest").(string) + treePath := strings.TrimSuffix(rest, "/") + ref := rc.UserValue("ref").(string) - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } gr, err := git.Open(path, ref) if err != nil { - d.Write404(w) - - return + return err } files, err := gr.FileTree(treePath) if err != nil { - d.Write500(w) - log.Println(err, treePath) - - return + return err } data := make(map[string]any) - data["name"] = name - data["displayname"] = getDisplayName(name) + data["name"] = repoName + data["displayname"] = getDisplayName(repoName) data["ref"] = ref data["parent"] = treePath data["desc"] = getDescription(path) data["dotdot"] = filepath.Dir(treePath) - d.listFiles(files, data, w) + rc.SetContentType("text/html; charset=utf-8") + + return d.listFiles(files, data, rc) } -func (d *deps) FileContent(w http.ResponseWriter, r *http.Request, params map[string]string) { +func (d *deps) FileContent(rc *atreugo.RequestCtx) error { var raw bool - if rawParam, err := strconv.ParseBool(r.URL.Query().Get("raw")); err == nil { + if rawParam, err := strconv.ParseBool(string(rc.Request.URI().QueryArgs().Peek("raw"))); err == nil { raw = rawParam } - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - treePath := params["rest"] - ref := params["ref"] + treePath := rc.UserValue("rest").(string) + ref := rc.UserValue("ref").(string) - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } gr, err := git.Open(path, ref) if err != nil { - d.Write404(w) - - return + return ErrNotFound } contents, err := gr.FileContent(treePath) if err != nil { - d.Write500(w) - - return + return err } data := make(map[string]any) - data["name"] = name - data["displayname"] = getDisplayName(name) + data["name"] = repoName + data["displayname"] = getDisplayName(repoName) data["ref"] = ref data["desc"] = getDescription(path) data["path"] = treePath if raw { - d.showRaw(contents, w) - } else { - d.showFile(contents, data, w) + return rc.TextResponse(contents) } + + rc.SetContentType("text/html; charset=utf-8") + + return d.showFile(contents, data, rc) } -func (d *deps) Archive(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) +func (d *deps) Archive(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - file := params["file"] + file := rc.UserValue("file").(string) if !strings.HasSuffix(file, ".tar.gz") { - d.Write404(w) - - return + return ErrNotFound } ref := strings.TrimSuffix(file, ".tar.gz") // This allows the browser to use a proper name for the file when downloading - filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) - setContentDisposition(w, filename) - setGZipMIME(w) + filename := fmt.Sprintf("%s-%s.tar.gz", repoName, ref) + setContentDisposition(rc, filename) + setGZipMIME(rc) - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } gr, err := git.Open(path, ref) if err != nil { - d.Write404(w) - - return + return ErrNotFound } - gw := gzip.NewWriter(w) + gw := gzip.NewWriter(rc) defer func() { err := gw.Close() if err != nil {
@@ -255,14 +238,14 @@ log.Printf("failed to close gzip writer: %s", err)
} }() - prefix := fmt.Sprintf("%s-%s", name, ref) + prefix := fmt.Sprintf("%s-%s", repoName, ref) err = gr.WriteTar(gw, prefix) if err != nil { // once we start writing to the body we can't report error anymore // so we are only left with printing the error. log.Println(err) - return + return nil } err = gw.Flush()
@@ -270,179 +253,154 @@ if err != nil {
// once we start writing to the body we can't report error anymore // so we are only left with printing the error. log.Println(err) + } - return - } + return nil } -func (d *deps) Log(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) +func (d *deps) Log(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - ref := params["ref"] + ref := rc.UserValue("ref").(string) - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } gr, err := git.Open(path, ref) if err != nil { - d.Write404(w) - - return + return ErrNotFound } commits, err := gr.Commits() if err != nil { - d.Write500(w) - log.Println(err) - - return + return err } pageData := templates.PageData{ Meta: d.c.Meta, - Name: name, - DisplayName: getDisplayName(name), + Name: repoName, + DisplayName: getDisplayName(repoName), Ref: ref, Description: getDescription(path), Log: true, } - if err := templates.LogPage(pageData, commits).Render(w); err != nil { - log.Println(err) + rc.SetContentType("text/html; charset=utf-8") - return - } + return templates.LogPage(pageData, commits).Render(rc) } -func (d *deps) Diff(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) +func (d *deps) Diff(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - ref := params["ref"] + ref := rc.UserValue("ref").(string) - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } gr, err := git.Open(path, ref) if err != nil { - d.Write404(w) - - return + return ErrNotFound } diff, err := gr.Diff() if err != nil { - d.Write500(w) - log.Println(err) - - return + return err } pageData := templates.PageData{ Meta: d.c.Meta, - Name: name, + Name: repoName, Stat: diff.Stat, Diff: diff.Diff, - DisplayName: getDisplayName(name), + DisplayName: getDisplayName(repoName), Ref: ref, Description: getDescription(path), } - if err := templates.CommitPage(pageData, diff).Render(w); err != nil { - log.Println(err) + rc.SetContentType("text/html; charset=utf-8") - return - } + return templates.CommitPage(pageData, diff).Render(rc) } // FileDiff shows the changes to a specific file in a commit -func (d *deps) FileDiff(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) +func (d *deps) FileDiff(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - ref := params["ref"] - filePath := strings.TrimSuffix(params["file"], "/") + ref := rc.UserValue("ref").(string) + filePath := strings.TrimSuffix(rc.UserValue("file").(string), "/") - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { log.Printf("getcleanpath error: %v", err) - d.Write404(w) - return + return ErrNotFound } g, err := git.Open(path, ref) if err != nil { - log.Printf("git open error: %v", err) - d.Write404(w) - - return + return err } diff, err := g.DiffFile(filePath) if err != nil { - log.Printf("git diff error: %v", err) - d.Write500(w) - - return + return err } pageData := templates.PageData{ Meta: d.c.Meta, - DisplayName: getDisplayName(name), - Name: name, + DisplayName: getDisplayName(repoName), + Name: repoName, Ref: ref, Description: getDescription(path), Path: filePath, Diff: diff.Diff, } - if err := templates.CommitPage(pageData, diff).Render(w); err != nil { - log.Printf("diff template: %s", err) - d.Write500(w) - } + rc.SetContentType("text/html; charset=utf-8") + + return templates.CommitPage(pageData, diff).Render(rc) } -func (d *deps) Refs(w http.ResponseWriter, _ *http.Request, params map[string]string) { - name := path.Join(params["category"], params["name"]) - if d.isNotAllowed(name) { - d.Write404(w) +func (d *deps) Refs(rc *atreugo.RequestCtx) error { + category, _ := rc.UserValue("category").(string) + name, _ := rc.UserValue("name").(string) + repoName := path.Join(category, name) - return + if d.isNotAllowed(repoName) { + return ErrNotFound } - path, err := d.GetCleanPath(name) + path, err := d.GetCleanPath(repoName) if err != nil { - log.Printf("getcleanpath error: %v", err) - d.Write404(w) - - return + return err } gr, err := git.Open(path, "") if err != nil { - d.Write404(w) - - return + return ErrNotFound } tags, err := gr.Tags()
@@ -453,22 +411,54 @@ }
branches, err := gr.Branches() if err != nil { - log.Println(err) - d.Write500(w) - - return + return err } pageData := templates.PageData{ Meta: d.c.Meta, - Name: name, - DisplayName: getDisplayName(name), + Name: repoName, + DisplayName: getDisplayName(repoName), Description: getDescription(path), } - if err := templates.RefsPage(pageData, branches, tags).Render(w); err != nil { - log.Println(err) + rc.SetContentType("text/html; charset=utf-8") + + return templates.RefsPage(pageData, branches, tags).Render(rc) +} + +func (d *deps) NotFound(rc *atreugo.RequestCtx) error { + log.Printf("Not found: %s", rc.Request.URI().RequestURI()) + + return ErrNotFound +} + +func (d *deps) Error(rc *atreugo.RequestCtx, cause error, statusCode int) { + log.Printf("Error: %v", cause) + + var err error + if rc.Request.Header.HasAcceptEncoding("text/html") { + rc.SetContentType("text/html; charset=utf-8") + + err = templates.ErrorPage(templates.PageData{ + Error: &templates.Error{ + Code: statusCode, + Message: cause.Error(), + }, + }).Render(rc) + } else { + err = rc.TextResponse(cause.Error(), statusCode) + } - return + if err != nil { + log.Printf("error response error: %v", err) + } +} + +func (d *deps) Panic(rc *atreugo.RequestCtx, data any) { + switch data := data.(type) { + case string, []byte: + d.Error(rc, fmt.Errorf("panic: %s", data), fasthttp.StatusInternalServerError) + case error: + d.Error(rc, fmt.Errorf("panic: %w", data), fasthttp.StatusInternalServerError) } }
M routes/template.goroutes/template.go
@@ -5,7 +5,6 @@ "bytes"
"fmt" "io" "log" - "net/http" "path/filepath" "strings"
@@ -13,37 +12,31 @@ "alin.ovh/elgit/git"
"alin.ovh/elgit/templates" "github.com/microcosm-cc/bluemonday" "github.com/russross/blackfriday/v2" + "github.com/savsgio/atreugo/v11" ) -func (d *deps) Write404(w http.ResponseWriter) { - d.WriteError(w, templates.Error{ - Code: http.StatusNotFound, - Message: "nothing like that here.", - }) -} - -func (d *deps) Write500(w http.ResponseWriter) { - d.WriteError(w, templates.Error{ - Code: http.StatusInternalServerError, - Message: "something broke!", - }) -} - -func (d *deps) WriteError(w http.ResponseWriter, err templates.Error) { - w.WriteHeader(err.Code) +func (d *deps) WriteError(rc *atreugo.RequestCtx, err error, i int) error { + rc.SetStatusCode(i) data := templates.PageData{ - Meta: d.c.Meta, - Error: &err, + Meta: d.c.Meta, + Error: &templates.Error{ + Code: i, + Message: err.Error(), + }, } - if err := templates.ErrorPage(data).Render(w); err != nil { + if err := templates.ErrorPage(data).Render(rc); err != nil { log.Printf("error template: %s", err) + + return err } + + return nil } -func (d *deps) listFiles(files []git.NiceTree, data map[string]any, w http.ResponseWriter) { +func (d *deps) listFiles(files []git.NiceTree, data map[string]any, rc *atreugo.RequestCtx) error { pageData := templates.PageData{ Meta: d.c.Meta, - DisplayName: getDisplayName(data["name"].(string)), + DisplayName: data["displayname"].(string), Name: data["name"].(string), Ref: data["ref"].(string), Description: data["desc"].(string),
@@ -64,11 +57,7 @@ if dotdotPath, ok := data["dotdot"]; ok && dotdotPath != nil {
dotdot = dotdotPath.(string) } - if err := templates.TreePage(pageData, files, readme, dotdot).Render(w); err != nil { - log.Println(err) - - return - } + return templates.TreePage(pageData, files, readme, dotdot).Render(rc) } func countLines(r io.Reader) (int, error) {
@@ -98,7 +87,7 @@ }
} } -func (d *deps) showFile(content string, data map[string]any, w http.ResponseWriter) { +func (d *deps) showFile(content string, data map[string]any, rc *atreugo.RequestCtx) error { var renderedContent string if len(content) > 0 { switch filepath.Ext(data["path"].(string)) {
@@ -140,19 +129,5 @@ Description: data["desc"].(string),
Path: data["path"].(string), } - if err := templates.FilePage(pageData).Render(w); err != nil { - log.Println(err) - - return - } -} - -func (d *deps) showRaw(content string, w http.ResponseWriter) { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "text/plain") - _, err := w.Write([]byte(content)) - if err != nil { - log.Println(err) - d.Write500(w) - } + return templates.FilePage(pageData).Render(rc) }
M routes/util.goroutes/util.go
@@ -3,7 +3,6 @@
import ( "bufio" "log" - "net/http" "os" "path/filepath" "slices"
@@ -13,6 +12,7 @@ "alin.ovh/elgit/data"
"alin.ovh/elgit/git" securejoin "github.com/cyphar/filepath-securejoin" "github.com/dimfeld/httptreemux/v5" + "github.com/savsgio/atreugo/v11" ) func isGoModule(gr *git.Repo) bool {
@@ -114,15 +114,11 @@
return entries } -func setContentDisposition(w http.ResponseWriter, name string) { +func setContentDisposition(rc *atreugo.RequestCtx, name string) { h := "inline; filename=\"" + name + "\"" - w.Header().Add("Content-Disposition", h) -} - -func setGZipMIME(w http.ResponseWriter) { - setMIME(w, "application/gzip") + rc.Response.Header.Set("Content-Disposition", h) } -func setMIME(w http.ResponseWriter, mime string) { - w.Header().Add("Content-Type", mime) +func setGZipMIME(rc *atreugo.RequestCtx) { + rc.SetContentType("application/gzip") }
M templates/page.gotemplates/page.go
@@ -107,9 +107,9 @@ return fmt.Sprintf("%s — %s: refs", metaTitle, data.Name)
case data.Commits != nil: if data.Log { return fmt.Sprintf("%s — %s: log", metaTitle, data.Name) - } else { - return fmt.Sprintf("%s — %s", metaTitle, data.Name) } + + return fmt.Sprintf("%s — %s", metaTitle, data.Name) case data.Name != "": return fmt.Sprintf("%s — %s", metaTitle, data.Name) default: