Replace our old faithful gracefulListener with Go 1.8's Shutdown()

This commit is contained in:
Matthew Holt
2017-01-24 20:05:53 -07:00
parent 16250da3f0
commit 139a3cfb13
2 changed files with 11 additions and 130 deletions

View File

@@ -2,6 +2,7 @@
package httpserver
import (
"context"
"crypto/tls"
"fmt"
"io"
@@ -27,9 +28,8 @@ type Server struct {
listener net.Listener
listenerMu sync.Mutex
sites []*SiteConfig
connTimeout time.Duration // max time to wait for a connection before force stop
connWg sync.WaitGroup // one increment per connection
tlsGovChan chan struct{} // close to stop the TLS maintenance goroutine
connTimeout time.Duration // max time to wait for a connection before force stop
tlsGovChan chan struct{} // close to stop the TLS maintenance goroutine
vhosts *vhostTrie
}
@@ -46,16 +46,6 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) {
connTimeout: GracefulTimeout,
}
s.Server.Handler = s // this is weird, but whatever
s.Server.ConnState = func(c net.Conn, cs http.ConnState) {
if cs == http.StateIdle {
s.listenerMu.Lock()
// server stopped, close idle connection
if s.listener == nil {
c.Close()
}
s.listenerMu.Unlock()
}
}
// Disable HTTP/2 if desired
if !HTTP2 {
@@ -68,14 +58,6 @@ func NewServer(addr string, group []*SiteConfig) (*Server, error) {
s.Server.Handler = s.wrapWithSvcHeaders(s.Server.Handler)
}
// We have to bound our wg with one increment
// to prevent a "race condition" that is hard-coded
// into sync.WaitGroup.Wait() - basically, an add
// with a positive delta must be guaranteed to
// occur before Wait() is called on the wg.
// In a way, this kind of acts as a safety barrier.
s.connWg.Add(1)
// Set up TLS configuration
var tlsConfigs []*caddytls.Config
for _, site := range group {
@@ -154,8 +136,6 @@ func (s *Server) Serve(ln net.Listener) error {
ln = tcpKeepAliveListener{TCPListener: tcpLn}
}
ln = newGracefulListener(ln, &s.connWg)
s.listenerMu.Lock()
s.listener = ln
s.listenerMu.Unlock()
@@ -300,40 +280,21 @@ func (s *Server) Address() string {
// Stop stops s gracefully (or forcefully after timeout) and
// closes its listener.
func (s *Server) Stop() (err error) {
s.Server.SetKeepAlivesEnabled(false)
func (s *Server) Stop() error {
ctx, cancel := context.WithTimeout(context.Background(), s.connTimeout)
defer cancel()
if runtime.GOOS != "windows" {
// force connections to close after timeout
done := make(chan struct{})
go func() {
s.connWg.Done() // decrement our initial increment used as a barrier
s.connWg.Wait()
close(done)
}()
// Wait for remaining connections to finish or
// force them all to close after timeout
select {
case <-time.After(s.connTimeout):
case <-done:
}
err := s.Server.Shutdown(ctx)
if err != nil {
return err
}
// Close the listener now; this stops the server without delay
s.listenerMu.Lock()
if s.listener != nil {
err = s.listener.Close()
s.listener = nil
}
s.listenerMu.Unlock()
// Closing this signals any TLS governor goroutines to exit
// signal any TLS governor goroutines to exit
if s.tlsGovChan != nil {
close(s.tlsGovChan)
}
return
return nil
}
// sanitizePath collapses any ./ ../ /// madness