http 中的 client 比较简单,请求的发送和接受都是使用的 RoundTripper 接口,这个接口的实现在 transport.go 文件中。

  1. type RoundTripper interface {
  2. RoundTrip(*Request) (*Response, error)
  3. }

Do()

http 中默认的 Get() , Post() , Head() 方法实际上都是调用 DefaultClient 的对应方法,DefaultClient 只是一个空的 Clinet 对象,他所有的值全部都是默认值。

  1. func Get(url string) (resp *Response, err error) {
  2. return DefaultClient.Get(url)
  3. }

Client 中的 Get() , Post() , Head() 方法也都是先生成一个对应方法的 Request 然后再 调用 Client 的 Do() 方法进行发送。 Do() 的底层也是使用 RoundTripper 接口,所以真正进行传输的是 transport 结构体。

  1. func (c *Client) Get(url string) (resp *Response, err error) {
  2. req, err := NewRequest("GET", url, nil)
  3. if err != nil {
  4. return nil, err
  5. }
  6. return c.Do(req)
  7. }

http client.png

Redirect()

Screen Shot 2019-09-28 at 1.40.46 PM.png

Do() 函数中,如果发送的 Response 状态码为 3xx ,就会产生重定向,默认重定向次数应该小于 10 次

  1. func redirectBehavior(reqMethod string, resp *Response, ireq *Request) (redirectMethod string, shouldRedirect, includeBody bool) {
  2. switch resp.StatusCode {
  3. case 301, 302, 303:
  4. // 301 Moved Permanently
  5. // 302 Found
  6. // 303 See Other
  7. redirectMethod = reqMethod
  8. shouldRedirect = true
  9. includeBody = false
  10. // RFC 2616 规定重定向只能是 GET 或者 HEAD 方法
  11. // RFC 7231 取消了这个限制,但是为了保持兼容,实现上仍然这样做
  12. if reqMethod != "GET" && reqMethod != "HEAD" {
  13. redirectMethod = "GET"
  14. }
  15. case 307, 308:
  16. // 307 Temporary Redirect
  17. // 308 Permanent Redirect
  18. redirectMethod = reqMethod
  19. shouldRedirect = true
  20. includeBody = true
  21. if resp.Header.Get("Location") == "" {
  22. shouldRedirect = false
  23. break
  24. }
  25. // 如果没有 Body 无法获得,直接返回重定向的 resp,而不是报错
  26. if ireq.GetBody == nil && ireq.outgoingLength() != 0 {
  27. shouldRedirect = false
  28. }
  29. }
  30. return redirectMethod, shouldRedirect, includeBody
  31. }

监测到需要重定向之后,会进入下一个循环,然后根据 redirectMethod 和 includeBody 生成一个新的 req,附加到 reqs 上,每次重定向都会生成一个新的 req。

Cookie

在调用 send() 方法之前和之后都会立即添加或者存储 Cookie

  1. if c.Jar != nil {
  2. for _, cookie := range c.Jar.Cookies(req.URL) {
  3. req.AddCookie(cookie)
  4. }
  5. }
  6. resp, didTimeout, err = send(req, c.transport(), deadline)
  7. if err != nil {
  8. return nil, didTimeout, err
  9. }
  10. if c.Jar != nil {
  11. if rc := resp.Cookies(); len(rc) > 0 {
  12. c.Jar.SetCookies(req.URL, rc)
  13. }
  14. }

这两个 四个关于 Cookie 的方法,有两个都是关于 CookieJar 的,还有一个 Request,一个Response,默认 CookieJar 是空的,它的实现在 http 的 cookiejar 文件夹中,也不是定义在 Client 中。

  1. type CookieJar interface {
  2. SetCookies(u *url.URL, cookies []*Cookie)
  3. Cookies(u *url.URL) []*Cookie
  4. }