diff --git a/vendor/github.com/tylerb/graceful/graceful.go b/vendor/github.com/tylerb/graceful/graceful.go index 4cb4790565..ebf0aeb7dc 100644 --- a/vendor/github.com/tylerb/graceful/graceful.go +++ b/vendor/github.com/tylerb/graceful/graceful.go @@ -6,9 +6,7 @@ import ( "net" "net/http" "os" - "os/signal" "sync" - "syscall" "time" ) @@ -141,12 +139,12 @@ func (srv *Server) ListenAndServe() error { if addr == "" { addr = ":http" } - l, err := net.Listen("tcp", addr) + conn, err := srv.newTCPListener(addr) if err != nil { return err } - return srv.Serve(l) + return srv.Serve(conn) } // ListenAndServeTLS is equivalent to http.Server.ListenAndServeTLS with graceful shutdown enabled. @@ -174,13 +172,18 @@ func (srv *Server) ListenTLS(certFile, keyFile string) (net.Listener, error) { } var err error - config.Certificates = make([]tls.Certificate, 1) - config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, err + if certFile != "" && keyFile != "" { + 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) + // Enable http2 + enableHTTP2ForTLSConfig(config) + + conn, err := srv.newTCPListener(addr) if err != nil { return nil, err } @@ -191,6 +194,28 @@ func (srv *Server) ListenTLS(certFile, keyFile string) (net.Listener, error) { 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) @@ -209,7 +234,7 @@ func (srv *Server) ListenAndServeTLSConfig(config *tls.Config) error { addr = ":https" } - conn, err := net.Listen("tcp", addr) + conn, err := srv.newTCPListener(addr) if err != nil { return err } @@ -226,6 +251,7 @@ func (srv *Server) ListenAndServeTLSConfig(config *tls.Config) error { // 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) } @@ -236,22 +262,21 @@ func (srv *Server) Serve(listener net.Listener) error { listener = LimitListener(listener, srv.ListenLimit) } - if srv.TCPKeepAlive != 0 { - listener = keepAliveListener{listener, srv.TCPKeepAlive} - } - // 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: @@ -269,12 +294,12 @@ func (srv *Server) Serve(listener net.Listener) error { // Manage open connections shutdown := make(chan chan struct{}) kill := make(chan struct{}) - go srv.manageConnections(add, idle, remove, shutdown, kill) + 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) + signalNotify(interrupt) } quitting := make(chan struct{}) go srv.handleInterrupt(interrupt, quitting, listener) @@ -311,8 +336,7 @@ func (srv *Server) Stop(timeout time.Duration) { defer srv.stopLock.Unlock() srv.Timeout = timeout - interrupt := srv.interruptChan() - interrupt <- syscall.SIGINT + sendSignalInt(srv.interruptChan()) } // StopChan gets the stop channel which will block until @@ -334,7 +358,7 @@ func DefaultLogger() *log.Logger { return log.New(os.Stderr, "[graceful] ", 0) } -func (srv *Server) manageConnections(add, idle, remove chan net.Conn, shutdown chan chan struct{}, kill chan struct{}) { +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{}{} @@ -342,8 +366,11 @@ func (srv *Server) manageConnections(add, idle, remove chan net.Conn, shutdown c 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) @@ -431,6 +458,8 @@ func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) { done := make(chan struct{}) shutdown <- done + srv.stopLock.Lock() + defer srv.stopLock.Unlock() if srv.Timeout > 0 { select { case <-done: @@ -447,3 +476,14 @@ func (srv *Server) shutdown(shutdown chan chan struct{}, kill chan struct{}) { } 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 deleted file mode 100644 index a094b80a35..0000000000 --- a/vendor/github.com/tylerb/graceful/graceful_test.go +++ /dev/null @@ -1,649 +0,0 @@ -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 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 deleted file mode 100644 index ea0c1f92ee..0000000000 --- a/vendor/github.com/tylerb/graceful/http2_test.go +++ /dev/null @@ -1,124 +0,0 @@ -// +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.Proto != "HTTP/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, 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, 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}, - } - - 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/signal.go b/vendor/github.com/tylerb/graceful/signal.go new file mode 100644 index 0000000000..9550978fe2 --- /dev/null +++ b/vendor/github.com/tylerb/graceful/signal.go @@ -0,0 +1,17 @@ +//+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 +} diff --git a/vendor/github.com/tylerb/graceful/signal_appengine.go b/vendor/github.com/tylerb/graceful/signal_appengine.go new file mode 100644 index 0000000000..6b776f087a --- /dev/null +++ b/vendor/github.com/tylerb/graceful/signal_appengine.go @@ -0,0 +1,13 @@ +//+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. +} diff --git a/vendor/github.com/urfave/negroni/LICENSE b/vendor/github.com/urfave/negroni/LICENSE new file mode 100644 index 0000000000..08b5e20ac4 --- /dev/null +++ b/vendor/github.com/urfave/negroni/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Jeremy Saenz + +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/urfave/negroni/doc.go b/vendor/github.com/urfave/negroni/doc.go new file mode 100644 index 0000000000..add1ed9f71 --- /dev/null +++ b/vendor/github.com/urfave/negroni/doc.go @@ -0,0 +1,25 @@ +// Package negroni is an idiomatic approach to web middleware in Go. It is tiny, non-intrusive, and encourages use of net/http Handlers. +// +// If you like the idea of Martini, but you think it contains too much magic, then Negroni is a great fit. +// +// For a full guide visit http://github.com/urfave/negroni +// +// package main +// +// import ( +// "github.com/urfave/negroni" +// "net/http" +// "fmt" +// ) +// +// 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") +// } +package negroni diff --git a/vendor/github.com/urfave/negroni/logger.go b/vendor/github.com/urfave/negroni/logger.go new file mode 100644 index 0000000000..032909f5de --- /dev/null +++ b/vendor/github.com/urfave/negroni/logger.go @@ -0,0 +1,80 @@ +package negroni + +import ( + "bytes" + + "log" + "net/http" + "os" + "text/template" + "time" +) + +// LoggerEntry is the structure +// passed to the template. +type LoggerEntry struct { + StartTime string + Status int + Duration time.Duration + Hostname string + Method string + Path string +} + +// LoggerDefaultFormat is the format +// logged used by the default Logger instance. +var LoggerDefaultFormat = "{{.StartTime}} | {{.Status}} | \t {{.Duration}} | {{.Hostname}} | {{.Method}} {{.Path}} \n" + +// LoggerDefaultDateFormat is the +// format used for date by the +// default Logger instance. +var LoggerDefaultDateFormat = time.RFC3339 + +// ALogger interface +type ALogger interface { + Println(v ...interface{}) + Printf(format string, v ...interface{}) +} + +// Logger is a middleware handler that logs the request as it goes in and the response as it goes out. +type Logger struct { + // ALogger implements just enough log.Logger interface to be compatible with other implementations + ALogger + dateFormat string + template *template.Template +} + +// NewLogger returns a new Logger instance +func NewLogger() *Logger { + logger := &Logger{ALogger: log.New(os.Stdout, "[negroni] ", 0), dateFormat: LoggerDefaultDateFormat} + logger.SetFormat(LoggerDefaultFormat) + return logger +} + +func (l *Logger) SetFormat(format string) { + l.template = template.Must(template.New("negroni_parser").Parse(format)) +} + +func (l *Logger) SetDateFormat(format string) { + l.dateFormat = format +} + +func (l *Logger) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + start := time.Now() + + next(rw, r) + + res := rw.(ResponseWriter) + log := LoggerEntry{ + StartTime: start.Format(l.dateFormat), + Status: res.Status(), + Duration: time.Since(start), + Hostname: r.Host, + Method: r.Method, + Path: r.URL.Path, + } + + buff := &bytes.Buffer{} + l.template.Execute(buff, log) + l.Printf(buff.String()) +} diff --git a/vendor/github.com/urfave/negroni/negroni.go b/vendor/github.com/urfave/negroni/negroni.go new file mode 100644 index 0000000000..d1d77820a0 --- /dev/null +++ b/vendor/github.com/urfave/negroni/negroni.go @@ -0,0 +1,169 @@ +package negroni + +import ( + "log" + "net/http" + "os" +) + +const ( + // DefaultAddress is used if no other is specified. + DefaultAddress = ":8080" +) + +// Handler handler is an interface that objects can implement to be registered to serve as middleware +// in the Negroni middleware stack. +// ServeHTTP should yield to the next middleware in the chain by invoking the next http.HandlerFunc +// passed in. +// +// If the Handler writes to the ResponseWriter, the next http.HandlerFunc should not be invoked. +type Handler interface { + ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) +} + +// HandlerFunc is an adapter to allow the use of ordinary functions as Negroni handlers. +// If f is a function with the appropriate signature, HandlerFunc(f) is a Handler object that calls f. +type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) + +func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + h(rw, r, next) +} + +type middleware struct { + handler Handler + next *middleware +} + +func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + m.handler.ServeHTTP(rw, r, m.next.ServeHTTP) +} + +// Wrap converts a http.Handler into a negroni.Handler so it can be used as a Negroni +// middleware. The next http.HandlerFunc is automatically called after the Handler +// is executed. +func Wrap(handler http.Handler) Handler { + return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + handler.ServeHTTP(rw, r) + next(rw, r) + }) +} + +// WrapFunc converts a http.HandlerFunc into a negroni.Handler so it can be used as a Negroni +// middleware. The next http.HandlerFunc is automatically called after the Handler +// is executed. +func WrapFunc(handlerFunc http.HandlerFunc) Handler { + return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + handlerFunc(rw, r) + next(rw, r) + }) +} + +// Negroni is a stack of Middleware Handlers that can be invoked as an http.Handler. +// Negroni middleware is evaluated in the order that they are added to the stack using +// the Use and UseHandler methods. +type Negroni struct { + middleware middleware + handlers []Handler +} + +// New returns a new Negroni instance with no middleware preconfigured. +func New(handlers ...Handler) *Negroni { + return &Negroni{ + handlers: handlers, + middleware: build(handlers), + } +} + +// With returns a new Negroni instance that is a combination of the negroni +// receiver's handlers and the provided handlers. +func (n *Negroni) With(handlers ...Handler) *Negroni { + return New( + append(n.handlers, handlers...)..., + ) +} + +// Classic returns a new Negroni instance with the default middleware already +// in the stack. +// +// Recovery - Panic Recovery Middleware +// Logger - Request/Response Logging +// Static - Static File Serving +func Classic() *Negroni { + return New(NewRecovery(), NewLogger(), NewStatic(http.Dir("public"))) +} + +func (n *Negroni) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + n.middleware.ServeHTTP(NewResponseWriter(rw), r) +} + +// Use adds a Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni. +func (n *Negroni) Use(handler Handler) { + if handler == nil { + panic("handler cannot be nil") + } + + n.handlers = append(n.handlers, handler) + n.middleware = build(n.handlers) +} + +// UseFunc adds a Negroni-style handler function onto the middleware stack. +func (n *Negroni) UseFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)) { + n.Use(HandlerFunc(handlerFunc)) +} + +// UseHandler adds a http.Handler onto the middleware stack. Handlers are invoked in the order they are added to a Negroni. +func (n *Negroni) UseHandler(handler http.Handler) { + n.Use(Wrap(handler)) +} + +// UseHandlerFunc adds a http.HandlerFunc-style handler function onto the middleware stack. +func (n *Negroni) UseHandlerFunc(handlerFunc func(rw http.ResponseWriter, r *http.Request)) { + n.UseHandler(http.HandlerFunc(handlerFunc)) +} + +// Run is a convenience function that runs the negroni stack as an HTTP +// server. The addr string, if provided, takes the same format as http.ListenAndServe. +// If no address is provided but the PORT environment variable is set, the PORT value is used. +// If neither is provided, the address' value will equal the DefaultAddress constant. +func (n *Negroni) Run(addr ...string) { + l := log.New(os.Stdout, "[negroni] ", 0) + finalAddr := detectAddress(addr...) + l.Printf("listening on %s", finalAddr) + l.Fatal(http.ListenAndServe(finalAddr, n)) +} + +func detectAddress(addr ...string) string { + if len(addr) > 0 { + return addr[0] + } + if port := os.Getenv("PORT"); port != "" { + return ":" + port + } + return DefaultAddress +} + +// Returns a list of all the handlers in the current Negroni middleware chain. +func (n *Negroni) Handlers() []Handler { + return n.handlers +} + +func build(handlers []Handler) middleware { + var next middleware + + if len(handlers) == 0 { + return voidMiddleware() + } else if len(handlers) > 1 { + next = build(handlers[1:]) + } else { + next = voidMiddleware() + } + + return middleware{handlers[0], &next} +} + +func voidMiddleware() middleware { + return middleware{ + HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {}), + &middleware{}, + } +} diff --git a/vendor/github.com/urfave/negroni/recovery.go b/vendor/github.com/urfave/negroni/recovery.go new file mode 100644 index 0000000000..8396cb1edf --- /dev/null +++ b/vendor/github.com/urfave/negroni/recovery.go @@ -0,0 +1,65 @@ +package negroni + +import ( + "fmt" + "log" + "net/http" + "os" + "runtime" + "runtime/debug" +) + +// Recovery is a Negroni middleware that recovers from any panics and writes a 500 if there was one. +type Recovery struct { + Logger ALogger + PrintStack bool + ErrorHandlerFunc func(interface{}) + StackAll bool + StackSize int +} + +// NewRecovery returns a new instance of Recovery +func NewRecovery() *Recovery { + return &Recovery{ + Logger: log.New(os.Stdout, "[negroni] ", 0), + PrintStack: true, + StackAll: false, + StackSize: 1024 * 8, + } +} + +func (rec *Recovery) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + defer func() { + if err := recover(); err != nil { + if rw.Header().Get("Content-Type") == "" { + rw.Header().Set("Content-Type", "text/plain; charset=utf-8") + } + + rw.WriteHeader(http.StatusInternalServerError) + + stack := make([]byte, rec.StackSize) + stack = stack[:runtime.Stack(stack, rec.StackAll)] + + f := "PANIC: %s\n%s" + rec.Logger.Printf(f, err, stack) + + if rec.PrintStack { + fmt.Fprintf(rw, f, err, stack) + } + + if rec.ErrorHandlerFunc != nil { + func() { + defer func() { + if err := recover(); err != nil { + rec.Logger.Printf("provided ErrorHandlerFunc panic'd: %s, trace:\n%s", err, debug.Stack()) + rec.Logger.Printf("%s\n", debug.Stack()) + } + }() + rec.ErrorHandlerFunc(err) + }() + } + } + }() + + next(rw, r) +} diff --git a/vendor/github.com/urfave/negroni/response_writer.go b/vendor/github.com/urfave/negroni/response_writer.go new file mode 100644 index 0000000000..cc507eb4b7 --- /dev/null +++ b/vendor/github.com/urfave/negroni/response_writer.go @@ -0,0 +1,113 @@ +package negroni + +import ( + "bufio" + "fmt" + "net" + "net/http" +) + +// ResponseWriter is a wrapper around http.ResponseWriter that provides extra information about +// the response. It is recommended that middleware handlers use this construct to wrap a responsewriter +// if the functionality calls for it. +type ResponseWriter interface { + http.ResponseWriter + http.Flusher + // Status returns the status code of the response or 0 if the response has + // not been written + Status() int + // Written returns whether or not the ResponseWriter has been written. + Written() bool + // Size returns the size of the response body. + Size() int + // Before allows for a function to be called before the ResponseWriter has been written to. This is + // useful for setting headers or any other operations that must happen before a response has been written. + Before(func(ResponseWriter)) +} + +type beforeFunc func(ResponseWriter) + +// NewResponseWriter creates a ResponseWriter that wraps an http.ResponseWriter +func NewResponseWriter(rw http.ResponseWriter) ResponseWriter { + nrw := &responseWriter{ + ResponseWriter: rw, + } + + if _, ok := rw.(http.CloseNotifier); ok { + return &responseWriterCloseNotifer{nrw} + } + + return nrw +} + +type responseWriter struct { + http.ResponseWriter + status int + size int + beforeFuncs []beforeFunc +} + +func (rw *responseWriter) WriteHeader(s int) { + rw.status = s + rw.callBefore() + rw.ResponseWriter.WriteHeader(s) +} + +func (rw *responseWriter) Write(b []byte) (int, error) { + if !rw.Written() { + // The status will be StatusOK if WriteHeader has not been called yet + rw.WriteHeader(http.StatusOK) + } + size, err := rw.ResponseWriter.Write(b) + rw.size += size + return size, err +} + +func (rw *responseWriter) Status() int { + return rw.status +} + +func (rw *responseWriter) Size() int { + return rw.size +} + +func (rw *responseWriter) Written() bool { + return rw.status != 0 +} + +func (rw *responseWriter) Before(before func(ResponseWriter)) { + rw.beforeFuncs = append(rw.beforeFuncs, before) +} + +func (rw *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) { + hijacker, ok := rw.ResponseWriter.(http.Hijacker) + if !ok { + return nil, nil, fmt.Errorf("the ResponseWriter doesn't support the Hijacker interface") + } + return hijacker.Hijack() +} + +func (rw *responseWriter) callBefore() { + for i := len(rw.beforeFuncs) - 1; i >= 0; i-- { + rw.beforeFuncs[i](rw) + } +} + +func (rw *responseWriter) Flush() { + flusher, ok := rw.ResponseWriter.(http.Flusher) + if ok { + if !rw.Written() { + // The status will be StatusOK if WriteHeader has not been called yet + rw.WriteHeader(http.StatusOK) + } + flusher.Flush() + } +} + +type responseWriterCloseNotifer struct { + *responseWriter +} + +func (rw *responseWriterCloseNotifer) CloseNotify() <-chan bool { + return rw.ResponseWriter.(http.CloseNotifier).CloseNotify() +} diff --git a/vendor/github.com/urfave/negroni/response_writer_pusher.go b/vendor/github.com/urfave/negroni/response_writer_pusher.go new file mode 100644 index 0000000000..213cb35f7c --- /dev/null +++ b/vendor/github.com/urfave/negroni/response_writer_pusher.go @@ -0,0 +1,16 @@ +//+build go1.8 + +package negroni + +import ( + "fmt" + "net/http" +) + +func (rw *responseWriter) Push(target string, opts *http.PushOptions) error { + pusher, ok := rw.ResponseWriter.(http.Pusher) + if ok { + return pusher.Push(target, opts) + } + return fmt.Errorf("the ResponseWriter doesn't support the Pusher interface") +} diff --git a/vendor/github.com/urfave/negroni/static.go b/vendor/github.com/urfave/negroni/static.go new file mode 100644 index 0000000000..34be967c05 --- /dev/null +++ b/vendor/github.com/urfave/negroni/static.go @@ -0,0 +1,88 @@ +package negroni + +import ( + "net/http" + "path" + "strings" +) + +// Static is a middleware handler that serves static files in the given +// directory/filesystem. If the file does not exist on the filesystem, it +// passes along to the next middleware in the chain. If you desire "fileserver" +// type behavior where it returns a 404 for unfound files, you should consider +// using http.FileServer from the Go stdlib. +type Static struct { + // Dir is the directory to serve static files from + Dir http.FileSystem + // Prefix is the optional prefix used to serve the static directory content + Prefix string + // IndexFile defines which file to serve as index if it exists. + IndexFile string +} + +// NewStatic returns a new instance of Static +func NewStatic(directory http.FileSystem) *Static { + return &Static{ + Dir: directory, + Prefix: "", + IndexFile: "index.html", + } +} + +func (s *Static) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { + if r.Method != "GET" && r.Method != "HEAD" { + next(rw, r) + return + } + file := r.URL.Path + // if we have a prefix, filter requests by stripping the prefix + if s.Prefix != "" { + if !strings.HasPrefix(file, s.Prefix) { + next(rw, r) + return + } + file = file[len(s.Prefix):] + if file != "" && file[0] != '/' { + next(rw, r) + return + } + } + f, err := s.Dir.Open(file) + if err != nil { + // discard the error? + next(rw, r) + return + } + defer f.Close() + + fi, err := f.Stat() + if err != nil { + next(rw, r) + return + } + + // try to serve index file + if fi.IsDir() { + // redirect if missing trailing slash + if !strings.HasSuffix(r.URL.Path, "/") { + http.Redirect(rw, r, r.URL.Path+"/", http.StatusFound) + return + } + + file = path.Join(file, s.IndexFile) + f, err = s.Dir.Open(file) + if err != nil { + next(rw, r) + return + } + defer f.Close() + + fi, err = f.Stat() + if err != nil || fi.IsDir() { + next(rw, r) + return + } + } + + http.ServeContent(rw, r, file, fi.ModTime(), f) +} diff --git a/vendor/gopkg.in/tylerb/graceful.v1/LICENSE b/vendor/gopkg.in/tylerb/graceful.v1/LICENSE new file mode 100644 index 0000000000..a4f2f281ba --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/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/gopkg.in/tylerb/graceful.v1/graceful.go b/vendor/gopkg.in/tylerb/graceful.v1/graceful.go new file mode 100644 index 0000000000..ebf0aeb7dc --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/graceful.go @@ -0,0 +1,489 @@ +package 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 +} diff --git a/vendor/gopkg.in/tylerb/graceful.v1/keepalive_listener.go b/vendor/gopkg.in/tylerb/graceful.v1/keepalive_listener.go new file mode 100644 index 0000000000..1484bc213a --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/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/gopkg.in/tylerb/graceful.v1/limit_listen.go b/vendor/gopkg.in/tylerb/graceful.v1/limit_listen.go new file mode 100644 index 0000000000..ce32ce992a --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/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/gopkg.in/tylerb/graceful.v1/signal.go b/vendor/gopkg.in/tylerb/graceful.v1/signal.go new file mode 100644 index 0000000000..9550978fe2 --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/signal.go @@ -0,0 +1,17 @@ +//+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 +} diff --git a/vendor/gopkg.in/tylerb/graceful.v1/signal_appengine.go b/vendor/gopkg.in/tylerb/graceful.v1/signal_appengine.go new file mode 100644 index 0000000000..6b776f087a --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/signal_appengine.go @@ -0,0 +1,13 @@ +//+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. +} diff --git a/vendor/gopkg.in/tylerb/graceful.v1/tests/main.go b/vendor/gopkg.in/tylerb/graceful.v1/tests/main.go new file mode 100644 index 0000000000..9380ae69cd --- /dev/null +++ b/vendor/gopkg.in/tylerb/graceful.v1/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() + +} diff --git a/vendor/manifest b/vendor/manifest index 8c83ad2e30..1a33f392f8 100644 --- a/vendor/manifest +++ b/vendor/manifest @@ -999,8 +999,9 @@ "importpath": "github.com/tylerb/graceful", "repository": "https://github.com/tylerb/graceful", "vcs": "git", - "revision": "d7d7a205e779a4738d38eda0671fff8bfc965f14", - "branch": "master" + "revision": "4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb", + "branch": "HEAD", + "notests": true }, { "importpath": "github.com/ugorji/go/codec", @@ -1011,6 +1012,14 @@ "path": "/codec", "notests": true }, + { + "importpath": "github.com/urfave/negroni", + "repository": "https://github.com/urfave/negroni", + "vcs": "git", + "revision": "ccc4a14984828dcb944e6f17fa4a967b18624f2b", + "branch": "master", + "notests": true + }, { "importpath": "github.com/vbatts/tar-split/archive/tar", "repository": "https://github.com/vbatts/tar-split", @@ -1416,6 +1425,14 @@ "revision": "dd632973f1e7218eb1089048e0798ec9ae7dceb8", "branch": "v1" }, + { + "importpath": "gopkg.in/tylerb/graceful.v1", + "repository": "https://gopkg.in/tylerb/graceful.v1", + "vcs": "git", + "revision": "4654dfbb6ad53cb5e27f37d99b02e16c1872fbbb", + "branch": "master", + "notests": true + }, { "importpath": "gopkg.in/urfave/cli.v1", "repository": "https://gopkg.in/urfave/cli.v1",