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 OtherredirectMethod = reqMethodshouldRedirect = trueincludeBody = false// RFC 2616 规定重定向只能是 GET 或者 HEAD 方法// RFC 7231 取消了这个限制,但是为了保持兼容,实现上仍然这样做if reqMethod != "GET" && reqMethod != "HEAD" {redirectMethod = "GET"}case 307, 308:// 307 Temporary Redirect// 308 Permanent RedirectredirectMethod = reqMethodshouldRedirect = trueincludeBody = trueif resp.Header.Get("Location") == "" {shouldRedirect = falsebreak}// 如果没有 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}
