Skip to content

net/http: server.ListenAndServe should honour SetKeepAlivesEnabled #18427

Closed
@aviddiviner

Description

@aviddiviner

Calling http.ListenAndServe will do a net.Listen(...) and then wrap that TCPListener in a tcpKeepAliveListener before calling Serve to handle any incoming connections.

This is clear from the docs on ListenAndServe, which states that:

Accepted connections are configured to enable TCP keep-alives.

But, http.Server also provides the handy SetKeepAlivesEnabled method. So, it would seem to me that calling SetKeepAlivesEnabled(false) and then ListenAndServe() would do the obvious thing and disable keep-alives on that server's connections.

... which it seems to do — sending Connection: close, and likely closing it off after writing the response — however...

As far as I can tell the tcpKeepAliveListener will still always call SetKeepAlive(true) and SetKeepAlivePeriod(3 * time.Minute) on each new connection, triggering a bunch of syscall.SetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_KEEPALIVE, boolint(keepalive)) magic each time.

Which can be avoided, especially if I'm disabling keep-alives before I even start serving my traffic.

So at the very least, this could be as simple as something like:

diff --git a/src/net/http/server.go b/src/net/http/server.go
index 89574a8..dd21955 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -2216,7 +2216,10 @@ func (srv *Server) ListenAndServe() error {
 	if err != nil {
 		return err
 	}
-	return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
+	if srv.doKeepAlives() {
+		return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
+	}
+	return srv.Serve(ln)
 }
 
 var testHookServerServe func(*Server, net.Listener) // used if non-nil

And doing the same on ListenAndServeTLS.

I haven't looked how feasible it would be to have the tcpKeepAliveListener itself stop setting socket keep-alives once the server setting is changed, but I guess that would be even better?

Am I missing something? Let me know what you think.

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

go version go1.7.4 darwin/amd64

What operating system and processor architecture are you using (go env)?

GOHOSTARCH="amd64"
GOHOSTOS="darwin"

What did you do?

package main

import "net/http"

func main() {
	s := &http.Server{Addr: ":8080"}
	s.SetKeepAlivesEnabled(false)
	s.ListenAndServe()
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions