标准库的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 中
复制代码