-
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: NewRequest started setting ContentLength to -1 and breaking people? #18117
Comments
(This is from investigation motivated by https://golang.org/cl/33577) |
CL https://golang.org/cl/33714 mentions this issue. |
Try this code, this works with normal releases, but break with devel branch. |
Draft means "a preliminary version of a piece of writing". Yes, we never saw that because it wasn't mailed. In Gerrit, you can write many comments in different places and then send it all together in one batch. |
That can't possibly work in "normal releases", because Please give me an example of code that both compiles & works in Go 1.7, but doesn't work at tip ("the devel branch"). |
Any user implemented reader that is not in the list of The difference is that you set ContentLength to -1 if the length can not be told in And this arises my another question, why there should be a pre-filled ContentLength? |
Ah, great! Now I can reproduce the regression. I was being misled by the trickery in DumpRequestOut: func DumpRequestOut(req *http.Request, body bool) ([]byte, error) {
save := req.Body
dummyBody := false
if !body || req.Body == nil {
req.Body = nil
if req.ContentLength != 0 {
req.Body = ioutil.NopCloser(io.LimitReader(neverEnding('x'), req.ContentLength)) My test in the first comment is kinda bogus. |
NoBody is new in Go 1.8. Found while investigating #18117 Change-Id: I6bda030f358e2270f090d108cb3a89c8a2665fcb Reviewed-on: https://go-review.googlesource.com/33714 Run-TryBot: Brad Fitzpatrick <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
@ggaaooppeenngg, I see what changed. The Transport and Request.Write are still strict. They have been since Go 1. What's different is that http.NewRequest now sets the ContentLength explicitly to -1 to mean unknown when it's unknown, rather than using 0. I suppose I could remove that. But does this actually matter? This isn't an API behavior we ever meant to guarantee, that you could use NewRequest and then nil out the field you just set. When does that actually occur? You never filed a bug (you just sent a change), so I don't know the motivation here. |
CL https://golang.org/cl/33801 mentions this issue. |
@bradfitz Actually I put a nil body as argument in the io.Reader (user implemented) constructor, then I reset the request body to nil depending on the initial body.
Since a interface containing nil is not nil, ContentLength becomes -1 in the Go tip. Actually this can be fixed in this way.
I make the change, because I think it is a good to not break the user space code no matter what is patched, but you can announce this change, so users can check it before update go version. |
I see. Okay, I'm tempted to revert my revert then (so the codebase can make forward progress) and let users fix their code like you suggested. Users need to learn about https://golang.org/doc/faq#nil_error eventually. But I'm also not sure how much it really matters. |
The // ContentLength records the length of the associated content.
// The value -1 indicates that the length is unknown.
// Values >= 0 indicate that the given number of bytes may
// be read from Body.
// For client requests, a value of 0 means unknown if Body is not nil.
ContentLength int64 It is confusing
I found old code that does exactly this :-/ The code breaks when testing against 1.8 before 4bc7b5a. The code is roughly: func CreateProxyRequest(req *http.Request) *http.Request {
req.Body = unknownWrapper(req.Body) // in caller
if req.Method == "POST" {
outreq = http.NewRequest(req.Method, req.URL.String(), req.Body)
} else {
outreq = http.NewRequest(rew.Method, req.URL.String(), nil /* disallow bodies otherwise */)
}
...
if outreq.Body != nil && outreq.ContentLength == 0 /* here's the problem */ {
outreq.ContentLength = parseCL(outreq.Header)
// instead of doing this, the code should set outreq.ContentLength = req.ContentLength
// in the "POST" branch, above
}
return outreq
} I submit this code as further evidence that the API may have ossified to the point that 4bc7b5a should not be reverted. That is, reverting 4bc7b5a will likely break actually code. That said, I am in favor of reverting 4bc7b5a. We will break code, but I can't imagine that we will break code that isn't already broken in other ways, and reverting 4bc7b5a is much cleaner in the long run. |
The integration tests revealed that AnimeService.List and MangaService.List started receiving a 411 Length required error from the MyAnimeList API. Both methods use mal.Client.NewRequest to create API requests. This is probably related to golang/go#18117 and it happened because when using http.NewRequest (inside mal.Client.NewRequest) with GET, the body argument was not nil as it should. This commit ensures that nil body is properly passed to http.NewRequest when mal.Client.NewRequest has no data to encode.
Since Go 1 (1.0), there has been this line in transfer.go:
https://github.com/golang/go/blob/release-branch.go1/src/pkg/net/http/transfer.go
Which causes a Request.Write of a Request with a nil Body and non-zero length to fail.
But the Transport accepts that, and makes a nil Body imply a ContentLength of 0.
This program should probably have the same output:
But it says:
For Go tip, Go 1.7, Go 1.6, etc.
Decide whether this is okay or we should fix.
The text was updated successfully, but these errors were encountered: