OSDN Git Service

new repo
[bytom/vapor.git] / vendor / golang.org / x / net / http2 / client_conn_pool.go
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.
4
5 // Transport code's client connection pooling.
6
7 package http2
8
9 import (
10         "crypto/tls"
11         "net/http"
12         "sync"
13 )
14
15 // ClientConnPool manages a pool of HTTP/2 client connections.
16 type ClientConnPool interface {
17         GetClientConn(req *http.Request, addr string) (*ClientConn, error)
18         MarkDead(*ClientConn)
19 }
20
21 // clientConnPoolIdleCloser is the interface implemented by ClientConnPool
22 // implementations which can close their idle connections.
23 type clientConnPoolIdleCloser interface {
24         ClientConnPool
25         closeIdleConnections()
26 }
27
28 var (
29         _ clientConnPoolIdleCloser = (*clientConnPool)(nil)
30         _ clientConnPoolIdleCloser = noDialClientConnPool{}
31 )
32
33 // TODO: use singleflight for dialing and addConnCalls?
34 type clientConnPool struct {
35         t *Transport
36
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
44 }
45
46 func (p *clientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
47         return p.getClientConn(req, addr, dialOnMiss)
48 }
49
50 const (
51         dialOnMiss   = true
52         noDialOnMiss = false
53 )
54
55 func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
56         if isConnectionCloseRequest(req) && dialOnMiss {
57                 // It gets its own connection.
58                 const singleUse = true
59                 cc, err := p.t.dialClientConn(addr, singleUse)
60                 if err != nil {
61                         return nil, err
62                 }
63                 return cc, nil
64         }
65         p.mu.Lock()
66         for _, cc := range p.conns[addr] {
67                 if cc.CanTakeNewRequest() {
68                         p.mu.Unlock()
69                         return cc, nil
70                 }
71         }
72         if !dialOnMiss {
73                 p.mu.Unlock()
74                 return nil, ErrNoCachedConn
75         }
76         call := p.getStartDialLocked(addr)
77         p.mu.Unlock()
78         <-call.done
79         return call.res, call.err
80 }
81
82 // dialCall is an in-flight Transport dial call to a host.
83 type dialCall struct {
84         p    *clientConnPool
85         done chan struct{} // closed when done
86         res  *ClientConn   // valid after done is closed
87         err  error         // valid after done is closed
88 }
89
90 // requires p.mu is held.
91 func (p *clientConnPool) getStartDialLocked(addr string) *dialCall {
92         if call, ok := p.dialing[addr]; ok {
93                 // A dial is already in-flight. Don't start another.
94                 return call
95         }
96         call := &dialCall{p: p, done: make(chan struct{})}
97         if p.dialing == nil {
98                 p.dialing = make(map[string]*dialCall)
99         }
100         p.dialing[addr] = call
101         go call.dial(addr)
102         return call
103 }
104
105 // run in its own goroutine.
106 func (c *dialCall) dial(addr string) {
107         const singleUse = false // shared conn
108         c.res, c.err = c.p.t.dialClientConn(addr, singleUse)
109         close(c.done)
110
111         c.p.mu.Lock()
112         delete(c.p.dialing, addr)
113         if c.err == nil {
114                 c.p.addConnLocked(addr, c.res)
115         }
116         c.p.mu.Unlock()
117 }
118
119 // addConnIfNeeded makes a NewClientConn out of c if a connection for key doesn't
120 // already exist. It coalesces concurrent calls with the same key.
121 // This is used by the http1 Transport code when it creates a new connection. Because
122 // the http1 Transport doesn't de-dup TCP dials to outbound hosts (because it doesn't know
123 // the protocol), it can get into a situation where it has multiple TLS connections.
124 // This code decides which ones live or die.
125 // The return value used is whether c was used.
126 // c is never closed.
127 func (p *clientConnPool) addConnIfNeeded(key string, t *Transport, c *tls.Conn) (used bool, err error) {
128         p.mu.Lock()
129         for _, cc := range p.conns[key] {
130                 if cc.CanTakeNewRequest() {
131                         p.mu.Unlock()
132                         return false, nil
133                 }
134         }
135         call, dup := p.addConnCalls[key]
136         if !dup {
137                 if p.addConnCalls == nil {
138                         p.addConnCalls = make(map[string]*addConnCall)
139                 }
140                 call = &addConnCall{
141                         p:    p,
142                         done: make(chan struct{}),
143                 }
144                 p.addConnCalls[key] = call
145                 go call.run(t, key, c)
146         }
147         p.mu.Unlock()
148
149         <-call.done
150         if call.err != nil {
151                 return false, call.err
152         }
153         return !dup, nil
154 }
155
156 type addConnCall struct {
157         p    *clientConnPool
158         done chan struct{} // closed when done
159         err  error
160 }
161
162 func (c *addConnCall) run(t *Transport, key string, tc *tls.Conn) {
163         cc, err := t.NewClientConn(tc)
164
165         p := c.p
166         p.mu.Lock()
167         if err != nil {
168                 c.err = err
169         } else {
170                 p.addConnLocked(key, cc)
171         }
172         delete(p.addConnCalls, key)
173         p.mu.Unlock()
174         close(c.done)
175 }
176
177 func (p *clientConnPool) addConn(key string, cc *ClientConn) {
178         p.mu.Lock()
179         p.addConnLocked(key, cc)
180         p.mu.Unlock()
181 }
182
183 // p.mu must be held
184 func (p *clientConnPool) addConnLocked(key string, cc *ClientConn) {
185         for _, v := range p.conns[key] {
186                 if v == cc {
187                         return
188                 }
189         }
190         if p.conns == nil {
191                 p.conns = make(map[string][]*ClientConn)
192         }
193         if p.keys == nil {
194                 p.keys = make(map[*ClientConn][]string)
195         }
196         p.conns[key] = append(p.conns[key], cc)
197         p.keys[cc] = append(p.keys[cc], key)
198 }
199
200 func (p *clientConnPool) MarkDead(cc *ClientConn) {
201         p.mu.Lock()
202         defer p.mu.Unlock()
203         for _, key := range p.keys[cc] {
204                 vv, ok := p.conns[key]
205                 if !ok {
206                         continue
207                 }
208                 newList := filterOutClientConn(vv, cc)
209                 if len(newList) > 0 {
210                         p.conns[key] = newList
211                 } else {
212                         delete(p.conns, key)
213                 }
214         }
215         delete(p.keys, cc)
216 }
217
218 func (p *clientConnPool) closeIdleConnections() {
219         p.mu.Lock()
220         defer p.mu.Unlock()
221         // TODO: don't close a cc if it was just added to the pool
222         // milliseconds ago and has never been used. There's currently
223         // a small race window with the HTTP/1 Transport's integration
224         // where it can add an idle conn just before using it, and
225         // somebody else can concurrently call CloseIdleConns and
226         // break some caller's RoundTrip.
227         for _, vv := range p.conns {
228                 for _, cc := range vv {
229                         cc.closeIfIdle()
230                 }
231         }
232 }
233
234 func filterOutClientConn(in []*ClientConn, exclude *ClientConn) []*ClientConn {
235         out := in[:0]
236         for _, v := range in {
237                 if v != exclude {
238                         out = append(out, v)
239                 }
240         }
241         // If we filtered it out, zero out the last item to prevent
242         // the GC from seeing it.
243         if len(in) != len(out) {
244                 in[len(in)-1] = nil
245         }
246         return out
247 }
248
249 // noDialClientConnPool is an implementation of http2.ClientConnPool
250 // which never dials. We let the HTTP/1.1 client dial and use its TLS
251 // connection instead.
252 type noDialClientConnPool struct{ *clientConnPool }
253
254 func (p noDialClientConnPool) GetClientConn(req *http.Request, addr string) (*ClientConn, error) {
255         return p.getClientConn(req, addr, noDialOnMiss)
256 }