Skip to content

Commit 2ba7206

Browse files
fraenkelbradfitz
authored andcommitted
http2: track unread bytes when the pipe is broken
Once the pipe is broken, any remaining data needs to be reported as well as any data that is written but dropped. The client side flow control can eventually run out of available bytes to be sent since no WINDOW_UPDATE is sent to reflect the data that is never read in the pipe. Updates golang/go#28634 Change-Id: I83f3c9d3614cd92517af2687489d2ccbf3a65456 Reviewed-on: https://go-review.googlesource.com/c/net/+/187377 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 491137f commit 2ba7206

File tree

3 files changed

+81
-28
lines changed

3 files changed

+81
-28
lines changed

http2/pipe.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type pipe struct {
1717
mu sync.Mutex
1818
c sync.Cond // c.L lazily initialized to &p.mu
1919
b pipeBuffer // nil when done reading
20+
unread int // bytes unread when done
2021
err error // read error once empty. non-nil means closed.
2122
breakErr error // immediate read error (caller doesn't see rest of b)
2223
donec chan struct{} // closed on error
@@ -33,7 +34,7 @@ func (p *pipe) Len() int {
3334
p.mu.Lock()
3435
defer p.mu.Unlock()
3536
if p.b == nil {
36-
return 0
37+
return p.unread
3738
}
3839
return p.b.Len()
3940
}
@@ -80,6 +81,7 @@ func (p *pipe) Write(d []byte) (n int, err error) {
8081
return 0, errClosedPipeWrite
8182
}
8283
if p.breakErr != nil {
84+
p.unread += len(d)
8385
return len(d), nil // discard when there is no reader
8486
}
8587
return p.b.Write(d)
@@ -117,6 +119,9 @@ func (p *pipe) closeWithError(dst *error, err error, fn func()) {
117119
}
118120
p.readFn = fn
119121
if dst == &p.breakErr {
122+
if p.b != nil {
123+
p.unread += p.b.Len()
124+
}
120125
p.b = nil
121126
}
122127
*dst = err

http2/pipe_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,13 +92,19 @@ func TestPipeCloseWithError(t *testing.T) {
9292
if err != a {
9393
t.Logf("read error = %v, %v", err, a)
9494
}
95+
if p.Len() != 0 {
96+
t.Errorf("pipe should have 0 unread bytes")
97+
}
9598
// Read and Write should fail.
9699
if n, err := p.Write([]byte("abc")); err != errClosedPipeWrite || n != 0 {
97100
t.Errorf("Write(abc) after close\ngot %v, %v\nwant 0, %v", n, err, errClosedPipeWrite)
98101
}
99102
if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 {
100103
t.Errorf("Read() after close\ngot %v, nil\nwant 0, %v", n, errClosedPipeWrite)
101104
}
105+
if p.Len() != 0 {
106+
t.Errorf("pipe should have 0 unread bytes")
107+
}
102108
}
103109

104110
func TestPipeBreakWithError(t *testing.T) {
@@ -116,13 +122,19 @@ func TestPipeBreakWithError(t *testing.T) {
116122
if p.b != nil {
117123
t.Errorf("buffer should be nil after BreakWithError")
118124
}
125+
if p.Len() != 3 {
126+
t.Errorf("pipe should have 3 unread bytes")
127+
}
119128
// Write should succeed silently.
120129
if n, err := p.Write([]byte("abc")); err != nil || n != 3 {
121130
t.Errorf("Write(abc) after break\ngot %v, %v\nwant 0, nil", n, err)
122131
}
123132
if p.b != nil {
124133
t.Errorf("buffer should be nil after Write")
125134
}
135+
if p.Len() != 6 {
136+
t.Errorf("pipe should have 6 unread bytes")
137+
}
126138
// Read should fail.
127139
if n, err := p.Read(make([]byte, 1)); err == nil || n != 0 {
128140
t.Errorf("Read() after close\ngot %v, nil\nwant 0, not nil", n)

http2/server_test.go

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3658,37 +3658,73 @@ func (f funcReader) Read(p []byte) (n int, err error) { return f(p) }
36583658
// golang.org/issue/16481 -- return flow control when streams close with unread data.
36593659
// (The Server version of the bug. See also TestUnreadFlowControlReturned_Transport)
36603660
func TestUnreadFlowControlReturned_Server(t *testing.T) {
3661-
unblock := make(chan bool, 1)
3662-
defer close(unblock)
3661+
for _, tt := range []struct {
3662+
name string
3663+
reqFn func(r *http.Request)
3664+
}{
3665+
{
3666+
"body-open",
3667+
func(r *http.Request) {},
3668+
},
3669+
{
3670+
"body-closed",
3671+
func(r *http.Request) {
3672+
r.Body.Close()
3673+
},
3674+
},
3675+
{
3676+
"read-1-byte-and-close",
3677+
func(r *http.Request) {
3678+
b := make([]byte, 1)
3679+
r.Body.Read(b)
3680+
r.Body.Close()
3681+
},
3682+
},
3683+
} {
3684+
t.Run(tt.name, func(t *testing.T) {
3685+
unblock := make(chan bool, 1)
3686+
defer close(unblock)
36633687

3664-
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
3665-
// Don't read the 16KB request body. Wait until the client's
3666-
// done sending it and then return. This should cause the Server
3667-
// to then return those 16KB of flow control to the client.
3668-
<-unblock
3669-
}, optOnlyServer)
3670-
defer st.Close()
3688+
timeOut := time.NewTimer(5 * time.Second)
3689+
defer timeOut.Stop()
3690+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
3691+
// Don't read the 16KB request body. Wait until the client's
3692+
// done sending it and then return. This should cause the Server
3693+
// to then return those 16KB of flow control to the client.
3694+
tt.reqFn(r)
3695+
select {
3696+
case <-unblock:
3697+
case <-timeOut.C:
3698+
t.Fatal(tt.name, "timedout")
3699+
}
3700+
}, optOnlyServer)
3701+
defer st.Close()
36713702

3672-
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
3673-
defer tr.CloseIdleConnections()
3703+
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
3704+
defer tr.CloseIdleConnections()
36743705

3675-
// This previously hung on the 4th iteration.
3676-
for i := 0; i < 6; i++ {
3677-
body := io.MultiReader(
3678-
io.LimitReader(neverEnding('A'), 16<<10),
3679-
funcReader(func([]byte) (n int, err error) {
3680-
unblock <- true
3681-
return 0, io.EOF
3682-
}),
3683-
)
3684-
req, _ := http.NewRequest("POST", st.ts.URL, body)
3685-
res, err := tr.RoundTrip(req)
3686-
if err != nil {
3687-
t.Fatal(err)
3688-
}
3689-
res.Body.Close()
3706+
// This previously hung on the 4th iteration.
3707+
iters := 100
3708+
if testing.Short() {
3709+
iters = 20
3710+
}
3711+
for i := 0; i < iters; i++ {
3712+
body := io.MultiReader(
3713+
io.LimitReader(neverEnding('A'), 16<<10),
3714+
funcReader(func([]byte) (n int, err error) {
3715+
unblock <- true
3716+
return 0, io.EOF
3717+
}),
3718+
)
3719+
req, _ := http.NewRequest("POST", st.ts.URL, body)
3720+
res, err := tr.RoundTrip(req)
3721+
if err != nil {
3722+
t.Fatal(tt.name, err)
3723+
}
3724+
res.Body.Close()
3725+
}
3726+
})
36903727
}
3691-
36923728
}
36933729

36943730
func TestServerIdleTimeout(t *testing.T) {

0 commit comments

Comments
 (0)