golang / go

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

net/http: Keep-Alive is not used #50777

Closed waldreiter closed 2 years ago

waldreiter commented 2 years ago

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

go1.17.6 darwin/amd64

Does this issue reproduce with the latest release?

Yes

What did you do?

A go server that responses with some text larger than 2 kB:

package main

import (
    "fmt"
    "log"
    "net/http"
    "strings"
)

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
        fmt.Fprint(w, strings.Repeat("@", 2049))
    })

    log.Fatal(http.ListenAndServe(":8080", nil))
}

ApacheBench calls it many times:

ab -c 8 -n 100000 -s 3 -k http://127.0.0.1:8080/

What did you expect to see?

ApacheBench should run successfully.

What did you see instead?

This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 10000 requests
apr_pollset_poll: The timeout specified has expired (70007)
Total of 16366 requests completed

Some observations

mknyszek commented 2 years ago

CC @neild

neild commented 2 years ago

I haven't dug into precisely what happens in this scenario, but I strongly suspect you'll get the behavior you want if you set a Content-Length header in your handler.

The reason for the change in behavior at 2048 bytes is that that's the cutoff for how much data the server will read from the response body before deciding it doesn't know the content length.

waldreiter commented 2 years ago

I tested setting the Content-Length and it worked.

My Actix Web server does not set the Content-Length, but there it works anyway. But I guess that`s life.

Your explanation makes sense. Thank you!