Copyright 2010 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package textproto

import (
	
	
	
	
	
	
	
)
A Reader implements convenience methods for reading requests or responses from a text protocol network connection.
type Reader struct {
	R   *bufio.Reader
	dot *dotReader
	buf []byte // a re-usable buffer for readContinuedLineSlice
}
NewReader returns a new Reader reading from r. To avoid denial of service attacks, the provided bufio.Reader should be reading from an io.LimitReader or similar Reader to bound the size of responses.
ReadLine reads a single line from r, eliding the final \n or \r\n from the returned string.
func ( *Reader) () (string, error) {
	,  := .readLineSlice()
	return string(), 
}
ReadLineBytes is like ReadLine but returns a []byte instead of a string.
func ( *Reader) () ([]byte, error) {
	,  := .readLineSlice()
	if  != nil {
		 := make([]byte, len())
		copy(, )
		 = 
	}
	return , 
}

func ( *Reader) () ([]byte, error) {
	.closeDot()
	var  []byte
	for {
		, ,  := .R.ReadLine()
		if  != nil {
			return nil, 
Avoid the copy if the first call produced a full line.
		if  == nil && ! {
			return , nil
		}
		 = append(, ...)
		if ! {
			break
		}
	}
	return , nil
}
ReadContinuedLine reads a possibly continued line from r, eliding the final trailing ASCII white space. Lines after the first are considered continuations if they begin with a space or tab character. In the returned data, continuation lines are separated from the previous line only by a single space: the newline and leading white space are removed. For example, consider this input: Line 1 continued... Line 2 The first call to ReadContinuedLine will return "Line 1 continued..." and the second will return "Line 2". Empty lines are never continued.
func ( *Reader) () (string, error) {
	,  := .readContinuedLineSlice(noValidation)
	return string(), 
}
trim returns s with leading and trailing spaces and tabs removed. It does not assume Unicode or UTF-8.
func ( []byte) []byte {
	 := 0
	for  < len() && ([] == ' ' || [] == '\t') {
		++
	}
	 := len()
	for  >  && ([-1] == ' ' || [-1] == '\t') {
		--
	}
	return [:]
}
ReadContinuedLineBytes is like ReadContinuedLine but returns a []byte instead of a string.
func ( *Reader) () ([]byte, error) {
	,  := .readContinuedLineSlice(noValidation)
	if  != nil {
		 := make([]byte, len())
		copy(, )
		 = 
	}
	return , 
}
readContinuedLineSlice reads continued lines from the reader buffer, returning a byte slice with all lines. The validateFirstLine function is run on the first read line, and if it returns an error then this error is returned from readContinuedLineSlice.
func ( *Reader) ( func([]byte) error) ([]byte, error) {
	if  == nil {
		return nil, fmt.Errorf("missing validateFirstLine func")
	}
Read the first line.
	,  := .readLineSlice()
	if  != nil {
		return nil, 
	}
	if len() == 0 { // blank line - no continuation
		return , nil
	}

	if  := ();  != nil {
		return nil, 
	}
Optimistically assume that we have started to buffer the next line and it starts with an ASCII letter (the next header key), or a blank line, so we can avoid copying that buffered data around in memory and skipping over non-existent whitespace.
	if .R.Buffered() > 1 {
		,  := .R.Peek(2)
		if len() > 0 && (isASCIILetter([0]) || [0] == '\n') ||
			len() == 2 && [0] == '\r' && [1] == '\n' {
			return trim(), nil
		}
	}
ReadByte or the next readLineSlice will flush the read buffer; copy the slice into buf.
	.buf = append(.buf[:0], trim()...)
Read continuation lines.
	for .skipSpace() > 0 {
		,  := .readLineSlice()
		if  != nil {
			break
		}
		.buf = append(.buf, ' ')
		.buf = append(.buf, trim()...)
	}
	return .buf, nil
}
skipSpace skips R over all spaces and returns the number of bytes skipped.
func ( *Reader) () int {
	 := 0
	for {
		,  := .R.ReadByte()
Bufio will keep err until next read.
			break
		}
		if  != ' ' &&  != '\t' {
			.R.UnreadByte()
			break
		}
		++
	}
	return 
}

func ( *Reader) ( int) ( int,  bool,  string,  error) {
	,  := .ReadLine()
	if  != nil {
		return
	}
	return parseCodeLine(, )
}

func ( string,  int) ( int,  bool,  string,  error) {
	if len() < 4 || [3] != ' ' && [3] != '-' {
		 = ProtocolError("short response: " + )
		return
	}
	 = [3] == '-'
	,  = strconv.Atoi([0:3])
	if  != nil ||  < 100 {
		 = ProtocolError("invalid response code: " + )
		return
	}
	 = [4:]
	if 1 <=  &&  < 10 && /100 !=  ||
		10 <=  &&  < 100 && /10 !=  ||
		100 <=  &&  < 1000 &&  !=  {
		 = &Error{, }
	}
	return
}
ReadCodeLine reads a response code line of the form code message where code is a three-digit status code and the message extends to the rest of the line. An example of such a line is: 220 plan9.bell-labs.com ESMTP If the prefix of the status does not match the digits in expectCode, ReadCodeLine returns with err set to &Error{code, message}. For example, if expectCode is 31, an error will be returned if the status is not in the range [310,319]. If the response is multi-line, ReadCodeLine returns an error. An expectCode <= 0 disables the check of the status code.
func ( *Reader) ( int) ( int,  string,  error) {
	, , ,  := .readCodeLine()
	if  == nil &&  {
		 = ProtocolError("unexpected multi-line response: " + )
	}
	return
}
ReadResponse reads a multi-line response of the form: code-message line 1 code-message line 2 ... code message line n where code is a three-digit status code. The first line starts with the code and a hyphen. The response is terminated by a line that starts with the same code followed by a space. Each line in message is separated by a newline (\n). See page 36 of RFC 959 (https://www.ietf.org/rfc/rfc959.txt) for details of another form of response accepted: code-message line 1 message line 2 ... code message line n If the prefix of the status does not match the digits in expectCode, ReadResponse returns with err set to &Error{code, message}. For example, if expectCode is 31, an error will be returned if the status is not in the range [310,319]. An expectCode <= 0 disables the check of the status code.
func ( *Reader) ( int) ( int,  string,  error) {
	, , ,  := .readCodeLine()
	 := 
	for  {
		,  := .ReadLine()
		if  != nil {
			return 0, "", 
		}

		var  int
		var  string
		, , ,  = parseCodeLine(, 0)
		if  != nil ||  !=  {
			 += "\n" + strings.TrimRight(, "\r\n")
			 = true
			continue
		}
		 += "\n" + 
	}
replace one line error message with all lines (full message)
		 = &Error{, }
	}
	return
}
DotReader returns a new Reader that satisfies Reads using the decoded text of a dot-encoded block read from r. The returned Reader is only valid until the next call to a method on r. Dot encoding is a common framing used for data blocks in text protocols such as SMTP. The data consists of a sequence of lines, each of which ends in "\r\n". The sequence itself ends at a line containing just a dot: ".\r\n". Lines beginning with a dot are escaped with an additional dot to avoid looking like the end of the sequence. The decoded form returned by the Reader's Read method rewrites the "\r\n" line endings into the simpler "\n", removes leading dot escapes if present, and stops with error io.EOF after consuming (and discarding) the end-of-sequence line.
func ( *Reader) () io.Reader {
	.closeDot()
	.dot = &dotReader{r: }
	return .dot
}

type dotReader struct {
	r     *Reader
	state int
}
Read satisfies reads by decoding dot-encoded data read from d.r.
Run data through a simple state machine to elide leading dots, rewrite trailing \r\n into \n, and detect ending .\r\n line.
	const (
		 = iota // beginning of line; initial state; must be zero
		              // read . at beginning of line
		            // read .\r at beginning of line
		               // read \r (possibly at end of line)
		             // reading data in middle of line
		              // reached .\r\n end marker line
	)
	 := .r.R
	for  < len() && .state !=  {
		var  byte
		,  = .ReadByte()
		if  != nil {
			if  == io.EOF {
				 = io.ErrUnexpectedEOF
			}
			break
		}
		switch .state {
		case :
			if  == '.' {
				.state = 
				continue
			}
			if  == '\r' {
				.state = 
				continue
			}
			.state = 

		case :
			if  == '\r' {
				.state = 
				continue
			}
			if  == '\n' {
				.state = 
				continue
			}
			.state = 

		case :
			if  == '\n' {
				.state = 
				continue
Not part of .\r\n. Consume leading dot and emit saved \r.
			.UnreadByte()
			 = '\r'
			.state = 

		case :
			if  == '\n' {
				.state = 
				break
Not part of \r\n. Emit saved \r
			.UnreadByte()
			 = '\r'
			.state = 

		case :
			if  == '\r' {
				.state = 
				continue
			}
			if  == '\n' {
				.state = 
			}
		}
		[] = 
		++
	}
	if  == nil && .state ==  {
		 = io.EOF
	}
	if  != nil && .r.dot ==  {
		.r.dot = nil
	}
	return
}
closeDot drains the current DotReader if any, making sure that it reads until the ending dot line.
func ( *Reader) () {
	if .dot == nil {
		return
	}
	 := make([]byte, 128)
When Read reaches EOF or an error, it will set r.dot == nil.
		.dot.Read()
	}
}
ReadDotBytes reads a dot-encoding and returns the decoded data. See the documentation for the DotReader method for details about dot-encoding.
func ( *Reader) () ([]byte, error) {
	return io.ReadAll(.DotReader())
}
ReadDotLines reads a dot-encoding and returns a slice containing the decoded lines, with the final \r\n or \n elided from each. See the documentation for the DotReader method for details about dot-encoding.
We could use ReadDotBytes and then Split it, but reading a line at a time avoids needing a large contiguous block of memory and is simpler.
	var  []string
	var  error
	for {
		var  string
		,  = .ReadLine()
		if  != nil {
			if  == io.EOF {
				 = io.ErrUnexpectedEOF
			}
			break
		}
Dot by itself marks end; otherwise cut one dot.
		if len() > 0 && [0] == '.' {
			if len() == 1 {
				break
			}
			 = [1:]
		}
		 = append(, )
	}
	return , 
}
ReadMIMEHeader reads a MIME-style header from r. The header is a sequence of possibly continued Key: Value lines ending in a blank line. The returned map m maps CanonicalMIMEHeaderKey(key) to a sequence of values in the same order encountered in the input. For example, consider this input: My-Key: Value 1 Long-Key: Even Longer Value My-Key: Value 2 Given that input, ReadMIMEHeader returns the map: map[string][]string{ "My-Key": {"Value 1", "Value 2"}, "Long-Key": {"Even Longer Value"}, }
Avoid lots of small slice allocations later by allocating one large one ahead of time which we'll cut up into smaller slices. If this isn't big enough later, we allocate small ones.
	var  []string
	 := .upcomingHeaderNewlines()
	if  > 0 {
		 = make([]string, )
	}

	 := make(MIMEHeader, )
The first line cannot start with a leading space.
	if ,  := .R.Peek(1);  == nil && ([0] == ' ' || [0] == '\t') {
		,  := .readLineSlice()
		if  != nil {
			return , 
		}
		return , ProtocolError("malformed MIME header initial line: " + string())
	}

	for {
		,  := .readContinuedLineSlice(mustHaveFieldNameColon)
		if len() == 0 {
			return , 
		}
Key ends at first colon.
		 := bytes.IndexByte(, ':')
		if  < 0 {
			return , ProtocolError("malformed MIME header line: " + string())
		}
		 := canonicalMIMEHeaderKey([:])
As per RFC 7230 field-name is a token, tokens consist of one or more chars. We could return a ProtocolError here, but better to be liberal in what we accept, so if we get an empty key, skip it.
		if  == "" {
			continue
		}
Skip initial spaces in value.
		++ // skip colon
		for  < len() && ([] == ' ' || [] == '\t') {
			++
		}
		 := string([:])

		 := []
More than likely this will be a single-element key. Most headers aren't multi-valued. Set the capacity on strs[0] to 1, so any future append won't extend the slice into the other strings.
			,  = [:1:1], [1:]
			[0] = 
			[] = 
		} else {
			[] = append(, )
		}

		if  != nil {
			return , 
		}
	}
}
noValidation is a no-op validation func for readContinuedLineSlice that permits any lines.
func ( []byte) error { return nil }
mustHaveFieldNameColon ensures that, per RFC 7230, the field-name is on a single line, so the first line must contain a colon.
func ( []byte) error {
	if bytes.IndexByte(, ':') < 0 {
		return ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q", ))
	}
	return nil
}
upcomingHeaderNewlines returns an approximation of the number of newlines that will be in this header. If it gets confused, it returns 0.
Try to determine the 'hint' size.
	.R.Peek(1) // force a buffer load if empty
	 := .R.Buffered()
	if  == 0 {
		return
	}
	,  := .R.Peek()
	for len() > 0 {
		 := bytes.IndexByte(, '\n')
Not present (-1) or found within the next few bytes, implying we're at the end ("\r\n\r\n" or "\n\n")
			return
		}
		++
		 = [+1:]
	}
	return
}
CanonicalMIMEHeaderKey returns the canonical format of the MIME header key s. The canonicalization converts the first letter and any letter following a hyphen to upper case; the rest are converted to lowercase. For example, the canonical key for "accept-encoding" is "Accept-Encoding". MIME header keys are assumed to be ASCII only. If s contains a space or invalid header field bytes, it is returned without modifications.
Quick check for canonical encoding.
	 := true
	for  := 0;  < len(); ++ {
		 := []
		if !validHeaderFieldByte() {
			return 
		}
		if  && 'a' <=  &&  <= 'z' {
			return canonicalMIMEHeaderKey([]byte())
		}
		if ! && 'A' <=  &&  <= 'Z' {
			return canonicalMIMEHeaderKey([]byte())
		}
		 =  == '-'
	}
	return 
}

const toLower = 'a' - 'A'
validHeaderFieldByte reports whether b is a valid byte in a header field name. RFC 7230 says: header-field = field-name ":" OWS field-value OWS field-name = token tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA token = 1*tchar
canonicalMIMEHeaderKey is like CanonicalMIMEHeaderKey but is allowed to mutate the provided byte slice before returning the string. For invalid inputs (if a contains spaces or non-token bytes), a is unchanged and a string copy is returned.
See if a looks like a header key. If not, return it unchanged.
	for ,  := range  {
		if validHeaderFieldByte() {
			continue
Don't canonicalize.
		return string()
	}

	 := true
Canonicalize: first letter upper case and upper case after each dash. (Host, User-Agent, If-Modified-Since). MIME headers are ASCII only, so no Unicode issues.
		if  && 'a' <=  &&  <= 'z' {
			 -= toLower
		} else if ! && 'A' <=  &&  <= 'Z' {
			 += toLower
		}
		[] = 
		 =  == '-' // for next time
The compiler recognizes m[string(byteSlice)] as a special case, so a copy of a's bytes into a new string does not happen in this map lookup:
	if  := commonHeader[string()];  != "" {
		return 
	}
	return string()
}
commonHeader interns common header strings.
var commonHeader map[string]string

var commonHeaderOnce sync.Once

func () {
	commonHeader = make(map[string]string)
	for ,  := range []string{
		"Accept",
		"Accept-Charset",
		"Accept-Encoding",
		"Accept-Language",
		"Accept-Ranges",
		"Cache-Control",
		"Cc",
		"Connection",
		"Content-Id",
		"Content-Language",
		"Content-Length",
		"Content-Transfer-Encoding",
		"Content-Type",
		"Cookie",
		"Date",
		"Dkim-Signature",
		"Etag",
		"Expires",
		"From",
		"Host",
		"If-Modified-Since",
		"If-None-Match",
		"In-Reply-To",
		"Last-Modified",
		"Location",
		"Message-Id",
		"Mime-Version",
		"Pragma",
		"Received",
		"Return-Path",
		"Server",
		"Set-Cookie",
		"Subject",
		"To",
		"User-Agent",
		"Via",
		"X-Forwarded-For",
		"X-Imforwards",
		"X-Powered-By",
	} {
		commonHeader[] = 
	}
}
isTokenTable is a copy of net/http/lex.go's isTokenTable. See https://httpwg.github.io/specs/rfc7230.html#rule.token.separators
var isTokenTable = [127]bool{
	'!':  true,
	'#':  true,
	'$':  true,
	'%':  true,
	'&':  true,
	'\'': true,
	'*':  true,
	'+':  true,
	'-':  true,
	'.':  true,
	'0':  true,
	'1':  true,
	'2':  true,
	'3':  true,
	'4':  true,
	'5':  true,
	'6':  true,
	'7':  true,
	'8':  true,
	'9':  true,
	'A':  true,
	'B':  true,
	'C':  true,
	'D':  true,
	'E':  true,
	'F':  true,
	'G':  true,
	'H':  true,
	'I':  true,
	'J':  true,
	'K':  true,
	'L':  true,
	'M':  true,
	'N':  true,
	'O':  true,
	'P':  true,
	'Q':  true,
	'R':  true,
	'S':  true,
	'T':  true,
	'U':  true,
	'W':  true,
	'V':  true,
	'X':  true,
	'Y':  true,
	'Z':  true,
	'^':  true,
	'_':  true,
	'`':  true,
	'a':  true,
	'b':  true,
	'c':  true,
	'd':  true,
	'e':  true,
	'f':  true,
	'g':  true,
	'h':  true,
	'i':  true,
	'j':  true,
	'k':  true,
	'l':  true,
	'm':  true,
	'n':  true,
	'o':  true,
	'p':  true,
	'q':  true,
	'r':  true,
	's':  true,
	't':  true,
	'u':  true,
	'v':  true,
	'w':  true,
	'x':  true,
	'y':  true,
	'z':  true,
	'|':  true,
	'~':  true,