Closed as not planned
Description
Go version
go version go1.24.3 darwin/amd64
Output of go env
in your module/workspace:
AR='ar'
CC='clang'
CGO_CFLAGS='-O2 -g'
CGO_CPPFLAGS=''
CGO_CXXFLAGS='-O2 -g'
CGO_ENABLED='1'
CGO_FFLAGS='-O2 -g'
CGO_LDFLAGS='-O2 -g'
CXX='clang++'
GCCGO='gccgo'
GO111MODULE='auto'
GOAMD64='v1'
GOARCH='amd64'
GOAUTH='netrc'
GOBIN=''
GOCACHE='/tmp/.gocache'
GOCACHEPROG=''
GODEBUG=''
GOENV='/Users/rittneje/Library/Application Support/go/env'
GOEXE=''
GOEXPERIMENT=''
GOFIPS140='off'
GOFLAGS=''
GOGCCFLAGS='-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -ffile-prefix-map=/var/folders/kf/kr7_s3xx0l12zbj3jrn082hmzy5gvy/T/go-build2339782732=/tmp/go-build -gno-record-gcc-switches -fno-common'
GOHOSTARCH='amd64'
GOHOSTOS='darwin'
GOINSECURE=''
GOMOD=''
GOMODCACHE='/Users/rittneje/go/pkg/mod'
GONOPROXY='[REDACTED]'
GONOSUMDB='[REDACTED]'
GOOS='darwin'
GOPATH='/Users/rittneje/go'
GOPRIVATE='[REDACTED]'
GOPROXY='https://proxy.golang.org,direct'
GOROOT='/Users/rittneje/go1.24.3'
GOSUMDB='sum.golang.org'
GOTELEMETRY='off'
GOTELEMETRYDIR='/Users/rittneje/Library/Application Support/go/telemetry'
GOTMPDIR=''
GOTOOLCHAIN='local'
GOTOOLDIR='/Users/rittneje/go1.24.3/pkg/tool/darwin_amd64'
GOVCS='[REDACTED]'
GOVERSION='go1.24.3'
GOWORK=''
PKG_CONFIG='pkg-config'
What did you do?
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/http/httputil"
"strings"
)
func main() {
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
b, err := httputil.DumpRequest(req, req.URL.Path == "/dump-body")
if err != nil {
panic(err)
}
fmt.Println("received request: " + string(b))
req.Body.Close()
fmt.Println(io.ReadAll(req.Body))
}))
defer s.Close()
req, err := http.NewRequest(http.MethodGet, s.URL, strings.NewReader("blahblahblah"))
if err != nil {
panic(err)
}
if _, err := s.Client().Do(req); err != nil {
panic(err)
}
fmt.Println("\n----------\n")
req, err = http.NewRequest(http.MethodGet, s.URL+"/dump-body", strings.NewReader("blahblahblah"))
if err != nil {
panic(err)
}
req.Header.Set("Dump", "true")
if _, err := s.Client().Do(req); err != nil {
panic(err)
}
}
What did you see happen?
For the first request, where the body isn't dumped, I see "http: invalid Read on closed Body", which corresponds to http.ErrBodyReadAfterClose
.
For the second request, where the body is dumped, reading the body after closing it worked.
This means that code that works when httputil.DumpRequest
is used may not work when it isn't.
What did you expect to see?
I expected httputil.DumpRequest
to replace the body in such a way that it preserves the semantics described in the net/http documentation. Namely, it should still return http.ErrBodyReadAfterClose
if you try to read the body after closing it.