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