# package textproto

`import "net/textproto"`

textproto实现了对基于文本的请求/回复协议的一般性支持，包括HTTP、NNTP和SMTP。

本包提供：

错误，代表服务端回复的错误码。Pipeline，以管理客户端中的管道化的请求/回复。Reader，读取数值回复码行，键值对形式的头域，一个作为后续行先导的空行，以及以只有一个"."的一行为结尾的整个文本块。Writer，写入点编码的文本。Conn，对Reader、Writer和Pipline的易用的包装，用于单个网络连接。

## type ProtocolError

    type ProtocolError string

ProtocolError描述一个违反协议的错误，如不合法的回复或者挂起的连接。

### func (ProtocolError) Error

    func (p ProtocolError) Error() string

## type Error

    type Error struct {
        Code int
        Msg  string
    }

Error代表一个服务端返回的数值状态码/错误码。

### func (*Error) Error

    func (e *Error) Error() string

## func CanonicalMIMEHeaderKey

    func CanonicalMIMEHeaderKey(s string) string

返回一个MIME头的键的规范格式。该标准会将首字母和所有"-"之后的字符改为大写，其余字母改为小写。举个例子，"accept-encoding"作为键的标准格式是"Accept-Encoding"。MIME头的键必须是ASCII码构成。

## func TrimBytes

    func TrimBytes(b []byte) []byte

去掉b前后的ASCII码空白（不去Unicode空白）

## func TrimString

    func TrimString(s string) string

去掉s前后的ASCII码空白（不去Unicode空白）

## type MIMEHeader

    type MIMEHeader map[string][]string

MIMEHeader代表一个MIME头，将键映射为值的集合。

### func (MIMEHeader) Get

    func (h MIMEHeader) Get(key string) string

Get方法返回键对应的值集的第一个值。如果键没有关联值，返回""。如要获得键对应的值集直接用map。

### func (MIMEHeader) Set

    func (h MIMEHeader) Set(key, value string)

Set方法将键对应的值集设置为只含有value一个值。没有就新建，有则删掉原有的值。

### func (MIMEHeader) Add

    func (h MIMEHeader) Add(key, value string)

Add方法向h中添加键值对，它会把新的值添加到键对应的值的集合里。

### func (MIMEHeader) Del

    func (h MIMEHeader) Del(key string)

Del方法删除键对应的值集。

## type Reader

    type Reader struct {
        R *bufio.Reader
        // 内含隐藏或非导出字段
    }

Reader实现了从一个文本协议网络连接中方便的读取请求/回复的方法。

### func NewReader

    func NewReader(r *bufio.Reader) *Reader

NewReader返回一个从r读取数据的Reader。

### func (*Reader) DotReader

    func (r *Reader) DotReader() io.Reader

DotReader方法返回一个io.Reader，该接口自动解码r中读取的点编码块。注意该接口仅在下一次调用r的方法之前才有效。点编码是文本协议如SMTP用于文本块的通用框架。数据包含多个行，每行以"\r\n"结尾。数据本身以一个只含有一个点的一行".\r\n"来结尾。以点起始的行会添加额外的点，来避免看起来像是文本的结尾。

返回接口的Read方法会将行尾的"\r\n"修改为"\n"，去掉起头的转义点，并在底层读取到（并抛弃掉）表示文本结尾的行时停止解码并返回io.EOF错误。

### func (*Reader) ReadLine

    func (r *Reader) ReadLine() (string, error)

ReadLine方法从r读取单行，去掉最后的\r\n或\n。

### func (*Reader) ReadLineBytes

    func (r *Reader) ReadLineBytes() ([]byte, error)

ReadLineBytes类似ReadLine但返回[]byte切片。

### func (*Reader) ReadContinuedLine

    func (r *Reader) ReadContinuedLine() (string, error)

ReadContinuedLine从r中读取可能有后续的行，会将该行尾段的ASCII空白剔除，并将该行后面所有以空格或者tab起始的行视为其后续，后续部分会剔除行头部的空白，所有这些行包括第一行以单个空格连接起来返回。

举例如下：

    Line 1
      continued...
    Line 2

第一次调用ReadContinuedLine会返回"Line 1 continued..."，第二次会返回"Line 2"

只有空格的行不被视为有后续的行。

### func (*Reader) ReadContinuedLineBytes

    func (r *Reader) ReadContinuedLineBytes() ([]byte, error)

ReadContinuedLineBytes类似ReadContinuedLine但返回[]byte切片。

### func (*Reader) ReadDotBytes

    func (r *Reader) ReadDotBytes() ([]byte, error)

ReadDotBytes读取点编码文本返回解码后的数据，点编码详见DotReader方法。

### func (*Reader) ReadDotLines

    func (r *Reader) ReadDotLines() ([]string, error)

ReadDotLines方法读取一个点编码文本块并返回一个包含解码后各行的切片，各行最后的\r\n或\n去掉。

### func (*Reader) ReadCodeLine

    func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error)

方法读取回复的状态码行，格式如下：

    code message

状态码是3位数字，message进一步描述状态，例如：

    220 plan9.bell-labs.com ESMTP

如果状态码字符串的前缀不匹配expectCode，方法返回错误&Error{code, message}。例如expectCode是31，则如果状态码不在区间[310, 319]内就会返回错误。如果回复是多行的则会返回错误。

如果expectCode <= 0，将不会检查状态码。

### func (*Reader) ReadResponse

    func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error)

ReadResponse方法读取如下格式的多行回复：

    code-message line 1
    code-message line 2
    ...
    code message line n

其中code是三位数的状态码。第一行以code和连字符开始，最后以同code后跟空格的行结束。返回值message每行以\n分隔。细节参见RFC 959(http://www.ietf.org/rfc/rfc959.txt)第36页。

如果状态码字符串的前缀不匹配expectCode，方法返回时err设为&Error{code, message}。例如expectCode是31，则如果状态码不在区间[310, 319]内就会返回错误。如果回复是多行的则会返回错误。

如果expectCode <= 0，将不会检查状态码。

### func (*Reader) ReadMIMEHeader

    func (r *Reader) ReadMIMEHeader() (MIMEHeader, error)

ReadMIMEHeader从r读取MIME风格的头域。该头域包含一系列可能有后续的键值行，以空行结束。返回的map映射CanonicalMIMEHeaderKey(key)到值的序列（顺序与输入相同）。

举例如下：

    My-Key: Value 1
    Long-Key: Even
           Longer Value
    My-Key: Value 2

对此输入，ReadMIMEHeader返回：

    map[string][]string{
    	"My-Key": {"Value 1", "Value 2"},
    	"Long-Key": {"Even Longer Value"},
    }

## type Writer

    type Writer struct {
        W *bufio.Writer
        // 内含隐藏或非导出字段
    }

Writer实现了方便的方法在一个文本协议网络连接中写入请求/回复。

### func NewWriter

    func NewWriter(w *bufio.Writer) *Writer

NewWriter函数返回一个底层写入w的Writer。

### func (*Writer) DotWriter

    func (w *Writer) DotWriter() io.WriteCloser

DotWriter方法返回一个io.WriteCloser，用于将点编码文本写入w。返回的接口会在必要时添加转义点，将行尾的\n替换为\r\n，并在关闭时添加最后的.\r\n行。调用者必须在下一次调用w的方法前关闭该接口。点编码文本格式参见Reader.DotReader方法。

### func (*Writer) PrintfLine

    func (w *Writer) PrintfLine(format string, args ...interface{}) error

PrintfLine方法将格式化的输出写入底层并在最后写入\r\n。

## type Pipeline

    type Pipeline struct {
        // 内含隐藏或非导出字段
    }

Pipeline管理管道化的有序请求/回复序列。

为了使用Pipeline管理一个连接的多个客户端，每个客户端应像下面一样运行：

    id := p.Next()      // 获取一个数字id
    p.StartRequest(id)  // 等待轮到该id发送请求
    «send request»
    p.EndRequest(id)    // 通知Pipeline请求发送完毕
    p.StartResponse(id) // 等待该id读取回复
    «read response»
    p.EndResponse(id)   // 通知Pipeline回复已经读取

一个管道化的服务器可以使用相同的调用来保证回复并行的生成并以正确的顺序写入。

### func (*Pipeline) Next

    func (p *Pipeline) Next() uint

返回下一对request/response的id。

### func (*Pipeline) StartRequest

    func (p *Pipeline) StartRequest(id uint)

阻塞程序，直到轮到给定id来发送（读取）request。

### func (*Pipeline) StartResponse

    func (p *Pipeline) StartResponse(id uint)

阻塞程序，直到轮到给定id来读取（发送）response。

### func (*Pipeline) EndRequest

    func (p *Pipeline) EndRequest(id uint)

通知p，给定id的request的操作已经结束了。

### func (*Pipeline) EndResponse

    func (p *Pipeline) EndResponse(id uint)

通知p，给定id的response的操作已经结束了。

## type Conn

    type Conn struct {
        Reader
        Writer
        Pipeline
        // 内含隐藏或非导出字段
    }

Conn代表一个文本网络协议的连接。它包含一个Reader和一个Writer来管理读写，一个Pipeline来对连接中并行的请求进行排序。匿名嵌入的类型字段是Conn可以调用它们的方法。

### func NewConn

    func NewConn(conn io.ReadWriteCloser) *Conn

NewConn函数返回以I/O为底层的Conn。

### func Dial

    func Dial(network, addr string) (*Conn, error)

Dial函数使用net.Dial在给定网络上和给定地址建立网络连接，并返回用于该连接的Conn。

### func (*Conn) Cmd

    func (c *Conn) Cmd(format string, args ...interface{}) (id uint, err error)

Cmd方法用于在管道中等待轮到它执行，并发送命令。命令文本是用给定的format字符串和参数格式化生成的。并会在最后添加上\r\n。Cmd函数返回该命令的Pipeline id，用于StartResponse和EndResponse方法。

例如，一个客户端可以使用如下代码执行HELP命令并返回解码后的点编码文本：

    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) Close

    func (c *Conn) Close() error

Close方法关闭连接。

## Bugs

☞为了让调用者处理拒绝服务攻击，Reader接口应该允许他们设置和重设从连接读取的字节数。