pax_global_header00006660000000000000000000000064130444367770014531gustar00rootroot0000000000000052 comment=4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb graceful-1.2.15/000077500000000000000000000000001304443677700134075ustar00rootroot00000000000000graceful-1.2.15/.gitignore000066400000000000000000000004031304443677700153740ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test graceful-1.2.15/.travis.yml000066400000000000000000000003451304443677700155220ustar00rootroot00000000000000language: go sudo: false go: - 1.7 - 1.6.2 - 1.5.4 - 1.4.3 - 1.3.3 before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover script: - $HOME/gopath/bin/goveralls -service=travis-ci graceful-1.2.15/LICENSE000066400000000000000000000020701304443677700144130ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Tyler Bunnell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. graceful-1.2.15/README.md000066400000000000000000000120251304443677700146660ustar00rootroot00000000000000graceful [![GoDoc](https://godoc.org/github.com/tylerb/graceful?status.png)](http://godoc.org/github.com/tylerb/graceful) [![Build Status](https://travis-ci.org/tylerb/graceful.svg?branch=master)](https://travis-ci.org/tylerb/graceful) [![Coverage Status](https://coveralls.io/repos/tylerb/graceful/badge.svg)](https://coveralls.io/r/tylerb/graceful) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tylerb/graceful?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) ======== Graceful is a Go 1.3+ package enabling graceful shutdown of http.Handler servers. ## Installation To install, simply execute: ``` go get gopkg.in/tylerb/graceful.v1 ``` I am using [gopkg.in](http://labix.org/gopkg.in) to control releases. ## Usage Using Graceful is easy. Simply create your http.Handler and pass it to the `Run` function: ```go package main import ( "gopkg.in/tylerb/graceful.v1" "net/http" "fmt" "time" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome to the home page!") }) graceful.Run(":3001",10*time.Second,mux) } ``` Another example, using [Negroni](https://github.com/codegangsta/negroni), functions in much the same manner: ```go package main import ( "github.com/codegangsta/negroni" "gopkg.in/tylerb/graceful.v1" "net/http" "fmt" "time" ) func main() { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Welcome to the home page!") }) n := negroni.Classic() n.UseHandler(mux) //n.Run(":3000") graceful.Run(":3001",10*time.Second,n) } ``` In addition to Run there are the http.Server counterparts ListenAndServe, ListenAndServeTLS and Serve, which allow you to configure HTTPS, custom timeouts and error handling. Graceful may also be used by instantiating its Server type directly, which embeds an http.Server: ```go mux := // ... srv := &graceful.Server{ Timeout: 10 * time.Second, Server: &http.Server{ Addr: ":1234", Handler: mux, }, } srv.ListenAndServe() ``` This form allows you to set the ConnState callback, which works in the same way as in http.Server: ```go mux := // ... srv := &graceful.Server{ Timeout: 10 * time.Second, ConnState: func(conn net.Conn, state http.ConnState) { // conn has a new state }, Server: &http.Server{ Addr: ":1234", Handler: mux, }, } srv.ListenAndServe() ``` ## Behaviour When Graceful is sent a SIGINT or SIGTERM (possibly from ^C or a kill command), it: 1. Disables keepalive connections. 2. Closes the listening socket, allowing another process to listen on that port immediately. 3. Starts a timer of `timeout` duration to give active requests a chance to finish. 4. When timeout expires, closes all active connections. 5. Closes the `stopChan`, waking up any blocking goroutines. 6. Returns from the function, allowing the server to terminate. ## Notes If the `timeout` argument to `Run` is 0, the server never times out, allowing all active requests to complete. If you wish to stop the server in some way other than an OS signal, you may call the `Stop()` function. This function stops the server, gracefully, using the new timeout value you provide. The `StopChan()` function returns a channel on which you can block while waiting for the server to stop. This channel will be closed when the server is stopped, allowing your execution to proceed. Multiple goroutines can block on this channel at the same time and all will be signalled when stopping is complete. ### Important things to note when setting `timeout` to 0: If you set the `timeout` to `0`, it waits for all connections to the server to disconnect before shutting down. This means that even though requests over a connection have finished, it is possible for the client to hold the connection open and block the server from shutting down indefinitely. This is especially evident when graceful is used to run HTTP/2 servers. Clients like Chrome and Firefox have been observed to hold onto the open connection indefinitely over HTTP/2, preventing the server from shutting down. In addition, there is also the risk of malicious clients holding and keeping the connection alive. It is understandable that sometimes, you might want to wait for the client indefinitely because they might be uploading large files. In these type of cases, it is recommended that you set a reasonable timeout to kill the connection, and have the client perform resumable uploads. For example, the client can divide the file into chunks and reupload chunks that were in transit when the connection was terminated. ## Contributing If you would like to contribute, please: 1. Create a GitHub issue regarding the contribution. Features and bugs should be discussed beforehand. 2. Fork the repository. 3. Create a pull request with your solution. This pull request should reference and close the issues (Fix #2). All pull requests should: 1. Pass [gometalinter -t .](https://github.com/alecthomas/gometalinter) with no warnings. 2. Be `go fmt` formatted. graceful-1.2.15/graceful.go000066400000000000000000000330661304443677700155360ustar00rootroot00000000000000package graceful import ( "crypto/tls" "log" "net" "net/http" "os" "sync" "time" ) // Server wraps an http.Server with graceful connection handling. // It may be used directly in the same way as http.Server, or may // be constructed with the global functions in this package. // // Example: // srv := &graceful.Server{ // Timeout: 5 * time.Second, // Server: &http.Server{Addr: ":1234", Handler: handler}, // } // srv.ListenAndServe() type Server struct { *http.Server // Timeout is the duration to allow outstanding requests to survive // before forcefully terminating them. Timeout time.Duration // Limit the number of outstanding requests ListenLimit int // TCPKeepAlive sets the TCP keep-alive timeouts on accepted // connections. It prunes dead TCP connections ( e.g. closing // laptop mid-download) TCPKeepAlive time.Duration // ConnState specifies an optional callback function that is // called when a client connection changes state. This is a proxy // to the underlying http.Server's ConnState, and the original // must not be set directly. ConnState func(net.Conn, http.ConnState) // BeforeShutdown is an optional callback function that is called // before the listener is closed. Returns true if shutdown is allowed BeforeShutdown func() bool // ShutdownInitiated is an optional callback function that is called // when shutdown is initiated. It can be used to notify the client // side of long lived connections (e.g. websockets) to reconnect. ShutdownInitiated func() // NoSignalHandling prevents graceful from automatically shutting down // on SIGINT and SIGTERM. If set to true, you must shut down the server // manually with Stop(). NoSignalHandling bool // Logger used to notify of errors on startup and on stop. Logger *log.Logger // LogFunc can be assigned with a logging function of your choice, allowing // you to use whatever logging approach you would like LogFunc func(format string, args ...interface{}) // Interrupted is true if the server is handling a SIGINT or SIGTERM // signal and is thus shutting down. Interrupted bool // interrupt signals the listener to stop serving connections, // and the server to shut down. interrupt chan os.Signal // stopLock is used to protect against concurrent calls to Stop stopLock sync.Mutex // stopChan is the channel on which callers may block while waiting for // the server to stop. stopChan chan struct{} // chanLock is used to protect access to the various channel constructors. chanLock sync.RWMutex // connections holds all connections managed by graceful connections map[net.Conn]struct{} // idleConnections holds all idle connections managed by graceful idleConnections map[net.Conn]struct{} } // Run serves the http.Handler with graceful shutdown enabled. // // timeout is the duration to wait until killing active requests and stopping the server. // If timeout is 0, the server never times out. It waits for all active requests to finish. func Run(addr string, timeout time.Duration, n http.Handler) { srv := &Server{ Timeout: timeout, TCPKeepAlive: 3 * time.Minute, Server: &http.Server{Addr: addr, Handler: n}, // Logger: DefaultLogger(), } if err := srv.ListenAndServe(); err != nil { if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { srv.logf("%s", err) os.Exit(1) } } } // RunWithErr is an alternative version of Run function which can return error. // // Unlike Run this version will not exit the program if an error is encountered but will // return it instead. func RunWithErr(addr string, timeout time.Duration, n http.Handler) error { srv := &Server{ Timeout: timeout, TCPKeepAlive: 3 * time.Minute, Server: &http.Server{Addr: addr, Handler: n}, Logger: DefaultLogger(), } return srv.ListenAndServe() } // ListenAndServe is equivalent to http.Server.ListenAndServe with graceful shutdown enabled. // // timeout is the duration to wait until killing active requests and stopping the server. // If timeout is 0, the server never times out. It waits for all active requests to finish. func ListenAndServe(server *http.Server, timeout time.Duration) error { srv := &Server{Timeout: timeout, Server: server, Logger: DefaultLogger()} return srv.ListenAndServe() } // ListenAndServe is equivalent to http.Server.ListenAndServe with graceful shutdown enabled. func (srv *Server) ListenAndServe() error { // Create the listener so we can control their lifetime addr := srv.Addr if addr == "" { addr = ":http" } conn, err := srv.newTCPListener(addr) if err != nil { return err } return srv.Serve(conn) } // ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled. // // timeout is the duration to wait until killing active requests and stopping the server. // If timeout is 0, the server never times out. It waits for all active requests to finish. func ListenAndServeTLS(server *http.Server, certFile, keyFile string, timeout time.Duration) error { srv := &Server{Timeout: timeout, Server: server, Logger: DefaultLogger()} return srv.ListenAndServeTLS(certFile, keyFile) } // ListenTLS is a convenience method that creates an https listener using the // provided cert and key files. Use this method if you need access to the // listener object directly. When ready, pass it to the Serve method. func (srv *Server) ListenTLS(certFile, keyFile string) (net.Listener, error) { // Create the listener ourselves so we can control its lifetime addr := srv.Addr if addr == "" { addr = ":https" } config := &tls.Config{} if srv.TLSConfig != nil { *config = *srv.TLSConfig } var err error if certFile != "" && keyFile != "" { config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return nil, err } } // Enable http2 enableHTTP2ForTLSConfig(config) conn, err := srv.newTCPListener(addr) if err != nil { return nil, err } srv.TLSConfig = config tlsListener := tls.NewListener(conn, config) return tlsListener, nil } // Enable HTTP2ForTLSConfig explicitly enables http/2 for a TLS Config. This is due to changes in Go 1.7 where // http servers are no longer automatically configured to enable http/2 if the server's TLSConfig is set. // See https://github.com/golang/go/issues/15908 func enableHTTP2ForTLSConfig(t *tls.Config) { if TLSConfigHasHTTP2Enabled(t) { return } t.NextProtos = append(t.NextProtos, "h2") } // TLSConfigHasHTTP2Enabled checks to see if a given TLS Config has http2 enabled. func TLSConfigHasHTTP2Enabled(t *tls.Config) bool { for _, value := range t.NextProtos { if value == "h2" { return true } } return false } // ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled. func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error { l, err := srv.ListenTLS(certFile, keyFile) if err != nil { return err } return srv.Serve(l) } // ListenAndServeTLSConfig can be used with an existing TLS config and is equivalent to // http.Server.ListenAndServeTLS with graceful shutdown enabled, func (srv *Server) ListenAndServeTLSConfig(config *tls.Config) error { addr := srv.Addr if addr == "" { addr = ":https" } conn, err := srv.newTCPListener(addr) if err != nil { return err } srv.TLSConfig = config tlsListener := tls.NewListener(conn, config) return srv.Serve(tlsListener) } // Serve is equivalent to http.Server.Serve with graceful shutdown enabled. // // timeout is the duration to wait until killing active requests and stopping the server. // If timeout is 0, the server never times out. It waits for all active requests to finish. func Serve(server *http.Server, l net.Listener, timeout time.Duration) error { srv := &Server{Timeout: timeout, Server: server, Logger: DefaultLogger()} return srv.Serve(l) } // Serve is equivalent to http.Server.Serve with graceful shutdown enabled. func (srv *Server) Serve(listener net.Listener) error { if srv.ListenLimit != 0 { listener = LimitListener(listener, srv.ListenLimit) } // Make our stopchan srv.StopChan() // Track connection state add := make(chan net.Conn) idle := make(chan net.Conn) active := make(chan net.Conn) remove := make(chan net.Conn) srv.Server.ConnState = func(conn net.Conn, state http.ConnState) { switch state { case http.StateNew: add <- conn case http.StateActive: active <- conn case http.StateIdle: idle <- conn case http.StateClosed, http.StateHijacked: remove <- conn } srv.stopLock.Lock() defer srv.stopLock.Unlock() if srv.ConnState != nil { srv.ConnState(conn, state) } } // Manage open connections shutdown := make(chan chan struct{}) kill := make(chan struct{}) go srv.manageConnections(add, idle, active, remove, shutdown, kill) interrupt := srv.interruptChan() // Set up the interrupt handler if !srv.NoSignalHandling { signalNotify(interrupt) } quitting := make(chan struct{}) go srv.handleInterrupt(interrupt, quitting, listener) // Serve with graceful listener. // Execution blocks here until listener.Close() is called, above. err := srv.Server.Serve(listener) if err != nil { // If the underlying listening is closed, Serve returns an error // complaining about listening on a closed socket. This is expected, so // let's ignore the error if we are the ones who explicitly closed the // socket. select { case <-quitting: err = nil default: } } srv.shutdown(shutdown, kill) return err } // Stop instructs the type to halt operations and close // the stop channel when it is finished. // // timeout is grace period for which to wait before shutting // down the server. The timeout value passed here will override the // timeout given when constructing the server, as this is an explicit // command to stop the server. func (srv *Server) Stop(timeout time.Duration) { srv.stopLock.Lock() defer srv.stopLock.Unlock() srv.Timeout = timeout sendSignalInt(srv.interruptChan()) } // StopChan gets the stop channel which will block until // stopping has completed, at which point it is closed. // Callers should never close the stop channel. func (srv *Server) StopChan() <-chan struct{} { srv.chanLock.Lock() defer srv.chanLock.Unlock() if srv.stopChan == nil { srv.stopChan = make(chan struct{}) } return srv.stopChan } // DefaultLogger returns the logger used by Run, RunWithErr, ListenAndServe, ListenAndServeTLS and Serve. // The logger outputs to STDERR by default. func DefaultLogger() *log.Logger { return log.New(os.Stderr, "[graceful] ", 0) } func (srv *Server) manageConnections(add, idle, active, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) { var done chan struct{} srv.connections = map[net.Conn]struct{}{} srv.idleConnections = map[net.Conn]struct{}{} for { select { case conn := <-add: srv.connections[conn] = struct{}{} srv.idleConnections[conn] = struct{}{} // Newly-added connections are considered idle until they become active. case conn := <-idle: srv.idleConnections[conn] = struct{}{} case conn := <-active: delete(srv.idleConnections, conn) case conn := <-remove: delete(srv.connections, conn) delete(srv.idleConnections, conn) if done != nil && len(srv.connections) == 0 { done <- struct{}{} return } case done = <-shutdown: if len(srv.connections) == 0 && len(srv.idleConnections) == 0 { done <- struct{}{} return } // a shutdown request has been received. if we have open idle // connections, we must close all of them now. this prevents idle // connections from holding the server open while waiting for them to // hit their idle timeout. for k := range srv.idleConnections { if err := k.Close(); err != nil { srv.logf("[ERROR] %s", err) } } case <-kill: srv.stopLock.Lock() defer srv.stopLock.Unlock() srv.Server.ConnState = nil for k := range srv.connections { if err := k.Close(); err != nil { srv.logf("[ERROR] %s", err) } } return } } } func (srv *Server) interruptChan() chan os.Signal { srv.chanLock.Lock() defer srv.chanLock.Unlock() if srv.interrupt == nil { srv.interrupt = make(chan os.Signal, 1) } return srv.interrupt } func (srv *Server) handleInterrupt(interrupt chan os.Signal, quitting chan struct{}, listener net.Listener) { for _ = range interrupt { if srv.Interrupted { srv.logf("already shutting down") continue } srv.logf("shutdown initiated") srv.Interrupted = true if srv.BeforeShutdown != nil { if !srv.BeforeShutdown() { srv.Interrupted = false continue } } close(quitting) srv.SetKeepAlivesEnabled(false) if err := listener.Close(); err != nil { srv.logf("[ERROR] %s", err) } if srv.ShutdownInitiated != nil { srv.ShutdownInitiated() } } } func (srv *Server) logf(format string, args ...interface{}) { if srv.LogFunc != nil { srv.LogFunc(format, args...) } else if srv.Logger != nil { srv.Logger.Printf(format, args...) } } func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) { // Request done notification done := make(chan struct{}) shutdown <- done srv.stopLock.Lock() defer srv.stopLock.Unlock() if srv.Timeout > 0 { select { case <-done: case <-time.After(srv.Timeout): close(kill) } } else { <-done } // Close the stopChan to wake up any blocked goroutines. srv.chanLock.Lock() if srv.stopChan != nil { close(srv.stopChan) } srv.chanLock.Unlock() } func (srv *Server) newTCPListener(addr string) (net.Listener, error) { conn, err := net.Listen("tcp", addr) if err != nil { return conn, err } if srv.TCPKeepAlive != 0 { conn = keepAliveListener{conn, srv.TCPKeepAlive} } return conn, nil } graceful-1.2.15/graceful_test.go000066400000000000000000000343371304443677700165770ustar00rootroot00000000000000package graceful import ( "bytes" "fmt" "io" "log" "net" "net/http" "net/url" "os" "reflect" "strings" "sync" "syscall" "testing" "time" ) const ( // The tests will run a test server on this port. port = 9654 concurrentRequestN = 8 killTime = 500 * time.Millisecond timeoutTime = 1000 * time.Millisecond waitTime = 100 * time.Millisecond ) func runQuery(t *testing.T, expected int, shouldErr bool, wg *sync.WaitGroup, once *sync.Once) { defer wg.Done() client := http.Client{} r, err := client.Get(fmt.Sprintf("http://localhost:%d", port)) if shouldErr && err == nil { once.Do(func() { t.Error("Expected an error but none was encountered.") }) } else if shouldErr && err != nil { if checkErr(t, err, once) { return } } if r != nil && r.StatusCode != expected { once.Do(func() { t.Errorf("Incorrect status code on response. Expected %d. Got %d", expected, r.StatusCode) }) } else if r == nil { once.Do(func() { t.Error("No response when a response was expected.") }) } } func checkErr(t *testing.T, err error, once *sync.Once) bool { if err.(*url.Error).Err == io.EOF { return true } var errno syscall.Errno switch oe := err.(*url.Error).Err.(type) { case *net.OpError: switch e := oe.Err.(type) { case syscall.Errno: errno = e case *os.SyscallError: errno = e.Err.(syscall.Errno) } if errno == syscall.ECONNREFUSED { return true } else if err != nil { once.Do(func() { t.Error("Error on Get:", err) }) } default: if strings.Contains(err.Error(), "transport closed before response was received") { return true } if strings.Contains(err.Error(), "server closed connection") { return true } fmt.Printf("unknown err: %s, %#v\n", err, err) } return false } func createListener(sleep time.Duration) (*http.Server, net.Listener, error) { mux := http.NewServeMux() mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { time.Sleep(sleep) rw.WriteHeader(http.StatusOK) }) server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: mux} l, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) return server, l, err } func launchTestQueries(t *testing.T, wg *sync.WaitGroup, c chan os.Signal) { defer wg.Done() var once sync.Once for i := 0; i < concurrentRequestN; i++ { wg.Add(1) go runQuery(t, http.StatusOK, false, wg, &once) } time.Sleep(waitTime) c <- os.Interrupt time.Sleep(waitTime) for i := 0; i < concurrentRequestN; i++ { wg.Add(1) go runQuery(t, 0, true, wg, &once) } } func TestGracefulRun(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) server, l, err := createListener(killTime / 2) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{Timeout: killTime, Server: server, interrupt: c} srv.Serve(l) }() wg.Add(1) go launchTestQueries(t, &wg, c) } func TestGracefulRunLimitKeepAliveListener(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) server, l, err := createListener(killTime / 2) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{ Timeout: killTime, ListenLimit: concurrentRequestN, TCPKeepAlive: 1 * time.Second, Server: server, interrupt: c, } srv.Serve(l) }() wg.Add(1) go launchTestQueries(t, &wg, c) } func TestGracefulRunTimesOut(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) server, l, err := createListener(killTime * 10) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{Timeout: killTime, Server: server, interrupt: c} srv.Serve(l) }() wg.Add(1) go func() { defer wg.Done() var once sync.Once for i := 0; i < concurrentRequestN; i++ { wg.Add(1) go runQuery(t, 0, true, &wg, &once) } time.Sleep(waitTime) c <- os.Interrupt time.Sleep(waitTime) for i := 0; i < concurrentRequestN; i++ { wg.Add(1) go runQuery(t, 0, true, &wg, &once) } }() } func TestGracefulRunDoesntTimeOut(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) server, l, err := createListener(killTime * 2) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{Timeout: 0, Server: server, interrupt: c} srv.Serve(l) }() wg.Add(1) go launchTestQueries(t, &wg, c) } func TestGracefulRunDoesntTimeOutAfterConnectionCreated(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) server, l, err := createListener(killTime) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{Timeout: 0, Server: server, interrupt: c} srv.Serve(l) }() time.Sleep(waitTime) // Make a sample first request. The connection will be left idle. resp, err := http.Get(fmt.Sprintf("http://localhost:%d", port)) if err != nil { panic(fmt.Sprintf("first request failed: %v", err)) } resp.Body.Close() wg.Add(1) go func() { defer wg.Done() // With idle connections improperly handled, the server doesn't wait for this // to complete and the request fails. It should be allowed to complete successfully. _, err := http.Get(fmt.Sprintf("http://localhost:%d", port)) if err != nil { t.Errorf("Get failed: %v", err) } }() // Ensure the request goes out time.Sleep(waitTime) c <- os.Interrupt wg.Wait() } func TestGracefulRunNoRequests(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) server, l, err := createListener(killTime * 2) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{Timeout: 0, Server: server, interrupt: c} srv.Serve(l) }() c <- os.Interrupt } func TestGracefulForwardsConnState(t *testing.T) { var stateLock sync.Mutex states := make(map[http.ConnState]int) connState := func(conn net.Conn, state http.ConnState) { stateLock.Lock() states[state]++ stateLock.Unlock() } var wg sync.WaitGroup defer wg.Wait() expected := map[http.ConnState]int{ http.StateNew: concurrentRequestN, http.StateActive: concurrentRequestN, http.StateClosed: concurrentRequestN, } c := make(chan os.Signal, 1) server, l, err := createListener(killTime / 2) if err != nil { t.Fatal(err) } wg.Add(1) go func() { defer wg.Done() srv := &Server{ ConnState: connState, Timeout: killTime, Server: server, interrupt: c, } srv.Serve(l) }() wg.Add(1) go launchTestQueries(t, &wg, c) wg.Wait() stateLock.Lock() if !reflect.DeepEqual(states, expected) { t.Errorf("Incorrect connection state tracking.\n actual: %v\nexpected: %v\n", states, expected) } stateLock.Unlock() } func TestGracefulExplicitStop(t *testing.T) { server, l, err := createListener(1 * time.Millisecond) if err != nil { t.Fatal(err) } srv := &Server{Timeout: killTime, Server: server} go func() { go srv.Serve(l) time.Sleep(waitTime) srv.Stop(killTime) }() // block on the stopChan until the server has shut down select { case <-srv.StopChan(): case <-time.After(timeoutTime): t.Fatal("Timed out while waiting for explicit stop to complete") } } func TestGracefulExplicitStopOverride(t *testing.T) { server, l, err := createListener(1 * time.Millisecond) if err != nil { t.Fatal(err) } srv := &Server{Timeout: killTime, Server: server} go func() { go srv.Serve(l) time.Sleep(waitTime) srv.Stop(killTime / 2) }() // block on the stopChan until the server has shut down select { case <-srv.StopChan(): case <-time.After(killTime): t.Fatal("Timed out while waiting for explicit stop to complete") } } func TestBeforeShutdownAndShutdownInitiatedCallbacks(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() server, l, err := createListener(1 * time.Millisecond) if err != nil { t.Fatal(err) } beforeShutdownCalled := make(chan struct{}) cb1 := func() bool { close(beforeShutdownCalled); return true } shutdownInitiatedCalled := make(chan struct{}) cb2 := func() { close(shutdownInitiatedCalled) } wg.Add(2) srv := &Server{Server: server, BeforeShutdown: cb1, ShutdownInitiated: cb2} go func() { defer wg.Done() srv.Serve(l) }() go func() { defer wg.Done() time.Sleep(waitTime) srv.Stop(killTime) }() beforeShutdown := false shutdownInitiated := false for i := 0; i < 2; i++ { select { case <-beforeShutdownCalled: beforeShutdownCalled = nil beforeShutdown = true case <-shutdownInitiatedCalled: shutdownInitiatedCalled = nil shutdownInitiated = true case <-time.After(killTime): t.Fatal("Timed out while waiting for ShutdownInitiated callback to be called") } } if !beforeShutdown { t.Fatal("beforeShutdown should be true") } if !shutdownInitiated { t.Fatal("shutdownInitiated should be true") } } func TestBeforeShutdownCanceled(t *testing.T) { var wg sync.WaitGroup wg.Add(1) server, l, err := createListener(1 * time.Millisecond) if err != nil { t.Fatal(err) } beforeShutdownCalled := make(chan struct{}) cb1 := func() bool { close(beforeShutdownCalled); return false } shutdownInitiatedCalled := make(chan struct{}) cb2 := func() { close(shutdownInitiatedCalled) } srv := &Server{Server: server, BeforeShutdown: cb1, ShutdownInitiated: cb2} go func() { srv.Serve(l) wg.Done() }() go func() { time.Sleep(waitTime) srv.Stop(killTime) }() beforeShutdown := false shutdownInitiated := false timeouted := false for i := 0; i < 2; i++ { select { case <-beforeShutdownCalled: beforeShutdownCalled = nil beforeShutdown = true case <-shutdownInitiatedCalled: shutdownInitiatedCalled = nil shutdownInitiated = true case <-time.After(killTime): timeouted = true } } if !beforeShutdown { t.Fatal("beforeShutdown should be true") } if !timeouted { t.Fatal("timeouted should be true") } if shutdownInitiated { t.Fatal("shutdownInitiated shouldn't be true") } srv.BeforeShutdown = func() bool { return true } srv.Stop(killTime) wg.Wait() } func hijackingListener(srv *Server) (*http.Server, net.Listener, error) { mux := http.NewServeMux() mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { conn, bufrw, err := rw.(http.Hijacker).Hijack() if err != nil { http.Error(rw, "webserver doesn't support hijacking", http.StatusInternalServerError) return } defer conn.Close() bufrw.WriteString("HTTP/1.1 200 OK\r\n\r\n") bufrw.Flush() }) server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: mux} l, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) return server, l, err } func TestNotifyClosed(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan os.Signal, 1) srv := &Server{Timeout: killTime, interrupt: c} server, l, err := hijackingListener(srv) if err != nil { t.Fatal(err) } srv.Server = server wg.Add(1) go func() { defer wg.Done() srv.Serve(l) }() var once sync.Once for i := 0; i < concurrentRequestN; i++ { wg.Add(1) runQuery(t, http.StatusOK, false, &wg, &once) } srv.Stop(0) // block on the stopChan until the server has shut down select { case <-srv.StopChan(): case <-time.After(timeoutTime): t.Fatal("Timed out while waiting for explicit stop to complete") } if len(srv.connections) > 0 { t.Fatal("hijacked connections should not be managed") } } func TestStopDeadlock(t *testing.T) { var wg sync.WaitGroup defer wg.Wait() c := make(chan struct{}) server, l, err := createListener(1 * time.Millisecond) if err != nil { t.Fatal(err) } srv := &Server{Server: server, NoSignalHandling: true} wg.Add(2) go func() { defer wg.Done() time.Sleep(waitTime) srv.Serve(l) }() go func() { defer wg.Done() srv.Stop(0) close(c) }() select { case <-c: l.Close() case <-time.After(timeoutTime): t.Fatal("Timed out while waiting for explicit stop to complete") } } // Run with --race func TestStopRace(t *testing.T) { server, l, err := createListener(1 * time.Millisecond) if err != nil { t.Fatal(err) } srv := &Server{Timeout: killTime, Server: server} go func() { go srv.Serve(l) srv.Stop(killTime) }() srv.Stop(0) select { case <-srv.StopChan(): case <-time.After(timeoutTime): t.Fatal("Timed out while waiting for explicit stop to complete") } } func TestInterruptLog(t *testing.T) { c := make(chan os.Signal, 1) server, l, err := createListener(killTime * 10) if err != nil { t.Fatal(err) } var buf bytes.Buffer var tbuf bytes.Buffer logger := log.New(&buf, "", 0) expected := log.New(&tbuf, "", 0) srv := &Server{Timeout: killTime, Server: server, Logger: logger, interrupt: c} go func() { srv.Serve(l) }() stop := srv.StopChan() c <- os.Interrupt expected.Print("shutdown initiated") <-stop if buf.String() != tbuf.String() { t.Fatal("shutdown log incorrect - got '" + buf.String() + "'") } } func TestMultiInterrupts(t *testing.T) { c := make(chan os.Signal, 1) server, l, err := createListener(killTime * 10) if err != nil { t.Fatal(err) } var wg sync.WaitGroup var bu bytes.Buffer buf := SyncBuffer{&wg, &bu} var tbuf bytes.Buffer logger := log.New(&buf, "", 0) expected := log.New(&tbuf, "", 0) srv := &Server{Timeout: killTime, Server: server, Logger: logger, interrupt: c} go func() { srv.Serve(l) }() stop := srv.StopChan() buf.Add(1 + 10) // Expecting 11 log calls c <- os.Interrupt expected.Printf("shutdown initiated") for i := 0; i < 10; i++ { c <- os.Interrupt expected.Printf("already shutting down") } <-stop wg.Wait() bb, bt := buf.Bytes(), tbuf.Bytes() for i, b := range bb { if b != bt[i] { t.Fatal(fmt.Sprintf("shutdown log incorrect - got '%s', expected '%s'", buf.String(), tbuf.String())) } } } func TestLogFunc(t *testing.T) { c := make(chan os.Signal, 1) server, l, err := createListener(killTime * 10) if err != nil { t.Fatal(err) } var called bool srv := &Server{Timeout: killTime, Server: server, LogFunc: func(format string, args ...interface{}) { called = true }, interrupt: c} stop := srv.StopChan() go func() { srv.Serve(l) }() c <- os.Interrupt <-stop if called != true { t.Fatal("Expected LogFunc to be called.") } } // SyncBuffer calls Done on the embedded wait group after each call to Write. type SyncBuffer struct { *sync.WaitGroup *bytes.Buffer } func (buf *SyncBuffer) Write(b []byte) (int, error) { defer buf.Done() return buf.Buffer.Write(b) } graceful-1.2.15/http2_test.go000066400000000000000000000051171304443677700160420ustar00rootroot00000000000000// +build go1.6 package graceful import ( "crypto/tls" "fmt" "net/http" "os" "sync" "testing" "time" "golang.org/x/net/http2" ) func createServer() *http.Server { mux := http.NewServeMux() mux.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(http.StatusOK) }) server := &http.Server{Addr: fmt.Sprintf(":%d", port), Handler: mux} return server } func checkIfConnectionToServerIsHTTP2(t *testing.T, wg *sync.WaitGroup, c chan os.Signal) { defer wg.Done() tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } err := http2.ConfigureTransport(tr) if err != nil { t.Fatal("Unable to upgrade client transport to HTTP/2") } client := http.Client{Transport: tr} r, err := client.Get(fmt.Sprintf("https://localhost:%d", port)) c <- os.Interrupt if err != nil { t.Fatalf("Error encountered while connecting to test server: %s", err) } if !r.ProtoAtLeast(2, 0) { t.Fatalf("Expected HTTP/2 connection to server, but connection was using %s", r.Proto) } } func TestHTTP2ListenAndServeTLS(t *testing.T) { c := make(chan os.Signal, 1) var wg sync.WaitGroup wg.Add(1) server := createServer() var srv *Server go func() { // set timeout of 0 to test idle connection closing srv = &Server{Timeout: 0, TCPKeepAlive: 1 * time.Minute, Server: server, interrupt: c} srv.ListenAndServeTLS("test-fixtures/cert.crt", "test-fixtures/key.pem") wg.Done() }() time.Sleep(waitTime) // Wait for the server to start wg.Add(1) go checkIfConnectionToServerIsHTTP2(t, &wg, c) wg.Wait() c <- os.Interrupt // kill the server to close idle connections // block on the stopChan until the server has shut down select { case <-srv.StopChan(): case <-time.After(killTime * 2): t.Fatal("Timed out while waiting for explicit stop to complete") } } func TestHTTP2ListenAndServeTLSConfig(t *testing.T) { c := make(chan os.Signal, 1) var wg sync.WaitGroup wg.Add(1) server2 := createServer() go func() { srv := &Server{Timeout: killTime, TCPKeepAlive: 1 * time.Minute, Server: server2, interrupt: c} cert, err := tls.LoadX509KeyPair("test-fixtures/cert.crt", "test-fixtures/key.pem") if err != nil { t.Fatalf("Unexpected error: %s", err) } tlsConf := &tls.Config{ Certificates: []tls.Certificate{cert}, NextProtos: []string{"h2"}, // We need to explicitly enable http/2 in Go 1.7+ } tlsConf.BuildNameToCertificate() srv.ListenAndServeTLSConfig(tlsConf) wg.Done() }() time.Sleep(waitTime) // Wait for the server to start wg.Add(1) go checkIfConnectionToServerIsHTTP2(t, &wg, c) wg.Wait() } graceful-1.2.15/keepalive_listener.go000066400000000000000000000012501304443677700176060ustar00rootroot00000000000000package graceful import ( "net" "time" ) type keepAliveConn interface { SetKeepAlive(bool) error SetKeepAlivePeriod(d time.Duration) error } // keepAliveListener sets TCP keep-alive timeouts on accepted // connections. It's used by ListenAndServe and ListenAndServeTLS so // dead TCP connections (e.g. closing laptop mid-download) eventually // go away. type keepAliveListener struct { net.Listener keepAlivePeriod time.Duration } func (ln keepAliveListener) Accept() (net.Conn, error) { c, err := ln.Listener.Accept() if err != nil { return nil, err } kac := c.(keepAliveConn) kac.SetKeepAlive(true) kac.SetKeepAlivePeriod(ln.keepAlivePeriod) return c, nil } graceful-1.2.15/limit_listen.go000066400000000000000000000036421304443677700164370ustar00rootroot00000000000000// Copyright 2013 The etcd Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package graceful import ( "errors" "net" "sync" "time" ) // ErrNotTCP indicates that network connection is not a TCP connection. var ErrNotTCP = errors.New("only tcp connections have keepalive") // LimitListener returns a Listener that accepts at most n simultaneous // connections from the provided Listener. func LimitListener(l net.Listener, n int) net.Listener { return &limitListener{l, make(chan struct{}, n)} } type limitListener struct { net.Listener sem chan struct{} } func (l *limitListener) acquire() { l.sem <- struct{}{} } func (l *limitListener) release() { <-l.sem } func (l *limitListener) Accept() (net.Conn, error) { l.acquire() c, err := l.Listener.Accept() if err != nil { l.release() return nil, err } return &limitListenerConn{Conn: c, release: l.release}, nil } type limitListenerConn struct { net.Conn releaseOnce sync.Once release func() } func (l *limitListenerConn) Close() error { err := l.Conn.Close() l.releaseOnce.Do(l.release) return err } func (l *limitListenerConn) SetKeepAlive(doKeepAlive bool) error { tcpc, ok := l.Conn.(*net.TCPConn) if !ok { return ErrNotTCP } return tcpc.SetKeepAlive(doKeepAlive) } func (l *limitListenerConn) SetKeepAlivePeriod(d time.Duration) error { tcpc, ok := l.Conn.(*net.TCPConn) if !ok { return ErrNotTCP } return tcpc.SetKeepAlivePeriod(d) } graceful-1.2.15/signal.go000066400000000000000000000004171304443677700152150ustar00rootroot00000000000000//+build !appengine package graceful import ( "os" "os/signal" "syscall" ) func signalNotify(interrupt chan<- os.Signal) { signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) } func sendSignalInt(interrupt chan<- os.Signal) { interrupt <- syscall.SIGINT } graceful-1.2.15/signal_appengine.go000066400000000000000000000003631304443677700172430ustar00rootroot00000000000000//+build appengine package graceful import "os" func signalNotify(interrupt chan<- os.Signal) { // Does not notify in the case of AppEngine. } func sendSignalInt(interrupt chan<- os.Signal) { // Does not send in the case of AppEngine. } graceful-1.2.15/test-fixtures/000077500000000000000000000000001304443677700162355ustar00rootroot00000000000000graceful-1.2.15/test-fixtures/cert.crt000066400000000000000000000050161304443677700177060ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDhTCCAm2gAwIBAgIUDvdWhjUd/JS+E5bxZlmCM+giGHMwDQYJKoZIhvcNAQEL BQAwHzEdMBsGA1UEAxMUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwHhcNMTYwNjAyMDMy MjA0WhcNMTkwNjAyMDMyMjM0WjAUMRIwEAYDVQQDEwlsb2NhbGhvc3QwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDoyMTUK2OSp+XhKRXB/+uO6YAJE/W 2rzqARahWT6boHZMDhHXRtdwYxWwiUqoxlEeBrEerQ2qPFAqlWkDw8zliE/DWgXg BiW+Vq5DAn3F1jZ5WskLWr1iP48oK4/l+BXEsDd44MHZFoSZiWlr2Fi4iaIHJE7+ LGBqPVQXwBYTyc7Jvi3HY8I4/waaAwXoSo8vDPjRiMCD2wlg24Rimocf4goa/2Xs Z0NU76Uf2jPdsZ5MujjKRqwHDEAjiBq0aPvm6igkNGAGoZ6QYEptO+J4t1oFrbdP gYRlpqCa3ekr9gc+wg5AO/V9x8/cypbQ8tpwFwvvSYg2TJaUMZ5abc+HAgMBAAGj gcMwgcAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBQC R0Y69NLOfFCLRiB5N3uoacILXTAfBgNVHSMEGDAWgBRm0fFHSXtDCVHC8UW7/obv DLp9tTBJBggrBgEFBQcBAQQ9MDswOQYIKwYBBQUHMAKGLWh0dHA6Ly9sb2NhbGhv c3Qvc2VsZi1pc3N1ZWQtaW50ZXJtZWRpYXRlLmNydDAUBgNVHREEDTALgglsb2Nh bGhvc3QwDQYJKoZIhvcNAQELBQADggEBALAf/nowwB0NJ7lGGaoVKhmMHxBEQkd1 K/jBAlJg9Kgmg1IJJ7zLE3SeYF8tGTNYATd4RLmqo1GakrMDaKWNXd74v3p/tWmb 4vqCh6WzFPHU1dpxDKtbbmaLt9Ije7s6DuQAz9bBXM0mN0vy5F0dORpx/j0h3u1B j7B5O8kLejPY2w/8pd+QECCb1Q5A6Xx1EEsJpzTlGXO0SBla/oCg+nvirsBGVpWr bGskAIwG9wNKuGfg4m5u1bL87iX80NemeLtWRWVM+Ry/RhfOokH59/EIFRAXeRz6 gXjIWa0vcXnhW1MOvbD1GFYhO6AJAnDwWes48WfBHysOhq0RycdpGw0= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDjTCCAnWgAwIBAgIUMzpit8+j2dWxdk1PdMqGWYalZyIwDQYJKoZIhvcNAQEL BQAwFzEVMBMGA1UEAxMMVGVzdCBSb290IENBMB4XDTE2MDUyOTEwNDYwMFoXDTMx MDUyNjEwNDYzMFowHzEdMBsGA1UEAxMUVGVzdCBJbnRlcm1lZGlhdGUgQ0EwggEi MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDs6kY6mHJWzupq5dsSavPZHuv6 0E9PczHbujWLuzv7+qbwzcAgfRvaeR0xgvf7q9pjMgJ7/kNANgneWGpwciLgHtiJ rSHii3RZfWlK4gdbCXya9EmHj8zO+9xGBHM0FrqfqA+IA70SimFcwGPrGHyERsdX +mqO64Z95yI5uJpoS8OBAUPU8i6xvNLZGmgUEF3CRhDDTYVGcTEtKAPcnnBuZzZU Ds+DrHf/MC7HHK0/l0auuRz3p+/GFNePGePG+FFbInS/vwHwrkMW2tzBKG41K+gD GfkTjVU8xBSiMYOiEja6YcJ4GuzEPcmu5LS+6BkLlsIbazDW5IM8p+7+8RKjAgMB AAGjgcgwgcUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O BBYEFGbR8UdJe0MJUcLxRbv+hu8Mun21MB8GA1UdIwQYMBaAFKmz0h3CW1HBO9uz uCzg+MNPGZtkMEEGCCsGAQUFBwEBBDUwMzAxBggrBgEFBQcwAoYlaHR0cDovL2xv Y2FsaG9zdC9zZWxmLWlzc3VlZC1yb290LmNydDAfBgNVHREEGDAWghRUZXN0IElu dGVybWVkaWF0ZSBDQTANBgkqhkiG9w0BAQsFAAOCAQEAaYVGqHbaE0c9F/kyIMgu S3HuNn4pBh2EwGcKIlPkDe43hqXjhS/+itmWk75rQz+Rw+acevGoxbpDR38abTIS RJd9L/3MA644z8F82er3pNjKqvS/vTre/wsvGYwmEM+GrgJw3HUcisc93qLgaWH2 kjky208k9kOuzJDiY45eu9TfSSmjSHSMCtxk8p5wYKDcfVz+uqlBhVEiHGjQIc2E 66SituusiwgQv/mdtEW7y48EvMGdzxPfLFcvj06B3vTsZaaYyB6GyKwMcaPFvHRr V0yYaKRZgAh4X6LHlgPJqvIv3gjMdJR55durAO7tI9Pos0o5Lv5WJgi0g0KvMsco qQ== -----END CERTIFICATE-----graceful-1.2.15/test-fixtures/key.pem000066400000000000000000000032161304443677700175320ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAw6MjE1Ctjkqfl4SkVwf/rjumACRP1tq86gEWoVk+m6B2TA4R 10bXcGMVsIlKqMZRHgaxHq0NqjxQKpVpA8PM5YhPw1oF4AYlvlauQwJ9xdY2eVrJ C1q9Yj+PKCuP5fgVxLA3eODB2RaEmYlpa9hYuImiByRO/ixgaj1UF8AWE8nOyb4t x2PCOP8GmgMF6EqPLwz40YjAg9sJYNuEYpqHH+IKGv9l7GdDVO+lH9oz3bGeTLo4 ykasBwxAI4gatGj75uooJDRgBqGekGBKbTvieLdaBa23T4GEZaagmt3pK/YHPsIO QDv1fcfP3MqW0PLacBcL70mINkyWlDGeWm3PhwIDAQABAoIBAQC87HWa2XZAyt+D OpxZT2ghoYiU6nwPR/zXHWX1OnGzaCnVGGEyOz8hUQ5JBMwMYDdFf8DbltJzavsf pFldQWBE6HXeeLjjtgwM2zg9jdJXkp3YY0tyo5XvouFkMW0s735WCrYHDUUllxFG E+SyOKK00nSd4PpHiiMxdTgYF286exwOpzjhcJfAkn7oBNeOGc5VLOvcvakrSrdq OYBAJ25HSVFnSQbeAAsCzBEBZC0WLyB1BQGcidbtEn8sxyGnV8HWjbXY+MJQWHg+ q2iK+uvO4wtrE/WC6p4Ty44Myh+AB79s35HWKYd4okwKkpI1QdD543TIiZnkNEVI aS/uH13BAoGBAP/psBxKzIft59hw+U9NscH6N9/ze8iAtOtqsWdER/qXCrlUn8+j F/xquJR6gDj5GwGBt07asEuoG8CKJMQI0c3AeHF7XBcmUunBStktb9O97Zsp6bNJ olsrWlM4yvVuCVizEwIYjHrMBOS3YIPErM1LmAyDHmzx3+yz+3+WxRQLAoGBAMO0 MaJDPisMC05pvieHRb91HlsiSrASeMkw1FmHI0b/gcC88mEnuXIze1ySoF6FE7B7 xaEm6Lf5Snl0JgXPDSj6ukd51NdaU2VmpKvDOrvQ5QQE9mXaDkXv/i2B0YkCh+Hy bkziW1IKnWT2PTRAAEIJQ22oK51MdQnvCdmtsIP1AoGBAKnMiEl9Z9AZDmgSLZls 17D5MPGrQEp8+43oMOVv7MJcTYVCnPbMJDIbLXV3AnTK9Bw/0TzE5YyNcjyCbHqV z39RYZkKXMQPbZwj4GHRQA2iS3FUkfeft9X+IeRuHlxSMmlkCAyv9SXVELog4i0L 5gwhSDWlGh73LbiEgy7Y/tKZAoGBALTiMhYGDMoA4dpiBi3G7AKgH6SgN2QyTo22 oi71pveSZb1dZrHB47fYOadApxV17tLqM6pVqjeRJPLJFfO8gi9kPxSdWMqLZBWP H5jaY8kAtQxYAd32A8dEoSwylxcJzcpbJvPNLBbSVNPifIN0vEhNA5OxIk7LQkoi NHqL/WCZAoGAPf3kb9Gw/NkBq4Cn86pQfP/xE0h7zcoNmFtLbdKIjId+DDDOPOeX 9tm33fZzw0SG4KlRQlsqgzFvm8aDD8rpW17341Z/rWlLo8uHNdRkMvbSabc34vPv 4lrs0rHSYW06MlqkJBNVraySRz7hmU4+n7YMvNI0Due9mVGmE1NU/vI= -----END RSA PRIVATE KEY-----graceful-1.2.15/tests/000077500000000000000000000000001304443677700145515ustar00rootroot00000000000000graceful-1.2.15/tests/main.go000066400000000000000000000013321304443677700160230ustar00rootroot00000000000000package main import ( "fmt" "sync" "github.com/urfave/negroni" "gopkg.in/tylerb/graceful.v1" ) func main() { var wg sync.WaitGroup wg.Add(3) go func() { n := negroni.New() fmt.Println("Launching server on :3000") graceful.Run(":3000", 0, n) fmt.Println("Terminated server on :3000") wg.Done() }() go func() { n := negroni.New() fmt.Println("Launching server on :3001") graceful.Run(":3001", 0, n) fmt.Println("Terminated server on :3001") wg.Done() }() go func() { n := negroni.New() fmt.Println("Launching server on :3002") graceful.Run(":3002", 0, n) fmt.Println("Terminated server on :3002") wg.Done() }() fmt.Println("Press ctrl+c. All servers should terminate.") wg.Wait() }