|
|
/*
|
|
|
Package graceful implements graceful shutdown for HTTP servers by closing idle
|
|
|
connections after receiving a signal. By default, this package listens for
|
|
|
interrupts (i.e., SIGINT), but when it detects that it is running under Einhorn
|
|
|
it will additionally listen for SIGUSR2 as well, giving your application
|
|
|
automatic support for graceful upgrades.
|
|
|
|
|
|
It's worth mentioning explicitly that this package is a hack to shim graceful
|
|
|
shutdown behavior into the net/http package provided in Go 1.2. It was written
|
|
|
by carefully reading the sequence of function calls net/http happened to use as
|
|
|
of this writing and finding enough surface area with which to add appropriate
|
|
|
behavior. There's a very good chance that this package will cease to work in
|
|
|
future versions of Go, but with any luck the standard library will add support
|
|
|
of its own by then (https://code.google.com/p/go/issues/detail?id=4674).
|
|
|
|
|
|
If you're interested in figuring out how this package works, we suggest you read
|
|
|
the documentation for WrapConn() and net.go.
|
|
|
*/
|
|
|
package graceful
|
|
|
|
|
|
import (
|
|
|
"crypto/tls"
|
|
|
"net"
|
|
|
"net/http"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
// Exactly like net/http's Server. In fact, it *is* a net/http Server, just with
|
|
|
// different method implementations
|
|
|
type Server http.Server
|
|
|
|
|
|
// About 200 years, also known as "forever"
|
|
|
const forever time.Duration = 200 * 365 * 24 * time.Hour
|
|
|
|
|
|
/*
|
|
|
You might notice that these methods look awfully similar to the methods of the
|
|
|
same name from the go standard library--that's because they were stolen from
|
|
|
there! If go were more like, say, Ruby, it'd actually be possible to shim just
|
|
|
the Serve() method, since we can do everything we want from there. However, it's
|
|
|
not possible to get the other methods which call Serve() (ListenAndServe(), say)
|
|
|
to call your shimmed copy--they always call the original.
|
|
|
|
|
|
Since I couldn't come up with a better idea, I just copy-and-pasted both
|
|
|
ListenAndServe and ListenAndServeTLS here more-or-less verbatim. "Oh well!"
|
|
|
*/
|
|
|
|
|
|
// Behaves exactly like the net/http function of the same name.
|
|
|
func (srv *Server) Serve(l net.Listener) (err error) {
|
|
|
go func() {
|
|
|
<-kill
|
|
|
l.Close()
|
|
|
}()
|
|
|
l = WrapListener(l)
|
|
|
|
|
|
// Spawn a shadow http.Server to do the actual servering. We do this
|
|
|
// because we need to sketch on some of the parameters you passed in,
|
|
|
// and it's nice to keep our sketching to ourselves.
|
|
|
shadow := *(*http.Server)(srv)
|
|
|
|
|
|
if shadow.ReadTimeout == 0 {
|
|
|
shadow.ReadTimeout = forever
|
|
|
}
|
|
|
shadow.Handler = Middleware(shadow.Handler)
|
|
|
|
|
|
err = shadow.Serve(l)
|
|
|
|
|
|
// We expect an error when we close the listener, so we indiscriminately
|
|
|
// swallow Serve errors when we're in a shutdown state.
|
|
|
select {
|
|
|
case <-kill:
|
|
|
return nil
|
|
|
default:
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Behaves exactly like the net/http function of the same name.
|
|
|
func (srv *Server) ListenAndServe() error {
|
|
|
addr := srv.Addr
|
|
|
if addr == "" {
|
|
|
addr = ":http"
|
|
|
}
|
|
|
l, e := net.Listen("tcp", addr)
|
|
|
if e != nil {
|
|
|
return e
|
|
|
}
|
|
|
return srv.Serve(l)
|
|
|
}
|
|
|
|
|
|
// Behaves exactly like the net/http function of the same name.
|
|
|
func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
|
|
|
addr := srv.Addr
|
|
|
if addr == "" {
|
|
|
addr = ":https"
|
|
|
}
|
|
|
config := &tls.Config{}
|
|
|
if srv.TLSConfig != nil {
|
|
|
*config = *srv.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
|
|
|
}
|
|
|
|
|
|
conn, err := net.Listen("tcp", addr)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
tlsListener := tls.NewListener(conn, config)
|
|
|
return srv.Serve(tlsListener)
|
|
|
}
|
|
|
|
|
|
// Behaves exactly like the net/http function of the same name.
|
|
|
func ListenAndServe(addr string, handler http.Handler) error {
|
|
|
server := &Server{Addr: addr, Handler: handler}
|
|
|
return server.ListenAndServe()
|
|
|
}
|
|
|
|
|
|
// Behaves exactly like the net/http function of the same name.
|
|
|
func ListenAndServeTLS(addr, certfile, keyfile string, handler http.Handler) error {
|
|
|
server := &Server{Addr: addr, Handler: handler}
|
|
|
return server.ListenAndServeTLS(certfile, keyfile)
|
|
|
}
|
|
|
|
|
|
// Behaves exactly like the net/http function of the same name.
|
|
|
func Serve(l net.Listener, handler http.Handler) error {
|
|
|
server := &Server{Handler: handler}
|
|
|
return server.Serve(l)
|
|
|
}
|