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
|
package fasthttp
import (
"bytes"
"errors"
"fmt"
)
type headerScanner struct {
initialized bool
b []byte
r int
key []byte
value []byte
err error
}
func (s *headerScanner) next() bool {
if !s.initialized {
if bytes.HasPrefix(s.b, strCRLF) {
s.r = 2
return false
}
i := bytes.Index(s.b, strCRLFCRLF)
if i < 0 {
s.err = errNeedMore
return false
}
i += 4
s.b = s.b[:i]
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, err := s.readContinuedLineSlice()
if len(kv) == 0 {
s.err = err
return false
}
// Key ends at first colon.
k, v, ok := bytes.Cut(kv, strColon)
if !ok {
s.err = fmt.Errorf("malformed MIME header line: %q", kv)
return false
}
if !isValidHeaderKey(k) {
s.err = fmt.Errorf("malformed MIME header line: %q", kv)
return false
}
// Skip initial spaces in value.
v = bytes.TrimLeft(v, " \t")
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.
func (s *headerScanner) readLine() (line []byte) {
searchStart := 0
for {
if i := bytes.IndexByte(s.b[s.r+searchStart:], '\n'); i >= 0 {
i += searchStart
line = s.b[s.r : s.r+i+1]
s.r += i + 1
break
}
searchStart = len(s.b) - s.r
}
if len(line) == 0 {
return nil
}
// drop \n and possible preceding \r
if line[len(line)-1] == '\n' {
drop := 1
if len(line) > 1 && line[len(line)-2] == '\r' {
drop = 2
}
line = line[:len(line)-drop]
}
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.
func (s *headerScanner) readContinuedLineSlice() ([]byte, error) {
line := s.readLine()
if len(line) == 0 { // blank line - no continuation
return line, nil
}
if bytes.IndexByte(line, ':') < 0 {
return nil, fmt.Errorf("malformed MIME header: missing colon: %q", line)
}
// If the 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), nil
}
}
mline := trim(line)
// Read continuation lines.
for s.skipSpace() {
mline = append(mline, ' ')
line := s.readLine()
mline = append(mline, trim(line)...)
}
return mline, 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]
}
|