예제 #1
0
// 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)
	}
}
예제 #2
0
// 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)
		}
	}
}
예제 #3
0
// 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)
	}
}
예제 #4
0
// 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
}
예제 #5
0
func testStateTransition(t *testing.T, test transitionTest) {
	server := newServer()
	wg := helpers.NewWaitGroup()
	server.wg = wg
	startServer(t, server, nil)

	conn := &gracefulConn{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)
	}
}
예제 #6
0
// Hijack listener
func TestHijackListener(t *testing.T) {
	server := NewServer()
	wg := helpers.NewWaitGroup()
	server.wg = wg
	listener, exitchan := startServer(t, server, nil)

	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)
	}

	// Make sure server1 got the request and added it to the waiting group
	<-wg.CountChanged

	wg2 := helpers.NewWaitGroup()
	server2, err := server.HijackListener(new(http.Server), nil)
	server2.wg = wg2
	if err != nil {
		t.Fatal("Failed to hijack listener", err)
	}

	listener2, exitchan2 := startServer(t, server2, nil)

	// Close the first server
	server.Close()

	// First server waits for the first request to finish
	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)
	}

	client2 := newClient(listener2.Addr(), false)
	client2.Run()

	// wait for client to connect, but don't let it send the request yet
	select {
	case err := <-client2.connected:
		if err != nil {
			t.Fatal("Client failed to connect to server", err)
		}
	case <-time.After(time.Second):
		t.Fatal("Timeout connecting to the server", err)
	}

	// Close the second server
	server2.Close()

	waiting = <-wg2.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)
	client2.sendrequest <- true
	// Make sure that request resulted in success
	if rr := <-client2.response; rr.err != nil {
		t.Errorf("Client failed to write the request, error: %s", err)
	}
	close(client2.sendrequest)
	if err := <-exitchan2; err != nil {
		t.Error("Unexpected error during shutdown", err)
	}
}