Repository: grpc/grpc-go
File: test/gracefulstop_test.go (Go)

Code before:
		acceptCalled: make(chan struct{}),
		closeCalled:  make(chan struct{}),
		allowCloseCh: make(chan struct{}),
	}

	ss := &stubserver.StubServer{
		Listener: dlis,
		FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
			_, err := stream.Recv()
			if err != nil {
				return err
			}
			return stream.Send(&testpb.StreamingOutputCallResponse{})
		},
		S: grpc.NewServer(),
	}
	stubserver.StartTestService(t, ss)

	// 1. Start Server
	gracefulStopDone := make(chan struct{})
	<-dlis.acceptCalled
	go func() {
		ss.S.GracefulStop()
		close(gracefulStopDone)
	}()
	// 2. GracefulStop() Server after listener's Accept is called, but don't
	//    allow Accept() to exit when Close() is called on it.

	// 3. Create a new connection to the server after listener.Close() is called.
	//    Server should close this connection immediately, before handshaking.

	<-dlis.closeCalled // Block until GracefulStop calls dlis.Close()

	// Now dial.  The listener's Accept method will return a valid connection,
	// even though GracefulStop has closed the listener.
	ctx, dialCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer dialCancel()
	dialer := func(ctx context.Context, _ string) (net.Conn, error) { return dlis.Dial(ctx) }
	cc, err := grpc.DialContext(ctx, "", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialer))
	if err != nil {
		t.Fatalf("grpc.DialContext(_, %q, _) = %v", lis.Addr().String(), err)
	}
	client := testgrpc.NewTestServiceClient(cc)
	defer cc.Close()

	// 4. Send an RPC on the new connection.
	// The server would send a GOAWAY first, but we are delaying the server's
	// writes for now until the client writes more than the preface.
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	if _, err = client.FullDuplexCall(ctx); err == nil || status.Code(err) != codes.Unavailable {
		t.Fatalf("FullDuplexCall= _, %v; want _, <status code Unavailable>", err)

Review comment (bug):
I think these comments need some more clarity and need to be in the right place. This is what I see, please correct me if I'm wrong:
1. Start the server and start serving by calling `Serve()`. This eventually ends up calling `Accept()` on the listener that was passed to it. So, when we block on `<-dlis.acceptCalled`, we know for a fact that the server is actually serving now.
2. Call `GracefulStop` from a goroutine. This will end up calling `Close` on the listener passed to the server. But the `Close` method on the listener that we are passing to the server will not actually close the listener until a connection is accepted.
3. Dial the server. This will cause a connection to be accepted. This will also unblock the `Close` method.
4. Make an RPC. At this point, we are certain that the server is in graceful stop. So, it will fail the RPC.

Code after:
		acceptCalled: make(chan struct{}),
		closeCalled:  make(chan struct{}),
		allowCloseCh: make(chan struct{}),
	}

	ss := &stubserver.StubServer{
		Listener: dlis,
		FullDuplexCallF: func(stream testgrpc.TestService_FullDuplexCallServer) error {
			if _, err := stream.Recv(); err != nil {
				return err
			}
			return stream.Send(&testpb.StreamingOutputCallResponse{})
		},
		S: grpc.NewServer(),
	}
	// 1. Start Server and start serving by calling Serve().
	stubserver.StartTestService(t, ss)

	// 2. Call GracefulStop from a goroutine. It will trigger Close on the
	// listener, but the listener will not actually close until a connection
	// is accepted.
	gracefulStopDone := make(chan struct{})
	<-dlis.acceptCalled
	go func() {
		ss.S.GracefulStop()
		close(gracefulStopDone)
	}()

	// 3. Create a new connection to the server after listener.Close() is called.
	// Server should close this connection immediately, before handshaking.

	<-dlis.closeCalled // Block until GracefulStop calls dlis.Close()

	// Dial the server. This will cause a connection to be accepted. This will
	// also unblock the Close method .
	ctx, dialCancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	defer dialCancel()
	dialer := func(ctx context.Context, _ string) (net.Conn, error) { return dlis.Dial(ctx) }
	cc, err := grpc.DialContext(ctx, "", grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithContextDialer(dialer))
	if err != nil {
		t.Fatalf("grpc.DialContext(_, %q, _) = %v", lis.Addr().String(), err)
	}
	client := testgrpc.NewTestServiceClient(cc)
	defer cc.Close()

	// 4. Make an RPC.
	// The server would send a GOAWAY first, but we are delaying the server's
	// writes for now until the client writes more than the preface.
	ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
	if _, err = client.FullDuplexCall(ctx); err == nil || status.Code(err) != codes.Unavailable {
		t.Fatalf("FullDuplexCall= _, %v; want _, <status code Unavailable>", err)