| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- 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
- }
- // 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
- }
- // 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]
- }
|