-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
test: cancelling a stream #801
base: main
Are you sure you want to change the base?
Conversation
7bba305
to
f9b7568
Compare
766df93
to
7451de7
Compare
Hmmm, this seems like a bug in the HTTP/2 support in I did a quick search and was unable to find any existing issues in github.com/golang/go that describe this, so I may need to play around a little more and file such a bug (if I can convince myself this is a bug in the |
c0deabc
to
a896d0b
Compare
Signed-off-by: oliverpool <[email protected]>
Signed-off-by: oliverpool <[email protected]>
Signed-off-by: oliverpool <[email protected]>
a896d0b
to
f0cce79
Compare
Signed-off-by: oliverpool <[email protected]>
f0cce79
to
d46d7d7
Compare
The rebase confirms that #791 solves the initial issue. The only remaining issue is the following:
If http2 is disabled, the bug does not appear. Might be related to https://thrawn01.org/posts/a-golang-http-server-refused-to-shutdown (from a quick look, the httptest.Server does not configure shutdown. I need to take a deeper look, but don't have time right now) |
@oliverpool, I just pulled down this branch and see the opposite: HTTP/2 works just fine but it's when HTTP/2 is disabled that it hangs. I suspect the reason is that HTTP 1.1 does not have a way for the server to tell the client to "go away", other than to add a "Connection: Close" response header to a response. So I think the way HTTP 1.1 servers shutdown is to await a request on an idle connection and then add that header to the response (or add it to responses for all requests that are in-progress at the time the shutdown call is made). And if there are no incoming requests, then they eventually close the connection after an idle timeout. I was able to get it to pass with a few changes:
With the above, the test passes with both HTTP 1.1 and HTTP/2. |
You are right! I oversaw this somehow.
But here the problem is that the client wants to tell the server to stop. The following reproducer indicates that this issue present in the stdlib: func TestHang(t *testing.T) {
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rc := http.NewResponseController(w)
n := byte(0)
for {
if n > 10 {
return
}
t.Log("write", n)
_, err := w.Write([]byte{n})
if err != nil {
t.Log("bye", err)
return
}
if n <= 2 {
rc.Flush() // if we flush, we get the failure a bit earlier
}
time.Sleep(time.Second)
n++
}
}))
ctx, cancel := context.WithCancel(context.Background())
req, err := http.NewRequestWithContext(ctx, "GET", s.URL, nil)
assert.Nil(t, err)
go func() {
rsp, err := s.Client().Do(req)
assert.Nil(t, err)
t.Log(io.Copy(os.Stdout, rsp.Body))
}()
time.Sleep(10 * time.Millisecond)
cancel()
t.Log("cancelled")
startClosing := time.Now()
s.Close()
assert.True(t, time.Since(startClosing) < time.Second, assert.Sprintf("server.Close took too long: %s", time.Since(startClosing)))
} |
Signed-off-by: oliverpool <[email protected]>
21aa99f
to
c3fdd5a
Compare
I found a solution: the server must take into account the context ( |
Signed-off-by: oliverpool <[email protected]>
Signed-off-by: oliverpool <[email protected]>
Related to #789 and #791.
According to @jhump in #789 (comment)
However when http2 is enabled, cancelling the context does not suffice to unblock
stream.Close()
.The
stream.Close
calltree indicates that it is blocked indiscard(reader io.Reader)
(add a-timeout 500ms
togo test
to get a dump).When http2 is not enabled, it seems to behave correctly.
#791 would fix this.