vendor manners - for real this time
sorry everyone.
Juan Batiz-Benet committed
Nov 15, 2014 at 22:24 UTC
5a372f6996cd824634cc156d29d2db09c05d9dba
6 files changed
+289
Godeps/_workspace/src/github.com/braintree/manners/LICENSE
new
+19
@@ -0,0 +1,19 @@
1
+Copyright (c) 2014 Braintree, a division of PayPal, Inc.
2
+
3
+Permission is hereby granted, free of charge, to any person obtaining a copy
4
+of this software and associated documentation files (the "Software"), to deal
5
+in the Software without restriction, including without limitation the rights
6
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+copies of the Software, and to permit persons to whom the Software is
8
+furnished to do so, subject to the following conditions:
9
+
10
+The above copyright notice and this permission notice shall be included in
11
+all copies or substantial portions of the Software.
12
+
13
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+THE SOFTWARE.
Godeps/_workspace/src/github.com/braintree/manners/README.md
new
+33
@@ -0,0 +1,33 @@
1
+# Manners
2
+
3
+A *polite* webserver for Go.
4
+
5
+Manners allows you to shut your Go webserver down gracefully, without dropping any requests. It can act as a drop-in replacement for the standard library's http.ListenAndServe function:
6
+
7
+```go
8
+func main() {
9
+ handler := MyHTTPHandler()
10
+ server := manners.NewServer()
11
+ server.ListenAndServe(":7000", handler)
12
+}
13
+```
14
+
15
+Then, when you want to shut the server down:
16
+
17
+```go
18
+server.Shutdown <- true
19
+```
20
+
21
+(Note that this does not block until all the requests are finished. Rather, the call to server.ListenAndServe will stop blocking when all the requests are finished.)
22
+
23
+Manners ensures that all requests are served by incrementing a WaitGroup when a request comes in and decrementing it when the request finishes.
24
+
25
+If your request handler spawns Goroutines that are not guaranteed to finish with the request, you can ensure they are also completed with the `StartRoutine` and `FinishRoutine` functions on the server.
26
+
27
+### Compatability
28
+
29
+Manners 0.3.0 and above uses standard library functionality introduced in Go 1.3.
30
+
31
+### Installation
32
+
33
+`go get github.com/braintree/manners`
Godeps/_workspace/src/github.com/braintree/manners/helper_test.go
new
+34
@@ -0,0 +1,34 @@
1
+package manners
2
+
3
+import (
4
+ "net/http"
5
+ "time"
6
+)
7
+
8
+// A response handler that blocks until it receives a signal; simulates an
9
+// arbitrarily long web request. The "ready" channel is to prevent a race
10
+// condition in the test where the test moves on before the server is ready
11
+// to handle the request.
12
+func newBlockingHandler(ready, done chan bool) *blockingHandler {
13
+ return &blockingHandler{ready, done}
14
+}
15
+
16
+type blockingHandler struct {
17
+ ready chan bool
18
+ done chan bool
19
+}
20
+
21
+func (h *blockingHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
22
+ h.ready <- true
23
+ time.Sleep(1e2)
24
+ h.done <- true
25
+}
26
+
27
+// A response handler that does nothing.
28
+func newTestHandler() testHandler {
29
+ return testHandler{}
30
+}
31
+
32
+type testHandler struct{}
33
+
34
+func (h testHandler) ServeHTTP(resp http.ResponseWriter, req *http.Request) {}
Godeps/_workspace/src/github.com/braintree/manners/listener.go
new
+49
@@ -0,0 +1,49 @@
1
+package manners
2
+
3
+import (
4
+ "net"
5
+ "sync"
6
+)
7
+
8
+func NewListener(l net.Listener, s *GracefulServer) *GracefulListener {
9
+ return &GracefulListener{l, true, s, sync.RWMutex{}}
10
+}
11
+
12
+// A GracefulListener differs from a standard net.Listener in one way: if
13
+// Accept() is called after it is gracefully closed, it returns a
14
+// listenerAlreadyClosed error. The GracefulServer will ignore this
15
+// error.
16
+type GracefulListener struct {
17
+ net.Listener
18
+ open bool
19
+ server *GracefulServer
20
+ rw sync.RWMutex
21
+}
22
+
23
+func (l *GracefulListener) Accept() (net.Conn, error) {
24
+ conn, err := l.Listener.Accept()
25
+ if err != nil {
26
+ l.rw.RLock()
27
+ defer l.rw.RUnlock()
28
+ if !l.open {
29
+ err = listenerAlreadyClosed{err}
30
+ }
31
+ return nil, err
32
+ }
33
+ return conn, nil
34
+}
35
+
36
+func (l *GracefulListener) Close() error {
37
+ l.rw.Lock()
38
+ defer l.rw.Unlock()
39
+ if !l.open {
40
+ return nil
41
+ }
42
+ l.open = false
43
+ err := l.Listener.Close()
44
+ return err
45
+}
46
+
47
+type listenerAlreadyClosed struct {
48
+ error
49
+}
Godeps/_workspace/src/github.com/braintree/manners/server.go
new
+83
@@ -0,0 +1,83 @@
1
+package manners
2
+
3
+import (
4
+ "net"
5
+ "net/http"
6
+ "sync"
7
+)
8
+
9
+// Creates a new GracefulServer. The server will begin shutting down when
10
+// a value is passed to the Shutdown channel.
11
+func NewServer() *GracefulServer {
12
+ return &GracefulServer{
13
+ Shutdown: make(chan bool),
14
+ }
15
+}
16
+
17
+// A GracefulServer maintains a WaitGroup that counts how many in-flight
18
+// requests the server is handling. When it receives a shutdown signal,
19
+// it stops accepting new requests but does not actually shut down until
20
+// all in-flight requests terminate.
21
+type GracefulServer struct {
22
+ Shutdown chan bool
23
+ wg sync.WaitGroup
24
+ shutdownHandler func()
25
+ InnerServer http.Server
26
+}
27
+
28
+// A helper function that emulates the functionality of http.ListenAndServe.
29
+func (s *GracefulServer) ListenAndServe(addr string, handler http.Handler) error {
30
+ oldListener, err := net.Listen("tcp", addr)
31
+ if err != nil {
32
+ return err
33
+ }
34
+
35
+ listener := NewListener(oldListener, s)
36
+ err = s.Serve(listener, handler)
37
+ return err
38
+}
39
+
40
+// Similar to http.Serve. The listener passed must wrap a GracefulListener.
41
+func (s *GracefulServer) Serve(listener net.Listener, handler http.Handler) error {
42
+ s.shutdownHandler = func() { listener.Close() }
43
+ s.listenForShutdown()
44
+ s.InnerServer.Handler = handler
45
+ s.InnerServer.ConnState = func(conn net.Conn, newState http.ConnState) {
46
+ switch newState {
47
+ case http.StateNew:
48
+ s.StartRoutine()
49
+ case http.StateClosed, http.StateHijacked:
50
+ s.FinishRoutine()
51
+ }
52
+ }
53
+ err := s.InnerServer.Serve(listener)
54
+
55
+ // This block is reached when the server has received a shut down command.
56
+ if err == nil {
57
+ s.wg.Wait()
58
+ return nil
59
+ } else if _, ok := err.(listenerAlreadyClosed); ok {
60
+ s.wg.Wait()
61
+ return nil
62
+ }
63
+ return err
64
+}
65
+
66
+// Increments the server's WaitGroup. Use this if a web request starts more
67
+// goroutines and these goroutines are not guaranteed to finish before the
68
+// request.
69
+func (s *GracefulServer) StartRoutine() {
70
+ s.wg.Add(1)
71
+}
72
+
73
+// Decrement the server's WaitGroup. Used this to complement StartRoutine().
74
+func (s *GracefulServer) FinishRoutine() {
75
+ s.wg.Done()
76
+}
77
+
78
+func (s *GracefulServer) listenForShutdown() {
79
+ go func() {
80
+ <-s.Shutdown
81
+ s.shutdownHandler()
82
+ }()
83
+}
Godeps/_workspace/src/github.com/braintree/manners/server_test.go
new
+71
@@ -0,0 +1,71 @@
1
+package manners
2
+
3
+import (
4
+ "net/http"
5
+ "testing"
6
+)
7
+
8
+// Tests that the server allows in-flight requests to complete before shutting
9
+// down.
10
+func TestGracefulness(t *testing.T) {
11
+ ready := make(chan bool)
12
+ done := make(chan bool)
13
+
14
+ exited := false
15
+
16
+ handler := newBlockingHandler(ready, done)
17
+ server := NewServer()
18
+
19
+ go func() {
20
+ err := server.ListenAndServe(":7000", handler)
21
+ if err != nil {
22
+ t.Error(err)
23
+ }
24
+
25
+ exited = true
26
+ }()
27
+
28
+ go func() {
29
+ _, err := http.Get("http://localhost:7000")
30
+ if err != nil {
31
+ t.Error(err)
32
+ }
33
+ }()
34
+
35
+ // This will block until the server is inside the handler function.
36
+ <-ready
37
+
38
+ server.Shutdown <- true
39
+ <-done
40
+
41
+ if exited {
42
+ t.Fatal("The request did not complete before server exited")
43
+ } else {
44
+ // The handler is being allowed to run to completion; test passes.
45
+ }
46
+}
47
+
48
+// Tests that the server begins to shut down when told to and does not accept
49
+// new requests
50
+func TestShutdown(t *testing.T) {
51
+ handler := newTestHandler()
52
+ server := NewServer()
53
+ exited := make(chan bool)
54
+
55
+ go func() {
56
+ err := server.ListenAndServe(":7100", handler)
57
+ if err != nil {
58
+ t.Error(err)
59
+ }
60
+ exited <- true
61
+ }()
62
+
63
+ server.Shutdown <- true
64
+
65
+ <-exited
66
+ _, err := http.Get("http://localhost:7100")
67
+
68
+ if err == nil {
69
+ t.Fatal("Did not receive an error when trying to connect to server.")
70
+ }
71
+}