+22
-17
knotserver/git.go
+22
-17
knotserver/git.go
···
34
34
func (d *Handle) UploadPack(w http.ResponseWriter, r *http.Request) {
35
35
did := chi.URLParam(r, "did")
36
36
name := chi.URLParam(r, "name")
37
-
repo, _ := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name))
38
-
39
-
w.Header().Set("content-type", "application/x-git-upload-pack-result")
40
-
w.Header().Set("Connection", "Keep-Alive")
41
-
w.Header().Set("Transfer-Encoding", "chunked")
42
-
w.WriteHeader(http.StatusOK)
43
-
44
-
cmd := service.ServiceCommand{
45
-
Dir: repo,
46
-
Stdout: w,
37
+
repo, err := securejoin.SecureJoin(d.c.Repo.ScanPath, filepath.Join(did, name))
38
+
if err != nil {
39
+
writeError(w, err.Error(), 500)
40
+
d.l.Error("git: failed to secure join repo path", "handler", "UploadPack", "error", err)
41
+
return
47
42
}
48
43
49
-
var reader io.ReadCloser
50
-
reader = r.Body
51
-
44
+
var bodyReader io.ReadCloser = r.Body
52
45
if r.Header.Get("Content-Encoding") == "gzip" {
53
-
reader, err := gzip.NewReader(r.Body)
46
+
gzipReader, err := gzip.NewReader(r.Body)
54
47
if err != nil {
55
48
writeError(w, err.Error(), 500)
56
49
d.l.Error("git: failed to create gzip reader", "handler", "UploadPack", "error", err)
57
50
return
58
51
}
59
-
defer reader.Close()
52
+
defer gzipReader.Close()
53
+
bodyReader = gzipReader
54
+
}
55
+
56
+
w.Header().Set("Content-Type", "application/x-git-upload-pack-result")
57
+
w.Header().Set("Connection", "Keep-Alive")
58
+
59
+
d.l.Info("git: executing git-upload-pack", "handler", "UploadPack", "repo", repo)
60
+
61
+
cmd := service.ServiceCommand{
62
+
Dir: repo,
63
+
Stdout: w,
64
+
Stdin: bodyReader,
60
65
}
61
66
62
-
cmd.Stdin = reader
67
+
w.WriteHeader(http.StatusOK)
68
+
63
69
if err := cmd.UploadPack(); err != nil {
64
-
writeError(w, err.Error(), 500)
65
70
d.l.Error("git: failed to execute git-upload-pack", "handler", "UploadPack", "error", err)
66
71
return
67
72
}
+31
-24
knotserver/git/service/service.go
+31
-24
knotserver/git/service/service.go
···
8
8
"net/http"
9
9
"os/exec"
10
10
"strings"
11
+
"sync"
11
12
"syscall"
12
13
)
13
14
···
68
69
}
69
70
70
71
func (c *ServiceCommand) UploadPack() error {
71
-
cmd := exec.Command("git", []string{
72
-
"-c", "uploadpack.allowFilter=true",
73
-
"upload-pack",
74
-
"--stateless-rpc",
75
-
".",
76
-
}...)
72
+
var stderr bytes.Buffer
73
+
74
+
cmd := exec.Command("git", "-c", "uploadpack.allowFilter=true",
75
+
"upload-pack", "--stateless-rpc", ".")
77
76
cmd.Dir = c.Dir
78
77
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
79
78
80
-
stdoutPipe, _ := cmd.StdoutPipe()
81
-
cmd.Stderr = cmd.Stdout
82
-
defer stdoutPipe.Close()
79
+
stdoutPipe, err := cmd.StdoutPipe()
80
+
if err != nil {
81
+
return fmt.Errorf("failed to create stdout pipe: %w", err)
82
+
}
83
+
84
+
cmd.Stderr = &stderr
83
85
84
86
stdinPipe, err := cmd.StdinPipe()
85
87
if err != nil {
86
-
return err
88
+
return fmt.Errorf("failed to create stdin pipe: %w", err)
87
89
}
88
-
defer stdinPipe.Close()
89
90
90
91
if err := cmd.Start(); err != nil {
91
-
log.Printf("git: failed to start git-upload-pack: %s", err)
92
-
return err
92
+
return fmt.Errorf("failed to start git-upload-pack: %w", err)
93
93
}
94
94
95
-
if _, err := io.Copy(stdinPipe, c.Stdin); err != nil {
96
-
log.Printf("git: failed to copy stdin: %s", err)
97
-
return err
98
-
}
99
-
stdinPipe.Close()
95
+
var wg sync.WaitGroup
96
+
97
+
wg.Add(1)
98
+
go func() {
99
+
defer wg.Done()
100
+
defer stdinPipe.Close()
101
+
io.Copy(stdinPipe, c.Stdin)
102
+
}()
103
+
104
+
wg.Add(1)
105
+
go func() {
106
+
defer wg.Done()
107
+
io.Copy(newWriteFlusher(c.Stdout), stdoutPipe)
108
+
stdoutPipe.Close()
109
+
}()
110
+
111
+
wg.Wait()
100
112
101
-
if _, err := io.Copy(newWriteFlusher(c.Stdout), stdoutPipe); err != nil {
102
-
log.Printf("git: failed to copy stdout: %s", err)
103
-
return err
104
-
}
105
113
if err := cmd.Wait(); err != nil {
106
-
log.Printf("git: failed to wait for git-upload-pack: %s", err)
107
-
return err
114
+
return fmt.Errorf("git-upload-pack failed: %w, stderr: %s", err, stderr.String())
108
115
}
109
116
110
117
return nil