Browse Source

http2: add Transport.AddIdleConn

This adds the http2 Transport method needed by
https://golang.org/cl/16090 to glue the HTTP/1 Transport
(net/http.Transport) to the http2.Transport without throwing away
fresh TCP connections after the TLS handshake determines which
NPN/ALPN protocol was selected.

Updates golang/go#6891

Change-Id: Iba004c8b1a149a5ee6c755d9a3fc1b19856a4e47
Reviewed-on: https://go-review.googlesource.com/16180
Reviewed-by: Andrew Gerrand <adg@golang.org>
Brad Fitzpatrick 10 years ago
parent
commit
e71042db1c
1 changed files with 44 additions and 8 deletions
  1. 44 8
      http2/transport.go

+ 44 - 8
http2/transport.go

@@ -157,29 +157,61 @@ func filterOutClientConn(in []*clientConn, exclude *clientConn) []*clientConn {
 	return out
 }
 
-func (t *Transport) getClientConn(host, port string) (*clientConn, error) {
+// AddIdleConn adds c as an idle conn for Transport.
+// It assumes that c has not yet exchanged SETTINGS frames.
+// The addr maybe be either "host" or "host:port".
+func (t *Transport) AddIdleConn(addr string, c *tls.Conn) error {
+	var key string
+	host, _, err := net.SplitHostPort(addr)
+	if err == nil {
+		key = addr
+	} else {
+		host = addr
+		key = addr + ":443"
+	}
+	cc, err := t.newClientConn(host, key, c)
+	if err != nil {
+		return err
+	}
+
+	t.addConn(key, cc)
+	return nil
+}
+
+func (t *Transport) addConn(key string, cc *clientConn) {
 	t.connMu.Lock()
 	defer t.connMu.Unlock()
+	if t.conns == nil {
+		t.conns = make(map[string][]*clientConn)
+	}
+	t.conns[key] = append(t.conns[key], cc)
+}
 
+func (t *Transport) getClientConn(host, port string) (*clientConn, error) {
 	key := net.JoinHostPort(host, port)
 
+	t.connMu.Lock()
 	for _, cc := range t.conns[key] {
 		if cc.canTakeNewRequest() {
+			t.connMu.Unlock()
 			return cc, nil
 		}
 	}
-	if t.conns == nil {
-		t.conns = make(map[string][]*clientConn)
-	}
-	cc, err := t.newClientConn(host, port, key)
+	t.connMu.Unlock()
+
+	// TODO(bradfitz): use a singleflight.Group to only lock once per 'key'.
+	// Probably need to vendor it in as github.com/golang/sync/singleflight
+	// though, since the net package already uses it? Also lines up with
+	// sameer, bcmills, et al wanting to open source some sync stuff.
+	cc, err := t.dialClientConn(host, port, key)
 	if err != nil {
 		return nil, err
 	}
-	t.conns[key] = append(t.conns[key], cc)
+	t.addConn(key, cc)
 	return cc, nil
 }
 
-func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
+func (t *Transport) dialClientConn(host, port, key string) (*clientConn, error) {
 	cfg := &tls.Config{
 		ServerName:         host,
 		NextProtos:         []string{NextProtoTLS},
@@ -189,11 +221,15 @@ func (t *Transport) newClientConn(host, port, key string) (*clientConn, error) {
 	if err != nil {
 		return nil, err
 	}
+	return t.newClientConn(host, key, tconn)
+}
+
+func (t *Transport) newClientConn(host, key string, tconn *tls.Conn) (*clientConn, error) {
 	if err := tconn.Handshake(); err != nil {
 		return nil, err
 	}
 	if !t.InsecureTLSDial {
-		if err := tconn.VerifyHostname(cfg.ServerName); err != nil {
+		if err := tconn.VerifyHostname(host); err != nil {
 			return nil, err
 		}
 	}