From 38ee83e45b4de7edf89bf9f0ef629eb4c6ad0fa8 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 12 May 2016 23:56:07 -0400 Subject: Moving to glide --- .../github.com/braintree/manners/helpers_test.go | 119 +++++++++ vendor/github.com/braintree/manners/server_test.go | 289 +++++++++++++++++++++ .../braintree/manners/test_helpers/certs.go | 29 +++ .../braintree/manners/test_helpers/conn.go | 13 + .../braintree/manners/test_helpers/listener.go | 34 +++ .../braintree/manners/test_helpers/temp_file.go | 27 ++ .../braintree/manners/test_helpers/wait_group.go | 33 +++ .../braintree/manners/transition_test.go | 54 ++++ 8 files changed, 598 insertions(+) create mode 100644 vendor/github.com/braintree/manners/helpers_test.go create mode 100644 vendor/github.com/braintree/manners/server_test.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/certs.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/conn.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/listener.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/temp_file.go create mode 100644 vendor/github.com/braintree/manners/test_helpers/wait_group.go create mode 100644 vendor/github.com/braintree/manners/transition_test.go (limited to 'vendor/github.com/braintree') 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) + } +} -- cgit v1.2.3-1-g7c22