• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// +build go1.6
6
7package http2
8
9import (
10	"crypto/tls"
11	"fmt"
12	"net/http"
13)
14
15func configureTransport(t1 *http.Transport) (*Transport, error) {
16	connPool := new(clientConnPool)
17	t2 := &Transport{
18		ConnPool: noDialClientConnPool{connPool},
19		t1:       t1,
20	}
21	connPool.t = t2
22	if err := registerHTTPSProtocol(t1, noDialH2RoundTripper{t2}); err != nil {
23		return nil, err
24	}
25	if t1.TLSClientConfig == nil {
26		t1.TLSClientConfig = new(tls.Config)
27	}
28	if !strSliceContains(t1.TLSClientConfig.NextProtos, "h2") {
29		t1.TLSClientConfig.NextProtos = append([]string{"h2"}, t1.TLSClientConfig.NextProtos...)
30	}
31	if !strSliceContains(t1.TLSClientConfig.NextProtos, "http/1.1") {
32		t1.TLSClientConfig.NextProtos = append(t1.TLSClientConfig.NextProtos, "http/1.1")
33	}
34	upgradeFn := func(authority string, c *tls.Conn) http.RoundTripper {
35		addr := authorityAddr("https", authority)
36		if used, err := connPool.addConnIfNeeded(addr, t2, c); err != nil {
37			go c.Close()
38			return erringRoundTripper{err}
39		} else if !used {
40			// Turns out we don't need this c.
41			// For example, two goroutines made requests to the same host
42			// at the same time, both kicking off TCP dials. (since protocol
43			// was unknown)
44			go c.Close()
45		}
46		return t2
47	}
48	if m := t1.TLSNextProto; len(m) == 0 {
49		t1.TLSNextProto = map[string]func(string, *tls.Conn) http.RoundTripper{
50			"h2": upgradeFn,
51		}
52	} else {
53		m["h2"] = upgradeFn
54	}
55	return t2, nil
56}
57
58// registerHTTPSProtocol calls Transport.RegisterProtocol but
59// converting panics into errors.
60func registerHTTPSProtocol(t *http.Transport, rt http.RoundTripper) (err error) {
61	defer func() {
62		if e := recover(); e != nil {
63			err = fmt.Errorf("%v", e)
64		}
65	}()
66	t.RegisterProtocol("https", rt)
67	return nil
68}
69
70// noDialH2RoundTripper is a RoundTripper which only tries to complete the request
71// if there's already has a cached connection to the host.
72type noDialH2RoundTripper struct{ t *Transport }
73
74func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
75	res, err := rt.t.RoundTrip(req)
76	if isNoCachedConnError(err) {
77		return nil, http.ErrSkipAltProtocol
78	}
79	return res, err
80}
81