summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/facebookgo/grace/gracehttp
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/facebookgo/grace/gracehttp')
-rw-r--r--vendor/github.com/facebookgo/grace/gracehttp/http.go190
-rw-r--r--vendor/github.com/facebookgo/grace/gracehttp/license30
2 files changed, 220 insertions, 0 deletions
diff --git a/vendor/github.com/facebookgo/grace/gracehttp/http.go b/vendor/github.com/facebookgo/grace/gracehttp/http.go
new file mode 100644
index 00000000..fa3ac883
--- /dev/null
+++ b/vendor/github.com/facebookgo/grace/gracehttp/http.go
@@ -0,0 +1,190 @@
+// Package gracehttp provides easy to use graceful restart
+// functionality for HTTP server.
+package gracehttp
+
+import (
+ "bytes"
+ "crypto/tls"
+ "fmt"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+ "sync"
+ "syscall"
+
+ "github.com/facebookgo/grace/gracenet"
+ "github.com/facebookgo/httpdown"
+)
+
+var (
+ logger *log.Logger
+ didInherit = os.Getenv("LISTEN_FDS") != ""
+ ppid = os.Getppid()
+)
+
+// An app contains one or more servers and associated configuration.
+type app struct {
+ servers []*http.Server
+ http *httpdown.HTTP
+ net *gracenet.Net
+ listeners []net.Listener
+ sds []httpdown.Server
+ errors chan error
+}
+
+func newApp(servers []*http.Server) *app {
+ return &app{
+ servers: servers,
+ http: &httpdown.HTTP{},
+ net: &gracenet.Net{},
+ listeners: make([]net.Listener, 0, len(servers)),
+ sds: make([]httpdown.Server, 0, len(servers)),
+
+ // 2x num servers for possible Close or Stop errors + 1 for possible
+ // StartProcess error.
+ errors: make(chan error, 1+(len(servers)*2)),
+ }
+}
+
+func (a *app) listen() error {
+ for _, s := range a.servers {
+ // TODO: default addresses
+ l, err := a.net.Listen("tcp", s.Addr)
+ if err != nil {
+ return err
+ }
+ if s.TLSConfig != nil {
+ l = tls.NewListener(l, s.TLSConfig)
+ }
+ a.listeners = append(a.listeners, l)
+ }
+ return nil
+}
+
+func (a *app) serve() {
+ for i, s := range a.servers {
+ a.sds = append(a.sds, a.http.Serve(s, a.listeners[i]))
+ }
+}
+
+func (a *app) wait() {
+ var wg sync.WaitGroup
+ wg.Add(len(a.sds) * 2) // Wait & Stop
+ go a.signalHandler(&wg)
+ for _, s := range a.sds {
+ go func(s httpdown.Server) {
+ defer wg.Done()
+ if err := s.Wait(); err != nil {
+ a.errors <- err
+ }
+ }(s)
+ }
+ wg.Wait()
+}
+
+func (a *app) term(wg *sync.WaitGroup) {
+ for _, s := range a.sds {
+ go func(s httpdown.Server) {
+ defer wg.Done()
+ if err := s.Stop(); err != nil {
+ a.errors <- err
+ }
+ }(s)
+ }
+}
+
+func (a *app) signalHandler(wg *sync.WaitGroup) {
+ ch := make(chan os.Signal, 10)
+ signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
+ for {
+ sig := <-ch
+ switch sig {
+ case syscall.SIGINT, syscall.SIGTERM:
+ // this ensures a subsequent INT/TERM will trigger standard go behaviour of
+ // terminating.
+ signal.Stop(ch)
+ a.term(wg)
+ return
+ case syscall.SIGUSR2:
+ // we only return here if there's an error, otherwise the new process
+ // will send us a TERM when it's ready to trigger the actual shutdown.
+ if _, err := a.net.StartProcess(); err != nil {
+ a.errors <- err
+ }
+ }
+ }
+}
+
+// Serve will serve the given http.Servers and will monitor for signals
+// allowing for graceful termination (SIGTERM) or restart (SIGUSR2).
+func Serve(servers ...*http.Server) error {
+ a := newApp(servers)
+
+ // Acquire Listeners
+ if err := a.listen(); err != nil {
+ return err
+ }
+
+ // Some useful logging.
+ if logger != nil {
+ if didInherit {
+ if ppid == 1 {
+ logger.Printf("Listening on init activated %s", pprintAddr(a.listeners))
+ } else {
+ const msg = "Graceful handoff of %s with new pid %d and old pid %d"
+ logger.Printf(msg, pprintAddr(a.listeners), os.Getpid(), ppid)
+ }
+ } else {
+ const msg = "Serving %s with pid %d"
+ logger.Printf(msg, pprintAddr(a.listeners), os.Getpid())
+ }
+ }
+
+ // Start serving.
+ a.serve()
+
+ // Close the parent if we inherited and it wasn't init that started us.
+ if didInherit && ppid != 1 {
+ if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
+ return fmt.Errorf("failed to close parent: %s", err)
+ }
+ }
+
+ waitdone := make(chan struct{})
+ go func() {
+ defer close(waitdone)
+ a.wait()
+ }()
+
+ select {
+ case err := <-a.errors:
+ if err == nil {
+ panic("unexpected nil error")
+ }
+ return err
+ case <-waitdone:
+ if logger != nil {
+ logger.Printf("Exiting pid %d.", os.Getpid())
+ }
+ return nil
+ }
+}
+
+// Used for pretty printing addresses.
+func pprintAddr(listeners []net.Listener) []byte {
+ var out bytes.Buffer
+ for i, l := range listeners {
+ if i != 0 {
+ fmt.Fprint(&out, ", ")
+ }
+ fmt.Fprint(&out, l.Addr())
+ }
+ return out.Bytes()
+}
+
+// SetLogger sets logger to be able to grab some useful logs
+func SetLogger(l *log.Logger) {
+ logger = l
+}
diff --git a/vendor/github.com/facebookgo/grace/gracehttp/license b/vendor/github.com/facebookgo/grace/gracehttp/license
new file mode 100644
index 00000000..3aea8753
--- /dev/null
+++ b/vendor/github.com/facebookgo/grace/gracehttp/license
@@ -0,0 +1,30 @@
+BSD License
+
+For grace software
+
+Copyright (c) 2015, Facebook, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name Facebook nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.