Files
mostlymatter/vendor/github.com/braintree/manners/server.go
2016-05-12 16:37:29 -04:00

293 строки
7.4 KiB
Go

/*
Package manners provides a wrapper for a standard net/http server that
ensures all active HTTP client have completed their current request
before the server shuts down.
It can be used a drop-in replacement for the standard http package,
or can wrap a pre-configured Server.
eg.
http.Handle("/hello", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello\n"))
})
log.Fatal(manners.ListenAndServe(":8080", nil))
or for a customized server:
s := manners.NewWithServer(&http.Server{
Addr: ":8080",
Handler: myHandler,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
})
log.Fatal(s.ListenAndServe())
The server will shut down cleanly when the Close() method is called:
go func() {
sigchan := make(chan os.Signal, 1)
signal.Notify(sigchan, os.Interrupt, os.Kill)
<-sigchan
log.Info("Shutting down...")
manners.Close()
}()
http.Handle("/hello", myHandler)
log.Fatal(manners.ListenAndServe(":8080", nil))
*/
package manners
import (
"crypto/tls"
"net"
"net/http"
"sync"
"sync/atomic"
)
// A GracefulServer maintains a WaitGroup that counts how many in-flight
// requests the server is handling. When it receives a shutdown signal,
// it stops accepting new requests but does not actually shut down until
// all in-flight requests terminate.
//
// GracefulServer embeds the underlying net/http.Server making its non-override
// methods and properties avaiable.
//
// It must be initialized by calling NewWithServer.
type GracefulServer struct {
*http.Server
shutdown chan bool
shutdownFinished chan bool
wg waitGroup
routinesCount int
lcsmu sync.RWMutex
connections map[net.Conn]bool
up chan net.Listener // Only used by test code.
}
// NewServer creates a new GracefulServer.
func NewServer() *GracefulServer {
return NewWithServer(new(http.Server))
}
// NewWithServer wraps an existing http.Server object and returns a
// GracefulServer that supports all of the original Server operations.
func NewWithServer(s *http.Server) *GracefulServer {
return &GracefulServer{
Server: s,
shutdown: make(chan bool),
shutdownFinished: make(chan bool, 1),
wg: new(sync.WaitGroup),
routinesCount: 0,
connections: make(map[net.Conn]bool),
}
}
// Close stops the server from accepting new requets and begins shutting down.
// It returns true if it's the first time Close is called.
func (s *GracefulServer) Close() bool {
return <-s.shutdown
}
// BlockingClose is similar to Close, except that it blocks until the last
// connection has been closed.
func (s *GracefulServer) BlockingClose() bool {
result := s.Close()
<-s.shutdownFinished
return result
}
// ListenAndServe provides a graceful equivalent of net/http.Serve.ListenAndServe.
func (s *GracefulServer) ListenAndServe() error {
addr := s.Addr
if addr == "" {
addr = ":http"
}
listener, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return s.Serve(listener)
}
// ListenAndServeTLS provides a graceful equivalent of net/http.Serve.ListenAndServeTLS.
func (s *GracefulServer) ListenAndServeTLS(certFile, keyFile string) error {
// direct lift from net/http/server.go
addr := s.Addr
if addr == "" {
addr = ":https"
}
config := &tls.Config{}
if s.TLSConfig != nil {
*config = *s.TLSConfig
}
if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
}
var err error
config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
if err != nil {
return err
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return s.Serve(tls.NewListener(ln, config))
}
// Serve provides a graceful equivalent net/http.Server.Serve.
func (s *GracefulServer) Serve(listener net.Listener) error {
// Wrap the server HTTP handler into graceful one, that will close kept
// alive connections if a new request is received after shutdown.
gracefulHandler := newGracefulHandler(s.Server.Handler)
s.Server.Handler = gracefulHandler
// Start a goroutine that waits for a shutdown signal and will stop the
// listener when it receives the signal. That in turn will result in
// unblocking of the http.Serve call.
go func() {
s.shutdown <- true
close(s.shutdown)
gracefulHandler.Close()
s.Server.SetKeepAlivesEnabled(false)
listener.Close()
}()
originalConnState := s.Server.ConnState
// s.ConnState is invoked by the net/http.Server every time a connection
// changes state. It keeps track of each connection's state over time,
// enabling manners to handle persisted connections correctly.
s.ConnState = func(conn net.Conn, newState http.ConnState) {
s.lcsmu.RLock()
protected := s.connections[conn]
s.lcsmu.RUnlock()
switch newState {
case http.StateNew:
// New connection -> StateNew
protected = true
s.StartRoutine()
case http.StateActive:
// (StateNew, StateIdle) -> StateActive
if gracefulHandler.IsClosed() {
conn.Close()
break
}
if !protected {
protected = true
s.StartRoutine()
}
default:
// (StateNew, StateActive) -> (StateIdle, StateClosed, StateHiJacked)
if protected {
s.FinishRoutine()
protected = false
}
}
s.lcsmu.Lock()
if newState == http.StateClosed || newState == http.StateHijacked {
delete(s.connections, conn)
} else {
s.connections[conn] = protected
}
s.lcsmu.Unlock()
if originalConnState != nil {
originalConnState(conn, newState)
}
}
// A hook to allow the server to notify others when it is ready to receive
// requests; only used by tests.
if s.up != nil {
s.up <- listener
}
err := s.Server.Serve(listener)
// An error returned on shutdown is not worth reporting.
if err != nil && gracefulHandler.IsClosed() {
err = nil
}
// Wait for pending requests to complete regardless the Serve result.
s.wg.Wait()
s.shutdownFinished <- true
return err
}
// StartRoutine increments the server's WaitGroup. Use this if a web request
// starts more goroutines and these goroutines are not guaranteed to finish
// before the request.
func (s *GracefulServer) StartRoutine() {
s.lcsmu.Lock()
defer s.lcsmu.Unlock()
s.wg.Add(1)
s.routinesCount++
}
// FinishRoutine decrements the server's WaitGroup. Use this to complement
// StartRoutine().
func (s *GracefulServer) FinishRoutine() {
s.lcsmu.Lock()
defer s.lcsmu.Unlock()
s.wg.Done()
s.routinesCount--
}
// RoutinesCount returns the number of currently running routines
func (s *GracefulServer) RoutinesCount() int {
s.lcsmu.RLock()
defer s.lcsmu.RUnlock()
return s.routinesCount
}
// gracefulHandler is used by GracefulServer to prevent calling ServeHTTP on
// to be closed kept-alive connections during the server shutdown.
type gracefulHandler struct {
closed int32 // accessed atomically.
wrapped http.Handler
}
func newGracefulHandler(wrapped http.Handler) *gracefulHandler {
return &gracefulHandler{
wrapped: wrapped,
}
}
func (gh *gracefulHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if atomic.LoadInt32(&gh.closed) == 0 {
gh.wrapped.ServeHTTP(w, r)
return
}
r.Body.Close()
// Server is shutting down at this moment, and the connection that this
// handler is being called on is about to be closed. So we do not need to
// actually execute the handler logic.
}
func (gh *gracefulHandler) Close() {
atomic.StoreInt32(&gh.closed, 1)
}
func (gh *gracefulHandler) IsClosed() bool {
return atomic.LoadInt32(&gh.closed) == 1
}