mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	Support http service graceful restart (#416)
* support http service graceful restart * fix dependencies
This commit is contained in:
		
							
								
								
									
										26
									
								
								cmd/web.go
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								cmd/web.go
									
									
									
									
									
								
							@@ -31,6 +31,7 @@ import (
 | 
				
			|||||||
	"code.gitea.io/gitea/routers/repo"
 | 
						"code.gitea.io/gitea/routers/repo"
 | 
				
			||||||
	"code.gitea.io/gitea/routers/user"
 | 
						"code.gitea.io/gitea/routers/user"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/facebookgo/grace/gracehttp"
 | 
				
			||||||
	"github.com/go-macaron/binding"
 | 
						"github.com/go-macaron/binding"
 | 
				
			||||||
	"github.com/go-macaron/cache"
 | 
						"github.com/go-macaron/cache"
 | 
				
			||||||
	"github.com/go-macaron/captcha"
 | 
						"github.com/go-macaron/captcha"
 | 
				
			||||||
@@ -615,10 +616,29 @@ func runWeb(ctx *cli.Context) error {
 | 
				
			|||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	switch setting.Protocol {
 | 
						switch setting.Protocol {
 | 
				
			||||||
	case setting.HTTP:
 | 
						case setting.HTTP:
 | 
				
			||||||
		err = http.ListenAndServe(listenAddr, m)
 | 
							err = gracehttp.Serve(&http.Server{
 | 
				
			||||||
 | 
								Addr:    listenAddr,
 | 
				
			||||||
 | 
								Handler: m,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	case setting.HTTPS:
 | 
						case setting.HTTPS:
 | 
				
			||||||
		server := &http.Server{Addr: listenAddr, TLSConfig: &tls.Config{MinVersion: tls.VersionTLS10}, Handler: m}
 | 
							config := &tls.Config{
 | 
				
			||||||
		err = server.ListenAndServeTLS(setting.CertFile, setting.KeyFile)
 | 
								MinVersion: tls.VersionTLS10,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if config.NextProtos == nil {
 | 
				
			||||||
 | 
								config.NextProtos = []string{"http/1.1"}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							config.Certificates = make([]tls.Certificate, 1)
 | 
				
			||||||
 | 
							config.Certificates[0], err = tls.LoadX509KeyPair(setting.CertFile, setting.KeyFile)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal(4, "Failed to load https cert file %s: %v", listenAddr, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							err = gracehttp.Serve(&http.Server{
 | 
				
			||||||
 | 
								Addr:      listenAddr,
 | 
				
			||||||
 | 
								Handler:   m,
 | 
				
			||||||
 | 
								TLSConfig: config,
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
	case setting.FCGI:
 | 
						case setting.FCGI:
 | 
				
			||||||
		err = fcgi.Serve(nil, m)
 | 
							err = fcgi.Serve(nil, m)
 | 
				
			||||||
	case setting.UnixSocket:
 | 
						case setting.UnixSocket:
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										21
									
								
								vendor/github.com/facebookgo/clock/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/facebookgo/clock/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
				
			|||||||
 | 
					The MIT License (MIT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Copyright (c) 2014 Ben Johnson
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
							
								
								
									
										104
									
								
								vendor/github.com/facebookgo/clock/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								vendor/github.com/facebookgo/clock/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
				
			|||||||
 | 
					clock [](https://drone.io/github.com/benbjohnson/clock/latest) [](https://coveralls.io/r/benbjohnson/clock?branch=master) [](https://godoc.org/github.com/benbjohnson/clock) 
 | 
				
			||||||
 | 
					=====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Clock is a small library for mocking time in Go. It provides an interface
 | 
				
			||||||
 | 
					around the standard library's [`time`][time] package so that the application
 | 
				
			||||||
 | 
					can use the realtime clock while tests can use the mock clock.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[time]: http://golang.org/pkg/time/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Realtime Clock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Your application can maintain a `Clock` variable that will allow realtime and
 | 
				
			||||||
 | 
					mock clocks to be interchangable. For example, if you had an `Application` type:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					import "github.com/benbjohnson/clock"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Application struct {
 | 
				
			||||||
 | 
						Clock clock.Clock
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You could initialize it to use the realtime clock like this:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					var app Application
 | 
				
			||||||
 | 
					app.Clock = clock.New()
 | 
				
			||||||
 | 
					...
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Then all timers and time-related functionality should be performed from the
 | 
				
			||||||
 | 
					`Clock` variable.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Mocking time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					In your tests, you will want to use a `Mock` clock:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/benbjohnson/clock"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestApplication_DoSomething(t *testing.T) {
 | 
				
			||||||
 | 
						mock := clock.NewMock()
 | 
				
			||||||
 | 
						app := Application{Clock: mock}
 | 
				
			||||||
 | 
						...
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Now that you've initialized your application to use the mock clock, you can
 | 
				
			||||||
 | 
					adjust the time programmatically. The mock clock always starts from the Unix
 | 
				
			||||||
 | 
					epoch (midnight, Jan 1, 1970 UTC).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Controlling time
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The mock clock provides the same functions that the standard library's `time`
 | 
				
			||||||
 | 
					package provides. For example, to find the current time, you use the `Now()`
 | 
				
			||||||
 | 
					function:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```go
 | 
				
			||||||
 | 
					mock := clock.NewMock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Find the current time.
 | 
				
			||||||
 | 
					mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Move the clock forward.
 | 
				
			||||||
 | 
					mock.Add(2 * time.Hour)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Check the time again. It's 2 hours later!
 | 
				
			||||||
 | 
					mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Timers and Tickers are also controlled by this same mock clock. They will only
 | 
				
			||||||
 | 
					execute when the clock is moved forward:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					mock := clock.NewMock()
 | 
				
			||||||
 | 
					count := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Kick off a timer to increment every 1 mock second.
 | 
				
			||||||
 | 
					go func() {
 | 
				
			||||||
 | 
					    ticker := clock.Ticker(1 * time.Second)
 | 
				
			||||||
 | 
					    for {
 | 
				
			||||||
 | 
					        <-ticker.C
 | 
				
			||||||
 | 
					        count++
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}()
 | 
				
			||||||
 | 
					runtime.Gosched()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Move the clock forward 10 second.
 | 
				
			||||||
 | 
					mock.Add(10 * time.Second)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// This prints 10.
 | 
				
			||||||
 | 
					fmt.Println(count)
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										363
									
								
								vendor/github.com/facebookgo/clock/clock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								vendor/github.com/facebookgo/clock/clock.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,363 @@
 | 
				
			|||||||
 | 
					package clock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Clock represents an interface to the functions in the standard library time
 | 
				
			||||||
 | 
					// package. Two implementations are available in the clock package. The first
 | 
				
			||||||
 | 
					// is a real-time clock which simply wraps the time package's functions. The
 | 
				
			||||||
 | 
					// second is a mock clock which will only make forward progress when
 | 
				
			||||||
 | 
					// programmatically adjusted.
 | 
				
			||||||
 | 
					type Clock interface {
 | 
				
			||||||
 | 
						After(d time.Duration) <-chan time.Time
 | 
				
			||||||
 | 
						AfterFunc(d time.Duration, f func()) *Timer
 | 
				
			||||||
 | 
						Now() time.Time
 | 
				
			||||||
 | 
						Sleep(d time.Duration)
 | 
				
			||||||
 | 
						Tick(d time.Duration) <-chan time.Time
 | 
				
			||||||
 | 
						Ticker(d time.Duration) *Ticker
 | 
				
			||||||
 | 
						Timer(d time.Duration) *Timer
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// New returns an instance of a real-time clock.
 | 
				
			||||||
 | 
					func New() Clock {
 | 
				
			||||||
 | 
						return &clock{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clock implements a real-time clock by simply wrapping the time package functions.
 | 
				
			||||||
 | 
					type clock struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
 | 
				
			||||||
 | 
						return &Timer{timer: time.AfterFunc(d, f)}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) Now() time.Time { return time.Now() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) Ticker(d time.Duration) *Ticker {
 | 
				
			||||||
 | 
						t := time.NewTicker(d)
 | 
				
			||||||
 | 
						return &Ticker{C: t.C, ticker: t}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c *clock) Timer(d time.Duration) *Timer {
 | 
				
			||||||
 | 
						t := time.NewTimer(d)
 | 
				
			||||||
 | 
						return &Timer{C: t.C, timer: t}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Mock represents a mock clock that only moves forward programmically.
 | 
				
			||||||
 | 
					// It can be preferable to a real-time clock when testing time-based functionality.
 | 
				
			||||||
 | 
					type Mock struct {
 | 
				
			||||||
 | 
						mu     sync.Mutex
 | 
				
			||||||
 | 
						now    time.Time   // current time
 | 
				
			||||||
 | 
						timers clockTimers // tickers & timers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						calls      Calls
 | 
				
			||||||
 | 
						waiting    []waiting
 | 
				
			||||||
 | 
						callsMutex sync.Mutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NewMock returns an instance of a mock clock.
 | 
				
			||||||
 | 
					// The current time of the mock clock on initialization is the Unix epoch.
 | 
				
			||||||
 | 
					func NewMock() *Mock {
 | 
				
			||||||
 | 
						return &Mock{now: time.Unix(0, 0)}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add moves the current time of the mock clock forward by the duration.
 | 
				
			||||||
 | 
					// This should only be called from a single goroutine at a time.
 | 
				
			||||||
 | 
					func (m *Mock) Add(d time.Duration) {
 | 
				
			||||||
 | 
						// Calculate the final current time.
 | 
				
			||||||
 | 
						t := m.now.Add(d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Continue to execute timers until there are no more before the new time.
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							if !m.runNextTimer(t) {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ensure that we end with the new time.
 | 
				
			||||||
 | 
						m.mu.Lock()
 | 
				
			||||||
 | 
						m.now = t
 | 
				
			||||||
 | 
						m.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Give a small buffer to make sure the other goroutines get handled.
 | 
				
			||||||
 | 
						gosched()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// runNextTimer executes the next timer in chronological order and moves the
 | 
				
			||||||
 | 
					// current time to the timer's next tick time. The next time is not executed if
 | 
				
			||||||
 | 
					// it's next time if after the max time. Returns true if a timer is executed.
 | 
				
			||||||
 | 
					func (m *Mock) runNextTimer(max time.Time) bool {
 | 
				
			||||||
 | 
						m.mu.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Sort timers by time.
 | 
				
			||||||
 | 
						sort.Sort(m.timers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If we have no more timers then exit.
 | 
				
			||||||
 | 
						if len(m.timers) == 0 {
 | 
				
			||||||
 | 
							m.mu.Unlock()
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Retrieve next timer. Exit if next tick is after new time.
 | 
				
			||||||
 | 
						t := m.timers[0]
 | 
				
			||||||
 | 
						if t.Next().After(max) {
 | 
				
			||||||
 | 
							m.mu.Unlock()
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Move "now" forward and unlock clock.
 | 
				
			||||||
 | 
						m.now = t.Next()
 | 
				
			||||||
 | 
						m.mu.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Execute timer.
 | 
				
			||||||
 | 
						t.Tick(m.now)
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// After waits for the duration to elapse and then sends the current time on the returned channel.
 | 
				
			||||||
 | 
					func (m *Mock) After(d time.Duration) <-chan time.Time {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.After)
 | 
				
			||||||
 | 
						return m.Timer(d).C
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AfterFunc waits for the duration to elapse and then executes a function.
 | 
				
			||||||
 | 
					// A Timer is returned that can be stopped.
 | 
				
			||||||
 | 
					func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.AfterFunc)
 | 
				
			||||||
 | 
						t := m.Timer(d)
 | 
				
			||||||
 | 
						t.C = nil
 | 
				
			||||||
 | 
						t.fn = f
 | 
				
			||||||
 | 
						return t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Now returns the current wall time on the mock clock.
 | 
				
			||||||
 | 
					func (m *Mock) Now() time.Time {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.Now)
 | 
				
			||||||
 | 
						m.mu.Lock()
 | 
				
			||||||
 | 
						defer m.mu.Unlock()
 | 
				
			||||||
 | 
						return m.now
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sleep pauses the goroutine for the given duration on the mock clock.
 | 
				
			||||||
 | 
					// The clock must be moved forward in a separate goroutine.
 | 
				
			||||||
 | 
					func (m *Mock) Sleep(d time.Duration) {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.Sleep)
 | 
				
			||||||
 | 
						<-m.After(d)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Tick is a convenience function for Ticker().
 | 
				
			||||||
 | 
					// It will return a ticker channel that cannot be stopped.
 | 
				
			||||||
 | 
					func (m *Mock) Tick(d time.Duration) <-chan time.Time {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.Tick)
 | 
				
			||||||
 | 
						return m.Ticker(d).C
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ticker creates a new instance of Ticker.
 | 
				
			||||||
 | 
					func (m *Mock) Ticker(d time.Duration) *Ticker {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.Ticker)
 | 
				
			||||||
 | 
						m.mu.Lock()
 | 
				
			||||||
 | 
						defer m.mu.Unlock()
 | 
				
			||||||
 | 
						ch := make(chan time.Time)
 | 
				
			||||||
 | 
						t := &Ticker{
 | 
				
			||||||
 | 
							C:    ch,
 | 
				
			||||||
 | 
							c:    ch,
 | 
				
			||||||
 | 
							mock: m,
 | 
				
			||||||
 | 
							d:    d,
 | 
				
			||||||
 | 
							next: m.now.Add(d),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.timers = append(m.timers, (*internalTicker)(t))
 | 
				
			||||||
 | 
						return t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Timer creates a new instance of Timer.
 | 
				
			||||||
 | 
					func (m *Mock) Timer(d time.Duration) *Timer {
 | 
				
			||||||
 | 
						defer m.inc(&m.calls.Timer)
 | 
				
			||||||
 | 
						m.mu.Lock()
 | 
				
			||||||
 | 
						defer m.mu.Unlock()
 | 
				
			||||||
 | 
						ch := make(chan time.Time)
 | 
				
			||||||
 | 
						t := &Timer{
 | 
				
			||||||
 | 
							C:    ch,
 | 
				
			||||||
 | 
							c:    ch,
 | 
				
			||||||
 | 
							mock: m,
 | 
				
			||||||
 | 
							next: m.now.Add(d),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.timers = append(m.timers, (*internalTimer)(t))
 | 
				
			||||||
 | 
						return t
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *Mock) removeClockTimer(t clockTimer) {
 | 
				
			||||||
 | 
						m.mu.Lock()
 | 
				
			||||||
 | 
						defer m.mu.Unlock()
 | 
				
			||||||
 | 
						for i, timer := range m.timers {
 | 
				
			||||||
 | 
							if timer == t {
 | 
				
			||||||
 | 
								copy(m.timers[i:], m.timers[i+1:])
 | 
				
			||||||
 | 
								m.timers[len(m.timers)-1] = nil
 | 
				
			||||||
 | 
								m.timers = m.timers[:len(m.timers)-1]
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sort.Sort(m.timers)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *Mock) inc(addr *uint32) {
 | 
				
			||||||
 | 
						m.callsMutex.Lock()
 | 
				
			||||||
 | 
						defer m.callsMutex.Unlock()
 | 
				
			||||||
 | 
						*addr++
 | 
				
			||||||
 | 
						var newWaiting []waiting
 | 
				
			||||||
 | 
						for _, w := range m.waiting {
 | 
				
			||||||
 | 
							if m.calls.atLeast(w.expected) {
 | 
				
			||||||
 | 
								close(w.done)
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							newWaiting = append(newWaiting, w)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						m.waiting = newWaiting
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Wait waits for at least the relevant calls before returning. The expected
 | 
				
			||||||
 | 
					// Calls are always over the lifetime of the Mock. Values in the Calls struct
 | 
				
			||||||
 | 
					// are used as the minimum number of calls, this allows you to wait for only
 | 
				
			||||||
 | 
					// the calls you care about.
 | 
				
			||||||
 | 
					func (m *Mock) Wait(s Calls) {
 | 
				
			||||||
 | 
						m.callsMutex.Lock()
 | 
				
			||||||
 | 
						if m.calls.atLeast(s) {
 | 
				
			||||||
 | 
							m.callsMutex.Unlock()
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						done := make(chan struct{})
 | 
				
			||||||
 | 
						m.waiting = append(m.waiting, waiting{expected: s, done: done})
 | 
				
			||||||
 | 
						m.callsMutex.Unlock()
 | 
				
			||||||
 | 
						<-done
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clockTimer represents an object with an associated start time.
 | 
				
			||||||
 | 
					type clockTimer interface {
 | 
				
			||||||
 | 
						Next() time.Time
 | 
				
			||||||
 | 
						Tick(time.Time)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// clockTimers represents a list of sortable timers.
 | 
				
			||||||
 | 
					type clockTimers []clockTimer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a clockTimers) Len() int           { return len(a) }
 | 
				
			||||||
 | 
					func (a clockTimers) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
				
			||||||
 | 
					func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Timer represents a single event.
 | 
				
			||||||
 | 
					// The current time will be sent on C, unless the timer was created by AfterFunc.
 | 
				
			||||||
 | 
					type Timer struct {
 | 
				
			||||||
 | 
						C     <-chan time.Time
 | 
				
			||||||
 | 
						c     chan time.Time
 | 
				
			||||||
 | 
						timer *time.Timer // realtime impl, if set
 | 
				
			||||||
 | 
						next  time.Time   // next tick time
 | 
				
			||||||
 | 
						mock  *Mock       // mock clock, if set
 | 
				
			||||||
 | 
						fn    func()      // AfterFunc function, if set
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stop turns off the ticker.
 | 
				
			||||||
 | 
					func (t *Timer) Stop() {
 | 
				
			||||||
 | 
						if t.timer != nil {
 | 
				
			||||||
 | 
							t.timer.Stop()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.mock.removeClockTimer((*internalTimer)(t))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type internalTimer Timer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *internalTimer) Next() time.Time { return t.next }
 | 
				
			||||||
 | 
					func (t *internalTimer) Tick(now time.Time) {
 | 
				
			||||||
 | 
						if t.fn != nil {
 | 
				
			||||||
 | 
							t.fn()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.c <- now
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.mock.removeClockTimer((*internalTimer)(t))
 | 
				
			||||||
 | 
						gosched()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Ticker holds a channel that receives "ticks" at regular intervals.
 | 
				
			||||||
 | 
					type Ticker struct {
 | 
				
			||||||
 | 
						C      <-chan time.Time
 | 
				
			||||||
 | 
						c      chan time.Time
 | 
				
			||||||
 | 
						ticker *time.Ticker  // realtime impl, if set
 | 
				
			||||||
 | 
						next   time.Time     // next tick time
 | 
				
			||||||
 | 
						mock   *Mock         // mock clock, if set
 | 
				
			||||||
 | 
						d      time.Duration // time between ticks
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stop turns off the ticker.
 | 
				
			||||||
 | 
					func (t *Ticker) Stop() {
 | 
				
			||||||
 | 
						if t.ticker != nil {
 | 
				
			||||||
 | 
							t.ticker.Stop()
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							t.mock.removeClockTimer((*internalTicker)(t))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type internalTicker Ticker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (t *internalTicker) Next() time.Time { return t.next }
 | 
				
			||||||
 | 
					func (t *internalTicker) Tick(now time.Time) {
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case t.c <- now:
 | 
				
			||||||
 | 
						case <-time.After(1 * time.Millisecond):
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						t.next = now.Add(t.d)
 | 
				
			||||||
 | 
						gosched()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sleep momentarily so that other goroutines can process.
 | 
				
			||||||
 | 
					func gosched() { runtime.Gosched() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Calls keeps track of the count of calls for each of the methods on the Clock
 | 
				
			||||||
 | 
					// interface.
 | 
				
			||||||
 | 
					type Calls struct {
 | 
				
			||||||
 | 
						After     uint32
 | 
				
			||||||
 | 
						AfterFunc uint32
 | 
				
			||||||
 | 
						Now       uint32
 | 
				
			||||||
 | 
						Sleep     uint32
 | 
				
			||||||
 | 
						Tick      uint32
 | 
				
			||||||
 | 
						Ticker    uint32
 | 
				
			||||||
 | 
						Timer     uint32
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// atLeast returns true if at least the number of calls in o have been made.
 | 
				
			||||||
 | 
					func (c Calls) atLeast(o Calls) bool {
 | 
				
			||||||
 | 
						if c.After < o.After {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.AfterFunc < o.AfterFunc {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Now < o.Now {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Sleep < o.Sleep {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Tick < o.Tick {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Ticker < o.Ticker {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if c.Timer < o.Timer {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type waiting struct {
 | 
				
			||||||
 | 
						expected Calls
 | 
				
			||||||
 | 
						done     chan struct{}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										186
									
								
								vendor/github.com/facebookgo/grace/gracehttp/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								vendor/github.com/facebookgo/grace/gracehttp/http.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,186 @@
 | 
				
			|||||||
 | 
					// Package gracehttp provides easy to use graceful restart
 | 
				
			||||||
 | 
					// functionality for HTTP server.
 | 
				
			||||||
 | 
					package gracehttp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/signal"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/facebookgo/grace/gracenet"
 | 
				
			||||||
 | 
						"github.com/facebookgo/httpdown"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						verbose    = flag.Bool("gracehttp.log", true, "Enable logging.")
 | 
				
			||||||
 | 
						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 *verbose {
 | 
				
			||||||
 | 
							if didInherit {
 | 
				
			||||||
 | 
								if ppid == 1 {
 | 
				
			||||||
 | 
									log.Printf("Listening on init activated %s", pprintAddr(a.listeners))
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									const msg = "Graceful handoff of %s with new pid %d and old pid %d"
 | 
				
			||||||
 | 
									log.Printf(msg, pprintAddr(a.listeners), os.Getpid(), ppid)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								const msg = "Serving %s with pid %d"
 | 
				
			||||||
 | 
								log.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 *verbose {
 | 
				
			||||||
 | 
								log.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()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										252
									
								
								vendor/github.com/facebookgo/grace/gracenet/net.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								vendor/github.com/facebookgo/grace/gracenet/net.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,252 @@
 | 
				
			|||||||
 | 
					// Package gracenet provides a family of Listen functions that either open a
 | 
				
			||||||
 | 
					// fresh connection or provide an inherited connection from when the process
 | 
				
			||||||
 | 
					// was started. The behave like their counterparts in the net package, but
 | 
				
			||||||
 | 
					// transparently provide support for graceful restarts without dropping
 | 
				
			||||||
 | 
					// connections. This is provided in a systemd socket activation compatible form
 | 
				
			||||||
 | 
					// to allow using socket activation.
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// BUG: Doesn't handle closing of listeners.
 | 
				
			||||||
 | 
					package gracenet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						// Used to indicate a graceful restart in the new process.
 | 
				
			||||||
 | 
						envCountKey       = "LISTEN_FDS"
 | 
				
			||||||
 | 
						envCountKeyPrefix = envCountKey + "="
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// In order to keep the working directory the same as when we started we record
 | 
				
			||||||
 | 
					// it at startup.
 | 
				
			||||||
 | 
					var originalWD, _ = os.Getwd()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Net provides the family of Listen functions and maintains the associated
 | 
				
			||||||
 | 
					// state. Typically you will have only once instance of Net per application.
 | 
				
			||||||
 | 
					type Net struct {
 | 
				
			||||||
 | 
						inherited   []net.Listener
 | 
				
			||||||
 | 
						active      []net.Listener
 | 
				
			||||||
 | 
						mutex       sync.Mutex
 | 
				
			||||||
 | 
						inheritOnce sync.Once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// used in tests to override the default behavior of starting from fd 3.
 | 
				
			||||||
 | 
						fdStart int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n *Net) inherit() error {
 | 
				
			||||||
 | 
						var retErr error
 | 
				
			||||||
 | 
						n.inheritOnce.Do(func() {
 | 
				
			||||||
 | 
							n.mutex.Lock()
 | 
				
			||||||
 | 
							defer n.mutex.Unlock()
 | 
				
			||||||
 | 
							countStr := os.Getenv(envCountKey)
 | 
				
			||||||
 | 
							if countStr == "" {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							count, err := strconv.Atoi(countStr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								retErr = fmt.Errorf("found invalid count value: %s=%s", envCountKey, countStr)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// In tests this may be overridden.
 | 
				
			||||||
 | 
							fdStart := n.fdStart
 | 
				
			||||||
 | 
							if fdStart == 0 {
 | 
				
			||||||
 | 
								// In normal operations if we are inheriting, the listeners will begin at
 | 
				
			||||||
 | 
								// fd 3.
 | 
				
			||||||
 | 
								fdStart = 3
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for i := fdStart; i < fdStart+count; i++ {
 | 
				
			||||||
 | 
								file := os.NewFile(uintptr(i), "listener")
 | 
				
			||||||
 | 
								l, err := net.FileListener(file)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									file.Close()
 | 
				
			||||||
 | 
									retErr = fmt.Errorf("error inheriting socket fd %d: %s", i, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if err := file.Close(); err != nil {
 | 
				
			||||||
 | 
									retErr = fmt.Errorf("error closing inherited socket fd %d: %s", i, err)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								n.inherited = append(n.inherited, l)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return retErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Listen announces on the local network address laddr. The network net must be
 | 
				
			||||||
 | 
					// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
 | 
				
			||||||
 | 
					// returns an inherited net.Listener for the matching network and address, or
 | 
				
			||||||
 | 
					// creates a new one using net.Listen.
 | 
				
			||||||
 | 
					func (n *Net) Listen(nett, laddr string) (net.Listener, error) {
 | 
				
			||||||
 | 
						switch nett {
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							return nil, net.UnknownNetworkError(nett)
 | 
				
			||||||
 | 
						case "tcp", "tcp4", "tcp6":
 | 
				
			||||||
 | 
							addr, err := net.ResolveTCPAddr(nett, laddr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return n.ListenTCP(nett, addr)
 | 
				
			||||||
 | 
						case "unix", "unixpacket", "invalid_unix_net_for_test":
 | 
				
			||||||
 | 
							addr, err := net.ResolveUnixAddr(nett, laddr)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return n.ListenUnix(nett, addr)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListenTCP announces on the local network address laddr. The network net must
 | 
				
			||||||
 | 
					// be: "tcp", "tcp4" or "tcp6". It returns an inherited net.Listener for the
 | 
				
			||||||
 | 
					// matching network and address, or creates a new one using net.ListenTCP.
 | 
				
			||||||
 | 
					func (n *Net) ListenTCP(nett string, laddr *net.TCPAddr) (*net.TCPListener, error) {
 | 
				
			||||||
 | 
						if err := n.inherit(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						n.mutex.Lock()
 | 
				
			||||||
 | 
						defer n.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// look for an inherited listener
 | 
				
			||||||
 | 
						for i, l := range n.inherited {
 | 
				
			||||||
 | 
							if l == nil { // we nil used inherited listeners
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if isSameAddr(l.Addr(), laddr) {
 | 
				
			||||||
 | 
								n.inherited[i] = nil
 | 
				
			||||||
 | 
								n.active = append(n.active, l)
 | 
				
			||||||
 | 
								return l.(*net.TCPListener), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// make a fresh listener
 | 
				
			||||||
 | 
						l, err := net.ListenTCP(nett, laddr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n.active = append(n.active, l)
 | 
				
			||||||
 | 
						return l, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListenUnix announces on the local network address laddr. The network net
 | 
				
			||||||
 | 
					// must be a: "unix" or "unixpacket". It returns an inherited net.Listener for
 | 
				
			||||||
 | 
					// the matching network and address, or creates a new one using net.ListenUnix.
 | 
				
			||||||
 | 
					func (n *Net) ListenUnix(nett string, laddr *net.UnixAddr) (*net.UnixListener, error) {
 | 
				
			||||||
 | 
						if err := n.inherit(); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						n.mutex.Lock()
 | 
				
			||||||
 | 
						defer n.mutex.Unlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// look for an inherited listener
 | 
				
			||||||
 | 
						for i, l := range n.inherited {
 | 
				
			||||||
 | 
							if l == nil { // we nil used inherited listeners
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if isSameAddr(l.Addr(), laddr) {
 | 
				
			||||||
 | 
								n.inherited[i] = nil
 | 
				
			||||||
 | 
								n.active = append(n.active, l)
 | 
				
			||||||
 | 
								return l.(*net.UnixListener), nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// make a fresh listener
 | 
				
			||||||
 | 
						l, err := net.ListenUnix(nett, laddr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						n.active = append(n.active, l)
 | 
				
			||||||
 | 
						return l, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// activeListeners returns a snapshot copy of the active listeners.
 | 
				
			||||||
 | 
					func (n *Net) activeListeners() ([]net.Listener, error) {
 | 
				
			||||||
 | 
						n.mutex.Lock()
 | 
				
			||||||
 | 
						defer n.mutex.Unlock()
 | 
				
			||||||
 | 
						ls := make([]net.Listener, len(n.active))
 | 
				
			||||||
 | 
						copy(ls, n.active)
 | 
				
			||||||
 | 
						return ls, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isSameAddr(a1, a2 net.Addr) bool {
 | 
				
			||||||
 | 
						if a1.Network() != a2.Network() {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						a1s := a1.String()
 | 
				
			||||||
 | 
						a2s := a2.String()
 | 
				
			||||||
 | 
						if a1s == a2s {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// This allows for ipv6 vs ipv4 local addresses to compare as equal. This
 | 
				
			||||||
 | 
						// scenario is common when listening on localhost.
 | 
				
			||||||
 | 
						const ipv6prefix = "[::]"
 | 
				
			||||||
 | 
						a1s = strings.TrimPrefix(a1s, ipv6prefix)
 | 
				
			||||||
 | 
						a2s = strings.TrimPrefix(a2s, ipv6prefix)
 | 
				
			||||||
 | 
						const ipv4prefix = "0.0.0.0"
 | 
				
			||||||
 | 
						a1s = strings.TrimPrefix(a1s, ipv4prefix)
 | 
				
			||||||
 | 
						a2s = strings.TrimPrefix(a2s, ipv4prefix)
 | 
				
			||||||
 | 
						return a1s == a2s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// StartProcess starts a new process passing it the active listeners. It
 | 
				
			||||||
 | 
					// doesn't fork, but starts a new process using the same environment and
 | 
				
			||||||
 | 
					// arguments as when it was originally started. This allows for a newly
 | 
				
			||||||
 | 
					// deployed binary to be started. It returns the pid of the newly started
 | 
				
			||||||
 | 
					// process when successful.
 | 
				
			||||||
 | 
					func (n *Net) StartProcess() (int, error) {
 | 
				
			||||||
 | 
						listeners, err := n.activeListeners()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Extract the fds from the listeners.
 | 
				
			||||||
 | 
						files := make([]*os.File, len(listeners))
 | 
				
			||||||
 | 
						for i, l := range listeners {
 | 
				
			||||||
 | 
							files[i], err = l.(filer).File()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return 0, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer files[i].Close()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Use the original binary location. This works with symlinks such that if
 | 
				
			||||||
 | 
						// the file it points to has been changed we will use the updated symlink.
 | 
				
			||||||
 | 
						argv0, err := exec.LookPath(os.Args[0])
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Pass on the environment and replace the old count key with the new one.
 | 
				
			||||||
 | 
						var env []string
 | 
				
			||||||
 | 
						for _, v := range os.Environ() {
 | 
				
			||||||
 | 
							if !strings.HasPrefix(v, envCountKeyPrefix) {
 | 
				
			||||||
 | 
								env = append(env, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
 | 
				
			||||||
 | 
						process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
 | 
				
			||||||
 | 
							Dir:   originalWD,
 | 
				
			||||||
 | 
							Env:   env,
 | 
				
			||||||
 | 
							Files: allFiles,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return process.Pid, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type filer interface {
 | 
				
			||||||
 | 
						File() (*os.File, error)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								vendor/github.com/facebookgo/grace/license
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/facebookgo/grace/license
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -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.
 | 
				
			||||||
							
								
								
									
										33
									
								
								vendor/github.com/facebookgo/grace/patents
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/facebookgo/grace/patents
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					Additional Grant of Patent Rights Version 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"Software" means the grace software distributed by Facebook, Inc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
 | 
				
			||||||
 | 
					("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
 | 
				
			||||||
 | 
					(subject to the termination provision below) license under any Necessary
 | 
				
			||||||
 | 
					Claims, to make, have made, use, sell, offer to sell, import, and otherwise
 | 
				
			||||||
 | 
					transfer the Software. For avoidance of doubt, no license is granted under
 | 
				
			||||||
 | 
					Facebook’s rights in any patent claims that are infringed by (i) modifications
 | 
				
			||||||
 | 
					to the Software made by you or any third party or (ii) the Software in
 | 
				
			||||||
 | 
					combination with any software or other technology.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The license granted hereunder will terminate, automatically and without notice,
 | 
				
			||||||
 | 
					if you (or any of your subsidiaries, corporate affiliates or agents) initiate
 | 
				
			||||||
 | 
					directly or indirectly, or take a direct financial interest in, any Patent
 | 
				
			||||||
 | 
					Assertion: (i) against Facebook or any of its subsidiaries or corporate
 | 
				
			||||||
 | 
					affiliates, (ii) against any party if such Patent Assertion arises in whole or
 | 
				
			||||||
 | 
					in part from any software, technology, product or service of Facebook or any of
 | 
				
			||||||
 | 
					its subsidiaries or corporate affiliates, or (iii) against any party relating
 | 
				
			||||||
 | 
					to the Software. Notwithstanding the foregoing, if Facebook or any of its
 | 
				
			||||||
 | 
					subsidiaries or corporate affiliates files a lawsuit alleging patent
 | 
				
			||||||
 | 
					infringement against you in the first instance, and you respond by filing a
 | 
				
			||||||
 | 
					patent infringement counterclaim in that lawsuit against that party that is
 | 
				
			||||||
 | 
					unrelated to the Software, the license granted hereunder will not terminate
 | 
				
			||||||
 | 
					under section (i) of this paragraph due to such counterclaim.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A "Necessary Claim" is a claim of a patent owned by Facebook that is
 | 
				
			||||||
 | 
					necessarily infringed by the Software standing alone.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
 | 
				
			||||||
 | 
					or contributory infringement or inducement to infringe any patent, including a
 | 
				
			||||||
 | 
					cross-claim or counterclaim.
 | 
				
			||||||
							
								
								
									
										376
									
								
								vendor/github.com/facebookgo/httpdown/httpdown.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										376
									
								
								vendor/github.com/facebookgo/httpdown/httpdown.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,376 @@
 | 
				
			|||||||
 | 
					// Package httpdown provides http.ConnState enabled graceful termination of
 | 
				
			||||||
 | 
					// http.Server.
 | 
				
			||||||
 | 
					package httpdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/signal"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/facebookgo/clock"
 | 
				
			||||||
 | 
						"github.com/facebookgo/stats"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						defaultStopTimeout = time.Minute
 | 
				
			||||||
 | 
						defaultKillTimeout = time.Minute
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// A Server allows encapsulates the process of accepting new connections and
 | 
				
			||||||
 | 
					// serving them, and gracefully shutting down the listener without dropping
 | 
				
			||||||
 | 
					// active connections.
 | 
				
			||||||
 | 
					type Server interface {
 | 
				
			||||||
 | 
						// Wait waits for the serving loop to finish. This will happen when Stop is
 | 
				
			||||||
 | 
						// called, at which point it returns no error, or if there is an error in the
 | 
				
			||||||
 | 
						// serving loop. You must call Wait after calling Serve or ListenAndServe.
 | 
				
			||||||
 | 
						Wait() error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Stop stops the listener. It will block until all connections have been
 | 
				
			||||||
 | 
						// closed.
 | 
				
			||||||
 | 
						Stop() error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HTTP defines the configuration for serving a http.Server. Multiple calls to
 | 
				
			||||||
 | 
					// Serve or ListenAndServe can be made on the same HTTP instance. The default
 | 
				
			||||||
 | 
					// timeouts of 1 minute each result in a maximum of 2 minutes before a Stop()
 | 
				
			||||||
 | 
					// returns.
 | 
				
			||||||
 | 
					type HTTP struct {
 | 
				
			||||||
 | 
						// StopTimeout is the duration before we begin force closing connections.
 | 
				
			||||||
 | 
						// Defaults to 1 minute.
 | 
				
			||||||
 | 
						StopTimeout time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// KillTimeout is the duration before which we completely give up and abort
 | 
				
			||||||
 | 
						// even though we still have connected clients. This is useful when a large
 | 
				
			||||||
 | 
						// number of client connections exist and closing them can take a long time.
 | 
				
			||||||
 | 
						// Note, this is in addition to the StopTimeout. Defaults to 1 minute.
 | 
				
			||||||
 | 
						KillTimeout time.Duration
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Stats is optional. If provided, it will be used to record various metrics.
 | 
				
			||||||
 | 
						Stats stats.Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Clock allows for testing timing related functionality. Do not specify this
 | 
				
			||||||
 | 
						// in production code.
 | 
				
			||||||
 | 
						Clock clock.Clock
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Serve provides the low-level API which is useful if you're creating your own
 | 
				
			||||||
 | 
					// net.Listener.
 | 
				
			||||||
 | 
					func (h HTTP) Serve(s *http.Server, l net.Listener) Server {
 | 
				
			||||||
 | 
						stopTimeout := h.StopTimeout
 | 
				
			||||||
 | 
						if stopTimeout == 0 {
 | 
				
			||||||
 | 
							stopTimeout = defaultStopTimeout
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						killTimeout := h.KillTimeout
 | 
				
			||||||
 | 
						if killTimeout == 0 {
 | 
				
			||||||
 | 
							killTimeout = defaultKillTimeout
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						klock := h.Clock
 | 
				
			||||||
 | 
						if klock == nil {
 | 
				
			||||||
 | 
							klock = clock.New()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ss := &server{
 | 
				
			||||||
 | 
							stopTimeout:  stopTimeout,
 | 
				
			||||||
 | 
							killTimeout:  killTimeout,
 | 
				
			||||||
 | 
							stats:        h.Stats,
 | 
				
			||||||
 | 
							clock:        klock,
 | 
				
			||||||
 | 
							oldConnState: s.ConnState,
 | 
				
			||||||
 | 
							listener:     l,
 | 
				
			||||||
 | 
							server:       s,
 | 
				
			||||||
 | 
							serveDone:    make(chan struct{}),
 | 
				
			||||||
 | 
							serveErr:     make(chan error, 1),
 | 
				
			||||||
 | 
							new:          make(chan net.Conn),
 | 
				
			||||||
 | 
							active:       make(chan net.Conn),
 | 
				
			||||||
 | 
							idle:         make(chan net.Conn),
 | 
				
			||||||
 | 
							closed:       make(chan net.Conn),
 | 
				
			||||||
 | 
							stop:         make(chan chan struct{}),
 | 
				
			||||||
 | 
							kill:         make(chan chan struct{}),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s.ConnState = ss.connState
 | 
				
			||||||
 | 
						go ss.manage()
 | 
				
			||||||
 | 
						go ss.serve()
 | 
				
			||||||
 | 
						return ss
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListenAndServe returns a Server for the given http.Server. It is equivalent
 | 
				
			||||||
 | 
					// to ListenAndServe from the standard library, but returns immediately.
 | 
				
			||||||
 | 
					// Requests will be accepted in a background goroutine. If the http.Server has
 | 
				
			||||||
 | 
					// a non-nil TLSConfig, a TLS enabled listener will be setup.
 | 
				
			||||||
 | 
					func (h HTTP) ListenAndServe(s *http.Server) (Server, error) {
 | 
				
			||||||
 | 
						addr := s.Addr
 | 
				
			||||||
 | 
						if addr == "" {
 | 
				
			||||||
 | 
							if s.TLSConfig == nil {
 | 
				
			||||||
 | 
								addr = ":http"
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								addr = ":https"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						l, err := net.Listen("tcp", addr)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							stats.BumpSum(h.Stats, "listen.error", 1)
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if s.TLSConfig != nil {
 | 
				
			||||||
 | 
							l = tls.NewListener(l, s.TLSConfig)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return h.Serve(s, l), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// server manages the serving process and allows for gracefully stopping it.
 | 
				
			||||||
 | 
					type server struct {
 | 
				
			||||||
 | 
						stopTimeout time.Duration
 | 
				
			||||||
 | 
						killTimeout time.Duration
 | 
				
			||||||
 | 
						stats       stats.Client
 | 
				
			||||||
 | 
						clock       clock.Clock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						oldConnState func(net.Conn, http.ConnState)
 | 
				
			||||||
 | 
						server       *http.Server
 | 
				
			||||||
 | 
						serveDone    chan struct{}
 | 
				
			||||||
 | 
						serveErr     chan error
 | 
				
			||||||
 | 
						listener     net.Listener
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						new    chan net.Conn
 | 
				
			||||||
 | 
						active chan net.Conn
 | 
				
			||||||
 | 
						idle   chan net.Conn
 | 
				
			||||||
 | 
						closed chan net.Conn
 | 
				
			||||||
 | 
						stop   chan chan struct{}
 | 
				
			||||||
 | 
						kill   chan chan struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stopOnce sync.Once
 | 
				
			||||||
 | 
						stopErr  error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *server) connState(c net.Conn, cs http.ConnState) {
 | 
				
			||||||
 | 
						if s.oldConnState != nil {
 | 
				
			||||||
 | 
							s.oldConnState(c, cs)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch cs {
 | 
				
			||||||
 | 
						case http.StateNew:
 | 
				
			||||||
 | 
							s.new <- c
 | 
				
			||||||
 | 
						case http.StateActive:
 | 
				
			||||||
 | 
							s.active <- c
 | 
				
			||||||
 | 
						case http.StateIdle:
 | 
				
			||||||
 | 
							s.idle <- c
 | 
				
			||||||
 | 
						case http.StateHijacked, http.StateClosed:
 | 
				
			||||||
 | 
							s.closed <- c
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *server) manage() {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							close(s.new)
 | 
				
			||||||
 | 
							close(s.active)
 | 
				
			||||||
 | 
							close(s.idle)
 | 
				
			||||||
 | 
							close(s.closed)
 | 
				
			||||||
 | 
							close(s.stop)
 | 
				
			||||||
 | 
							close(s.kill)
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var stopDone chan struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conns := map[net.Conn]http.ConnState{}
 | 
				
			||||||
 | 
						var countNew, countActive, countIdle float64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// decConn decrements the count associated with the current state of the
 | 
				
			||||||
 | 
						// given connection.
 | 
				
			||||||
 | 
						decConn := func(c net.Conn) {
 | 
				
			||||||
 | 
							switch conns[c] {
 | 
				
			||||||
 | 
							default:
 | 
				
			||||||
 | 
								panic(fmt.Errorf("unknown existing connection: %s", c))
 | 
				
			||||||
 | 
							case http.StateNew:
 | 
				
			||||||
 | 
								countNew--
 | 
				
			||||||
 | 
							case http.StateActive:
 | 
				
			||||||
 | 
								countActive--
 | 
				
			||||||
 | 
							case http.StateIdle:
 | 
				
			||||||
 | 
								countIdle--
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// setup a ticker to report various values every minute. if we don't have a
 | 
				
			||||||
 | 
						// Stats implementation provided, we Stop it so it never ticks.
 | 
				
			||||||
 | 
						statsTicker := s.clock.Ticker(time.Minute)
 | 
				
			||||||
 | 
						if s.stats == nil {
 | 
				
			||||||
 | 
							statsTicker.Stop()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-statsTicker.C:
 | 
				
			||||||
 | 
								// we'll only get here when s.stats is not nil
 | 
				
			||||||
 | 
								s.stats.BumpAvg("http-state.new", countNew)
 | 
				
			||||||
 | 
								s.stats.BumpAvg("http-state.active", countActive)
 | 
				
			||||||
 | 
								s.stats.BumpAvg("http-state.idle", countIdle)
 | 
				
			||||||
 | 
								s.stats.BumpAvg("http-state.total", countNew+countActive+countIdle)
 | 
				
			||||||
 | 
							case c := <-s.new:
 | 
				
			||||||
 | 
								conns[c] = http.StateNew
 | 
				
			||||||
 | 
								countNew++
 | 
				
			||||||
 | 
							case c := <-s.active:
 | 
				
			||||||
 | 
								decConn(c)
 | 
				
			||||||
 | 
								countActive++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								conns[c] = http.StateActive
 | 
				
			||||||
 | 
							case c := <-s.idle:
 | 
				
			||||||
 | 
								decConn(c)
 | 
				
			||||||
 | 
								countIdle++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								conns[c] = http.StateIdle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// if we're already stopping, close it
 | 
				
			||||||
 | 
								if stopDone != nil {
 | 
				
			||||||
 | 
									c.Close()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case c := <-s.closed:
 | 
				
			||||||
 | 
								stats.BumpSum(s.stats, "conn.closed", 1)
 | 
				
			||||||
 | 
								decConn(c)
 | 
				
			||||||
 | 
								delete(conns, c)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// if we're waiting to stop and are all empty, we just closed the last
 | 
				
			||||||
 | 
								// connection and we're done.
 | 
				
			||||||
 | 
								if stopDone != nil && len(conns) == 0 {
 | 
				
			||||||
 | 
									close(stopDone)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							case stopDone = <-s.stop:
 | 
				
			||||||
 | 
								// if we're already all empty, we're already done
 | 
				
			||||||
 | 
								if len(conns) == 0 {
 | 
				
			||||||
 | 
									close(stopDone)
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// close current idle connections right away
 | 
				
			||||||
 | 
								for c, cs := range conns {
 | 
				
			||||||
 | 
									if cs == http.StateIdle {
 | 
				
			||||||
 | 
										c.Close()
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// continue the loop and wait for all the ConnState updates which will
 | 
				
			||||||
 | 
								// eventually close(stopDone) and return from this goroutine.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							case killDone := <-s.kill:
 | 
				
			||||||
 | 
								// force close all connections
 | 
				
			||||||
 | 
								stats.BumpSum(s.stats, "kill.conn.count", float64(len(conns)))
 | 
				
			||||||
 | 
								for c := range conns {
 | 
				
			||||||
 | 
									c.Close()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// don't block the kill.
 | 
				
			||||||
 | 
								close(killDone)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// continue the loop and we wait for all the ConnState updates and will
 | 
				
			||||||
 | 
								// return from this goroutine when we're all done. otherwise we'll try to
 | 
				
			||||||
 | 
								// send those ConnState updates on closed channels.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *server) serve() {
 | 
				
			||||||
 | 
						stats.BumpSum(s.stats, "serve", 1)
 | 
				
			||||||
 | 
						s.serveErr <- s.server.Serve(s.listener)
 | 
				
			||||||
 | 
						close(s.serveDone)
 | 
				
			||||||
 | 
						close(s.serveErr)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *server) Wait() error {
 | 
				
			||||||
 | 
						if err := <-s.serveErr; !isUseOfClosedError(err) {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *server) Stop() error {
 | 
				
			||||||
 | 
						s.stopOnce.Do(func() {
 | 
				
			||||||
 | 
							defer stats.BumpTime(s.stats, "stop.time").End()
 | 
				
			||||||
 | 
							stats.BumpSum(s.stats, "stop", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// first disable keep-alive for new connections
 | 
				
			||||||
 | 
							s.server.SetKeepAlivesEnabled(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// then close the listener so new connections can't connect come thru
 | 
				
			||||||
 | 
							closeErr := s.listener.Close()
 | 
				
			||||||
 | 
							<-s.serveDone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// then trigger the background goroutine to stop and wait for it
 | 
				
			||||||
 | 
							stopDone := make(chan struct{})
 | 
				
			||||||
 | 
							s.stop <- stopDone
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// wait for stop
 | 
				
			||||||
 | 
							select {
 | 
				
			||||||
 | 
							case <-stopDone:
 | 
				
			||||||
 | 
							case <-s.clock.After(s.stopTimeout):
 | 
				
			||||||
 | 
								defer stats.BumpTime(s.stats, "kill.time").End()
 | 
				
			||||||
 | 
								stats.BumpSum(s.stats, "kill", 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// stop timed out, wait for kill
 | 
				
			||||||
 | 
								killDone := make(chan struct{})
 | 
				
			||||||
 | 
								s.kill <- killDone
 | 
				
			||||||
 | 
								select {
 | 
				
			||||||
 | 
								case <-killDone:
 | 
				
			||||||
 | 
								case <-s.clock.After(s.killTimeout):
 | 
				
			||||||
 | 
									// kill timed out, give up
 | 
				
			||||||
 | 
									stats.BumpSum(s.stats, "kill.timeout", 1)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if closeErr != nil && !isUseOfClosedError(closeErr) {
 | 
				
			||||||
 | 
								stats.BumpSum(s.stats, "listener.close.error", 1)
 | 
				
			||||||
 | 
								s.stopErr = closeErr
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return s.stopErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isUseOfClosedError(err error) bool {
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if opErr, ok := err.(*net.OpError); ok {
 | 
				
			||||||
 | 
							err = opErr.Err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err.Error() == "use of closed network connection"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ListenAndServe is a convenience function to serve and wait for a SIGTERM
 | 
				
			||||||
 | 
					// or SIGINT before shutting down.
 | 
				
			||||||
 | 
					func ListenAndServe(s *http.Server, hd *HTTP) error {
 | 
				
			||||||
 | 
						if hd == nil {
 | 
				
			||||||
 | 
							hd = &HTTP{}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						hs, err := hd.ListenAndServe(s)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						waiterr := make(chan error, 1)
 | 
				
			||||||
 | 
						go func() {
 | 
				
			||||||
 | 
							defer close(waiterr)
 | 
				
			||||||
 | 
							waiterr <- hs.Wait()
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						signals := make(chan os.Signal, 10)
 | 
				
			||||||
 | 
						signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						select {
 | 
				
			||||||
 | 
						case err := <-waiterr:
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case <-signals:
 | 
				
			||||||
 | 
							signal.Stop(signals)
 | 
				
			||||||
 | 
							if err := hs.Stop(); err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if err := <-waiterr; err != nil {
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								vendor/github.com/facebookgo/httpdown/license
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/facebookgo/httpdown/license
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					BSD License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For httpdown 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.
 | 
				
			||||||
							
								
								
									
										33
									
								
								vendor/github.com/facebookgo/httpdown/patents
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/facebookgo/httpdown/patents
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					Additional Grant of Patent Rights Version 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"Software" means the httpdown software distributed by Facebook, Inc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
 | 
				
			||||||
 | 
					("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
 | 
				
			||||||
 | 
					(subject to the termination provision below) license under any Necessary
 | 
				
			||||||
 | 
					Claims, to make, have made, use, sell, offer to sell, import, and otherwise
 | 
				
			||||||
 | 
					transfer the Software. For avoidance of doubt, no license is granted under
 | 
				
			||||||
 | 
					Facebook’s rights in any patent claims that are infringed by (i) modifications
 | 
				
			||||||
 | 
					to the Software made by you or any third party or (ii) the Software in
 | 
				
			||||||
 | 
					combination with any software or other technology.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The license granted hereunder will terminate, automatically and without notice,
 | 
				
			||||||
 | 
					if you (or any of your subsidiaries, corporate affiliates or agents) initiate
 | 
				
			||||||
 | 
					directly or indirectly, or take a direct financial interest in, any Patent
 | 
				
			||||||
 | 
					Assertion: (i) against Facebook or any of its subsidiaries or corporate
 | 
				
			||||||
 | 
					affiliates, (ii) against any party if such Patent Assertion arises in whole or
 | 
				
			||||||
 | 
					in part from any software, technology, product or service of Facebook or any of
 | 
				
			||||||
 | 
					its subsidiaries or corporate affiliates, or (iii) against any party relating
 | 
				
			||||||
 | 
					to the Software. Notwithstanding the foregoing, if Facebook or any of its
 | 
				
			||||||
 | 
					subsidiaries or corporate affiliates files a lawsuit alleging patent
 | 
				
			||||||
 | 
					infringement against you in the first instance, and you respond by filing a
 | 
				
			||||||
 | 
					patent infringement counterclaim in that lawsuit against that party that is
 | 
				
			||||||
 | 
					unrelated to the Software, the license granted hereunder will not terminate
 | 
				
			||||||
 | 
					under section (i) of this paragraph due to such counterclaim.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A "Necessary Claim" is a claim of a patent owned by Facebook that is
 | 
				
			||||||
 | 
					necessarily infringed by the Software standing alone.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
 | 
				
			||||||
 | 
					or contributory infringement or inducement to infringe any patent, including a
 | 
				
			||||||
 | 
					cross-claim or counterclaim.
 | 
				
			||||||
							
								
								
									
										41
									
								
								vendor/github.com/facebookgo/httpdown/readme.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								vendor/github.com/facebookgo/httpdown/readme.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
				
			|||||||
 | 
					httpdown [](https://travis-ci.org/facebookgo/httpdown)
 | 
				
			||||||
 | 
					========
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Documentation: https://godoc.org/github.com/facebookgo/httpdown
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Package httpdown provides a library that makes it easy to build a HTTP server
 | 
				
			||||||
 | 
					that can be shutdown gracefully (that is, without dropping any connections).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					If you want graceful restart and not just graceful shutdown, look at the
 | 
				
			||||||
 | 
					[grace](https://github.com/facebookgo/grace) package which uses this package
 | 
				
			||||||
 | 
					underneath but also provides graceful restart.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Usage
 | 
				
			||||||
 | 
					-----
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Demo HTTP Server with graceful termination:
 | 
				
			||||||
 | 
					https://github.com/facebookgo/httpdown/blob/master/httpdown_example/main.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Install the demo application
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        go get github.com/facebookgo/httpdown/httpdown_example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Start it in the first terminal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        httpdown_example
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   This will output something like:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        2014/11/18 21:57:50 serving on http://127.0.0.1:8080/ with pid 17
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. In a second terminal start a slow HTTP request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        curl 'http://localhost:8080/?duration=20s'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. In a third terminal trigger a graceful shutdown (using the pid from your output):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        kill -TERM 17
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This will demonstrate that the slow request was served before the server was
 | 
				
			||||||
 | 
					shutdown. You could also have used `Ctrl-C` instead of `kill` as the example
 | 
				
			||||||
 | 
					application triggers graceful shutdown on TERM or INT signals.
 | 
				
			||||||
							
								
								
									
										35
									
								
								vendor/github.com/facebookgo/stats/aggregation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/facebookgo/stats/aggregation.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					package stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Average returns the average value
 | 
				
			||||||
 | 
					func Average(values []float64) float64 {
 | 
				
			||||||
 | 
						if len(values) == 0 {
 | 
				
			||||||
 | 
							return 0
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var val float64
 | 
				
			||||||
 | 
						for _, point := range values {
 | 
				
			||||||
 | 
							val += point
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return val / float64(len(values))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Sum returns the sum of all the given values
 | 
				
			||||||
 | 
					func Sum(values []float64) float64 {
 | 
				
			||||||
 | 
						var val float64
 | 
				
			||||||
 | 
						for _, point := range values {
 | 
				
			||||||
 | 
							val += point
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return val
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Percentiles returns a map containing the asked for percentiles
 | 
				
			||||||
 | 
					func Percentiles(values []float64, percentiles map[string]float64) map[string]float64 {
 | 
				
			||||||
 | 
						sort.Float64s(values)
 | 
				
			||||||
 | 
						results := map[string]float64{}
 | 
				
			||||||
 | 
						for label, p := range percentiles {
 | 
				
			||||||
 | 
							results[label] = values[int(float64(len(values))*p)]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return results
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										112
									
								
								vendor/github.com/facebookgo/stats/counter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										112
									
								
								vendor/github.com/facebookgo/stats/counter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,112 @@
 | 
				
			|||||||
 | 
					package stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Type is the type of aggregation of apply
 | 
				
			||||||
 | 
					type Type int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						AggregateAvg Type = iota
 | 
				
			||||||
 | 
						AggregateSum
 | 
				
			||||||
 | 
						AggregateHistogram
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var (
 | 
				
			||||||
 | 
						// HistogramPercentiles is used to determine which percentiles to return for
 | 
				
			||||||
 | 
						// SimpleCounter.Aggregate
 | 
				
			||||||
 | 
						HistogramPercentiles = map[string]float64{
 | 
				
			||||||
 | 
							"p50": 0.5,
 | 
				
			||||||
 | 
							"p95": 0.95,
 | 
				
			||||||
 | 
							"p99": 0.99,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// MinSamplesForPercentiles is used by SimpleCounter.Aggregate to determine
 | 
				
			||||||
 | 
						// what the minimum number of samples is required for percentile analysis
 | 
				
			||||||
 | 
						MinSamplesForPercentiles = 10
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Aggregates can be used to merge counters together. This is not goroutine safe
 | 
				
			||||||
 | 
					type Aggregates map[string]Counter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Add adds the counter for aggregation. This is not goroutine safe
 | 
				
			||||||
 | 
					func (a Aggregates) Add(c Counter) error {
 | 
				
			||||||
 | 
						key := c.FullKey()
 | 
				
			||||||
 | 
						if counter, ok := a[key]; ok {
 | 
				
			||||||
 | 
							if counter.GetType() != c.GetType() {
 | 
				
			||||||
 | 
								return fmt.Errorf("stats: mismatched aggregation type for: %s", key)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							counter.AddValues(c.GetValues()...)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							a[key] = c
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Counter is the interface used by Aggregates to merge counters together
 | 
				
			||||||
 | 
					type Counter interface {
 | 
				
			||||||
 | 
						// FullKey is used to uniquely identify the counter
 | 
				
			||||||
 | 
						FullKey() string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// AddValues adds values for aggregation
 | 
				
			||||||
 | 
						AddValues(...float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetValues returns the values for aggregation
 | 
				
			||||||
 | 
						GetValues() []float64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// GetType returns the type of aggregation to apply
 | 
				
			||||||
 | 
						GetType() Type
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SimpleCounter is a basic implementation of the Counter interface
 | 
				
			||||||
 | 
					type SimpleCounter struct {
 | 
				
			||||||
 | 
						Key    string
 | 
				
			||||||
 | 
						Values []float64
 | 
				
			||||||
 | 
						Type   Type
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FullKey is part of the Counter interace
 | 
				
			||||||
 | 
					func (s *SimpleCounter) FullKey() string {
 | 
				
			||||||
 | 
						return s.Key
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetValues is part of the Counter interface
 | 
				
			||||||
 | 
					func (s *SimpleCounter) GetValues() []float64 {
 | 
				
			||||||
 | 
						return s.Values
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AddValues is part of the Counter interface
 | 
				
			||||||
 | 
					func (s *SimpleCounter) AddValues(vs ...float64) {
 | 
				
			||||||
 | 
						s.Values = append(s.Values, vs...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetType is part of the Counter interface
 | 
				
			||||||
 | 
					func (s *SimpleCounter) GetType() Type {
 | 
				
			||||||
 | 
						return s.Type
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Aggregate aggregates the provided values appropriately, returning a map
 | 
				
			||||||
 | 
					// from key to value. If AggregateHistogram is specified, the map will contain
 | 
				
			||||||
 | 
					// the relevant percentiles as specified by HistogramPercentiles
 | 
				
			||||||
 | 
					func (s *SimpleCounter) Aggregate() map[string]float64 {
 | 
				
			||||||
 | 
						switch s.Type {
 | 
				
			||||||
 | 
						case AggregateAvg:
 | 
				
			||||||
 | 
							return map[string]float64{
 | 
				
			||||||
 | 
								s.Key: Average(s.Values),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case AggregateSum:
 | 
				
			||||||
 | 
							return map[string]float64{
 | 
				
			||||||
 | 
								s.Key: Sum(s.Values),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case AggregateHistogram:
 | 
				
			||||||
 | 
							histogram := map[string]float64{
 | 
				
			||||||
 | 
								s.Key: Average(s.Values),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(s.Values) > MinSamplesForPercentiles {
 | 
				
			||||||
 | 
								for k, v := range Percentiles(s.Values, HistogramPercentiles) {
 | 
				
			||||||
 | 
									histogram[fmt.Sprintf("%s.%s", s.Key, k)] = v
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return histogram
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						panic("stats: unsupported aggregation type")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								vendor/github.com/facebookgo/stats/license
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								vendor/github.com/facebookgo/stats/license
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					BSD License
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For stats 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.
 | 
				
			||||||
							
								
								
									
										33
									
								
								vendor/github.com/facebookgo/stats/patents
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								vendor/github.com/facebookgo/stats/patents
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					Additional Grant of Patent Rights Version 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					"Software" means the stats software distributed by Facebook, Inc.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
 | 
				
			||||||
 | 
					("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
 | 
				
			||||||
 | 
					(subject to the termination provision below) license under any Necessary
 | 
				
			||||||
 | 
					Claims, to make, have made, use, sell, offer to sell, import, and otherwise
 | 
				
			||||||
 | 
					transfer the Software. For avoidance of doubt, no license is granted under
 | 
				
			||||||
 | 
					Facebook’s rights in any patent claims that are infringed by (i) modifications
 | 
				
			||||||
 | 
					to the Software made by you or any third party or (ii) the Software in
 | 
				
			||||||
 | 
					combination with any software or other technology.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The license granted hereunder will terminate, automatically and without notice,
 | 
				
			||||||
 | 
					if you (or any of your subsidiaries, corporate affiliates or agents) initiate
 | 
				
			||||||
 | 
					directly or indirectly, or take a direct financial interest in, any Patent
 | 
				
			||||||
 | 
					Assertion: (i) against Facebook or any of its subsidiaries or corporate
 | 
				
			||||||
 | 
					affiliates, (ii) against any party if such Patent Assertion arises in whole or
 | 
				
			||||||
 | 
					in part from any software, technology, product or service of Facebook or any of
 | 
				
			||||||
 | 
					its subsidiaries or corporate affiliates, or (iii) against any party relating
 | 
				
			||||||
 | 
					to the Software. Notwithstanding the foregoing, if Facebook or any of its
 | 
				
			||||||
 | 
					subsidiaries or corporate affiliates files a lawsuit alleging patent
 | 
				
			||||||
 | 
					infringement against you in the first instance, and you respond by filing a
 | 
				
			||||||
 | 
					patent infringement counterclaim in that lawsuit against that party that is
 | 
				
			||||||
 | 
					unrelated to the Software, the license granted hereunder will not terminate
 | 
				
			||||||
 | 
					under section (i) of this paragraph due to such counterclaim.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A "Necessary Claim" is a claim of a patent owned by Facebook that is
 | 
				
			||||||
 | 
					necessarily infringed by the Software standing alone.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
 | 
				
			||||||
 | 
					or contributory infringement or inducement to infringe any patent, including a
 | 
				
			||||||
 | 
					cross-claim or counterclaim.
 | 
				
			||||||
							
								
								
									
										4
									
								
								vendor/github.com/facebookgo/stats/readme.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/facebookgo/stats/readme.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					stats [](https://travis-ci.org/facebookgo/stats)
 | 
				
			||||||
 | 
					=====
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Documentation: https://godoc.org/github.com/facebookgo/stats
 | 
				
			||||||
							
								
								
									
										166
									
								
								vendor/github.com/facebookgo/stats/stats.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								vendor/github.com/facebookgo/stats/stats.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
				
			|||||||
 | 
					// Package stats defines a lightweight interface for collecting statistics. It
 | 
				
			||||||
 | 
					// doesn't provide an implementation, just the shared interface.
 | 
				
			||||||
 | 
					package stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Client provides methods to collection statistics.
 | 
				
			||||||
 | 
					type Client interface {
 | 
				
			||||||
 | 
						// BumpAvg bumps the average for the given key.
 | 
				
			||||||
 | 
						BumpAvg(key string, val float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BumpSum bumps the sum for the given key.
 | 
				
			||||||
 | 
						BumpSum(key string, val float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BumpHistogram bumps the histogram for the given key.
 | 
				
			||||||
 | 
						BumpHistogram(key string, val float64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// BumpTime is a special version of BumpHistogram which is specialized for
 | 
				
			||||||
 | 
						// timers. Calling it starts the timer, and it returns a value on which End()
 | 
				
			||||||
 | 
						// can be called to indicate finishing the timer. A convenient way of
 | 
				
			||||||
 | 
						// recording the duration of a function is calling it like such at the top of
 | 
				
			||||||
 | 
						// the function:
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						//     defer s.BumpTime("my.function").End()
 | 
				
			||||||
 | 
						BumpTime(key string) interface {
 | 
				
			||||||
 | 
							End()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PrefixClient adds multiple keys for the same value, with each prefix
 | 
				
			||||||
 | 
					// added to the key and calls the underlying client.
 | 
				
			||||||
 | 
					func PrefixClient(prefixes []string, client Client) Client {
 | 
				
			||||||
 | 
						return &prefixClient{
 | 
				
			||||||
 | 
							Prefixes: prefixes,
 | 
				
			||||||
 | 
							Client:   client,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type prefixClient struct {
 | 
				
			||||||
 | 
						Prefixes []string
 | 
				
			||||||
 | 
						Client   Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *prefixClient) BumpAvg(key string, val float64) {
 | 
				
			||||||
 | 
						for _, prefix := range p.Prefixes {
 | 
				
			||||||
 | 
							p.Client.BumpAvg(prefix+key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *prefixClient) BumpSum(key string, val float64) {
 | 
				
			||||||
 | 
						for _, prefix := range p.Prefixes {
 | 
				
			||||||
 | 
							p.Client.BumpSum(prefix+key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *prefixClient) BumpHistogram(key string, val float64) {
 | 
				
			||||||
 | 
						for _, prefix := range p.Prefixes {
 | 
				
			||||||
 | 
							p.Client.BumpHistogram(prefix+key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (p *prefixClient) BumpTime(key string) interface {
 | 
				
			||||||
 | 
						End()
 | 
				
			||||||
 | 
					} {
 | 
				
			||||||
 | 
						var m multiEnder
 | 
				
			||||||
 | 
						for _, prefix := range p.Prefixes {
 | 
				
			||||||
 | 
							m = append(m, p.Client.BumpTime(prefix+key))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return m
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// multiEnder combines many enders together.
 | 
				
			||||||
 | 
					type multiEnder []interface {
 | 
				
			||||||
 | 
						End()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m multiEnder) End() {
 | 
				
			||||||
 | 
						for _, e := range m {
 | 
				
			||||||
 | 
							e.End()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HookClient is useful for testing. It provides optional hooks for each
 | 
				
			||||||
 | 
					// expected method in the interface, which if provided will be called. If a
 | 
				
			||||||
 | 
					// hook is not provided, it will be ignored.
 | 
				
			||||||
 | 
					type HookClient struct {
 | 
				
			||||||
 | 
						BumpAvgHook       func(key string, val float64)
 | 
				
			||||||
 | 
						BumpSumHook       func(key string, val float64)
 | 
				
			||||||
 | 
						BumpHistogramHook func(key string, val float64)
 | 
				
			||||||
 | 
						BumpTimeHook      func(key string) interface {
 | 
				
			||||||
 | 
							End()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpAvg will call BumpAvgHook if defined.
 | 
				
			||||||
 | 
					func (c *HookClient) BumpAvg(key string, val float64) {
 | 
				
			||||||
 | 
						if c.BumpAvgHook != nil {
 | 
				
			||||||
 | 
							c.BumpAvgHook(key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpSum will call BumpSumHook if defined.
 | 
				
			||||||
 | 
					func (c *HookClient) BumpSum(key string, val float64) {
 | 
				
			||||||
 | 
						if c.BumpSumHook != nil {
 | 
				
			||||||
 | 
							c.BumpSumHook(key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpHistogram will call BumpHistogramHook if defined.
 | 
				
			||||||
 | 
					func (c *HookClient) BumpHistogram(key string, val float64) {
 | 
				
			||||||
 | 
						if c.BumpHistogramHook != nil {
 | 
				
			||||||
 | 
							c.BumpHistogramHook(key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpTime will call BumpTimeHook if defined.
 | 
				
			||||||
 | 
					func (c *HookClient) BumpTime(key string) interface {
 | 
				
			||||||
 | 
						End()
 | 
				
			||||||
 | 
					} {
 | 
				
			||||||
 | 
						if c.BumpTimeHook != nil {
 | 
				
			||||||
 | 
							return c.BumpTimeHook(key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NoOpEnd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type noOpEnd struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (n noOpEnd) End() {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NoOpEnd provides a dummy value for use in tests as valid return value for
 | 
				
			||||||
 | 
					// BumpTime().
 | 
				
			||||||
 | 
					var NoOpEnd = noOpEnd{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpAvg calls BumpAvg on the Client if it isn't nil. This is useful when a
 | 
				
			||||||
 | 
					// component has an optional stats.Client.
 | 
				
			||||||
 | 
					func BumpAvg(c Client, key string, val float64) {
 | 
				
			||||||
 | 
						if c != nil {
 | 
				
			||||||
 | 
							c.BumpAvg(key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpSum calls BumpSum on the Client if it isn't nil. This is useful when a
 | 
				
			||||||
 | 
					// component has an optional stats.Client.
 | 
				
			||||||
 | 
					func BumpSum(c Client, key string, val float64) {
 | 
				
			||||||
 | 
						if c != nil {
 | 
				
			||||||
 | 
							c.BumpSum(key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpHistogram calls BumpHistogram on the Client if it isn't nil. This is
 | 
				
			||||||
 | 
					// useful when a component has an optional stats.Client.
 | 
				
			||||||
 | 
					func BumpHistogram(c Client, key string, val float64) {
 | 
				
			||||||
 | 
						if c != nil {
 | 
				
			||||||
 | 
							c.BumpHistogram(key, val)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// BumpTime calls BumpTime on the Client if it isn't nil. If the Client is nil
 | 
				
			||||||
 | 
					// it still returns a valid return value which will be a no-op. This is useful
 | 
				
			||||||
 | 
					// when a component has an optional stats.Client.
 | 
				
			||||||
 | 
					func BumpTime(c Client, key string) interface {
 | 
				
			||||||
 | 
						End()
 | 
				
			||||||
 | 
					} {
 | 
				
			||||||
 | 
						if c != nil {
 | 
				
			||||||
 | 
							return c.BumpTime(key)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return NoOpEnd
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								vendor/github.com/facebookgo/stats/stopper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/facebookgo/stats/stopper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package stats
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Stopper calls Client.BumpSum and Client.BumpHistogram when End'ed
 | 
				
			||||||
 | 
					type Stopper struct {
 | 
				
			||||||
 | 
						Key    string
 | 
				
			||||||
 | 
						Start  time.Time
 | 
				
			||||||
 | 
						Client Client
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// End the Stopper
 | 
				
			||||||
 | 
					func (s *Stopper) End() {
 | 
				
			||||||
 | 
						since := time.Since(s.Start).Seconds() * 1000.0
 | 
				
			||||||
 | 
						s.Client.BumpSum(s.Key+".total", since)
 | 
				
			||||||
 | 
						s.Client.BumpHistogram(s.Key, since)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								vendor/vendor.json
									
									
									
									
										vendored
									
									
								
							@@ -92,6 +92,36 @@
 | 
				
			|||||||
			"revision": "57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2",
 | 
								"revision": "57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2",
 | 
				
			||||||
			"revisionTime": "2015-12-24T04:54:52Z"
 | 
								"revisionTime": "2015-12-24T04:54:52Z"
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "imR2wF388/0fBU6RRWx8RvTi8Q8=",
 | 
				
			||||||
 | 
								"path": "github.com/facebookgo/clock",
 | 
				
			||||||
 | 
								"revision": "600d898af40aa09a7a93ecb9265d87b0504b6f03",
 | 
				
			||||||
 | 
								"revisionTime": "2015-04-10T01:09:13Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "j2O1k8Kvw6btFkJKt45nQQuAHM0=",
 | 
				
			||||||
 | 
								"path": "github.com/facebookgo/grace/gracehttp",
 | 
				
			||||||
 | 
								"revision": "5729e484473f52048578af1b80d0008c7024089b",
 | 
				
			||||||
 | 
								"revisionTime": "2016-09-26T23:17:15Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "nj3ykV8Em7WOfvHNIo/XNTT9LOM=",
 | 
				
			||||||
 | 
								"path": "github.com/facebookgo/grace/gracenet",
 | 
				
			||||||
 | 
								"revision": "5729e484473f52048578af1b80d0008c7024089b",
 | 
				
			||||||
 | 
								"revisionTime": "2016-09-26T23:17:15Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "8SgB/r3Cf+OvXmg8bIPnlEq03Bs=",
 | 
				
			||||||
 | 
								"path": "github.com/facebookgo/httpdown",
 | 
				
			||||||
 | 
								"revision": "a3b1354551a26449fbe05f5d855937f6e7acbd71",
 | 
				
			||||||
 | 
								"revisionTime": "2016-03-23T22:10:27Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"checksumSHA1": "SzXvrWxavFo8HD/2hnbdu17C3VE=",
 | 
				
			||||||
 | 
								"path": "github.com/facebookgo/stats",
 | 
				
			||||||
 | 
								"revision": "1b76add642e42c6ffba7211ad7b3939ce654526e",
 | 
				
			||||||
 | 
								"revisionTime": "2015-10-06T22:16:25Z"
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			"checksumSHA1": "qTJizMr1DBhDTZiRNmC+khEClz8=",
 | 
								"checksumSHA1": "qTJizMr1DBhDTZiRNmC+khEClz8=",
 | 
				
			||||||
			"path": "github.com/go-macaron/bindata",
 | 
								"path": "github.com/go-macaron/bindata",
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user