]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: update h2_bundle.go
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 30 Nov 2015 20:07:50 +0000 (12:07 -0800)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 30 Nov 2015 20:48:24 +0000 (20:48 +0000)
Updates to x/net rev 195180cf
(golang.org/cl/17134, http2: merge duplicate Transport dials)

Change-Id: I50b9c73b30c6a21e725aad80126b713d8b0fa362
Reviewed-on: https://go-review.googlesource.com/17261
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/http/h2_bundle.go

index d9046c4c1896ffb0f6cff4b53789c2316d833ed2..41b4bc14dd8ba226b38a7e645c91d37b6534b4ae 100644 (file)
@@ -45,11 +45,12 @@ type http2ClientConnPool interface {
 
 type http2clientConnPool struct {
        t  *http2Transport
-       mu sync.Mutex // TODO: switch to RWMutex
+       mu sync.Mutex // TODO: maybe switch to RWMutex
        // TODO: add support for sharing conns based on cert names
        // (e.g. share conn for googleapis.com and appspot.com)
-       conns map[string][]*http2ClientConn // key is host:port
-       keys  map[*http2ClientConn][]string
+       conns   map[string][]*http2ClientConn // key is host:port
+       dialing map[string]*http2dialCall     // currently in-flight dials
+       keys    map[*http2ClientConn][]string
 }
 
 func (p *http2clientConnPool) GetClientConn(req *Request, addr string) (*http2ClientConn, error) {
@@ -64,22 +65,65 @@ func (p *http2clientConnPool) getClientConn(req *Request, addr string, dialOnMis
                        return cc, nil
                }
        }
-       p.mu.Unlock()
        if !dialOnMiss {
+               p.mu.Unlock()
                return nil, http2ErrNoCachedConn
        }
+       call := p.getStartDialLocked(addr)
+       p.mu.Unlock()
+       <-call.done
+       return call.res, call.err
+}
 
-       cc, err := p.t.dialClientConn(addr)
-       if err != nil {
-               return nil, err
+// dialCall is an in-flight Transport dial call to a host.
+type http2dialCall struct {
+       p    *http2clientConnPool
+       done chan struct{}    // closed when done
+       res  *http2ClientConn // valid after done is closed
+       err  error            // valid after done is closed
+}
+
+// requires p.mu is held.
+func (p *http2clientConnPool) getStartDialLocked(addr string) *http2dialCall {
+       if call, ok := p.dialing[addr]; ok {
+
+               return call
        }
-       p.addConn(addr, cc)
-       return cc, nil
+       call := &http2dialCall{p: p, done: make(chan struct{})}
+       if p.dialing == nil {
+               p.dialing = make(map[string]*http2dialCall)
+       }
+       p.dialing[addr] = call
+       go call.dial(addr)
+       return call
+}
+
+// run in its own goroutine.
+func (c *http2dialCall) dial(addr string) {
+       c.res, c.err = c.p.t.dialClientConn(addr)
+       close(c.done)
+
+       c.p.mu.Lock()
+       delete(c.p.dialing, addr)
+       if c.err == nil {
+               c.p.addConnLocked(addr, c.res)
+       }
+       c.p.mu.Unlock()
 }
 
 func (p *http2clientConnPool) addConn(key string, cc *http2ClientConn) {
        p.mu.Lock()
-       defer p.mu.Unlock()
+       p.addConnLocked(key, cc)
+       p.mu.Unlock()
+}
+
+// p.mu must be held
+func (p *http2clientConnPool) addConnLocked(key string, cc *http2ClientConn) {
+       for _, v := range p.conns[key] {
+               if v == cc {
+                       return
+               }
+       }
        if p.conns == nil {
                p.conns = make(map[string][]*http2ClientConn)
        }