golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
124.12k stars 17.68k forks source link

net/http: http.response not flushing body after writing 1XX headers when client has header Expect: 100-continue #57696

Open WeidiDeng opened 1 year ago

WeidiDeng commented 1 year ago

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

$ go version
go version go1.19.4 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
Not relevant

What did you do?

func runServer() {
    panic(http.ListenAndServe(":80", http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {
        _, _ = io.Copy(io.Discard, request.Body)
        writer.WriteHeader(100)
        writer.WriteHeader(201)
        _, _ = io.WriteString(writer, "CREATED")
    })))
}

func Test100Http(t *testing.T) {
    go runServer()
    time.Sleep(time.Second)
    conn, _ := net.Dial("tcp", "127.0.0.1:80")
    file, _ := os.Open("/tmp/go1.20rc2.src.tar.gz")
    req, _ := http.NewRequest(http.MethodPut, "http://127.0.0.1/go.tar.gz", file)
    req.Header.Set("Expect", "100-continue")
    _ = req.Write(conn)
    _, _ = io.Copy(os.Stdout, conn)
}

What did you expect to see?

output

HTTP/1.1 100 Continue

HTTP/1.1 100 Continue

HTTP/1.1 201 Created
Date: Mon, 09 Jan 2023 09:00:36 GMT
Content-Length: 7
Content-Type: text/plain; charset=utf-8

CREATED

What did you see instead?

The program hangs with the following output

HTTP/1.1 100 Continue

HTTP/1.1 100 Continue

HTTP/1.1 201 Created
Date: Mon, 09 Jan 2023 09:00:36 GMT
Content-Length: 7
Content-Type: text/plain; charset=utf-8

notice response body is not written CREATED

Additional Info

This problem was first discovered when a caddy user find his webdav server not longer works when he upgraded caddy version. Caddy is using a heavily modified reverse_proxy handler from stdlib and merged 1xx status codes which was backported from 53164.

After some digging in caddy, I determined it's a problem with golang http. The handler is a reduced version of caddy reverse_proxy which forwards 1XX from backend. And the example file is small 25MB, using large files like 134MB will produce the correct output.

bcmills commented 1 year ago

(attn @neild)

Compare:

helmut72 commented 1 year ago

Still the same problem with go 1.19.7 (and using Caddy 2.6.4)