http 中的 client 比较简单,请求的发送和接受都是使用的 RoundTripper
接口,这个接口的实现在 transport.go 文件中。
type RoundTripper interface {
RoundTrip(*Request) (*Response, error)
}
Do()
http 中默认的 Get()
, Post()
, Head()
方法实际上都是调用 DefaultClient 的对应方法,DefaultClient 只是一个空的 Clinet 对象,他所有的值全部都是默认值。
func Get(url string) (resp *Response, err error) {
return DefaultClient.Get(url)
}
Client 中的 Get()
, Post()
, Head()
方法也都是先生成一个对应方法的 Request 然后再 调用 Client 的 Do()
方法进行发送。 Do()
的底层也是使用 RoundTripper
接口,所以真正进行传输的是 transport 结构体。
func (c *Client) Get(url string) (resp *Response, err error) {
req, err := NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
return c.Do(req)
}
Redirect()
在 Do()
函数中,如果发送的 Response 状态码为 3xx ,就会产生重定向,默认重定向次数应该小于 10 次
func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
switch resp.StatusCode {
case 301, 302, 303:
// 301 Moved Permanently
// 302 Found
// 303 See Other
redirectMethod = reqMethod
shouldRedirect = true
includeBody = false
// RFC 2616 规定重定向只能是 GET 或者 HEAD 方法
// RFC 7231 取消了这个限制,但是为了保持兼容,实现上仍然这样做
if reqMethod != "GET" && reqMethod != "HEAD" {
redirectMethod = "GET"
}
case 307, 308:
// 307 Temporary Redirect
// 308 Permanent Redirect
redirectMethod = reqMethod
shouldRedirect = true
includeBody = true
if resp.Header.Get("Location") == "" {
shouldRedirect = false
break
}
// 如果没有 Body 无法获得,直接返回重定向的 resp,而不是报错
if ireq.GetBody == nil && ireq.outgoingLength() != 0 {
shouldRedirect = false
}
}
return redirectMethod, shouldRedirect, includeBody
}
监测到需要重定向之后,会进入下一个循环,然后根据 redirectMethod 和 includeBody 生成一个新的 req,附加到 reqs 上,每次重定向都会生成一个新的 req。
Cookie
在调用 send() 方法之前和之后都会立即添加或者存储 Cookie
if c.Jar != nil {
for _, cookie := range c.Jar.Cookies(req.URL) {
req.AddCookie(cookie)
}
}
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)
}
}
这两个 四个关于 Cookie 的方法,有两个都是关于 CookieJar 的,还有一个 Request,一个Response,默认 CookieJar 是空的,它的实现在 http 的 cookiejar 文件夹中,也不是定义在 Client 中。
type CookieJar interface {
SetCookies(u *url.URL, cookies []*Cookie)
Cookies(u *url.URL) []*Cookie
}