golang源码解读之http.client

client.go 文件 内容总括
① 首先 定义了客户端对象, 以及客户端的send发送请求获取响应的方法(调用了内部send方法),获取截止时间方法、获取往返处理器方法;
② 然后 内部send方法实现,主要是判断请求内容,以及使用 RoundTripper 发送请求获取响应, 判断响应并返回响应;
③ 然后 内部setRequestCancel 设置请求体取消方法实现;
④ 然后 重定向检查,以及如果重定向 怎么复制请求头 和cookie进行二次请求的实现;
⑤ 然后 发送get、post、header、postForm方式请求方法的实现,都是对内部 默认客户端的请求方法的封装;
⑥ 然后自定义客户端 发送请求的do方法的实现(url检查、重定向检查、请求体检查、发送第一个请求、发送重定向请求,陆续返回相应)
⑦ 以及涉及的一些复制请求头、检查重定向的内部方法

源码解读
①client结构体

type Client struct {
   
	// Transport是一个接口,表示HTTP事务,用于处理客户端的请求并等待服务端的响应。
	// 如果Transport为nil,则使用DefaultTransport。
	Transport RoundTripper

	// CheckRedirect指定处理重定向的策略(函数类型)
	// 如果CheckRedirect不为nil,客户端会在执行重定向之前调用本函数字段。
	// 参数req和via是将要执行的请求和已经执行的请求(切片,越新的请求越靠后)。
	// 如果CheckRedirect返回一个错误,本类型的Get方法不会发送请求req,
	// 而是返回之前得到的最后一个回复和该错误。(包装进url.Error类型里)
	// 如果CheckRedirect为nil,会采用默认策略:连续10此请求后停止。
	// 函数指定处理重定向的策略。当使用 HTTP Client 的Get()或者是Head()方法发送 HTTP 请求时,若响应返回的状态码为 30x (比如 301 / 302 / 303 / 307), HTTP Client会在遵循跳转规则之前先调用这个CheckRedirect函数
	CheckRedirect func(req *Request, via []*Request) error

	// Jar指定cookie管理器:将相关cookie插入到每个出站请求中,并用每个入站响应的cookie值进行更新
	// 如果Jar为nil,请求中不会发送cookie,回复中的cookie会被忽略。(只有在请求中显式设置了cookie时,才会发送cookie)
	Jar CookieJar

	// Timeout指定本类型的值执行请求的时间限制。
	// 该超时限制包括连接时间、重定向和读取回复主体的时间。
	// 计时器会在Head、Get、Post或Do方法返回后继续运作并在超时后中断回复主体的读取。
	// Timeout为零值表示不设置超时。
	Timeout time.Duration
}

一个Client实例 就是一个HTTP客户端。它的零值(DefaultClient)是使用DefaultTransport的可用客户端。
客户机的传输通常具有内部状态(缓存的TCP连接),因此应该重新用客户机,而不是根据需要创建客户机。多个goroutine并发使用客户端是安全的。客户机的级别比往返程序(如传输)更高,另外还处理诸如cookies和重定向之类的HTTP细节。

send() 方法
客户端的send发送请求获取响应的方法

// 客户机send()方法:客户端 发送请求获取响应 内部方法
// 只有在出现错误时,didTimeout才是非nil
func (c *Client) send(req *Request, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
   
	// 1、如果 客户机的cookie管理器不为空,则使用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)
		}
	}
	return resp, nil, nil
}

②内部send()方法实现

// send() 方法发出一个HTTP请求。 调用方 在读完响应体时应关闭它
func send(ireq *Request, rt RoundTripper, deadline time.Time) (resp *Response, didTimeout func() bool, err error) {
   
	req := ireq // req is either the original request, or a modified fork
	// 1、如果RoundTripper或者url 是空的,则不发送请求直接返回
	if rt == nil {
   
		req.closeBody()
		return nil, alwaysFalse, errors.New("http: no Client.Transport or DefaultTransport")
	}

	if req.URL == nil {
   
		req.closeBody()
		return nil, alwaysFalse, errors.New("http: nil Request.URL")
	}

	// 2、如果 Request.RequestURI有值也返回,因为其不能再客户机请求体内设置
	if req.RequestURI != "" {
   
		req.closeBody()
		return nil, alwaysFalse, errors.New("http: Request.RequestURI can't be set in client requests")
	}

    // forkReq是一个函数:在第一次调用时将req分叉到ireq的浅克隆中。
	forkReq := func() {
   
		if ireq == req {
   
			req = new(Request)
			*req = *ireq // shallow clone
		}
	}

	// 3、如果请求头是空的,则调用forkReq函数(将req分叉到ireq的浅克隆中),然后初始化请求头
	// send的大多数调用方(Get、Post等)不需要头文件,使其处于未初始化状态。不过,我们向传输部门保证这已经初始化
	if req.Header == nil {
   
		forkReq()
		req.Header = make(Header)
	}

	// 4、取出url中的User字段,给请求头设置 用户密钥(token)
	if u := req.URL.User; u != nil && req.Header.Get("Authorization") == "" {
   
		username := u.Username()
		password, _ := u.Password()
		forkReq()
		req.Header 

你可能感兴趣的:(golang源码解读之http.client)