var errMessageTooLarge … type Reader … // 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. func NewReader(r *bufio.Reader) *Reader { … } // ReadLine reads a single line from r, // eliding the final \n or \r\n from the returned string. func (r *Reader) ReadLine() (string, error) { … } // ReadLineBytes is like [Reader.ReadLine] but returns a []byte instead of a string. func (r *Reader) ReadLineBytes() ([]byte, error) { … } // readLineSlice reads a single line from r, // up to lim bytes long (or unlimited if lim is less than 0), // eliding the final \r or \r\n from the returned string. func (r *Reader) readLineSlice(lim int64) ([]byte, error) { … } // 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 (r *Reader) ReadContinuedLine() (string, error) { … } // trim returns s with leading and trailing spaces and tabs removed. // It does not assume Unicode or UTF-8. func trim(s []byte) []byte { … } // ReadContinuedLineBytes is like [Reader.ReadContinuedLine] but // returns a []byte instead of a string. func (r *Reader) ReadContinuedLineBytes() ([]byte, error) { … } // 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. // It reads up to lim bytes of data (or unlimited if lim is less than 0). func (r *Reader) readContinuedLineSlice(lim int64, validateFirstLine func([]byte) error) ([]byte, error) { … } // skipSpace skips R over all spaces and returns the number of bytes skipped. func (r *Reader) skipSpace() int { … } func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) { … } func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) { … } // 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 (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) { … } // 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 (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) { … } // 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 (r *Reader) DotReader() io.Reader { … } type dotReader … // Read satisfies reads by decoding dot-encoded data read from d.r. func (d *dotReader) Read(b []byte) (n int, err error) { … } // closeDot drains the current DotReader if any, // making sure that it reads until the ending dot line. func (r *Reader) closeDot() { … } // ReadDotBytes reads a dot-encoding and returns the decoded data. // // See the documentation for the [Reader.DotReader] method for details about dot-encoding. func (r *Reader) ReadDotBytes() ([]byte, error) { … } // 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 [Reader.DotReader] method for details about dot-encoding. func (r *Reader) ReadDotLines() ([]string, error) { … } var colon … // 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"}, // } func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) { … } // readMIMEHeader is a version of ReadMIMEHeader which takes a limit on the header size. // It is called by the mime/multipart package. func readMIMEHeader(r *Reader, maxMemory, maxHeaders int64) (MIMEHeader, error) { … } // noValidation is a no-op validation func for readContinuedLineSlice // that permits any lines. func noValidation(_ []byte) error { … } // mustHaveFieldNameColon ensures that, per RFC 7230, the // field-name is on a single line, so the first line must // contain a colon. func mustHaveFieldNameColon(line []byte) error { … } var nl … // upcomingHeaderKeys returns an approximation of the number of keys // that will be in this header. If it gets confused, it returns 0. func (r *Reader) upcomingHeaderKeys() (n int) { … } // 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. func CanonicalMIMEHeaderKey(s string) string { … } const toLower … // validHeaderFieldByte reports whether c 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 func validHeaderFieldByte(c byte) bool { … } // validHeaderValueByte reports whether c is a valid byte in a header // field value. RFC 7230 says: // // field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] // field-vchar = VCHAR / obs-text // obs-text = %x80-FF // // RFC 5234 says: // // HTAB = %x09 // SP = %x20 // VCHAR = %x21-7E func validHeaderValueByte(c byte) bool { … } // 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. // // ok is true if the header key contains only valid characters and spaces. // ReadMIMEHeader accepts header keys containing spaces, but does not // canonicalize them. func canonicalMIMEHeaderKey(a []byte) (_ string, ok bool) { … } var commonHeader … var commonHeaderOnce … func initCommonHeader() { … }