Commit 916682ea authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: add Server.SetKeepAlivesEnabled

Part of graceful shutdown.

Update #4674

LGTM=adg, josharian
R=adg, josharian, r
CC=golang-codereviews
https://golang.org/cl/69670043
parent 1f8b2a69
...@@ -2358,6 +2358,22 @@ func mustGet(t *testing.T, url string, headers ...string) { ...@@ -2358,6 +2358,22 @@ func mustGet(t *testing.T, url string, headers ...string) {
} }
} }
func TestServerKeepAlivesEnabled(t *testing.T) {
defer afterTest(t)
ts := httptest.NewUnstartedServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
ts.Config.SetKeepAlivesEnabled(false)
ts.Start()
defer ts.Close()
res, err := Get(ts.URL)
if err != nil {
t.Fatal(err)
}
defer res.Body.Close()
if !res.Close {
t.Errorf("Body.Close == false; want true")
}
}
func BenchmarkClientServer(b *testing.B) { func BenchmarkClientServer(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
b.StopTimer() b.StopTimer()
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
...@@ -703,6 +704,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { ...@@ -703,6 +704,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
cw.wroteHeader = true cw.wroteHeader = true
w := cw.res w := cw.res
keepAlivesEnabled := w.conn.server.doKeepAlives()
isHEAD := w.req.Method == "HEAD" isHEAD := w.req.Method == "HEAD"
// header is written out to w.conn.buf below. Depending on the // header is written out to w.conn.buf below. Depending on the
...@@ -750,7 +752,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { ...@@ -750,7 +752,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
// If this was an HTTP/1.0 request with keep-alive and we sent a // If this was an HTTP/1.0 request with keep-alive and we sent a
// Content-Length back, we can make this a keep-alive response ... // Content-Length back, we can make this a keep-alive response ...
if w.req.wantsHttp10KeepAlive() { if w.req.wantsHttp10KeepAlive() && keepAlivesEnabled {
sentLength := header.get("Content-Length") != "" sentLength := header.get("Content-Length") != ""
if sentLength && header.get("Connection") == "keep-alive" { if sentLength && header.get("Connection") == "keep-alive" {
w.closeAfterReply = false w.closeAfterReply = false
...@@ -769,7 +771,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { ...@@ -769,7 +771,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
w.closeAfterReply = true w.closeAfterReply = true
} }
if header.get("Connection") == "close" { if header.get("Connection") == "close" || !keepAlivesEnabled {
w.closeAfterReply = true w.closeAfterReply = true
} }
...@@ -851,7 +853,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { ...@@ -851,7 +853,7 @@ func (cw *chunkWriter) writeHeader(p []byte) {
return return
} }
if w.closeAfterReply && !hasToken(cw.header.get("Connection"), "close") { if w.closeAfterReply && (!keepAlivesEnabled || !hasToken(cw.header.get("Connection"), "close")) {
delHeader("Connection") delHeader("Connection")
if w.req.ProtoAtLeast(1, 1) { if w.req.ProtoAtLeast(1, 1) {
setHeader.connection = "close" setHeader.connection = "close"
...@@ -1579,6 +1581,7 @@ func Serve(l net.Listener, handler Handler) error { ...@@ -1579,6 +1581,7 @@ func Serve(l net.Listener, handler Handler) error {
} }
// A Server defines parameters for running an HTTP server. // A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct { type Server struct {
Addr string // TCP address to listen on, ":http" if empty Addr string // TCP address to listen on, ":http" if empty
Handler Handler // handler to invoke, http.DefaultServeMux if nil Handler Handler // handler to invoke, http.DefaultServeMux if nil
...@@ -1600,6 +1603,8 @@ type Server struct { ...@@ -1600,6 +1603,8 @@ type Server struct {
// called when a client connection changes state. See the // called when a client connection changes state. See the
// ConnState type and associated constants for details. // ConnState type and associated constants for details.
ConnState func(net.Conn, ConnState) ConnState func(net.Conn, ConnState)
disableKeepAlives int32 // accessed atomically.
} }
// A ConnState represents the state of a client connection to a server. // A ConnState represents the state of a client connection to a server.
...@@ -1714,6 +1719,22 @@ func (srv *Server) Serve(l net.Listener) error { ...@@ -1714,6 +1719,22 @@ func (srv *Server) Serve(l net.Listener) error {
} }
} }
func (s *Server) doKeepAlives() bool {
return atomic.LoadInt32(&s.disableKeepAlives) == 0
}
// SetKeepAlivesEnabled controls whether HTTP keep-alives are enabled.
// By default, keep-alives are always enabled. Only very
// resource-constrained environments or servers in the process of
// shutting down should disable them.
func (s *Server) SetKeepAlivesEnabled(v bool) {
if v {
atomic.StoreInt32(&s.disableKeepAlives, 0)
} else {
atomic.StoreInt32(&s.disableKeepAlives, 1)
}
}
// ListenAndServe listens on the TCP network address addr // ListenAndServe listens on the TCP network address addr
// and then calls Serve with handler to handle requests // and then calls Serve with handler to handle requests
// on incoming connections. Handler is typically nil, // on incoming connections. Handler is typically nil,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment