// 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 implements generic support for text-based request/response // protocols in the style of HTTP, NNTP, and SMTP. // // The package provides: // // [Error], which represents a numeric error response from // a server. // // [Pipeline], to manage pipelined requests and responses // in a client. // // [Reader], to read numeric response code lines, // key: value headers, lines wrapped with leading spaces // on continuation lines, and whole text blocks ending // with a dot on a line by itself. // // [Writer], to write dot-encoded text blocks. // // [Conn], a convenient packaging of [Reader], [Writer], and [Pipeline] for use // with a single network connection.
package textproto import ( ) // An Error represents a numeric error response from a server. type Error struct { Code int Msg string } func ( *Error) () string { return fmt.Sprintf("%03d %s", .Code, .Msg) } // A ProtocolError describes a protocol violation such // as an invalid response or a hung-up connection. type ProtocolError string func ( ProtocolError) () string { return string() } // A Conn represents a textual network protocol connection. // It consists of a [Reader] and [Writer] to manage I/O // and a [Pipeline] to sequence concurrent requests on the connection. // These embedded types carry methods with them; // see the documentation of those types for details. type Conn struct { Reader Writer Pipeline conn io.ReadWriteCloser } // NewConn returns a new [Conn] using conn for I/O. func ( io.ReadWriteCloser) *Conn { return &Conn{ Reader: Reader{R: bufio.NewReader()}, Writer: Writer{W: bufio.NewWriter()}, conn: , } } // Close closes the connection. func ( *Conn) () error { return .conn.Close() } // Dial connects to the given address on the given network using [net.Dial] // and then returns a new [Conn] for the connection. func (, string) (*Conn, error) { , := net.Dial(, ) if != nil { return nil, } return NewConn(), nil } // Cmd is a convenience method that sends a command after // waiting its turn in the pipeline. The command text is the // result of formatting format with args and appending \r\n. // Cmd returns the id of the command, for use with StartResponse and EndResponse. // // For example, a client might run a HELP command that returns a dot-body // by using: // // id, err := c.Cmd("HELP") // if err != nil { // return nil, err // } // // c.StartResponse(id) // defer c.EndResponse(id) // // if _, _, err = c.ReadCodeLine(110); err != nil { // return nil, err // } // text, err := c.ReadDotBytes() // if err != nil { // return nil, err // } // return c.ReadCodeLine(250) func ( *Conn) ( string, ...any) ( uint, error) { = .Next() .StartRequest() = .PrintfLine(, ...) .EndRequest() if != nil { return 0, } return , nil } // TrimString returns s without leading and trailing ASCII space. func ( string) string { for len() > 0 && isASCIISpace([0]) { = [1:] } for len() > 0 && isASCIISpace([len()-1]) { = [:len()-1] } return } // TrimBytes returns b without leading and trailing ASCII space. func ( []byte) []byte { for len() > 0 && isASCIISpace([0]) { = [1:] } for len() > 0 && isASCIISpace([len()-1]) { = [:len()-1] } return } func isASCIISpace( byte) bool { return == ' ' || == '\t' || == '\n' || == '\r' } func isASCIILetter( byte) bool { |= 0x20 // make lower case return 'a' <= && <= 'z' }