go: x/net/http2: RoundTrip hangs when the response status code > 299
What version of Go are you using (go version)?
$ go version go version go1.15.5 darwin/amd64
Does this issue reproduce with the latest release?
Yes.
What operating system and processor architecture are you using (go env)?
go env Output
$ go env go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/Users/cf000197/Library/Caches/go-build" GOENV="/Users/cf000197/Library/Application Support/go/env" GOEXE="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="darwin" GOINSECURE="" GOMODCACHE="/Users/cf000197/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="darwin" GOPATH="/Users/cf000197/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/Cellar/go/1.15.5/libexec" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/Cellar/go/1.15.5/libexec/pkg/tool/darwin_amd64" GCCGO="gccgo" AR="ar" CC="clang" CXX="clang++" CGO_ENABLED="1" GOMOD="/Users/cf000197/go/issues/go.mod" 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=/var/folders/t1/fvz_vb0n6g9fpqwmdcl2yfbh0000gn/T/go-build998981477=/tmp/go-build -gno-record-gcc-switches -fno-common"
What did you do?
I followed https://github.com/golang/go/issues/13444 to create bidirectional streams. It works great when the server returns 200, but on the client side we are seeing roundtrip hanging when the response status > 299, after upgrading to latest http2. The commit that introduced the issue is https://github.com/golang/net/commit/ff519b6c91021e6316e1df005bc19f266994ddda. Even though abortRequestBodyWrite is called when response status > 299, Read in https://github.com/golang/net/blame/master/http2/transport.go#L1333 is blocked indefinitely.
Here is a program to reproduce the issue.
package main
import (
"fmt"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"time"
"golang.org/x/net/http2"
)
func main() {
pr, pw := io.Pipe()
// Host doesn't matter in this case because we already have a connection to the server
req, err := http.NewRequest(http.MethodPut, "https://http2.golang.org", ioutil.NopCloser(pr))
if err != nil {
log.Fatal(err)
}
clientConn, serverConn := net.Pipe()
if err != nil {
log.Fatal(err)
}
go func() {
server := http2.Server{}
server.ServeConn(serverConn, &http2.ServeConnOpts{
Handler: http.HandlerFunc(errorEndpoint),
})
}()
go func() {
transport := http2.Transport{}
http2ClientConn, err := transport.NewClientConn(clientConn)
res, err := http2ClientConn.RoundTrip(req)
if err != nil {
log.Fatal(err)
}
log.Printf("Got: %#v", res)
go streamRequest(pw)
n, err := io.Copy(os.Stdout, res.Body)
log.Fatalf("copied %d, %v", n, err)
}()
select {}
}
func streamRequest(pw io.Writer) {
for {
time.Sleep(1 * time.Second)
fmt.Fprintf(pw, "It is now %v\n", time.Now())
}
}
func errorEndpoint(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte(http.StatusText(http.StatusInternalServerError)))
}
Go module that causes the roundtrip to hang:
module main
go 1.15
require (
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.3 // indirect
)
Go module that was working:
module main
go 1.15
require (
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect
golang.org/x/text v0.3.3 // indirect
)
What did you expect to see?
Roundtrip returns with a 500 response.
What did you see instead?
Roundtrip hangs indefinitely
About this issue
- Original URL
- State: open
- Created 3 years ago
- Reactions: 5
- Comments: 17 (7 by maintainers)
Commits related to this issue
- turn off http2 for TLS setups for now due to lots of issues with x/net/http2, as well as the hundled h2_bundle.go in the go runtime should be avoided for now. https://github.com/golang/go/issues/235... — committed to harshavardhana/minio by harshavardhana 3 years ago
- turn off http2 for TLS setups for now due to lots of issues with x/net/http2, as well as the bundled h2_bundle.go in the go runtime should be avoided for now. https://github.com/golang/go/issues/235... — committed to harshavardhana/minio by harshavardhana 3 years ago
- turn off http2 for TLS setups for now (#11523) due to lots of issues with x/net/http2, as well as the bundled h2_bundle.go in the go runtime should be avoided for now. https://github.com/golang/... — committed to minio/minio by harshavardhana 3 years ago
- Rebase to upstream master (#9) * fix: metacache should only rename entries during cleanup (#11503) To avoid large delays in metacache cleanup, use rename instead of recursive delete calls, rename... — committed to poornas/minio by krisis 3 years ago
- turn off http2 for TLS setups for now (#11523) due to lots of issues with x/net/http2, as well as the bundled h2_bundle.go in the go runtime should be avoided for now. https://github.com/golang/... — committed to minio/minio by harshavardhana 3 years ago
- turn off http2 for TLS setups for now (#11523) due to lots of issues with x/net/http2, as well as the bundled h2_bundle.go in the go runtime should be avoided for now. https://github.com/golang/go/i... — committed to kerneltime/minio by harshavardhana 3 years ago
- turn off http2 for TLS setups for now (#11523) (#11569) due to lots of issues with x/net/http2, as well as the bundled h2_bundle.go in the go runtime should be avoided for now. https://github.co... — committed to minio/minio by kerneltime 3 years ago
- http2: close the request body if needed As per client.Do and Request.Body, the transport is responsible to close the request Body. If there was an error or non 1xx/2xx status code, the transport will... — committed to golang/net by fraenkel 3 years ago
- TUN-5405: Update net package to v0.0.0-20211109214657-ef0fda0de508 This version contains fix to https://github.com/golang/go/issues/43989 — committed to cloudflare/cloudflared by chungthuang 3 years ago
- http2: close the request body if needed As per client.Do and Request.Body, the transport is responsible to close the request Body. If there was an error or non 1xx/2xx status code, the transport will... — committed to dteh/fhttp by fraenkel 3 years ago
Change https://golang.org/cl/323689 mentions this issue:
http2: Transport will close the request bodyHi @fraenkel,
the issue has been reported against
x/net/http2module, however this package version has been integrated intoGo 1.15from the 8th revision (see the release notes andGo 1.16. Hence, it is now mainstream.This has caused an unfortunate change of behaviour impacting any type of tunnelling relying on the proxy server to acknowledge/decline the transaction before writing the content in the body.
I am providing here below another example using the HTTP CONNECT verb, based on the discussion provided in https://github.com/golang/go/issues/13717.
I’d greatly appreciate your input on this matter. Thanks!