-
Notifications
You must be signed in to change notification settings - Fork 0
/
git_proto.go
129 lines (107 loc) · 3.62 KB
/
git_proto.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package gitserver
import (
"fmt"
"net/http"
"path/filepath"
"strings"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"go.uber.org/zap"
)
// Serve a git client
func (gs *GitServer) serveGitClient(repoPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// Only dumb protocol is implemented at the moment
return gs.serveGitDumb(repoPath, w, r, next)
}
// Serve dumb git client files. These are generated on-the-fly
func (gs *GitServer) serveGitDumb(repoPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// repl := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
// root := repl.ReplaceAll(gs.Root, ".")
// Detect 'info/refs' and generate and serve
if strings.HasSuffix(r.URL.Path, "info/refs") {
// Try to open repo
repo, err := git.PlainOpen(repoPath)
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, fmt.Errorf("could not load repository"))
}
// Log the clone attempt
gs.logger.Info("git clone attempt",
zap.String("path", r.RequestURI),
zap.String("git_repo", repoPath),
zap.String("git_protocol", r.Header.Get("Git-Protocol")),
zap.String("git_client", r.UserAgent()),
)
var refs []string
// Collect all heads in repo
repoHeads, err := repo.Branches()
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
// Write heads to connection
repoHeads.ForEach(func(r *plumbing.Reference) error {
fmt.Fprintf(w, "%s\t%s\n", r.Hash().String(), r.Name().String())
refs = append(refs, r.String())
return nil
})
// Collect all tags in repo
repoTags, err := repo.Tags()
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
// Write tags to connection
repoTags.ForEach(func(r *plumbing.Reference) error {
fmt.Fprintf(w, "%s\t%s\n", r.Hash().String(), r.Name().String())
refs = append(refs, r.String())
return nil
})
gs.logger.Debug("generating dumb info/refs",
zap.String("git_repo", repoPath),
zap.String("req_path", r.URL.Path),
zap.String("refs", strings.Join(refs, ",")),
)
// The approach below is without a git library //
// // Find all ref files in GIT_DIR/refs/*/*
// refPath := filepath.Join(gitDir, "refs/*/*")
// refFiles, err := filepath.Glob(refPath)
// if err != nil {
// return err
// }
// // Generate 'info/refs'
// var infoRefs string
// for _, s := range refFiles {
// refDirs, refName := filepath.Split(s)
// _, refDir := filepath.Split(strings.TrimSuffix(refDirs, "/"))
// refName = filepath.Join("refs", refDir, refName)
// refHash, err := os.ReadFile(s)
// if err != nil {
// return err
// }
// infoRefs += strings.TrimSpace(string(refHash)) + "\t" + refName + "\n"
// }
// // Write info/refs to connection and close it
// fmt.Fprintf(w, "%s", infoRefs)
// //
return nil
}
// Detect 'objects/info/packs' and generate and serve
if strings.HasSuffix(r.URL.Path, "objects/info/packs") {
// Try to open repo
_, err := git.PlainOpen(repoPath)
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
// Get packs in repo
packFiles, err := filepath.Glob(filepath.Join(repoPath, "objects/pack/*.pack"))
if err != nil {
return caddyhttp.Error(http.StatusInternalServerError, err)
}
// Write pack file response
for _, packFile := range packFiles {
fmt.Fprintf(w, "P %s\n", filepath.Base(packFile))
}
return nil
}
// Serve the file if it exists
return gs.FileServer.ServeHTTP(w, r, next)
}