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
|
package stats
import (
"bytes"
"errors"
"fmt"
"io"
"time"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/pktline"
)
// SendPack is used to parse the response body of a git-send-pack(1) request.
type SendPack struct {
// ReportProgress is an optional callback set by the caller. If set, this function
// will be called for all received progress packets.
ReportProgress func([]byte)
updatedRefs int
packets int
largestPacketSize int
unpackOK time.Time
responseBody time.Time
multiband map[string]*bandInfo
}
// Parse parses the server response from a git-send-pack(1) request. The expected response is
// documented in git.git:Documentation/techincal/pack-protocol.txt. We expect that the report-status
// capability is active and thus don't support the report-status-v2 protocol.
func (s *SendPack) Parse(body io.Reader) error {
s.multiband = make(map[string]*bandInfo)
for _, band := range Bands() {
s.multiband[band] = &bandInfo{}
}
scanner := pktline.NewScanner(body)
seenFlush := false
seenPack := false
// We're requesting the side-band-64k capability and thus receive a multiplexed stream from
// git-receive-pack(1). The first byte of each pktline will identify the multiplexing band.
for ; scanner.Scan(); s.packets++ {
if seenFlush {
return errors.New("received extra packet after flush")
}
if pktline.IsFlush(scanner.Bytes()) {
seenFlush = true
continue
}
data := pktline.Data(scanner.Bytes())
if len(data) == 0 {
return errors.New("empty packet in PACK data")
}
if len(data) > s.largestPacketSize {
s.largestPacketSize = len(data)
}
band, err := bandToHuman(data[0])
if err != nil {
return fmt.Errorf("converting band: %w", err)
}
s.multiband[band].consume(data)
data = data[1:]
switch band {
case bandPack:
// The pack band contains our "normal" pktlines which are nested into the
// outer pktline.
scanner := pktline.NewScanner(bytes.NewReader(data))
for scanner.Scan() {
if pktline.IsFlush(scanner.Bytes()) {
break
}
data := pktline.Data(scanner.Bytes())
if !seenPack {
if !bytes.Equal(data, []byte("unpack ok\n")) {
return fmt.Errorf("expected unpack ok, got %q", data)
}
s.unpackOK = time.Now()
seenPack = true
} else {
if bytes.HasPrefix(data, []byte("ok ")) {
s.updatedRefs++
} else if bytes.HasPrefix(data, []byte("ng ")) {
return fmt.Errorf("reference update failed: %q", data)
} else {
return fmt.Errorf("unsupported packet line: %q", data)
}
}
}
case bandProgress:
if s.ReportProgress != nil {
s.ReportProgress(data)
}
case bandError:
return fmt.Errorf("received error: %q", data)
default:
return fmt.Errorf("unsupported band: %q", band)
}
}
s.responseBody = time.Now()
return nil
}
|