author | Brad Fitzpatrick
<bradfitz@golang.org> 2016-07-07 22:24:52 UTC |
committer | Brad Fitzpatrick
<bradfitz@golang.org> 2016-07-13 21:26:39 UTC |
parent | c9a3c546a145a4c113a83b79d6fb85dab38df6e1 |
http2/client_conn_pool.go | +10 | -1 |
http2/server_test.go | +2 | -0 |
http2/transport.go | +8 | -4 |
http2/transport_test.go | +3 | -1 |
diff --git a/http2/client_conn_pool.go b/http2/client_conn_pool.go index 8cb3eaa..547e238 100644 --- a/http2/client_conn_pool.go +++ b/http2/client_conn_pool.go @@ -52,7 +52,16 @@ const ( noDialOnMiss = false ) -func (p *clientConnPool) getClientConn(_ *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) { +func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) { + if req.Close && dialOnMiss { + // It gets its own connection. + cc, err := p.t.dialClientConn(addr) + if err != nil { + return nil, err + } + cc.singleUse = true + return cc, nil + } p.mu.Lock() for _, cc := range p.conns[addr] { if cc.CanTakeNewRequest() { diff --git a/http2/server_test.go b/http2/server_test.go index 61a7f9d..a45905f 100644 --- a/http2/server_test.go +++ b/http2/server_test.go @@ -104,6 +104,8 @@ func newServerTester(t testing.TB, handler http.HandlerFunc, opts ...interface{} case optQuiet: quiet = true } + case func(net.Conn, http.ConnState): + ts.Config.ConnState = v default: t.Fatalf("unknown newServerTester option type %T", v) } diff --git a/http2/transport.go b/http2/transport.go index ad65ab6..fb8dd99 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -139,9 +139,10 @@ func (t *Transport) initConnPool() { // ClientConn is the state of a single HTTP/2 client connection to an // HTTP/2 server. type ClientConn struct { - t *Transport - tconn net.Conn // usually *tls.Conn, except specialized impls - tlsState *tls.ConnectionState // nil only for specialized impls + t *Transport + tconn net.Conn // usually *tls.Conn, except specialized impls + tlsState *tls.ConnectionState // nil only for specialized impls + singleUse bool // whether being used for a single http.Request // readLoop goroutine fields: readerDone chan struct{} // closed on error @@ -515,6 +516,9 @@ func (cc *ClientConn) CanTakeNewRequest() bool { } func (cc *ClientConn) canTakeNewRequestLocked() bool { + if cc.singleUse && cc.nextStreamID > 1 { + return false + } return cc.goAway == nil && !cc.closed && int64(len(cc.streams)+1) < int64(cc.maxConcurrentStreams) && cc.nextStreamID < 2147483647 @@ -1223,7 +1227,7 @@ func (rl *clientConnReadLoop) cleanup() { func (rl *clientConnReadLoop) run() error { cc := rl.cc - rl.closeWhenIdle = cc.t.disableKeepAlives() + rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse gotReply := false // ever saw a reply for { f, err := cc.fr.ReadFrame() diff --git a/http2/transport_test.go b/http2/transport_test.go index ec7d7e0..6009cef 100644 --- a/http2/transport_test.go +++ b/http2/transport_test.go @@ -150,7 +150,9 @@ func TestTransport(t *testing.T) { func onSameConn(t *testing.T, modReq func(*http.Request)) bool { st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) { io.WriteString(w, r.RemoteAddr) - }, optOnlyServer) + }, optOnlyServer, func(c net.Conn, st http.ConnState) { + t.Logf("conn %v is now state %v", c.RemoteAddr(), st) + }) defer st.Close() tr := &Transport{TLSClientConfig: tlsConfigInsecure} defer tr.CloseIdleConnections()