summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/braintree/manners
diff options
context:
space:
mode:
authorChristopher Speller <crspeller@gmail.com>2016-05-12 23:56:07 -0400
committerChristopher Speller <crspeller@gmail.com>2016-05-12 23:56:07 -0400
commit38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8 (patch)
treea4fde09672192b97d453ad605b030bd5a10c5a45 /vendor/github.com/braintree/manners
parent84d2482ddbff9564c9ad75b2d30af66e3ddfd44d (diff)
downloadchat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.tar.gz
chat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.tar.bz2
chat-38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8.zip
Moving to glide
Diffstat (limited to 'vendor/github.com/braintree/manners')
-rw-r--r--vendor/github.com/braintree/manners/helpers_test.go119
-rw-r--r--vendor/github.com/braintree/manners/server_test.go289
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/certs.go29
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/conn.go13
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/listener.go34
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/temp_file.go27
-rw-r--r--vendor/github.com/braintree/manners/test_helpers/wait_group.go33
-rw-r--r--vendor/github.com/braintree/manners/transition_test.go54
8 files changed, 598 insertions, 0 deletions
diff --git a/vendor/github.com/braintree/manners/helpers_test.go b/vendor/github.com/braintree/manners/helpers_test.go
new file mode 100644
index 000000000..3c11a081d
--- /dev/null
+++ b/vendor/github.com/braintree/manners/helpers_test.go
@@ -0,0 +1,119 @@
+package manners
+
+import (
+ "bufio"
+ "crypto/tls"
+ "io/ioutil"
+ "net"
+ "net/http"
+ "testing"
+)
+
+// a simple step-controllable http client
+type client struct {
+ tls bool
+ addr net.Addr
+ connected chan error
+ sendrequest chan bool
+ response chan *rawResponse
+ closed chan bool
+}
+
+type rawResponse struct {
+ body []string
+ err error
+}
+
+func (c *client) Run() {
+ go func() {
+ var err error
+ conn, err := net.Dial(c.addr.Network(), c.addr.String())
+ if err != nil {
+ c.connected <- err
+ return
+ }
+ if c.tls {
+ conn = tls.Client(conn, &tls.Config{InsecureSkipVerify: true})
+ }
+ c.connected <- nil
+ for <-c.sendrequest {
+ _, err = conn.Write([]byte("GET / HTTP/1.1\nHost: localhost:8000\n\n"))
+ if err != nil {
+ c.response <- &rawResponse{err: err}
+ }
+ // Read response; no content
+ scanner := bufio.NewScanner(conn)
+ var lines []string
+ for scanner.Scan() {
+ // our null handler doesn't send a body, so we know the request is
+ // done when we reach the blank line after the headers
+ line := scanner.Text()
+ if line == "" {
+ break
+ }
+ lines = append(lines, line)
+ }
+ c.response <- &rawResponse{lines, scanner.Err()}
+ }
+ conn.Close()
+ ioutil.ReadAll(conn)
+ c.closed <- true
+ }()
+}
+
+func newClient(addr net.Addr, tls bool) *client {
+ return &client{
+ addr: addr,
+ tls: tls,
+ connected: make(chan error),
+ sendrequest: make(chan bool),
+ response: make(chan *rawResponse),
+ closed: make(chan bool),
+ }
+}
+
+// a handler that returns 200 ok with no body
+var nullHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
+
+func startGenericServer(t *testing.T, server *GracefulServer, statechanged chan http.ConnState, runner func() error) (l net.Listener, errc chan error) {
+ server.Addr = "localhost:0"
+ server.Handler = nullHandler
+ if statechanged != nil {
+ // Wrap the ConnState handler with something that will notify
+ // the statechanged channel when a state change happens
+ server.ConnState = func(conn net.Conn, newState http.ConnState) {
+ statechanged <- newState
+ }
+ }
+
+ server.up = make(chan net.Listener)
+ exitchan := make(chan error)
+
+ go func() {
+ exitchan <- runner()
+ }()
+
+ // wait for server socket to be bound
+ select {
+ case l = <-server.up:
+ // all good
+
+ case err := <-exitchan:
+ // all bad
+ t.Fatal("Server failed to start", err)
+ }
+ return l, exitchan
+}
+
+func startServer(t *testing.T, server *GracefulServer, statechanged chan http.ConnState) (
+ l net.Listener, errc chan error) {
+ return startGenericServer(t, server, statechanged, server.ListenAndServe)
+}
+
+func startTLSServer(t *testing.T, server *GracefulServer, certFile, keyFile string, statechanged chan http.ConnState) (l net.Listener, errc chan error) {
+ runner := func() error {
+ return server.ListenAndServeTLS(certFile, keyFile)
+ }
+
+ return startGenericServer(t, server, statechanged, runner)
+}
diff --git a/vendor/github.com/braintree/manners/server_test.go b/vendor/github.com/braintree/manners/server_test.go
new file mode 100644
index 000000000..994284216
--- /dev/null
+++ b/vendor/github.com/braintree/manners/server_test.go
@@ -0,0 +1,289 @@
+package manners
+
+import (
+ "net"
+ "net/http"
+ "testing"
+ "time"
+
+ helpers "github.com/braintree/manners/test_helpers"
+)
+
+type httpInterface interface {
+ ListenAndServe() error
+ ListenAndServeTLS(certFile, keyFile string) error
+ Serve(listener net.Listener) error
+}
+
+// Test that the method signatures of the methods we override from net/http/Server match those of the original.
+func TestInterface(t *testing.T) {
+ var original, ours interface{}
+ original = &http.Server{}
+ ours = &GracefulServer{}
+ if _, ok := original.(httpInterface); !ok {
+ t.Errorf("httpInterface definition does not match the canonical server!")
+ }
+ if _, ok := ours.(httpInterface); !ok {
+ t.Errorf("GracefulServer does not implement httpInterface")
+ }
+}
+
+// Tests that the server allows in-flight requests to complete
+// before shutting down.
+func TestGracefulness(t *testing.T) {
+ server := NewServer()
+ wg := helpers.NewWaitGroup()
+ server.wg = wg
+ statechanged := make(chan http.ConnState)
+ listener, exitchan := startServer(t, server, statechanged)
+
+ client := newClient(listener.Addr(), false)
+ client.Run()
+
+ // wait for client to connect, but don't let it send the request yet
+ if err := <-client.connected; err != nil {
+ t.Fatal("Client failed to connect to server", err)
+ }
+ // Even though the client is connected, the server ConnState handler may
+ // not know about that yet. So wait until it is called.
+ waitForState(t, statechanged, http.StateNew, "Request not received")
+
+ server.Close()
+
+ waiting := <-wg.WaitCalled
+ if waiting < 1 {
+ t.Errorf("Expected the waitgroup to equal 1 at shutdown; actually %d", waiting)
+ }
+
+ // allow the client to finish sending the request and make sure the server exits after
+ // (client will be in connected but idle state at that point)
+ client.sendrequest <- true
+ close(client.sendrequest)
+ if err := <-exitchan; err != nil {
+ t.Error("Unexpected error during shutdown", err)
+ }
+}
+
+// Tests that starting the server and closing in 2 new, separate goroutines doesnot
+// get flagged by the race detector (need to run 'go test' w/the -race flag)
+func TestRacyClose(t *testing.T) {
+ go func() {
+ ListenAndServe(":9000", nil)
+ }()
+
+ go func() {
+ Close()
+ }()
+}
+
+// Tests that the server begins to shut down when told to and does not accept
+// new requests once shutdown has begun
+func TestShutdown(t *testing.T) {
+ server := NewServer()
+ wg := helpers.NewWaitGroup()
+ server.wg = wg
+ statechanged := make(chan http.ConnState)
+ listener, exitchan := startServer(t, server, statechanged)
+
+ client1 := newClient(listener.Addr(), false)
+ client1.Run()
+
+ // wait for client1 to connect
+ if err := <-client1.connected; err != nil {
+ t.Fatal("Client failed to connect to server", err)
+ }
+ // Even though the client is connected, the server ConnState handler may
+ // not know about that yet. So wait until it is called.
+ waitForState(t, statechanged, http.StateNew, "Request not received")
+
+ // start the shutdown; once it hits waitgroup.Wait()
+ // the listener should of been closed, though client1 is still connected
+ if server.Close() != true {
+ t.Fatal("first call to Close returned false")
+ }
+ if server.Close() != false {
+ t.Fatal("second call to Close returned true")
+ }
+
+ waiting := <-wg.WaitCalled
+ if waiting != 1 {
+ t.Errorf("Waitcount should be one, got %d", waiting)
+ }
+
+ // should get connection refused at this point
+ client2 := newClient(listener.Addr(), false)
+ client2.Run()
+
+ if err := <-client2.connected; err == nil {
+ t.Fatal("client2 connected when it should of received connection refused")
+ }
+
+ // let client1 finish so the server can exit
+ close(client1.sendrequest) // don't bother sending an actual request
+
+ <-exitchan
+}
+
+// If a request is sent to a closed server via a kept alive connection then
+// the server closes the connection upon receiving the request.
+func TestRequestAfterClose(t *testing.T) {
+ // Given
+ server := NewServer()
+ srvStateChangedCh := make(chan http.ConnState, 100)
+ listener, srvClosedCh := startServer(t, server, srvStateChangedCh)
+
+ client := newClient(listener.Addr(), false)
+ client.Run()
+ <-client.connected
+ client.sendrequest <- true
+ <-client.response
+
+ server.Close()
+ if err := <-srvClosedCh; err != nil {
+ t.Error("Unexpected error during shutdown", err)
+ }
+
+ // When
+ client.sendrequest <- true
+ rr := <-client.response
+
+ // Then
+ if rr.body != nil || rr.err != nil {
+ t.Errorf("Request should be rejected, body=%v, err=%v", rr.body, rr.err)
+ }
+}
+
+func waitForState(t *testing.T, waiter chan http.ConnState, state http.ConnState, errmsg string) {
+ for {
+ select {
+ case ns := <-waiter:
+ if ns == state {
+ return
+ }
+ case <-time.After(time.Second):
+ t.Fatal(errmsg)
+ }
+ }
+}
+
+// Test that a request moving from active->idle->active using an actual
+// network connection still results in a corect shutdown
+func TestStateTransitionActiveIdleActive(t *testing.T) {
+ server := NewServer()
+ wg := helpers.NewWaitGroup()
+ statechanged := make(chan http.ConnState)
+ server.wg = wg
+ listener, exitchan := startServer(t, server, statechanged)
+
+ client := newClient(listener.Addr(), false)
+ client.Run()
+
+ // wait for client to connect, but don't let it send the request
+ if err := <-client.connected; err != nil {
+ t.Fatal("Client failed to connect to server", err)
+ }
+
+ for i := 0; i < 2; i++ {
+ client.sendrequest <- true
+ waitForState(t, statechanged, http.StateActive, "Client failed to reach active state")
+ <-client.response
+ waitForState(t, statechanged, http.StateIdle, "Client failed to reach idle state")
+ }
+
+ // client is now in an idle state
+
+ server.Close()
+ waiting := <-wg.WaitCalled
+ if waiting != 0 {
+ t.Errorf("Waitcount should be zero, got %d", waiting)
+ }
+
+ if err := <-exitchan; err != nil {
+ t.Error("Unexpected error during shutdown", err)
+ }
+}
+
+// Test state transitions from new->active->-idle->closed using an actual
+// network connection and make sure the waitgroup count is correct at the end.
+func TestStateTransitionActiveIdleClosed(t *testing.T) {
+ var (
+ listener net.Listener
+ exitchan chan error
+ )
+
+ keyFile, err1 := helpers.NewTempFile(helpers.Key)
+ certFile, err2 := helpers.NewTempFile(helpers.Cert)
+ defer keyFile.Unlink()
+ defer certFile.Unlink()
+
+ if err1 != nil || err2 != nil {
+ t.Fatal("Failed to create temporary files", err1, err2)
+ }
+
+ for _, withTLS := range []bool{false, true} {
+ server := NewServer()
+ wg := helpers.NewWaitGroup()
+ statechanged := make(chan http.ConnState)
+ server.wg = wg
+ if withTLS {
+ listener, exitchan = startTLSServer(t, server, certFile.Name(), keyFile.Name(), statechanged)
+ } else {
+ listener, exitchan = startServer(t, server, statechanged)
+ }
+
+ client := newClient(listener.Addr(), withTLS)
+ client.Run()
+
+ // wait for client to connect, but don't let it send the request
+ if err := <-client.connected; err != nil {
+ t.Fatal("Client failed to connect to server", err)
+ }
+
+ client.sendrequest <- true
+ waitForState(t, statechanged, http.StateActive, "Client failed to reach active state")
+
+ rr := <-client.response
+ if rr.err != nil {
+ t.Fatalf("tls=%t unexpected error from client %s", withTLS, rr.err)
+ }
+
+ waitForState(t, statechanged, http.StateIdle, "Client failed to reach idle state")
+
+ // client is now in an idle state
+ close(client.sendrequest)
+ <-client.closed
+ waitForState(t, statechanged, http.StateClosed, "Client failed to reach closed state")
+
+ server.Close()
+ waiting := <-wg.WaitCalled
+ if waiting != 0 {
+ t.Errorf("Waitcount should be zero, got %d", waiting)
+ }
+
+ if err := <-exitchan; err != nil {
+ t.Error("Unexpected error during shutdown", err)
+ }
+ }
+}
+
+func TestRoutinesCount(t *testing.T) {
+ var count int
+ server := NewServer()
+
+ count = server.RoutinesCount()
+ if count != 0 {
+ t.Errorf("Expected the routines count to equal 0; actually %d", count)
+ }
+
+ server.StartRoutine()
+ count = server.RoutinesCount()
+ if count != 1 {
+ t.Errorf("Expected the routines count to equal 1; actually %d", count)
+ }
+
+ server.FinishRoutine()
+ count = server.RoutinesCount()
+ if count != 0 {
+ t.Errorf("Expected the routines count to equal 0; actually %d", count)
+ }
+}
diff --git a/vendor/github.com/braintree/manners/test_helpers/certs.go b/vendor/github.com/braintree/manners/test_helpers/certs.go
new file mode 100644
index 000000000..ede248b3d
--- /dev/null
+++ b/vendor/github.com/braintree/manners/test_helpers/certs.go
@@ -0,0 +1,29 @@
+package test_helpers
+
+// A PEM-encoded TLS cert with SAN IPs "127.0.0.1" and "[::1]", expiring at the
+// last second of 2049 (the end of ASN.1 time).
+
+// generated from src/pkg/crypto/tls:
+// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
+var (
+ Cert = []byte(`-----BEGIN CERTIFICATE-----
+MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
+bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
+bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
+IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
+AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
+EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
+AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
+Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
+-----END CERTIFICATE-----`)
+
+ Key = []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
+0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
+NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
+AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
+MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
+EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
+1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
+-----END RSA PRIVATE KEY-----`)
+)
diff --git a/vendor/github.com/braintree/manners/test_helpers/conn.go b/vendor/github.com/braintree/manners/test_helpers/conn.go
new file mode 100644
index 000000000..8c610f58e
--- /dev/null
+++ b/vendor/github.com/braintree/manners/test_helpers/conn.go
@@ -0,0 +1,13 @@
+package test_helpers
+
+import "net"
+
+type Conn struct {
+ net.Conn
+ CloseCalled bool
+}
+
+func (c *Conn) Close() error {
+ c.CloseCalled = true
+ return nil
+}
diff --git a/vendor/github.com/braintree/manners/test_helpers/listener.go b/vendor/github.com/braintree/manners/test_helpers/listener.go
new file mode 100644
index 000000000..e3af35a6e
--- /dev/null
+++ b/vendor/github.com/braintree/manners/test_helpers/listener.go
@@ -0,0 +1,34 @@
+package test_helpers
+
+import (
+ "errors"
+ "net"
+)
+
+type Listener struct {
+ AcceptRelease chan bool
+ CloseCalled chan bool
+}
+
+func NewListener() *Listener {
+ return &Listener{
+ make(chan bool, 1),
+ make(chan bool, 1),
+ }
+}
+
+func (l *Listener) Addr() net.Addr {
+ addr, _ := net.ResolveTCPAddr("tcp", "127.0.0.1:8080")
+ return addr
+}
+
+func (l *Listener) Close() error {
+ l.CloseCalled <- true
+ l.AcceptRelease <- true
+ return nil
+}
+
+func (l *Listener) Accept() (net.Conn, error) {
+ <-l.AcceptRelease
+ return nil, errors.New("connection closed")
+}
diff --git a/vendor/github.com/braintree/manners/test_helpers/temp_file.go b/vendor/github.com/braintree/manners/test_helpers/temp_file.go
new file mode 100644
index 000000000..c4aa263a0
--- /dev/null
+++ b/vendor/github.com/braintree/manners/test_helpers/temp_file.go
@@ -0,0 +1,27 @@
+package test_helpers
+
+import (
+ "io/ioutil"
+ "os"
+)
+
+type TempFile struct {
+ *os.File
+}
+
+func NewTempFile(content []byte) (*TempFile, error) {
+ f, err := ioutil.TempFile("", "graceful-test")
+ if err != nil {
+ return nil, err
+ }
+
+ f.Write(content)
+ return &TempFile{f}, nil
+}
+
+func (tf *TempFile) Unlink() {
+ if tf.File != nil {
+ os.Remove(tf.Name())
+ tf.File = nil
+ }
+}
diff --git a/vendor/github.com/braintree/manners/test_helpers/wait_group.go b/vendor/github.com/braintree/manners/test_helpers/wait_group.go
new file mode 100644
index 000000000..1df590db7
--- /dev/null
+++ b/vendor/github.com/braintree/manners/test_helpers/wait_group.go
@@ -0,0 +1,33 @@
+package test_helpers
+
+import "sync"
+
+type WaitGroup struct {
+ sync.Mutex
+ Count int
+ WaitCalled chan int
+}
+
+func NewWaitGroup() *WaitGroup {
+ return &WaitGroup{
+ WaitCalled: make(chan int, 1),
+ }
+}
+
+func (wg *WaitGroup) Add(delta int) {
+ wg.Lock()
+ wg.Count++
+ wg.Unlock()
+}
+
+func (wg *WaitGroup) Done() {
+ wg.Lock()
+ wg.Count--
+ wg.Unlock()
+}
+
+func (wg *WaitGroup) Wait() {
+ wg.Lock()
+ wg.WaitCalled <- wg.Count
+ wg.Unlock()
+}
diff --git a/vendor/github.com/braintree/manners/transition_test.go b/vendor/github.com/braintree/manners/transition_test.go
new file mode 100644
index 000000000..5d398514e
--- /dev/null
+++ b/vendor/github.com/braintree/manners/transition_test.go
@@ -0,0 +1,54 @@
+package manners
+
+import (
+ helpers "github.com/braintree/manners/test_helpers"
+ "net/http"
+ "strings"
+ "testing"
+)
+
+func TestStateTransitions(t *testing.T) {
+ tests := []transitionTest{
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive}, 1},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateClosed}, 0},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateClosed}, 0},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateHijacked}, 0},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle}, 0},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive}, 1},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive, http.StateIdle}, 0},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive, http.StateClosed}, 0},
+ transitionTest{[]http.ConnState{http.StateNew, http.StateActive, http.StateIdle, http.StateActive, http.StateIdle, http.StateClosed}, 0},
+ }
+
+ for _, test := range tests {
+ testStateTransition(t, test)
+ }
+}
+
+type transitionTest struct {
+ states []http.ConnState
+ expectedWgCount int
+}
+
+func testStateTransition(t *testing.T, test transitionTest) {
+ server := NewServer()
+ wg := helpers.NewWaitGroup()
+ server.wg = wg
+ startServer(t, server, nil)
+
+ conn := &helpers.Conn{}
+ for _, newState := range test.states {
+ server.ConnState(conn, newState)
+ }
+
+ server.Close()
+ waiting := <-wg.WaitCalled
+ if waiting != test.expectedWgCount {
+ names := make([]string, len(test.states))
+ for i, s := range test.states {
+ names[i] = s.String()
+ }
+ transitions := strings.Join(names, " -> ")
+ t.Errorf("%s - Waitcount should be %d, got %d", transitions, test.expectedWgCount, waiting)
+ }
+}