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.
5 // Transport code's client connection pooling.
15 // ClientConnPool manages a pool of HTTP/2 client connections.
16 type ClientConnPool interface {
17 GetClientConn(req *http.Request, addr string) (*ClientConn, error)
21 // clientConnPoolIdleCloser is the interface implemented by ClientConnPool
22 // implementations which can close their idle connections.
23 type clientConnPoolIdleCloser interface {
25 closeIdleConnections()
29 _ clientConnPoolIdleCloser = (*clientConnPool)(nil)
30 _ clientConnPoolIdleCloser = noDialClientConnPool{}
33 // TODO: use singleflight for dialing and addConnCalls?
34 type clientConnPool struct {
37 mu sync.Mutex // TODO: maybe switch to RWMutex
38 // TODO: add support for sharing conns based on cert names
39 // (e.g. share conn for googleapis.com and appspot.com)
40 conns map[string][]*ClientConn // key is host:port
41 dialing map[string]*dialCall // currently in-flight dials
42 keys map[*ClientConn][]string
43 addConnCalls map[string]*addConnCall // in-flight addConnIfNeede calls
46 func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
47 return p.getClientConn(req, addr, dialOnMiss)
55 // shouldTraceGetConn reports whether getClientConn should call any
56 // ClientTrace.GetConn hook associated with the http.Request.
58 // This complexity is needed to avoid double calls of the GetConn hook
59 // during the back-and-forth between net/http and x/net/http2 (when the
60 // net/http.Transport is upgraded to also speak http2), as well as support
61 // the case where x/net/http2 is being used directly.
62 func (p *clientConnPool) shouldTraceGetConn(st clientConnIdleState) bool {
63 // If our Transport wasn't made via ConfigureTransport, always
64 // trace the GetConn hook if provided, because that means the
65 // http2 package is being used directly and it's the one
66 // dialing, as opposed to net/http.
67 if _, ok := p.t.ConnPool.(noDialClientConnPool); !ok {
70 // Otherwise, only use the GetConn hook if this connection has
71 // been used previously for other requests. For fresh
72 // connections, the net/http package does the dialing.
76 func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
77 if isConnectionCloseRequest(req) && dialOnMiss {
78 // It gets its own connection.
79 traceGetConn(req, addr)
80 const singleUse = true
81 cc, err := p.t.dialClientConn(addr, singleUse)
88 for _, cc := range p.conns[addr] {
89 if st := cc.idleState(); st.canTakeNewRequest {
90 if p.shouldTraceGetConn(st) {
91 traceGetConn(req, addr)
99 return nil, ErrNoCachedConn
101 traceGetConn(req, addr)
102 call := p.getStartDialLocked(addr)
105 return call.res, call.err
108 // dialCall is an in-flight Transport dial call to a host.
109 type dialCall struct {
111 done chan struct{} // closed when done
112 res *ClientConn // valid after done is closed
113 err error // valid after done is closed
116 // requires p.mu is held.
117 func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
118 if call, ok := p.dialing[addr]; ok {
119 // A dial is already in-flight. Don't start another.
122 call := &dialCall{p: p, done: make(chan struct{})}
123 if p.dialing == nil {
124 p.dialing = make(map[string]*dialCall)
126 p.dialing[addr] = call
131 // run in its own goroutine.
132 func (c *dialCall) dial(addr string) {
133 const singleUse = false // shared conn
134 c.res, c.err = c.p.t.dialClientConn(addr, singleUse)
138 delete(c.p.dialing, addr)
140 c.p.addConnLocked(addr, c.res)
145 // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
146 // already exist. It coalesces concurrent calls with the same key.
147 // This is used by the http1 Transport code when it creates a new connection. Because
148 // the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know
149 // the protocol), it can get into a situation where it has multiple TLS connections.
150 // This code decides which ones live or die.
151 // The return value used is whether c was used.
152 // c is never closed.
153 func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
155 for _, cc := range p.conns[key] {
156 if cc.CanTakeNewRequest() {
161 call, dup := p.addConnCalls[key]
163 if p.addConnCalls == nil {
164 p.addConnCalls = make(map[string]*addConnCall)
168 done: make(chan struct{}),
170 p.addConnCalls[key] = call
171 go call.run(t, key, c)
177 return false, call.err
182 type addConnCall struct {
184 done chan struct{} // closed when done
188 func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
189 cc, err := t.NewClientConn(tc)
196 p.addConnLocked(key, cc)
198 delete(p.addConnCalls, key)
203 func (p *clientConnPool) addConn(key string, cc *ClientConn) {
205 p.addConnLocked(key, cc)
210 func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
211 for _, v := range p.conns[key] {
217 p.conns = make(map[string][]*ClientConn)
220 p.keys = make(map[*ClientConn][]string)
222 p.conns[key] = append(p.conns[key], cc)
223 p.keys[cc] = append(p.keys[cc], key)
226 func (p *clientConnPool) MarkDead(cc *ClientConn) {
229 for _, key := range p.keys[cc] {
230 vv, ok := p.conns[key]
234 newList := filterOutClientConn(vv, cc)
235 if len(newList) > 0 {
236 p.conns[key] = newList
244 func (p *clientConnPool) closeIdleConnections() {
247 // TODO: don't close a cc if it was just added to the pool
248 // milliseconds ago and has never been used. There's currently
249 // a small race window with the HTTP/1 Transport's integration
250 // where it can add an idle conn just before using it, and
251 // somebody else can concurrently call CloseIdleConns and
252 // break some caller's RoundTrip.
253 for _, vv := range p.conns {
254 for _, cc := range vv {
260 func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
262 for _, v := range in {
267 // If we filtered it out, zero out the last item to prevent
268 // the GC from seeing it.
269 if len(in) != len(out) {
275 // noDialClientConnPool is an implementation of http2.ClientConnPool
276 // which never dials. We let the HTTP/1.1 client dial and use its TLS
277 // connection instead.
278 type noDialClientConnPool struct{ *clientConnPool }
280 func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
281 return p.getClientConn(req, addr, noDialOnMiss)