-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathheaderscanner.go
More file actions
186 lines (160 loc) · 4.24 KB
/
Copy pathheaderscanner.go
File metadata and controls
186 lines (160 loc) · 4.24 KB
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
package fasthttp
import (
"bytes"
"errors"
"fmt"
)
type headerScanner struct {
initialized bool
b []byte
r int
// blockEnd is the end of the header block in b when the caller has
// already found it (see readRawHeaders), 0 otherwise. next only trusts
// it if the block really ends in CRLFCRLF there.
blockEnd int
key []byte
value []byte
// keyHasSpace reports whether key contains a space that survives
// trailing-whitespace trimming; such keys must not be canonicalized.
keyHasSpace bool
err error
}
func (s *headerScanner) next() bool {
if !s.initialized {
if bytes.HasPrefix(s.b, strCRLF) {
s.r = 2
return false
}
if s.blockEnd >= 4 && s.blockEnd <= len(s.b) &&
bytes.Equal(s.b[s.blockEnd-4:s.blockEnd], strCRLFCRLF) {
// The caller already found the end of the block, no need to
// search for it again. The first CRLFCRLF can only sit at
// blockEnd-4 since readRawHeaders stops at the first blank line.
s.b = s.b[:s.blockEnd]
} else {
i := bytes.Index(s.b, strCRLFCRLF)
if i < 0 {
s.err = ErrNeedMore
return false
}
s.b = s.b[:i+4]
}
if len(s.b) > 0 && (s.b[0] == ' ' || s.b[0] == '\t') {
s.err = errors.New("invalid headers, headers cannot start with space or tab")
return false
}
s.initialized = true
}
kv, colon, err := s.readContinuedLineSlice()
if len(kv) == 0 {
s.err = err
return false
}
// Key ends at the first colon, already found by readContinuedLineSlice.
k, v := kv[:colon], kv[colon+1:]
valid, innerSpace := isValidHeaderKey(k)
if !valid {
s.err = fmt.Errorf("malformed mime header line: %q", kv)
return false
}
s.keyHasSpace = innerSpace
// Skip initial spaces in value, without bytes.TrimLeft: it would
// rebuild its ASCII set on every call.
for len(v) > 0 && (v[0] == ' ' || v[0] == '\t') {
v = v[1:]
}
s.key = k
s.value = v
if err != nil {
s.err = err
return false
}
return true
}
// readLine reads a line from b, starting at s.r, and returns it with the
// trailing \n and a possible preceding \r dropped. b is truncated at the
// header block terminator, so every line ends in \n.
func (s *headerScanner) readLine() []byte {
i := bytes.IndexByte(s.b[s.r:], '\n')
if i < 0 {
return nil
}
line := s.b[s.r : s.r+i]
s.r += i + 1
if i > 0 && line[i-1] == '\r' {
line = line[:i-1]
}
return line
}
// readContinuedLineSlice reads continued lines from b until it finds a line
// that does not start with a space or tab, or it reaches the end of b.
// It also returns the position of the first colon in the returned line:
// the line can never start with a space or tab (the scanner rejects that for
// the first line and joins such lines into the previous header), so trimming
// it doesn't shift the colon.
func (s *headerScanner) readContinuedLineSlice() ([]byte, int, error) {
line := s.readLine()
if len(line) == 0 { // blank line - no continuation
return line, -1, nil
}
colon := bytes.IndexByte(line, ':')
if colon < 0 {
return nil, -1, fmt.Errorf("malformed mime header: missing colon: %q", line)
}
// If the next line doesn't start with a space or tab, we are done.
if len(s.b)-s.r > 1 {
peek := s.b[s.r : s.r+2]
if len(peek) > 0 && (isASCIILetter(peek[0]) || peek[0] == '\n') ||
len(peek) == 2 && peek[0] == '\r' && peek[1] == '\n' {
return trim(line), colon, nil
}
}
mline := trim(line)
// Read continuation lines.
for s.skipSpace() {
mline = append(mline, ' ')
line := s.readLine()
mline = append(mline, trim(line)...)
}
return mline, colon, nil
}
// skipSpace skips one or multiple spaces and tabs in b.
func (s *headerScanner) skipSpace() bool {
skipped := false
for {
c := s.b[s.r]
if c != ' ' && c != '\t' {
break
}
s.r++
skipped = true
}
return skipped
}
func isASCIILetter(b byte) bool {
b |= 0x20 // Make lower case.
return 'a' <= b && b <= 'z'
}
// trim returns s with leading and trailing spaces and tabs removed.
// It does not assume Unicode or UTF-8.
func trim(s []byte) []byte {
i := 0
for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
i++
}
n := len(s)
for n > i && (s[n-1] == ' ' || s[n-1] == '\t') {
n--
}
return s[i:n]
}
func trimTrailingSpace(s []byte) []byte {
for len(s) > 0 {
c := s[len(s)-1]
if c != ' ' && c != '\t' {
break
}
s = s[:len(s)-1]
}
return s
}