-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
net/http: clarify whether Requests can be reused #19653
Comments
Sorry, just getting to this. Can you clarify what you mean by unreliable? |
Yep, really wish I'd put the steps to reproduce in the original report. I think it was code like this:
(or maybe even without any concurrency) It's unclear that state is held in the request struct and that a Request shouldn't be re-used. |
There's no state in the Request struct. There's only one unexported field and it's just the context, which we don't want people to mutate. The only thing I can think of is the old pre-cancel/pre-context I don't see why Requests couldn't be used concurrently. Could you try to whip up a repro? |
Tried, couldn't repro. Code: https://github.com/broady/upwatch/blob/master/http.go#L86 Changes: diff --git a/http.go b/http.go
index 99f775f..928b03d 100644
--- a/http.go
+++ b/http.go
@@ -83,7 +83,8 @@ func boom(w http.ResponseWriter, r *http.Request) {
return
}
log.Printf("GET %q", url)
- if _, err := http.NewRequest("GET", url, nil); err != nil {
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
http.Error(w, "Invalid URL", http.StatusBadRequest)
return
}
@@ -116,7 +117,8 @@ func boom(w http.ResponseWriter, r *http.Request) {
<-sem
go func() {
start := time.Now()
- resp, err := http.Get(url)
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
r := result{err: err, duration: time.Now().Sub(start)}
sem <- true |
Change https://golang.org/cl/75530 mentions this issue: |
Based on golang/go#19653, it should be possible to reuse an http.Request object after the outstanding request has completed. This CL fixes a race in the http/2 library that occurs when a caller tries to reuse an http.Request just after the request completed. The new test failed with -race before this CL and passes after this CL. Verified with -count 10000. Updates golang/go#21316 Change-Id: I014cf9cefd0dd21f6f41763ba554d23ddc7fca40 Reviewed-on: https://go-review.googlesource.com/75530 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
@bradfitz I think this is actually more subtle because of HTTP/2. Suppose a client does: req, _ := http.NewRequest("GET", url, nil)
resp, _ := http.DefaultTransport.Roundtrip(req)
resp.Body.Close() It should be fine for the caller to reuse req, _ := http.NewRequest("POST", url, body)
resp, _ := http.DefaultTransport.Roundtrip(req)
resp.Body.Close() We cannot guarantee that So, I think this may deserve clarification, in two respects:
The simplest resolution is to say: A Request cannot be reused if it has a non-nil Body. If the Request has a nil Body, then the Request should not be modified until either RoundTrip returns an error or Respond.Body.Close is called. (This covers both of the above corner cases.) An even simpler resolution is to forbid reusing Requests in any cases, but I suspect this would break callers. WDYT? |
Or we just say it's only safe to reuse a Request if Body is nil or the the Request.Body has been read to EOF. (the caller could wrap the Body in their own wrapper which notes EOF if they wanted). |
Change https://golang.org/cl/75671 mentions this issue: |
Based on golang/go#19653, it should be possible to reuse an http.Request object after the outstanding request has completed. This CL fixes a race in the http/2 library that occurs when a caller tries to reuse an http.Request just after the request completed. The new test failed with -race before this CL and passes after this CL. Verified with -count 10000. Updates golang/go#21316 Change-Id: I014cf9cefd0dd21f6f41763ba554d23ddc7fca40 Reviewed-on: https://go-review.googlesource.com/75530 Reviewed-by: Brad Fitzpatrick <[email protected]> Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
This patch changes the interfaces of `NewRequest` and `Do` around a little so that we can set a new request body with every request. In the era of HTTP (1), it was safe to reuse a `Request` object, but with the addition of HTTP/2, it's now only sometimes safe. Reusing a `Request` with a body will break. See some more information here: golang/go#19653 (comment) Fixes #642.
This patch changes the interfaces of `NewRequest` and `Do` around a little so that we can set a new request body with every request. In the era of HTTP (1), it was safe to reuse a `Request` object, but with the addition of HTTP/2, it's now only sometimes safe. Reusing a `Request` with a body will break. See some more information here: golang/go#19653 (comment) Fixes #642.
This patch changes the interfaces of `NewRequest` and `Do` around a little so that we can set a new request body with every request. In the era of HTTP (1), it was safe to reuse a `Request` object, but with the addition of HTTP/2, it's now only sometimes safe. Reusing a `Request` with a body will break. See some more information here: golang/go#19653 (comment) Fixes #642.
This patch changes the interfaces of `NewRequest` and `Do` around a little so that we can set a new request body with every request. In the era of HTTP (1), it was safe to reuse a `Request` object, but with the addition of HTTP/2, it's now only sometimes safe. Reusing a `Request` with a body will break. See some more information here: golang/go#19653 (comment) Fixes #642.
Re-using requests is currently unreliable.
If that's intended, then I think it should be documented. If not intended, then I can provide steps to reproduce.
The text was updated successfully, but these errors were encountered: