client_conn_pool.go 7.4 KB

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