diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/net/http/transport.go | 99 |
1 files changed, 83 insertions, 16 deletions
diff --git a/src/net/http/transport.go b/src/net/http/transport.go index 31599237e0..ad8aa19c2e 100644 --- a/src/net/http/transport.go +++ b/src/net/http/transport.go @@ -25,6 +25,10 @@ import ( "time" ) +// h2DefaultTransport is the HTTP/2 version of DefaultTransport. +// DefaultTransport and h2DefaultTransport are wired up together. +var h2DefaultTransport = &http2Transport{} + // DefaultTransport is the default implementation of Transport and is // used by DefaultClient. It establishes network connections as needed // and caches them for reuse by subsequent calls. It uses HTTP proxies @@ -40,6 +44,41 @@ var DefaultTransport RoundTripper = &Transport{ ExpectContinueTimeout: 1 * time.Second, } +func init() { + // TODO(bradfitz,adg): remove the following line before Go 1.6 + // ships. This just gives us a mechanism to temporarily + // enable the http2 client during development. + if !strings.Contains(os.Getenv("GODEBUG"), "h2client=1") { + return + } + + t := DefaultTransport.(*Transport) + + // TODO(bradfitz,adg): move all this up to DefaultTransport before Go 1.6: + t.RegisterProtocol("https", noDialH2Transport{h2DefaultTransport}) + t.TLSClientConfig = &tls.Config{ + NextProtos: []string{"h2"}, + } + t.TLSNextProto = map[string]func(string, *tls.Conn) RoundTripper{ + "h2": http2TransportForConn, + } +} + +// noDialH2Transport is a RoundTripper which only tries to complete the request if +// the wrapped *http2Transport already has a cached connection to the host. +type noDialH2Transport struct{ rt *http2Transport } + +func (t noDialH2Transport) RoundTrip(req *Request) (*Response, error) { + // TODO(bradfitz): wire up http2.Transport + return nil, ErrSkipAltProtocol +} + +func http2TransportForConn(authority string, c *tls.Conn) RoundTripper { + // TODO(bradfitz): donate c to h2DefaultTransport: + // h2DefaultTransport.AddIdleConn(authority, c) + return h2DefaultTransport +} + // DefaultMaxIdleConnsPerHost is the default value of Transport's // MaxIdleConnsPerHost. const DefaultMaxIdleConnsPerHost = 2 @@ -121,6 +160,16 @@ type Transport struct { // This time does not include the time to send the request header. ExpectContinueTimeout time.Duration + // TLSNextProto specifies how the Transport switches to an + // alternate protocol (such as HTTP/2) after a TLS NPN/ALPN + // protocol negotiation. If Transport dials an TLS connection + // with a non-empty protocol name and TLSNextProto contains a + // map entry for that key (such as "h2"), then the func is + // called with the request's authority (such as "example.com" + // or "example.com:1234") and the TLS connection. The function + // must return a RoundTripper that then handles the request. + TLSNextProto map[string]func(authority string, c *tls.Conn) RoundTripper + // TODO: tunable on global max cached connections // TODO: tunable on timeout on cached connections } @@ -196,7 +245,7 @@ func (tr *transportRequest) extraHeaders() Header { // // For higher-level HTTP client support (such as handling of cookies // and redirects), see Get, Post, and the Client type. -func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) { +func (t *Transport) RoundTrip(req *Request) (*Response, error) { if req.URL == nil { req.closeBody() return nil, errors.New("http: nil Request.URL") @@ -205,18 +254,18 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) { req.closeBody() return nil, errors.New("http: nil Request.Header") } - if req.URL.Scheme != "http" && req.URL.Scheme != "https" { - t.altMu.RLock() - var rt RoundTripper - if t.altProto != nil { - rt = t.altProto[req.URL.Scheme] + // TODO(bradfitz): switch to atomic.Value for this map instead of RWMutex + t.altMu.RLock() + altRT := t.altProto[req.URL.Scheme] + t.altMu.RUnlock() + if altRT != nil { + if resp, err := altRT.RoundTrip(req); err != ErrSkipAltProtocol { + return resp, err } - t.altMu.RUnlock() - if rt == nil { - req.closeBody() - return nil, &badStringError{"unsupported protocol scheme", req.URL.Scheme} - } - return rt.RoundTrip(req) + } + if s := req.URL.Scheme; s != "http" && s != "https" { + req.closeBody() + return nil, &badStringError{"unsupported protocol scheme", s} } if req.URL.Host == "" { req.closeBody() @@ -239,20 +288,27 @@ func (t *Transport) RoundTrip(req *Request) (resp *Response, err error) { req.closeBody() return nil, err } - + if pconn.alt != nil { + // HTTP/2 path. + return pconn.alt.RoundTrip(req) + } return pconn.roundTrip(treq) } +// ErrSkipAltProtocol is a sentinel error value defined by Transport.RegisterProtocol. +var ErrSkipAltProtocol = errors.New("net/http: skip alternate protocol") + // RegisterProtocol registers a new protocol with scheme. // The Transport will pass requests using the given scheme to rt. // It is rt's responsibility to simulate HTTP request semantics. // // RegisterProtocol can be used by other packages to provide // implementations of protocol schemes like "ftp" or "file". +// +// If rt.RoundTrip returns ErrSkipAltProtocol, the Transport will +// handle the RoundTrip itself for that one request, as if the +// protocol were not registered. func (t *Transport) RegisterProtocol(scheme string, rt RoundTripper) { - if scheme == "http" || scheme == "https" { - panic("protocol " + scheme + " already registered") - } t.altMu.Lock() defer t.altMu.Unlock() if t.altProto == nil { @@ -688,6 +744,12 @@ func (t *Transport) dialConn(cm connectMethod) (*persistConn, error) { pconn.conn = tlsConn } + if s := pconn.tlsState; s != nil && s.NegotiatedProtocolIsMutual && s.NegotiatedProtocol != "" { + if next, ok := t.TLSNextProto[s.NegotiatedProtocol]; ok { + return &persistConn{alt: next(cm.targetAddr, pconn.conn.(*tls.Conn))}, nil + } + } + pconn.br = bufio.NewReader(noteEOFReader{pconn.conn, &pconn.sawEOF}) pconn.bw = bufio.NewWriter(pconn.conn) go pconn.readLoop() @@ -817,6 +879,11 @@ func (k connectMethodKey) String() string { // persistConn wraps a connection, usually a persistent one // (but may be used for non-keep-alive requests as well) type persistConn struct { + // alt optionally specifies the TLS NextProto RoundTripper. + // This is used for HTTP/2 today and future protocol laters. + // If it's non-nil, the rest of the fields are unused. + alt RoundTripper + t *Transport cacheKey connectMethodKey conn net.Conn |
