Repository: go
Issue: net/http: EOF returned from http.Transport
Labels: NeedsInvestigation

### What version of Go are you using (`go version`)?

<pre>
$ go version
go version go1.18.3 darwin/amd64
</pre>

### Does this issue reproduce with the latest release?
yes

### What operating system and processor architecture are you using (`go env`)?

<details><summary><code>go env</code> Output</summary><br><pre>
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/mitr/Library/Caches/go-build"
GOENV="/Users/mitr/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="/Users/mitr/Go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="/Users/mitr/Go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.18.3"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD="/Users/mitr/Go/src/github.com/containers/image/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/tp/yfcwvlb55vx8lkv5gppb43cm0000gn/T/go-build3737075247=/tmp/go-build -gno-record-gcc-switches -fno-common"
</pre></details>

### What did you do?

Given a HTTP server that reads the request, but (cleanly) closes the connection without producing any response:
```go
package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
)

func server(ln net.Listener) {
	for {
		conn, err := ln.Accept()
		if err != nil {
			return
		}
		log.Printf("%v: Accepted", conn.RemoteAddr())
		var buf [4096]byte          // Hopefully enough for a full header
		n, err := conn.Read(buf[:]) // Completely read and ignore the header
		if err != nil {
			panic(err)
		}
		log.Printf("%v: Read %d", conn.RemoteAddr(), n)
		err = conn.Close()
		if err != nil {
			panic(err)
		}
		log.Printf("%v: Closed", conn.RemoteAddr())
	}
}

func main() {
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		panic(err)
	}
	go server(ln)

	res, err := http.Get(fmt.Sprintf("http://%s/", ln.Addr().String()))
	fmt.Printf("res=%#v, err=%v (%#v)", res, err, err)
}
```

### What did you expect to see?
An error saying something about an unexpectedly closed connection.


### What did you see instead?
```
2022/06/21 02:28:30 127.0.0.1:64135: Accepted
2022/06/21 02:28:30 127.0.0.1:64135: Read 96
2022/06/21 02:28:30 127.0.0.1:64135: Closed
res=(*http.Response)(nil), err=Get "http://127.0.0.1:64134/": EOF (&url.Error{Op:"Get", URL:"http://127.0.0.1:64134/", Err:(*errors.errorString)(0xc000098060)})
```
i.e. the error is `io.EOF`, which seems inconsistent with the official definition of that value:
> Functions should return EOF only to signal a graceful end of input. If the EOF occurs unexpectedly in a structured data stream, the appropriate error is either ErrUnexpectedEOF or some other error giving more detail.

### Notes
I appreciate that this might not be possible to change due to the compatibility promise.

The immediate cause is https://github.com/golang/go/blob/3fcbfb07a82c5332e6b50cddba333af6e6e3e488/src/net/http/transport.go#L2092 ; if that returns `io.EOF`, it is wrapped in https://github.com/golang/go/blob/3fcbfb07a82c5332e6b50cddba333af6e6e3e488/src/net/http/transport.go#L2109 , but later only unwrapped to become raw `io.EOF` again, with no logic anywhere to turn it into an “this was unexpected” error.