标准库的net/http包提供了HTTP客户端和服务端的实现,本文通过几个主要的结构体来了解http包中与客户端相关的主要功能。

1. Client

client 负责把请求发送给server端,通过一个http.Transport实例指定http请求的低级别配置,如果没有配置会用一个默认的DefualtTransport实例代替。

// A Client is an HTTP client. Its zero value (DefaultClient) is a usable client that uses DefaultTransport.
type Client struct {
	Transport RoundTripper
	CheckRedirect func(req *Request, via []*Request) error
	Jar CookieJar
	Timeout time.Duration
}

// RoundTripper接口提供一个最终发送请求的方法,http.Transport实现了RoundTripper接口
type RoundTripper interface {
	// RoundTrip执行单个HTTP事务,为所提供的请求返回响应。
    RoundTrip(*Request) (*Response, error)
}

// DefaultTransport
var DefaultTransport RoundTripper = &Transport{
	Proxy: ProxyFromEnvironment,
	DialContext: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		DualStack: true,
	}).DialContext,
	ForceAttemptHTTP2:     true,
	MaxIdleConns:          100,
	IdleConnTimeout:       90 * time.Second,
	TLSHandshakeTimeout:   10 * time.Second,
	ExpectContinueTimeout: 1 * time.Second,
}
复制代码

Do()方法最终都会调用RoundTrip处理请求

// send issues an HTTP request.
func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {

	// 为了节省空间,隐藏这部分代码

	resp, err = rt.RoundTrip(req)

	// 为了节省空间,隐藏这部分代码

	return resp, nil, nil
}
复制代码

为http.Client配置Transport实例

tr := &http.Transport{
		Proxy: http.ProxyURL(w.ProxyAddr),
		MaxIdleConnsPerHost: min(w.C, maxConn),
		DisableCompression:  w.DisableCompression,
		DisableKeepAlives:   w.DisableKeepAlives,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: true,
			ServerName:         url.Host,
		},
	}
	// 通过Transport实例为Http.Client增加配置
	client := &http.Client{
		Transport: tr,
		Timeout:   w.ConfigYaml.Http.Timeout * time.Second,
	}
复制代码

Client结构体提供的发送http请求的方法:

Do()接收一个http.Request参数,其他几个请求内部都是调用Do()方法

Get(),Head(),

Post(),可以自定义contentType,接收一个body数据

func (c *Client) Post(url, contentType string, body io.Reader) (resp *Response, err error) {
	req, err := NewRequest("POST", url, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", contentType)
	return c.Do(req)
}
复制代码

PostForm(),也是发送一个post请求,区别在于PostForm的contentType固定为"application/x-www-form-urlencoded",接收一个url.Values类型的数据,再将数据编码为"URL编码"形式("bar=baz&foo=quux"),

func (c *Client) PostForm(url string, data url.Values) (resp *Response, err error) {
	return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode()))
}
复制代码

CloseIdleConnections(),关闭以前请求的但现在处于“keep-alive”状态的所有连接。它不会中断任何当前正在使用的连接。

2. Transport

配置http请求的低级别信息的结构体

Package:
http

// A Transport is a low-level primitive for making HTTP and HTTPS requests.
// For high-level functionality, such as cookies and redirects, see Client.
type Transport struct {
    idleMu                 sync.Mutex
    closeIdle              bool
    idleConn               map[connectMethodKey][]*persistConn
    idleConnWait           map[connectMethodKey]wantConnQueue
    idleLRU                connLRU
    reqMu                  sync.Mutex
    reqCanceler            map[*Request]func(error)
    altMu                  sync.Mutex
    altProto               atomic.Value
    connsPerHostMu         sync.Mutex
    connsPerHost           map[connectMethodKey]int
    connsPerHostWait       map[connectMethodKey]wantConnQueue
    Proxy                  func(*Request) (*url.URL, error)
    DialContext            func(ctx context.Context, network string, addr string) (net.Conn, error)
    Dial                   func(network string, addr string) (net.Conn, error)
    DialTLSContext         func(ctx context.Context, network string, addr string) (net.Conn, error)
    DialTLS                func(network string, addr string) (net.Conn, error)
    TLSClientConfig        *tls.Config
    TLSHandshakeTimeout    time.Duration
    DisableKeepAlives      bool
    DisableCompression     bool
    MaxIdleConns           int
    MaxIdleConnsPerHost    int
    MaxConnsPerHost        int
    IdleConnTimeout        time.Duration
    ResponseHeaderTimeout  time.Duration
    ExpectContinueTimeout  time.Duration
    TLSNextProto           map[string]func(authority string, c *tls.Conn) RoundTripper
    ProxyConnectHeader     Header
    MaxResponseHeaderBytes int64
    WriteBufferSize        int
    ReadBufferSize         int
    nextProtoOnce          sync.Once
    h2transport            h2Transport
    tlsNextProtoWasNil     bool
    ForceAttemptHTTP2      bool
}
复制代码

http.Client实际执行请求时会带上Transport实例,如果没配置就用默认的DefaultTransport

func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
	if c.Jar != nil {
		for _, cookie := range c.Jar.Cookies(req.URL) {
			req.AddCookie(cookie)
		}
	}

	// 实际执行请求时会带上Transport实例
	resp, didTimeout, err = send(req, c.transport(), deadline)
	if err != nil {
		return nil, didTimeout, err
	}
	if c.Jar != nil {
		if rc := resp.Cookies(); len(rc) > 0 {
			c.Jar.SetCookies(req.URL, rc)
		}
	}
	return resp, nil, nil
}

// 如果没配置Http.Transport,会用一个DefaultTransport代替
func (c *Client) transport() RoundTripper {
	if c.Transport != nil {
		return c.Transport
	}
	return DefaultTransport
}

复制代码

3. Request

Request代表通过一个Server接收的请求,或者一个通过Client发送的请求。

//A Request represents an HTTP request received by a server or to be sent by a client.
type Request struct {
    Method           string
    URL              *url.URL
    Proto            string
    ProtoMajor       int
    ProtoMinor       int
    Header           Header
    Body             io.ReadCloser
    GetBody          func() (io.ReadCloser, error)
    ContentLength    int64
    TransferEncoding []string
    Close            bool
    Host             string
    Form             url.Values
    PostForm         url.Values
    MultipartForm    *multipart.Form
    Trailer          Header
    RemoteAddr       string
    RequestURI       string
    TLS              *tls.ConnectionState
    Cancel           <-chan struct{}
    Response         *Response
    ctx              context.Context
}
复制代码

4. Cookie

Cookie表示在HTTP响应Header中的"Set-Cookie",或HTTP请求header中的"Cookie"发送的HTTP Cookie。

type Cookie struct {
    Name       string
    Value      string
    Path       string
    Domain     string
    Expires    time.Time
    RawExpires string
    MaxAge     int
    Secure     bool
    HttpOnly   bool
    SameSite   SameSite
    Raw        string
    Unparsed   []string
}

Methods:
func (c *Cookie) String() string
func SetCookie(w ResponseWriter, cookie *Cookie)
复制代码

5. Response

Response表示来自HTTP请求的响应。 Http.Client和http.Transport 返回Response,一旦这个response header 被接收完,Response的body字段需要被当作流来处理。

type Response struct {
    Status           string
    StatusCode       int
    Proto            string
    ProtoMajor       int
    ProtoMinor       int
    Header           Header
    Body             io.ReadCloser
    ContentLength    int64
    TransferEncoding []string
    Close            bool
    Uncompressed     bool
	Trailer          Header

	// Request is the request that was sent to obtain this Response.
	// Request's Body is nil (having already been consumed).
	// This is only populated for Client requests.
    Request          *Request
    TLS              *tls.ConnectionState
}

Methods:
Cookies() []*Cookie // 从header中解析并返回所有Set-cookie
Location() (*url.URL, error) 
ProtoAtLeast(major int, minor int) bool //确认响应中使用的HTTP协议是否至少是major.minor。
Write(w io.Writer) error // 把response的内容写到 w 中

复制代码