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

net/http: client do not set Content-Length: 0 with an io.Reader with no bytes. #20257

Closed
SunRunAway opened this issue May 5, 2017 · 3 comments

Comments

@SunRunAway
Copy link

SunRunAway commented May 5, 2017

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.8.1 darwin/amd64

What operating system and processor architecture are you using (go env)?

darwin/amd64

What did you do?

I called http.NewRequest and explicitly set req.ContentLength to 0.

package main

import (
	"io"
	"log"
	"net/http"
	"os"
)

type nilbody struct{}

func (nilbody) Read(p []byte) (int, error) {
	return 0, io.EOF
}

func main() {
	body := nilbody{}
	req, err := http.NewRequest("POST", "http://httpbin.org/post", body)
	if err != nil {
		log.Fatal(err)
	}
	req.ContentLength = 0
	resp, _ := http.DefaultClient.Do(req)
	io.Copy(os.Stdout, resp.Body)
}

What did you expect to see?

server should get a header with Content-Length: 0

What did you see instead?

With go1.8.1, server get a chunked request body, but older version get a explicit Content-Length header.

[sunrunaway:/tmp]$ go run nilbody.go
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Connection": "close", 
    "Host": "httpbin.org", 
    "Transfer-Encoding": "chunked", 
    "User-Agent": "Go-http-client/1.1"
  }, 
  "json": null, 
  "origin": "180.168.57.238", 
  "url": "http://httpbin.org/post"
}
[sunrunaway:/tmp]$ /usr/local/Cellar/go/1.7.3/bin/go run nilbody.go 
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {}, 
  "headers": {
    "Accept-Encoding": "gzip", 
    "Connection": "close", 
    "Content-Length": "0", 
    "Host": "httpbin.org", 
    "User-Agent": "Go-http-client/1.1"
  }, 
  "json": null, 
  "origin": "180.168.57.238", 
  "url": "http://httpbin.org/post"
}

I know this is documented that 'a value of 0 means unknown if Body is not nil', but could it be possible to do some fix and not break our old code.

@SunRunAway SunRunAway changed the title net/http: client do not set Content-Length: 0 net/http: client do not set Content-Length: 0 with an io.Reader with no bytes. May 5, 2017
@bradfitz
Copy link
Contributor

bradfitz commented May 5, 2017

Use nil or the new Go 1.8 http.NoBody to send a Content-Length of 0 if your peer is sensitive to the difference between chunked zero and content-length zero.

This was intentionally changed during Go 1.8.

See https://golang.org/doc/go1.8#net_http

Some background can be found in git log (search for NoBody) or starting from b992c39 and https://golang.org/cl/31445 and #17480 and #17071

@bradfitz bradfitz closed this as completed May 5, 2017
@SunRunAway
Copy link
Author

This changes breaks our code in many places.

We have many interface like below, and every implement with http package should be fixed up for the compatibility unless we know exactly which server is sensitive to the explicit content-length.

type Sender interface {
    Send(r io.Reader, n int64)
}

Is it a good change for the go1 compatibility?

@bradfitz
Copy link
Contributor

bradfitz commented May 5, 2017

@SunRunAway, we considered this a lot during the past cycle, weighing various trade-offs. In the end we decided this was the best thing to do. If you want to be involved in such decisions in the future, please test the beta & rc releases. That's when we can still make changes. Now that Go 1.8 is out, things aren't changing.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants