Skip to content

Commit ab1d67c

Browse files
bradfitzdmitshur
authored andcommitted
[internal-branch.go1.16-vendor] http2: shut down idle Transport connections after protocol errors
Updates golang/go#49076 Change-Id: Ic4e85cdc75b4baef7cd61a65b1b09f430a2ffc4b Reviewed-on: https://go-review.googlesource.com/c/net/+/352449 Trust: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Damien Neil <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/net/+/356985 Trust: Damien Neil <[email protected]> Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 3741e47 commit ab1d67c

File tree

2 files changed

+15
-79
lines changed

2 files changed

+15
-79
lines changed

http2/transport.go

+15-4
Original file line numberDiff line numberDiff line change
@@ -866,6 +866,12 @@ func (cc *ClientConn) closeIfIdle() {
866866
cc.tconn.Close()
867867
}
868868

869+
func (cc *ClientConn) isDoNotReuseAndIdle() bool {
870+
cc.mu.Lock()
871+
defer cc.mu.Unlock()
872+
return cc.doNotReuse && len(cc.streams) == 0
873+
}
874+
869875
var shutdownEnterWaitStateHook = func() {}
870876

871877
// Shutdown gracefully close the client connection, waiting for running streams to complete.
@@ -2271,6 +2277,9 @@ func (b transportResponseBody) Close() error {
22712277
func (rl *clientConnReadLoop) processData(f *DataFrame) error {
22722278
cc := rl.cc
22732279
cs := cc.streamByID(f.StreamID, f.StreamEnded())
2280+
if f.StreamEnded() && cc.isDoNotReuseAndIdle() {
2281+
rl.closeWhenIdle = true
2282+
}
22742283
data := f.Data()
22752284
if cs == nil {
22762285
cc.mu.Lock()
@@ -2517,11 +2526,15 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
25172526
}
25182527

25192528
func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
2520-
cs := rl.cc.streamByID(f.StreamID, true)
2529+
cc := rl.cc
2530+
cs := cc.streamByID(f.StreamID, true)
25212531
if cs == nil {
25222532
// TODO: return error if server tries to RST_STEAM an idle stream
25232533
return nil
25242534
}
2535+
if cc.isDoNotReuseAndIdle() {
2536+
rl.closeWhenIdle = true
2537+
}
25252538
select {
25262539
case <-cs.peerReset:
25272540
// Already reset.
@@ -2533,9 +2546,7 @@ func (rl *clientConnReadLoop) processResetStream(f *RSTStreamFrame) error {
25332546
if f.ErrCode == ErrCodeProtocol {
25342547
rl.cc.SetDoNotReuse()
25352548
serr.Cause = errFromPeer
2536-
// TODO(bradfitz): increment a varz here, once Transport
2537-
// takes an optional interface-typed field that expvar.Map.Add
2538-
// implements.
2549+
rl.closeWhenIdle = true
25392550
}
25402551
cs.resetErr = serr
25412552
close(cs.peerReset)

http2/transport_test.go

-75
Original file line numberDiff line numberDiff line change
@@ -5296,78 +5296,3 @@ func (p *collectClientsConnPool) GetClientConn(req *http.Request, addr string) (
52965296
func (p *collectClientsConnPool) MarkDead(cc *ClientConn) {
52975297
p.lower.MarkDead(cc)
52985298
}
5299-
5300-
func TestTransportRetriesOnStreamProtocolError(t *testing.T) {
5301-
ct := newClientTester(t)
5302-
pool := &collectClientsConnPool{
5303-
lower: &clientConnPool{t: ct.tr},
5304-
}
5305-
ct.tr.ConnPool = pool
5306-
done := make(chan struct{})
5307-
ct.client = func() error {
5308-
req, _ := http.NewRequest("GET", "https://dummy.tld/", nil)
5309-
res, err := ct.tr.RoundTrip(req)
5310-
const want = "only one dial allowed in test mode"
5311-
if got := fmt.Sprint(err); got != want {
5312-
t.Errorf("didn't dial again: got %#q; want %#q", got, want)
5313-
}
5314-
close(done)
5315-
ct.sc.Close()
5316-
if res != nil {
5317-
res.Body.Close()
5318-
}
5319-
5320-
pool.mu.Lock()
5321-
defer pool.mu.Unlock()
5322-
if pool.getErrs != 1 {
5323-
t.Errorf("pool get errors = %v; want 1", pool.getErrs)
5324-
}
5325-
if len(pool.got) == 1 {
5326-
cc := pool.got[0]
5327-
cc.mu.Lock()
5328-
if !cc.doNotReuse {
5329-
t.Error("ClientConn not marked doNotReuse")
5330-
}
5331-
cc.mu.Unlock()
5332-
} else {
5333-
t.Errorf("pool get success = %v; want 1", len(pool.got))
5334-
}
5335-
return nil
5336-
}
5337-
ct.server = func() error {
5338-
ct.greet()
5339-
var sentErr bool
5340-
for {
5341-
f, err := ct.fr.ReadFrame()
5342-
if err != nil {
5343-
select {
5344-
case <-done:
5345-
return nil
5346-
default:
5347-
return err
5348-
}
5349-
}
5350-
switch f := f.(type) {
5351-
case *WindowUpdateFrame, *SettingsFrame:
5352-
case *HeadersFrame:
5353-
if !sentErr {
5354-
sentErr = true
5355-
ct.fr.WriteRSTStream(f.StreamID, ErrCodeProtocol)
5356-
continue
5357-
}
5358-
var buf bytes.Buffer
5359-
enc := hpack.NewEncoder(&buf)
5360-
// send headers without Trailer header
5361-
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
5362-
ct.fr.WriteHeaders(HeadersFrameParam{
5363-
StreamID: f.StreamID,
5364-
EndHeaders: true,
5365-
EndStream: true,
5366-
BlockFragment: buf.Bytes(),
5367-
})
5368-
}
5369-
}
5370-
return nil
5371-
}
5372-
ct.run()
5373-
}

0 commit comments

Comments
 (0)