快速理解golang标准库net/http包(一)客户端篇

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

  1. Client

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
golang复制代码// 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处理请求

1
2
3
4
5
6
7
8
9
10
11
golang复制代码// 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实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
golang复制代码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数据

1
2
3
4
5
6
7
8
golang复制代码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”),

1
2
3
golang复制代码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”状态的所有连接。它不会中断任何当前正在使用的连接。

  1. Transport

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
golang复制代码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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
golang复制代码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
}
  1. Request

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
golang复制代码//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
}
  1. Cookie

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
golang复制代码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)
  1. Response

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
golang复制代码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 中

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%