Description
What version of Go are you using (go version
)?
$ docker run --rm docker.io/golang:1.15.6-alpine3.12 go version go version go1.15.6 linux/amd64
Using docker.io/golang:1.15.6-alpine3.12
It's the same on 1.15.2 though. So I wouldn't say it's some new regression.
Does this issue reproduce with the latest release?
Yes
What operating system and processor architecture are you using (go env
)?
go env
Output
$ docker run --rm docker.io/golang:1.15.6-alpine3.12 go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GCCGO="gccgo" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="" CGO_CFLAGS="-g -O2" CGO_CPPFLAGS="" CGO_CXXFLAGS="-g -O2" CGO_FFLAGS="-g -O2" CGO_LDFLAGS="-g -O2" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build719806207=/tmp/go-build -gno-record-gcc-switches"
What did you do?
package main
import (
"net/http"
"io/ioutil"
"bytes"
)
func main() {
payloadBytes := []byte("abcd")
request, err := http.NewRequest(
"POST",
"https://example.com",
// Without `ioutil.NopCloser()`, we'd get it to find the content length.
// With it, it remains unknown (0).
// This is somewhat contrived.
ioutil.NopCloser(bytes.NewReader(payloadBytes)),
)
if err != nil {
panic(err)
}
// If this is not set, `Transfer-Encoding: chunked` may be used,
// even over HTTP2, which is not supported:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
// request.ContentLength = int64(len(payloadBytes))
http.DefaultClient.Do(request)
}
I can't reproduce it with a server of my own, but I had another person (on shared Apache hosting) show me the request headers (in PHP). We saw that it's an HTTP2 connection and that there's a Transfer-Encoding: chunked
request header.
The original problem was that he couldn't read the request body (JSON).. We tracked it down to this. As soon as we set .ContentLength
explicitly, Go's HTTP client no longer sets Transfer-Encoding: chunked
and the problem goes away.
The same code (even without explicitly setting .ContentLength
) works as expected on HTTP2-capable servers of mine (nginx).
So it seems to be some weird interaction between that specific Apache version (which is unknown so far) and Golang's HTTP client. There may be some reverse-proxies inbetween, so it may be something else's fault entirely (not Golang's HTTP client).
Unfortunately, I neither have an HTTP2-capable Apache server of mine, nor do I know the exact Apache version that his shared hosting provider runs.
Obviously, there's lack of information about this (Apache version used, whether there are other reverse-proxies involved, etc.), but perhaps this is still enough to track down the problem of net/http
's willingness to combine HTTP2 with chunked
transfer encoding under weird circumstances..? Again, that is, unless something else (some reverse-proxy) is actually causing this.