diff options
author | Christopher Speller <crspeller@gmail.com> | 2016-10-03 16:03:15 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-10-03 16:03:15 -0400 |
commit | 8f91c777559748fa6e857d9fc1f4ae079a532813 (patch) | |
tree | 190f7cef373764a0d47a91045fdb486ee3d6781d /vendor/github.com/tylerb/graceful | |
parent | 5f8e5c401bd96cba9a98b2db02d72f9cbacb0103 (diff) | |
download | chat-8f91c777559748fa6e857d9fc1f4ae079a532813.tar.gz chat-8f91c777559748fa6e857d9fc1f4ae079a532813.tar.bz2 chat-8f91c777559748fa6e857d9fc1f4ae079a532813.zip |
Adding ability to serve TLS directly from Mattermost server (#4119)
Diffstat (limited to 'vendor/github.com/tylerb/graceful')
-rw-r--r-- | vendor/github.com/tylerb/graceful/.gitignore | 23 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/.travis.yml | 13 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/LICENSE | 21 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/README.md | 152 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/graceful.go | 487 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/graceful_test.go | 692 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/http2_test.go | 125 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/keepalive_listener.go | 32 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/limit_listen.go | 77 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/test-fixtures/cert.crt | 43 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/test-fixtures/key.pem | 27 | ||||
-rw-r--r-- | vendor/github.com/tylerb/graceful/tests/main.go | 40 |
12 files changed, 1732 insertions, 0 deletions
diff --git a/vendor/github.com/tylerb/graceful/.gitignore b/vendor/github.com/tylerb/graceful/.gitignore new file mode 100644 index 000000000..836562412 --- /dev/null +++ b/vendor/github.com/tylerb/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/vendor/github.com/tylerb/graceful/.travis.yml b/vendor/github.com/tylerb/graceful/.travis.yml new file mode 100644 index 000000000..66fdff76d --- /dev/null +++ b/vendor/github.com/tylerb/graceful/.travis.yml @@ -0,0 +1,13 @@ +language: 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 diff --git a/vendor/github.com/tylerb/graceful/LICENSE b/vendor/github.com/tylerb/graceful/LICENSE new file mode 100644 index 000000000..a4f2f281b --- /dev/null +++ b/vendor/github.com/tylerb/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/vendor/github.com/tylerb/graceful/README.md b/vendor/github.com/tylerb/graceful/README.md new file mode 100644 index 000000000..328c3acf8 --- /dev/null +++ b/vendor/github.com/tylerb/graceful/README.md @@ -0,0 +1,152 @@ +graceful [![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. diff --git a/vendor/github.com/tylerb/graceful/graceful.go b/vendor/github.com/tylerb/graceful/graceful.go new file mode 100644 index 000000000..a5e2395e0 --- /dev/null +++ b/vendor/github.com/tylerb/graceful/graceful.go @@ -0,0 +1,487 @@ +package graceful + +import ( + "crypto/tls" + "log" + "net" + "net/http" + "os" + "os/signal" + "sync" + "syscall" + "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 + 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 { + 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() + defer srv.stopLock.Unlock() + + srv.Timeout = timeout + interrupt := srv.interruptChan() + interrupt <- syscall.SIGINT +} + +// 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{}{} + 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 + + 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 +} diff --git a/vendor/github.com/tylerb/graceful/graceful_test.go b/vendor/github.com/tylerb/graceful/graceful_test.go new file mode 100644 index 000000000..b9c49336b --- /dev/null +++ b/vendor/github.com/tylerb/graceful/graceful_test.go @@ -0,0 +1,692 @@ +package 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) +} diff --git a/vendor/github.com/tylerb/graceful/http2_test.go b/vendor/github.com/tylerb/graceful/http2_test.go new file mode 100644 index 000000000..5b2ebbb8f --- /dev/null +++ b/vendor/github.com/tylerb/graceful/http2_test.go @@ -0,0 +1,125 @@ +// +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() +} diff --git a/vendor/github.com/tylerb/graceful/keepalive_listener.go b/vendor/github.com/tylerb/graceful/keepalive_listener.go new file mode 100644 index 000000000..1484bc213 --- /dev/null +++ b/vendor/github.com/tylerb/graceful/keepalive_listener.go @@ -0,0 +1,32 @@ +package 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 +} diff --git a/vendor/github.com/tylerb/graceful/limit_listen.go b/vendor/github.com/tylerb/graceful/limit_listen.go new file mode 100644 index 000000000..ce32ce992 --- /dev/null +++ b/vendor/github.com/tylerb/graceful/limit_listen.go @@ -0,0 +1,77 @@ +// 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) +} diff --git a/vendor/github.com/tylerb/graceful/test-fixtures/cert.crt b/vendor/github.com/tylerb/graceful/test-fixtures/cert.crt new file mode 100644 index 000000000..84bd02a3d --- /dev/null +++ b/vendor/github.com/tylerb/graceful/test-fixtures/cert.crt @@ -0,0 +1,43 @@ +-----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-----
\ No newline at end of file diff --git a/vendor/github.com/tylerb/graceful/test-fixtures/key.pem b/vendor/github.com/tylerb/graceful/test-fixtures/key.pem new file mode 100644 index 000000000..78f3232c8 --- /dev/null +++ b/vendor/github.com/tylerb/graceful/test-fixtures/key.pem @@ -0,0 +1,27 @@ +-----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-----
\ No newline at end of file diff --git a/vendor/github.com/tylerb/graceful/tests/main.go b/vendor/github.com/tylerb/graceful/tests/main.go new file mode 100644 index 000000000..9380ae69c --- /dev/null +++ b/vendor/github.com/tylerb/graceful/tests/main.go @@ -0,0 +1,40 @@ +package 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() + +} |