Go发起HTTP2.0请求流程分析(前篇)

来自公众号:新世界杂货铺

前言

Go中的HTTP请求之——HTTP1.1请求流程分析之后,中间断断续续,历时近一月,终于才敢开始码字写下本文。

阅读建议

HTTP2.0在建立TCP连接和安全的TLS传输通道与HTTP1.1的流程基本一致。所以笔者建议没有看过Go中的HTTP请求之——HTTP1.1请求流程分析这篇文章的先去补一下课,本文会基于前一篇文章仅介绍和HTTP2.0相关的逻辑。

(*Transport).roundTrip

(*Transport).roundTrip方法会调用t.nextProtoOnce.Do(t.onceSetNextProtoDefaults)初始化TLSClientConfig以及h2transport,而这两者都和HTTP2.0有着紧密的联系。

TLSClientConfig: 初始化client支持的http协议, 并在tls握手时告知server。

h2transport: 如果本次请求是http2,那么h2transport会接管连接,请求和响应的处理逻辑。

下面看看源码:

func (t *Transport) onceSetNextProtoDefaults() {
   
	// ...此处省略代码...
	t2, err := http2configureTransport(t)
	if err != nil {
   
		log.Printf("Error enabling Transport HTTP/2 support: %v", err)
		return
	}
	t.h2transport = t2

	// ...此处省略代码...
}
func http2configureTransport(t1 *Transport) (*http2Transport, error) {
   
	connPool := new(http2clientConnPool)
	t2 := &http2Transport{
   
		ConnPool: http2noDialClientConnPool{
   connPool},
		t1:       t1,
	}
	connPool.t = t2
	if err := http2registerHTTPSProtocol(t1, http2noDialH2RoundTripper{
   t2}); err != nil {
   
		return nil, err
	}
	if t1.TLSClientConfig == nil {
   
		t1.TLSClientConfig = new(tls.Config)
	}
	if !http2strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
   
		t1.TLSClientConfig.NextProtos = append([]string{
   "h2"}, t1.TLSClientConfig.NextProtos...)
	}
	if !http2strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
   
		t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
	}
	upgradeFn := func(authority string, c *tls.Conn) RoundTripper {
   
		addr := http2authorityAddr("https", authority)
		if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
   
			go c.Close()
			return http2erringRoundTripper{
   err}
		} else if !used {
   
			// Turns out we don't need this c.
			// For example, two goroutines made requests to the same host
			// at the same time, both kicking off TCP dials. (since protocol
			// was unknown)
			go c.Close()
		}
		return t2
	}
	if m := t1.TLSNextProto; len(m) == 0 {
   
		t1.TLSNextProto = map[string]func(string, *tls.Conn) RoundTripper{
   
			"h2": upgradeFn,
		}
	} else {
   
		m["h2"] = upgradeFn
	}
	return t2, nil
}

笔者将上述的源码简单拆解为以下几个步骤:

  1. 新建一个http2clientConnPool并复制给t2,以后http2的请求会优先从该连接池中获取连接。
  2. 初始化TLSClientConfig,并将支持的h2http1.1协议添加到TLSClientConfig.NextProtos中。
  3. 定义一个h2upgradeFn存储到t1.TLSNextProto里。

鉴于前一篇文章对新建连接前的步骤有了较为详细的介绍,所以这里直接看和server建立连接的部分源码,即(*Transport).dialConn方法:

func (t *Transport) dialConn(ctx context.Context, cm connectMethod) (pconn *persistConn, err error) {
   
	// ...此处省略代码...
	if cm.scheme() == "https" && t.hasCustomTLSDialer() {
   
		// ...此处省略代码...
	} else {
   
		conn, err := t.dial(ctx, "tcp", cm.addr())
		if err != nil {
   
			return nil, wrapErr(err)
		}
		pconn.conn = conn
		if cm.scheme() == "https" {
   
			var firstTLSHost string
			if firstTLSHost, _, err = net.SplitHostPort(cm.addr()); err != nil {
   
				return nil, wrapErr(err)
			}
			if err = pconn.addTLS(firstTLSHost, trace); err != nil {
   
				return nil, wrapErr(err)
			}
		}
	}

	// Proxy setup.
	// ...此处省略代码...

	if s := pconn.tlsState; s != nil && s
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值