Skip to content

Commit

Permalink
net/http2: add test to demonstrate transport can hang forever
Browse files Browse the repository at this point in the history
The transport can hang forever if the server hangs after accepting the
connection and the request headers (or control frames) exceeds the connection
write buffer. Without respect to the context's deadline.

The ClientConn writes to the connection without setting a write deadline thus
blocks forever if the buffer is flushed to the socket.

go test -v -run TestTransportTimeoutServerHangs ./http2
  • Loading branch information
gwik committed Jan 25, 2018
1 parent 0ed95ab commit e4c191a
Showing 1 changed file with 48 additions and 0 deletions.
48 changes: 48 additions & 0 deletions http2/transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ package http2
import (

This comment has been minimized.

Copy link
@king40or

king40or Mar 12, 2021

http2/transport_test.go

"bufio"
"bytes"
"context"
"crypto/tls"
"encoding/hex"
"errors"
"flag"
"fmt"
Expand Down Expand Up @@ -790,6 +792,52 @@ func (r countingReader) Read(p []byte) (n int, err error) {
return len(p), err
}

// TestTransportTimeoutServerHangs demonstrates that client can hang forever
// when the server hangs and the headers exceed the conn buffer size (forcing a
// flush). Without respect to the context's deadline.
func TestTransportTimeoutServerHangs(t *testing.T) {
clientDone := make(chan struct{})
ct := newClientTester(t)
ct.client = func() error {
defer ct.cc.(*net.TCPConn).CloseWrite()
defer close(clientDone)

buf := make([]byte, 1<<19)
_, err := rand.Read(buf)
if err != nil {
t.Fatal("fail to gen random data")
}
headerVal := hex.EncodeToString(buf)

req, err := http.NewRequest("PUT", "https://dummy.tld/", nil)
if err != nil {
return err
}

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
req = req.WithContext(ctx)
req.Header.Add("Authorization", headerVal)
_, err = ct.tr.RoundTrip(req)
if err == nil {
return errors.New("error should not be nil")
}
if ne, ok := err.(net.Error); !ok || !ne.Timeout() {
return fmt.Errorf("error should be a net error timeout was: +%v", err)
}
return nil
}
ct.server = func() error {
ct.greet()
select {
case <-time.After(5 * time.Second):
case <-clientDone:
}
return nil
}
ct.run()
}

func TestTransportReqBodyAfterResponse_200(t *testing.T) { testTransportReqBodyAfterResponse(t, 200) }
func TestTransportReqBodyAfterResponse_403(t *testing.T) { testTransportReqBodyAfterResponse(t, 403) }

Expand Down

5 comments on commit e4c191a

@ikenchina
Copy link

@ikenchina ikenchina commented on e4c191a May 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hello, i have ran this snippets but it didn't hang up.
after 1 second, it returned.
and i have changed my linux parameter
`
sysctl -p

net.ipv4.tcp_wmem = 4096 16384 41943
`

how did you test it?

@gwik
Copy link
Owner Author

@gwik gwik commented on e4c191a May 8, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ikenchina it was on mac os X, try increasing buf size, 1 << 20, 1 << 21 ?

@ikenchina
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't work,
and i have already changed linux kernel
net.ipv4.tcp_wmem = 4096 16384 41943

ct.client = func() error { returned after 1 second.
and go version is 1.9.3 , os is centos 6

@gwik
Copy link
Owner Author

@gwik gwik commented on e4c191a May 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ikenchina it fails on my linux at 1<<22 == 4194304 which is the max for tcp send buffer size on my machine:

net.ipv4.tcp_wmem = 4096	16384	4194304

@ikenchina
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, i have also test it, same result.
here you said :Also happened on linux/amd64, see below.

Please sign in to comment.