func skipCRLF(r *bufio.Reader) error { // There maybe servers using single '\n' for line ending if _, err := r.ReadSlice('\n'); err != nil { errl.Println("Error reading CRLF:", err) return err } return nil }
// Learned from net.textproto. One difference is that this one keeps the // ending '\n' in the returned line. Buf if there's only CRLF in the line, // return nil for the line. func readContinuedLineSlice(r *bufio.Reader) ([]byte, error) { // feedly.com request headers contains things like: // "$Authorization.feedly: $FeedlyAuth\r\n", so we must test for only // continuation spaces. isspace := func(b byte) bool { return b == ' ' || b == '\t' } // Read the first line. line, err := r.ReadSlice('\n') if err != nil { return nil, err } // There are servers that use \n for line ending, so trim first before check ending. // For example, the 404 page for http://plan9.bell-labs.com/magic/man2html/1/2l trimmed := TrimSpace(line) if len(trimmed) == 0 { if len(line) > 2 { return nil, fmt.Errorf("malformed end of headers, len: %d, %#v", len(line), string(line)) } return nil, nil } if isspace(line[0]) { return nil, fmt.Errorf("malformed header, start with space: %#v", string(line)) } // Optimistically assume that we have started to buffer the next line // and it starts with an ASCII letter (the next header key), so we can // avoid copying that buffered data around in memory and skipping over // non-existent whitespace. if r.Buffered() > 0 { peek, err := r.Peek(1) if err == nil && !isspace(peek[0]) { return line, nil } } var buf []byte buf = append(buf, trimmed...) // Read continuation lines. for skipSpace(r) > 0 { line, err := r.ReadSlice('\n') if err != nil { break } buf = append(buf, ' ') buf = append(buf, TrimTrailingSpace(line)...) } buf = append(buf, '\r', '\n') return buf, nil }
// Only add headers that are of interest for a proxy into request/response's header map. func (h *Header) parseHeader(reader *bufio.Reader, raw *bytes.Buffer, url *URL) (err error) { h.ContLen = -1 dummyLastLine := []byte{} // Read request header and body var s, name, val, lastLine []byte for { if s, err = reader.ReadSlice('\n'); err != nil { return } // There are servers that use \n for line ending, so trim first before check ending. // For example, the 404 page for http://plan9.bell-labs.com/magic/man2html/1/2l trimmed := TrimSpace(s) if len(trimmed) == 0 { // end of headers return } if (s[0] == ' ' || s[0] == '\t') && lastLine != nil { // multi-line header // I've never seen multi-line header used in headers that's of interest. // Disable multi-line support to avoid copy for now. errl.Printf("Multi-line support disabled: %v %s", url, s) return errNotSupported // combine previous line with current line // trimmed = bytes.Join([][]byte{lastLine, []byte{' '}, trimmed}, nil) } if name, val, err = splitHeader(trimmed); err != nil { return } // Wait Go to solve/provide the string<->[]byte optimization kn := string(name) if parseFunc, ok := headerParser[kn]; ok { // lastLine = append([]byte(nil), trimmed...) // copy to avoid next read invalidating the trimmed line lastLine = dummyLastLine val = TrimSpace(val) if len(val) == 0 { continue } parseFunc(h, ASCIIToLower(val), raw) } else { // mark this header as not of interest to proxy lastLine = nil } if hopByHopHeader[kn] { continue } raw.Write(s) // debug.Printf("len %d %s", len(s), s) } return }
// Use this function until we find Trailer headers actually in use. func skipTrailer(r *bufio.Reader) error { // It's possible to get trailer headers, but the body will always end with // a line with just CRLF. for { s, err := r.ReadSlice('\n') if err != nil { errl.Println("skip trailer:", err) return err } if len(s) == 2 && s[0] == '\r' && s[1] == '\n' { return nil } errl.Printf("skip trailer: %#v", string(s)) if len(s) == 1 || len(s) == 2 { return fmt.Errorf("malformed chunk body end: %#v", string(s)) } } }