diff options
| author | Felix Hanley <felix@userspace.com.au> | 2016-05-11 07:43:19 +0000 |
|---|---|---|
| committer | Felix Hanley <felix@userspace.com.au> | 2016-05-11 07:43:19 +0000 |
| commit | 01d4169b551675aa04e9075915113d2e94fdb72d (patch) | |
| tree | 0eaad50b248928795ba6c5c24873b7b84b0bca09 /src/dict2rest/vendor/github.com/stretchr/graceful | |
| parent | a5be2d0a2f450dd2e2c699e45773016528d43876 (diff) | |
| download | go-dict2rest-01d4169b551675aa04e9075915113d2e94fdb72d.tar.gz go-dict2rest-01d4169b551675aa04e9075915113d2e94fdb72d.tar.bz2 | |
Use Golang 1.6 vendor structure
Diffstat (limited to 'src/dict2rest/vendor/github.com/stretchr/graceful')
6 files changed, 1019 insertions, 0 deletions
diff --git a/src/dict2rest/vendor/github.com/stretchr/graceful/.gitignore b/src/dict2rest/vendor/github.com/stretchr/graceful/.gitignore new file mode 100644 index 0000000..8365624 --- /dev/null +++ b/src/dict2rest/vendor/github.com/stretchr/graceful/.gitignore @@ -0,0 +1,23 @@ +# 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 diff --git a/src/dict2rest/vendor/github.com/stretchr/graceful/LICENSE b/src/dict2rest/vendor/github.com/stretchr/graceful/LICENSE new file mode 100644 index 0000000..a4f2f28 --- /dev/null +++ b/src/dict2rest/vendor/github.com/stretchr/graceful/LICENSE @@ -0,0 +1,21 @@ +The 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. diff --git a/src/dict2rest/vendor/github.com/stretchr/graceful/README.md b/src/dict2rest/vendor/github.com/stretchr/graceful/README.md new file mode 100644 index 0000000..531249a --- /dev/null +++ b/src/dict2rest/vendor/github.com/stretchr/graceful/README.md @@ -0,0 +1,137 @@ +graceful [](http://godoc.org/github.com/tylerb/graceful) [](https://drone.io/github.com/tylerb/graceful/latest) [](https://coveralls.io/r/tylerb/graceful?branch=dronedebug) [](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. + +## 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. diff --git a/src/dict2rest/vendor/github.com/stretchr/graceful/graceful.go b/src/dict2rest/vendor/github.com/stretchr/graceful/graceful.go new file mode 100644 index 0000000..d3a7175 --- /dev/null +++ b/src/dict2rest/vendor/github.com/stretchr/graceful/graceful.go @@ -0,0 +1,359 @@ +package graceful + +import ( + "crypto/tls" + "log" + "net" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "time" + + "golang.org/x/net/netutil" +) + +// 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 + + // 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. + BeforeShutdown func() + + // 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 + + // 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{} +} + +// 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, + Server: &http.Server{Addr: addr, Handler: n}, + } + + if err := srv.ListenAndServe(); err != nil { + if opErr, ok := err.(*net.OpError); !ok || (ok && opErr.Op != "accept") { + logger := log.New(os.Stdout, "[graceful] ", 0) + logger.Fatal(err) + } + } + +} + +// 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} + 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" + } + l, err := net.Listen("tcp", addr) + if err != nil { + return err + } + + return srv.Serve(l) +} + +// 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} + 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 + } + 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 nil, err + } + + conn, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + + tlsListener := tls.NewListener(conn, config) + return tlsListener, nil +} + +// 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 := net.Listen("tcp", addr) + if err != nil { + return err + } + + 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} + 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 = netutil.LimitListener(listener, srv.ListenLimit) + } + + // Track connection state + add := 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.StateClosed, http.StateHijacked: + remove <- conn + } + if srv.ConnState != nil { + srv.ConnState(conn, state) + } + } + + // Manage open connections + shutdown := make(chan chan struct{}) + kill := make(chan struct{}) + go srv.manageConnections(add, remove, shutdown, kill) + + interrupt := srv.interruptChan() + // Set up the interrupt handler + if !srv.NoSignalHandling { + signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM) + } + 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() + srv.Timeout = timeout + interrupt := srv.interruptChan() + interrupt <- syscall.SIGINT + srv.stopLock.Unlock() +} + +// 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() + if srv.stopChan == nil { + srv.stopChan = make(chan struct{}) + } + srv.chanLock.Unlock() + return srv.stopChan +} + +func (srv *Server) manageConnections(add, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) { + var done chan struct{} + srv.connections = map[net.Conn]struct{}{} + for { + select { + case conn := <-add: + srv.connections[conn] = struct{}{} + case conn := <-remove: + delete(srv.connections, conn) + if done != nil && len(srv.connections) == 0 { + done <- struct{}{} + return + } + case done = <-shutdown: + if len(srv.connections) == 0 { + done <- struct{}{} + return + } + case <-kill: + for k := range srv.connections { + _ = k.Close() // nothing to do here if it errors + } + return + } + } +} + +func (srv *Server) interruptChan() chan os.Signal { + srv.chanLock.Lock() + if srv.interrupt == nil { + srv.interrupt = make(chan os.Signal, 1) + } + srv.chanLock.Unlock() + + return srv.interrupt +} + +func (srv *Server) handleInterrupt(interrupt chan os.Signal, quitting chan struct{}, listener net.Listener) { + <-interrupt + + if srv.BeforeShutdown != nil { + srv.BeforeShutdown() + } + + close(quitting) + srv.SetKeepAlivesEnabled(false) + _ = listener.Close() // we are shutting down anyway. ignore error. + + if srv.ShutdownInitiated != nil { + srv.ShutdownInitiated() + } + + srv.stopLock.Lock() + signal.Stop(interrupt) + close(interrupt) + srv.interrupt = nil + srv.stopLock.Unlock() +} + +func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) { + // Request done notification + done := make(chan struct{}) + shutdown <- done + + 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() +} diff --git a/src/dict2rest/vendor/github.com/stretchr/graceful/graceful_test.go b/src/dict2rest/vendor/github.com/stretchr/graceful/graceful_test.go new file mode 100644 index 0000000..83acac3 --- /dev/null +++ b/src/dict2rest/vendor/github.com/stretchr/graceful/graceful_test.go @@ -0,0 +1,439 @@ +package graceful + +import ( + "fmt" + "io" + "net" + "net/http" + "net/url" + "os" + "reflect" + "strings" + "sync" + "syscall" + "testing" + "time" +) + +var ( + 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) { + wg.Add(1) + defer wg.Done() + client := http.Client{} + r, err := client.Get("http://localhost:9654") + if shouldErr && err == nil { + once.Do(func() { + t.Fatal("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.Fatalf("Incorrect status code on response. Expected %d. Got %d", expected, r.StatusCode) + }) + } else if r == nil { + once.Do(func() { + t.Fatal("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.Fatal("Error on Get:", err) + }) + } + default: + if strings.Contains(err.Error(), "transport closed before response was received") { + 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) + }) + + time.Sleep(1 * time.Second) + server := &http.Server{Addr: ":9654", Handler: mux} + l, err := net.Listen("tcp", ":9654") + if err != nil { + } + return server, l, err +} + +func runServer(timeout, sleep time.Duration, c chan os.Signal) error { + server, l, err := createListener(sleep) + if err != nil { + return err + } + + srv := &Server{Timeout: timeout, Server: server, interrupt: c} + return srv.Serve(l) +} + +func launchTestQueries(t *testing.T, wg *sync.WaitGroup, c chan os.Signal) { + var once sync.Once + for i := 0; i < 8; i++ { + go runQuery(t, http.StatusOK, false, wg, &once) + } + + time.Sleep(waitTime) + c <- os.Interrupt + time.Sleep(waitTime) + + for i := 0; i < 8; i++ { + go runQuery(t, 0, true, wg, &once) + } + + wg.Done() +} + +func TestGracefulRun(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(killTime, killTime/2, c) + wg.Done() + }() + time.Sleep(1 * time.Second) + + wg.Add(1) + go launchTestQueries(t, &wg, c) + wg.Wait() +} + +func TestGracefulRunTimesOut(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(killTime, killTime*10, c) + wg.Done() + }() + time.Sleep(2 * time.Second) + + var once sync.Once + wg.Add(1) + go func() { + for i := 0; i < 8; i++ { + go runQuery(t, 0, true, &wg, &once) + } + time.Sleep(waitTime) + c <- os.Interrupt + time.Sleep(waitTime) + for i := 0; i < 8; i++ { + go runQuery(t, 0, true, &wg, &once) + } + wg.Done() + }() + + wg.Wait() + +} + +func TestGracefulRunDoesntTimeOut(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(0, killTime*2, c) + wg.Done() + }() + time.Sleep(1 * time.Second) + + wg.Add(1) + go launchTestQueries(t, &wg, c) + wg.Wait() +} + +func TestGracefulRunNoRequests(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + go func() { + runServer(0, killTime*2, c) + wg.Done() + }() + time.Sleep(1 * time.Second) + + c <- os.Interrupt + + wg.Wait() + +} + +func TestGracefulForwardsConnState(t *testing.T) { + c := make(chan os.Signal, 1) + states := make(map[http.ConnState]int) + var stateLock sync.Mutex + + connState := func(conn net.Conn, state http.ConnState) { + stateLock.Lock() + states[state]++ + stateLock.Unlock() + } + + var wg sync.WaitGroup + wg.Add(1) + + expected := map[http.ConnState]int{ + http.StateNew: 8, + http.StateActive: 8, + http.StateClosed: 8, + } + + go func() { + server, l, _ := createListener(killTime / 2) + srv := &Server{ + ConnState: connState, + Timeout: killTime, + Server: server, + interrupt: c, + } + srv.Serve(l) + + wg.Done() + }() + time.Sleep(2 * time.Second) + + 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) { + server, l, err := createListener(1 * time.Millisecond) + if err != nil { + t.Fatal(err) + } + + beforeShutdownCalled := make(chan struct{}) + cb1 := func() { close(beforeShutdownCalled) } + shutdownInitiatedCalled := make(chan struct{}) + cb2 := func() { close(shutdownInitiatedCalled) } + + srv := &Server{Server: server, BeforeShutdown: cb1, ShutdownInitiated: cb2} + + go func() { + go srv.Serve(l) + 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 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: ":9654", Handler: mux} + l, err := net.Listen("tcp", ":9654") + return server, l, err +} + +func TestNotifyClosed(t *testing.T) { + c := make(chan os.Signal, 1) + + var wg sync.WaitGroup + wg.Add(1) + + srv := &Server{Timeout: killTime, interrupt: c} + server, l, err := hijackingListener(srv) + if err != nil { + t.Fatal(err) + } + + srv.Server = server + + go func() { + srv.Serve(l) + wg.Done() + }() + + var once sync.Once + for i := 0; i < 8; i++ { + 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) { + c := make(chan struct{}) + + server, l, err := createListener(1 * time.Millisecond) + if err != nil { + t.Fatal(err) + } + + srv := &Server{Server: server, NoSignalHandling: true} + + go func() { + time.Sleep(waitTime) + srv.Serve(l) + }() + + go func() { + 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") + } +} diff --git a/src/dict2rest/vendor/github.com/stretchr/graceful/tests/main.go b/src/dict2rest/vendor/github.com/stretchr/graceful/tests/main.go new file mode 100644 index 0000000..8c8fa20 --- /dev/null +++ b/src/dict2rest/vendor/github.com/stretchr/graceful/tests/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "fmt" + "sync" + + "github.com/codegangsta/negroni" + "github.com/tylerb/graceful" +) + +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() + +} |
