Skip to content
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

http3: return the context cancellation error from RoundTrip #4203

Merged
merged 1 commit into from
Dec 21, 2023

Conversation

marten-seemann
Copy link
Member

@marten-seemann marten-seemann commented Dec 13, 2023

@jhump Can you try out this PR? Does it fix your problem?

@marten-seemann marten-seemann force-pushed the http3-roundtrip-context-cancellation branch from 5716998 to 7c2ca28 Compare December 13, 2023 12:02
Copy link

codecov bot commented Dec 13, 2023

Codecov Report

All modified and coverable lines are covered by tests ✅

Comparison is base (0489409) 83.71% compared to head (7c2ca28) 83.74%.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #4203      +/-   ##
==========================================
+ Coverage   83.71%   83.74%   +0.03%     
==========================================
  Files         150      150              
  Lines       15470    15477       +7     
==========================================
+ Hits        12950    12960      +10     
+ Misses       2021     2019       -2     
+ Partials      499      498       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jhump
Copy link

jhump commented Dec 13, 2023

@marten-seemann, on the face of it, this looks right, but I'm seeing something weird in my client program that I need to track down. One thing that is absent here is doing something similar for errors returned from Body.Read, since consuming the response data can also encounter context timeout/cancellation.

Comment on lines +302 to +316
It("handles context cancellations", func() {
mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
<-r.Context().Done()
})

ctx, cancel := context.WithCancel(context.Background())
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil)
Expect(err).ToNot(HaveOccurred())
time.AfterFunc(50*time.Millisecond, cancel)

_, err = client.Do(req)
Expect(err).To(HaveOccurred())
Expect(err).To(MatchError(context.Canceled))
})

Copy link

@jhump jhump Dec 13, 2023

Choose a reason for hiding this comment

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

Another useful test would be to verify receipt of deadline exceeded, which looks very similar to this case.

	It("handles context deadline exceeded", func() {
		mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
			<-r.Context().Done()
		})

		ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil)
		Expect(err).ToNot(HaveOccurred())

		_, err = client.Do(req)
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError(context.DeadlineExceeded))
	})

Copy link

Choose a reason for hiding this comment

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

A test case for the body-read error wouldn't be much different:

	It("handles context cancellations when reading body", func() {
		mux.HandleFunc("/cancel", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusOK)
			<-r.Context().Done()
		})

		ctx, cancel := context.WithCancel(context.Background())
		req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://localhost:%d/cancel", port), nil)
		Expect(err).ToNot(HaveOccurred())
		time.AfterFunc(50*time.Millisecond, cancel)

		resp, err = client.Do(req)
		Expect(err).ToNot(HaveOccurred())
		_, err = resp.Body.Read(make([]byte, 1024))
		Expect(err).To(HaveOccurred())
		Expect(err).To(MatchError(context.Canceled))
	})

Copy link
Member Author

Choose a reason for hiding this comment

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

A test case for the body-read error wouldn't be much different:

That's a much bigger change. The Go documentation for the request context says:

For an outgoing client request, the context controls the entire lifetime of a request and its response: obtaining a connection, sending the request, and reading the response headers and body.

So you're right, the body needs to return the context error as well. Currently, we don't even pass the context to the body, and it's not clear to me how to correctly respect the context without spawning a new Go routine, since the Stream.Read call is blocking call.

Copy link
Member Author

Choose a reason for hiding this comment

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

I suggest doing this in a follow-up PR. I'll open a new issue.

@marten-seemann
Copy link
Member Author

Merging this PR, since it at least partially fixes #4196. The remainder of the fix is tracked in #4205.

@marten-seemann marten-seemann merged commit 2243fde into master Dec 21, 2023
34 checks passed
@marten-seemann marten-seemann deleted the http3-roundtrip-context-cancellation branch December 28, 2023 05:11
nanokatze pushed a commit to nanokatze/quic-go that referenced this pull request Feb 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants