Golang标准库——net(3)

  • mail
  • rpc
  • rpc/jsonrpc
  • smtp
  • textproto
  • url

mail

本包大部分都遵守RFC 5322规定的语法,值得注意的区别是:

* 旧格式地址和嵌入远端信息的地址不会被解析
* 组地址不会被解析
* 不支持全部的间隔符(CFWS语法元素),如分属两行的地址

Variables

var ErrHeaderNotPresent = errors.New("mail: header not in message")

type Address

type Address struct {
    Name    string // 固有名,可以为空
    Address string // user@domain
}

Address类型表示一个邮箱地址。

例如地址"Barry Gibbs bg@example.com"表示为Address{Name: "Barry Gibbs", Address: "bg@example.com"}

func ParseAddress

func ParseAddress(address string) (*Address, error)

解析单个的RFC 5322地址,例如"Barry Gibbs bg@example.com"。

func (*Address) String

func (a *Address) String() string

将a代表的地址表示为合法的RFC 5322地址字符串。如果Name字段包含非ASCII字符将根据RFC 2047转义。

func ParseAddressList

func ParseAddressList(list string) ([]*Address, error)

函数将list作为一串邮箱地址并解析返回。

type Header

type Header map[string][]string

Header代表邮件头域的多个键值对。

func (Header) AddressList

func (h Header) AddressList(key string) ([]*Address, error)

将键key对应的值(字符串)作为邮箱地址列表解析并返回。

func (Header) Date

func (h Header) Date() (time.Time, error)

解析头域Date项的值并返回。

func (Header) Get

func (h Header) Get(key string) string

返回键key对应的第一个值,如果没有对应值,将返回空字符串。

type Message

type Message struct {
    Header Header
    Body   io.Reader
}

Message代表一个解析后的邮件。

func ReadMessage

func ReadMessage(r io.Reader) (msg *Message, err error)

从r读取一个邮件,会解析邮件头域,消息主体可以从r/msg.Body中读取。

RPC

rpc包提供了通过网络或其他I/O连接对一个对象的导出方法的访问。服务端注册一个对象,使它作为一个服务被暴露,服务的名字是该对象的类型名。注册之后,对象的导出方法就可以被远程访问。服务端可以注册多个不同类型的对象(服务),但注册具有相同类型的多个对象是错误的。

只有满足如下标准的方法才能用于远程访问,其余方法会被忽略:

- 方法是导出的
- 方法有两个参数,都是导出类型或内建类型
- 方法的第二个参数是指针
- 方法只有一个error接口类型的返回值

事实上,方法必须看起来像这样:

func (t *T) MethodName(argType T1, replyType *T2) error

其中T、T1和T2都能被encoding/gob包序列化。这些限制即使使用不同的编解码器也适用。(未来,对定制的编解码器可能会使用较宽松一点的限制)

方法的第一个参数代表调用者提供的参数;第二个参数代表返回给调用者的参数。方法的返回值,如果非nil,将被作为字符串回传,在客户端看来就和errors.New创建的一样。如果返回了错误,回复的参数将不会被发送给客户端。

服务端可能会单个连接上调用ServeConn管理请求。更典型地,它会创建一个网络监听器然后调用Accept;或者,对于HTTP监听器,调用HandleHTTP和http.Serve。

想要使用服务的客户端会创建一个连接,然后用该连接调用NewClient。

更方便的函数Dial(DialHTTP)会在一个原始的连接(或HTTP连接)上依次执行这两个步骤。

生成的Client类型值有两个方法,Call和Go,它们的参数为要调用的服务和方法、一个包含参数的指针、一个用于接收接个的指针。

Call方法会等待远端调用完成,而Go方法异步的发送调用请求并使用返回的Call结构体类型的Done通道字段传递完成信号。

除非设置了显式的编解码器,本包默认使用encoding/gob包来传输数据。

这是一个简单的例子。一个服务端想要导出Arith类型的一个对象:

package server
type Args struct {
    A, B int
}
type Quotient struct {
    Quo, Rem int
}
type Arith int
func (t *Arith) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}
func (t *Arith) Divide(args *Args, quo *Quotient) error {
    if args.B == 0 {
        return errors.New("divide by zero")
    }
    quo.Quo = args.A / args.B
    quo.Rem = args.A % args.B
    return nil
}

服务端会调用(用于HTTP服务):

arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
    log.Fatal("listen error:", e)
}
go http.Serve(l, nil)

此时,客户端可看到服务"Arith"及它的方法"Arith.Multiply"、"Arith.Divide"。要调用方法,客户端首先呼叫服务端:

client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
    log.Fatal("dialing:", err)
}

然后,客户端可以执行远程调用:

// Synchronous call
args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
    log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)

或:

// Asynchronous call
quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.

服务端的实现应为客户端提供简单、类型安全的包装。

Constants

const (
    // HandleHTTP使用的默认值
    DefaultRPCPath   = "/_goRPC_"
    DefaultDebugPath = "/debug/rpc"
)

Variables

var DefaultServer = NewServer()

DefaultServer是*Server的默认实例,本包和Server方法同名的函数都是对其方法的封装。

var ErrShutdown = errors.New("connection is shut down")

type ServerError

type ServerError string

ServerError表示已从RPC连接的远程端返回的错误。

func (ServerError) Error

func (e ServerError) Error() string

type Request

type Request struct {
    ServiceMethod string // 格式:"Service.Method"
    Seq           uint64 // 由客户端选择的序列号
    // 内含隐藏或非导出字段
}

Request是每个RPC调用请求的头域。它是被内部使用的,这里的文档用于帮助debug,如分析网络拥堵时。

type Response

type Response struct {
    ServiceMethod string // 对应请求的同一字段
    Seq           uint64 // 对应请求的同一字段
    Error         string // 可能的错误
    // 内含隐藏或非导出字段
}

Response是每个RPC调用回复的头域。它是被内部使用的,这里的文档用于帮助debug,如分析网络拥堵时。

type ClientCodec

type ClientCodec interface {
    // 本方法必须能安全的被多个go程同时使用
    WriteRequest(*Request, interface{}) error
    ReadResponseHeader(*Response) error
    ReadResponseBody(interface{}) error
    Close() error
}

ClientCodec接口实现了RPC会话的客户端一侧RPC请求的写入和RPC回复的读取。客户端调用WriteRequest来写入请求到连接,然后成对调用ReadRsponseHeader和ReadResponseBody以读取回复。客户端在结束该连接的事务时调用Close方法。ReadResponseBody可以使用nil参数调用,以强制回复的主体被读取然后丢弃。

type ServerCodec

type ServerCodec interface {
    ReadRequestHeader(*Request) error
    ReadRequestBody(interface{}) error
    // 本方法必须能安全的被多个go程同时使用
    WriteResponse(*Response, interface{}) error
    Close() error
}

ServerCodec接口实现了RPC会话的服务端一侧RPC请求的读取和RPC回复的写入。服务端通过成对调用方法ReadRequestHeader和ReadRequestBody从连接读取请求,然后调用WriteResponse来写入回复。服务端在结束该连接的事务时调用Close方法。ReadRequestBody可以使用nil参数调用,以强制请求的主体被读取然后丢弃。

type Call

type Call struct {
    ServiceMethod string      // 调用的服务和方法的名称
    Args          interface{} // 函数的参数(下层为结构体指针)
    Reply         interface{} // 函数的回复(下层为结构体指针)
    Error         error       // 在调用结束后,保管错误的状态
    Done          chan *Call  // 对其的接收操作会阻塞,直到远程调用结束
}

Call类型代表一个执行中/执行完毕的RPC会话。

type Client

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

Client类型代表RPC客户端。同一个客户端可能有多个未返回的调用,也可能被多个go程同时使用。

func NewClient

func NewClient(conn io.ReadWriteCloser) *Client

NewClient返回一个新的Client,以管理对连接另一端的服务的请求。它添加缓冲到连接的写入侧,以便将回复的头域和有效负载作为一个单元发送。

func NewClientWithCodec

func NewClientWithCodec(codec ClientCodec) *Client

NewClientWithCodec类似NewClient,但使用指定的编解码器,以编码请求主体和解码回复主体。

func Dial

func Dial(network, address string) (*Client, error)

Dial在指定的网络和地址与RPC服务端连接。

func DialHTTP

func DialHTTP(network, address string) (*Client, error)

DialHTTP在指定的网络和地址与在默认HTTP RPC路径监听的HTTP RPC服务端连接。

func DialHTTPPath

func DialHTTPPath(network, address, path string) (*Client, error)

DialHTTPPath在指定的网络、地址和路径与HTTP RPC服务端连接。

func (*Client) Call

func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error

Call调用指定的方法,等待调用返回,将结果写入reply,然后返回执行的错误状态。

func (*Client) Go

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call

Go异步的调用函数。本方法Call结构体类型指针的返回值代表该次远程调用。通道类型的参数done会在本次调用完成时发出信号(通过返回本次Go方法的返回值)。如果done为nil,Go会申请一个新的通道(写入返回值的Done字段);如果done非nil,done必须有缓冲,否则Go方法会故意崩溃。

func (*Client) Close

func (client *Client) Close() error

type Server

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

Server代表RPC服务端。

func NewServer

func NewServer() *Server

NewServer创建并返回一个*Server。

func (*Server) Register

func (server *Server) Register(rcvr interface{}) error

Register在server注册并公布rcvr的方法集中满足如下要求的方法:

- 方法是导出的
- 方法有两个参数,都是导出类型或内建类型
- 方法的第二个参数是指针
- 方法只有一个error接口类型的返回值

如果rcvr不是一个导出类型的值,或者该类型没有满足要求的方法,Register会返回错误。Register也会使用log包将错误写入日志。客户端可以使用格式为"Type.Method"的字符串访问这些方法,其中Type是rcvr的具体类型。

func (*Server) RegisterName

func (server *Server) RegisterName(name string, rcvr interface{}) error

RegisterName类似Register,但使用提供的name代替rcvr的具体类型名作为服务名。

func (*Server) Accept

func (server *Server) Accept(lis net.Listener)

Accept接收监听器l获取的连接,然后服务每一个连接。Accept会阻塞,调用者应另开线程:"go server.Accept(l)"

func (*Server) ServeConn

func (server *Server) ServeConn(conn io.ReadWriteCloser)

ServeConn在单个连接上执行server。ServeConn会阻塞,服务该连接直到客户端挂起。调用者一般应另开线程调用本函数:"go server.ServeConn(conn)"。ServeConn在该连接使用gob(参见encoding/gob包)有线格式。要使用其他的编解码器,可调用ServeCodec方法。

func (*Server) ServeCodec

func (server *Server) ServeCodec(codec ServerCodec)

ServeCodec类似ServeConn,但使用指定的编解码器,以编码请求主体和解码回复主体。

func (*Server) ServeRequest

func (server *Server) ServeRequest(codec ServerCodec) error

ServeRequest类似ServeCodec,但异步的服务单个请求。它不会在调用结束后关闭codec。

func (*Server) ServeHTTP

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP实现了回应RPC请求的http.Handler接口。

func (*Server) HandleHTTP

func (server *Server) HandleHTTP(rpcPath, debugPath string)

HandleHTTP注册server的RPC信息HTTP处理器对应到rpcPath,注册server的debug信息HTTP处理器对应到debugPath。HandleHTTP会注册到http.DefaultServeMux。之后,仍需要调用http.Serve(),一般会另开线程:"go http.Serve(l, nil)"

func Register

func Register(rcvr interface{}) error

Register在DefaultServer注册并公布rcvr的方法。

func RegisterName

func RegisterName(name string, rcvr interface{}) error

RegisterName函数类似Register函数,但使用提供的name代替rcvr的具体类型名作为服务名。

func Accept

func Accept(lis net.Listener)

Accept接收监听器l获取的连接,然后将每一个连接交给DefaultServer服务。Accept会阻塞,调用者应另开线程:"go server.Accept(l)"

func ServeConn

func ServeConn(conn io.ReadWriteCloser)

ServeConn在单个连接上执行DefaultServer。ServeConn会阻塞,服务该连接直到客户端挂起。调用者一般应另开线程调用本函数:"go ServeConn(conn)"。ServeConn在该连接使用gob(参见encoding/gob包)有线格式。要使用其他的编解码器,可调用ServeCodec方法。

func ServeCodec

func ServeCodec(codec ServerCodec)

ServeCodec类似ServeConn,但使用指定的编解码器,以编码请求主体和解码回复主体。

func ServeRequest

func ServeRequest(codec ServerCodec) error

ServeRequest类似ServeCodec,但异步的服务单个请求。它不会在调用结束后关闭codec。

func HandleHTTP

func HandleHTTP()

HandleHTTP函数注册DefaultServer的RPC信息HTTP处理器对应到DefaultRPCPath,和DefaultServer的debug处理器对应到DefaultDebugPath。HandleHTTP函数会注册到http.DefaultServeMux。之后,仍需要调用http.Serve(),一般会另开线程:"go http.Serve(l, nil)"

rpc/jsonrpc

jsonrpc包实现了JSON-RPC的ClientCodec和ServerCodec接口,可用于rpc包。

func Dial

func Dial(network, address string) (*rpc.Client, error)

Dial在指定的网络和地址连接一个JSON-RPC服务端。

func NewClient

func NewClient(conn io.ReadWriteCloser) *rpc.Client

NewClient返回一个新的rpc.Client,以管理对连接另一端的服务的请求。

func NewClientCodec

func NewClientCodec(conn io.ReadWriteCloser) rpc.ClientCodec

NewClientCodec返回一个在连接上使用JSON-RPC的rpc.ClientCodec。

func NewServerCodec

func NewServerCodec(conn io.ReadWriteCloser) rpc.ServerCodec

NewServerCodec返回一个在连接上使用JSON-RPC的rpc. ServerCodec。

func ServeConn

func ServeConn(conn io.ReadWriteCloser)

ServeConn在单个连接上执行DefaultServer。ServeConn会阻塞,服务该连接直到客户端挂起。调用者一般应另开线程调用本函数:"go serveConn(conn)"。ServeConn在该连接使用JSON编解码格式。

smtp

smtp包实现了简单邮件传输协议(SMTP),参见RFC 5321。同时本包还实现了如下扩展:

8BITMIME  RFC 1652
AUTH      RFC 2554
STARTTLS  RFC 3207

客户端可以自行管理其他的扩展。

// Connect to the remote SMTP server.
c, err := smtp.Dial("mail.example.com:25")
if err != nil {
    log.Fatal(err)
}
// Set the sender and recipient first
if err := c.Mail("sender@example.org"); err != nil {
    log.Fatal(err)
}
if err := c.Rcpt("recipient@example.net"); err != nil {
    log.Fatal(err)
}
// Send the email body.
wc, err := c.Data()
if err != nil {
    log.Fatal(err)
}
_, err = fmt.Fprintf(wc, "This is the email body")
if err != nil {
    log.Fatal(err)
}
err = wc.Close()
if err != nil {
    log.Fatal(err)
}
// Send the QUIT command and close the connection.
err = c.Quit()
if err != nil {
    log.Fatal(err)
}

type ServerInfo

type ServerInfo struct {
    Name string   // SMTP服务器的名字
    TLS  bool     // Name有合法的证书并使用TLS时为真
    Auth []string // 支持的认证机制
}

ServerInfo类型记录一个SMTP服务器的信息。

type Auth

type Auth interface {
    // 方法开始和服务端的认证。
    // 它返回认证协议的名字和可能有的应发送给服务端的包含初始认证信息的数据。
    // 如果返回值proto == "",表示应跳过认证;
    // 如果返回一个非nil的错误,SMTP客户端应中断认证身份的尝试并关闭连接。
    Start(server *ServerInfo) (proto string, toServer []byte, err error)
    // 方法继续认证过程。fromServer为服务端刚发送的数据。
    // 如果more为真,服务端会期望一个回复,回复内容应被Next返回,即toServer;
    // 否则返回值toServer应为nil。
    // 如果返回一个非nil的错误,SMTP客户端应中断认证身份的尝试并关闭连接。
    Next(fromServer []byte, more bool) (toServer []byte, err error)
}

Auth接口应被每一个SMTP认证机制实现。

func CRAMMD5Auth

func CRAMMD5Auth(username, secret string) Auth

返回一个实现了CRAM-MD5身份认证机制(参见RFC 2195)的Auth接口。返回的接口使用给出的用户名和密码,采用响应——回答机制与服务端进行身份认证。

func PlainAuth

func PlainAuth(identity, username, password, host string) Auth

返回一个实现了PLAIN身份认证机制(参见RFC 4616)的Auth接口。返回的接口使用给出的用户名和密码,通过TLS连接到主机认证,采用identity为身份管理和行动(通常应设identity为"",以便使用username为身份)。

// Set up authentication information.
auth := smtp.PlainAuth("", "user@example.com", "password", "mail.example.com")
// Connect to the server, authenticate, set the sender and recipient,
// and send the email all in one step.
to := []string{"recipient@example.net"}
msg := []byte("This is the email body.")
err := smtp.SendMail("mail.example.com:25", auth, "sender@example.org", to, msg)
if err != nil {
    log.Fatal(err)
}

type Client

type Client struct {
    // 代表被Client使用的textproto.Conn,它可以导出,以便使用者添加扩展。
    Text *textproto.Conn
    // 内含隐藏或非导出字段
}

Client代表一个连接到SMTP服务器的客户端。

func Dial

func Dial(addr string) (*Client, error)

Dial返回一个连接到地址为addr的SMTP服务器的*Client;addr必须包含端口号。

func NewClient

func NewClient(conn net.Conn, host string) (*Client, error)

NewClient使用已经存在的连接conn和作为服务器名的host(用于身份认证)来创建一个*Client。

func (*Client) Extension

func (c *Client) Extension(ext string) (bool, string)

Extension返回服务端是否支持某个扩展,扩展名是大小写不敏感的。如果扩展被支持,方法还会返回一个包含指定给该扩展的各个参数的字符串。

func (*Client) Hello

func (c *Client) Hello(localName string) error

Hello发送给服务端一个HELO或EHLO命令。本方法只有使用者需要控制使用的本地主机名时才应使用,否则程序会将本地主机名设为“localhost”,Hello方法只能在最开始调用。

func (*Client) Auth

func (c *Client) Auth(a Auth) error

Auth使用提供的认证机制进行认证。失败的认证会关闭该连接。只有服务端支持AUTH时,本方法才有效。(但是不支持时,调用会默默的成功)

func (*Client) Verify

func (c *Client) Verify(addr string) error

Verify检查一个邮箱地址在其服务器是否合法,如果合法会返回nil;但非nil的返回值并不代表不合法,因为许多服务器出于安全原因不支持这种查询。

func (*Client) StartTLS

func (c *Client) StartTLS(config *tls.Config) error

StartTLS方法发送STARTTLS命令,并将之后的所有数据往来加密。只有服务器附加了STARTTLS扩展,这个方法才有效。

func (*Client) Mail

func (c *Client) Mail(from string) error

Mail发送MAIL命令和邮箱地址from到服务器。如果服务端支持8BITMIME扩展,本方法会添加BODY=8BITMIME参数。方法初始化一次邮件传输,后应跟1到多个Rcpt方法的调用。

func (*Client) Rcpt

func (c *Client) Rcpt(to string) error

Rcpt发送RCPT命令和邮箱地址to到服务器。调用Rcpt方法之前必须调用了Mail方法,之后可以再一次调用Rcpt方法,也可以调用Data方法。

func (*Client) Data

func (c *Client) Data() (io.WriteCloser, error)

Data发送DATA指令到服务器并返回一个io.WriteCloser,用于写入邮件信息。调用者必须在调用c的下一个方法之前关闭这个io.WriteCloser。方法必须在一次或多次Rcpt方法之后调用。

func (*Client) Reset

func (c *Client) Reset() error

Reset向服务端发送REST命令,中断当前的邮件传输。

func (*Client) Quit

func (c *Client) Quit() error

Quit发送QUIT命令并关闭到服务端的连接。

func (*Client) Close

func (c *Client) Close() error

Close关闭连接。

func SendMail

func SendMail(addr string, a Auth, from string, to []string, msg []byte) error

SendMail连接到addr指定的服务器;如果支持会开启TLS;如果支持会使用a认证身份;然后以from为邮件源地址发送邮件msg到目标地址to。(可以是多个目标地址:群发)

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接口应该允许他们设置和重设从连接读取的字节数。

url

url包解析URL并实现了查询的逸码,参见RFC 3986

func QueryEscape

func QueryEscape(s string) string

QueryEscape函数对s进行转码使之可以安全的用在URL查询里。

func QueryUnescape

func QueryUnescape(s string) (string, error)

QueryUnescape函数用于将QueryEscape转码的字符串还原。它会把%AB改为字节0xAB,将'+'改为' '。如果有某个%后面未跟两个十六进制数字,本函数会返回错误。

type Error

type Error struct {
    Op  string
    URL string
    Err error
}

Error会报告一个错误,以及导致该错误发生的URL和操作。

func (*Error) Error

func (e *Error) Error() string

type EscapeError

type EscapeError string

func (EscapeError) Error

func (e EscapeError) Error() string

type URL

type URL struct {
    Scheme   string
    Opaque   string    // 编码后的不透明数据
    User     *Userinfo // 用户名和密码信息
    Host     string    // host或host:port
    Path     string
    RawQuery string // 编码后的查询字符串,没有'?'
    Fragment string // 引用的片段(文档位置),没有'#'
}

URL类型代表一个解析后的URL(或者说,一个URL参照)。URL基本格式如下:

scheme://[userinfo@]host/path[?query][#fragment]

scheme后不是冒号加双斜线的URL被解释为如下格式:

scheme:opaque[?query][#fragment]

注意路径字段是以解码后的格式保存的,如/%47%6f%2f会变成/Go/。这导致我们无法确定Path字段中的斜线是来自原始URL还是解码前的%2f。除非一个客户端必须使用其他程序/函数来解析原始URL或者重构原始URL,这个区别并不重要。此时,HTTP服务端可以查询req.RequestURI,而HTTP客户端可以使用URL{Host: "example.com", Opaque: "//example.com/Go%2f"}代替{Host: "example.com", Path: "/Go/"}。

func Parse

func Parse(rawurl string) (url *URL, err error)

Parse函数解析rawurl为一个URL结构体,rawurl可以是绝对地址,也可以是相对地址。

func ParseRequestURI

func ParseRequestURI(rawurl string) (url *URL, err error)

ParseRequestURI函数解析rawurl为一个URL结构体,本函数会假设rawurl是在一个HTTP请求里,因此会假设该参数是一个绝对URL或者绝对路径,并会假设该URL没有#fragment后缀。(网页浏览器会在去掉该后缀后才将网址发送到网页服务器)

func (*URL) IsAbs

func (u *URL) IsAbs() bool

函数在URL是绝对URL时才返回真。

func (*URL) Query

func (u *URL) Query() Values

Query方法解析RawQuery字段并返回其表示的Values类型键值对。

func (*URL) RequestURI

func (u *URL) RequestURI() string

RequestURI方法返回编码好的path?query或opaque?query字符串,用在HTTP请求里。

func (*URL) String

func (u *URL) String() string

String将URL重构为一个合法URL字符串。

func (*URL) Parse

func (u *URL) Parse(ref string) (*URL, error)

Parse方法以u为上下文来解析一个URL,ref可以是绝对或相对URL。

本方法解析失败会返回nil, err;否则返回结果和ResolveReference一致。

func (*URL) ResolveReference

func (u *URL) ResolveReference(ref *URL) *URL

本方法根据一个绝对URI将一个URI补全为一个绝对URI,参见RFC 3986 节 5.2。参数ref可以是绝对URI或者相对URI。ResolveReference总是返回一个新的URL实例,即使该实例和u或者ref完全一样。如果ref是绝对URI,本方法会忽略参照URI并返回ref的一个拷贝。

type Userinfo

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

Userinfo类型是一个URL的用户名和密码细节的一个不可修改的封装。一个真实存在的Userinfo值必须保证有用户名(但根据 RFC 2396可以是空字符串)以及一个可选的密码。

func User

func User(username string) *Userinfo

User函数返回一个用户名设置为username的不设置密码的*Userinfo。

func UserPassword

func UserPassword(username, password string) *Userinfo

UserPassword函数返回一个用户名设置为username、密码设置为password的*Userinfo。

这个函数应该只用于老式的站点,因为风险很大,不建议使用,参见RFC 2396

func (*Userinfo) Username

func (u *Userinfo) Username() string

Username方法返回用户名。

func (*Userinfo) Password

func (u *Userinfo) Password() (string, bool)

如果设置了密码返回密码和真,否则会返回假。

func (*Userinfo) String

func (u *Userinfo) String() string

String方法返回编码后的用户信息,格式为"username[:password]"。

type Values

type Values map[string][]string

Values将建映射到值的列表。它一般用于查询的参数和表单的属性。不同于http.Header这个字典类型,Values的键是大小写敏感的。

func ParseQuery

func ParseQuery(query string) (m Values, err error)

ParseQuery函数解析一个URL编码的查询字符串,并返回可以表示该查询的Values类型的字典。本函数总是返回一个包含了所有合法查询参数的非nil字典,err用来描述解码时遇到的(如果有)第一个错误。

func (Values) Get

func (v Values) Get(key string) string

Get会获取key对应的值集的第一个值。如果没有对应key的值集会返回空字符串。获取值集请直接用map。

func (Values) Set

func (v Values) Set(key, value string)

Set方法将key对应的值集设为只有value,它会替换掉已有的值集。

func (Values) Add

func (v Values) Add(key, value string)

Add将value添加到key关联的值集里原有的值的后面。

func (Values) Del

func (v Values) Del(key string)

Del删除key关联的值集。

func (Values) Encode

func (v Values) Encode() string

Encode方法将v编码为url编码格式("bar=baz&foo=quux"),编码时会以键进行排序。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345