From: Brad Fitzpatrick Date: Mon, 30 Nov 2015 20:07:50 +0000 (-0800) Subject: net/http: update h2_bundle.go X-Git-Tag: go1.6beta1~264 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ccdcd6e903dd01d7d1cb8385edd0275740bab694;p=gostls13.git net/http: update h2_bundle.go 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 Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go index d9046c4c18..41b4bc14dd 100644 --- a/src/net/http/h2_bundle.go +++ b/src/net/http/h2_bundle.go @@ -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) }