| // Copyright 2014 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "net/socket/websocket_endpoint_lock_manager.h" | 
 |  | 
 | #include "base/check.h" | 
 | #include "base/run_loop.h" | 
 | #include "base/time/time.h" | 
 | #include "net/base/ip_address.h" | 
 | #include "net/base/net_errors.h" | 
 | #include "net/log/net_log_with_source.h" | 
 | #include "net/socket/next_proto.h" | 
 | #include "net/socket/socket_test_util.h" | 
 | #include "net/test/gtest_util.h" | 
 | #include "net/test/test_with_task_environment.h" | 
 | #include "net/traffic_annotation/network_traffic_annotation.h" | 
 | #include "testing/gmock/include/gmock/gmock.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | using net::test::IsOk; | 
 |  | 
 | namespace net { | 
 |  | 
 | namespace { | 
 |  | 
 | class FakeWaiter : public WebSocketEndpointLockManager::Waiter { | 
 |  public: | 
 |   FakeWaiter() = default; | 
 |  | 
 |   void GotEndpointLock() override { | 
 |     CHECK(!called_); | 
 |     called_ = true; | 
 |   } | 
 |  | 
 |   bool called() const { return called_; } | 
 |  | 
 |  private: | 
 |   bool called_ = false; | 
 | }; | 
 |  | 
 | class BlockingWaiter : public FakeWaiter { | 
 |  public: | 
 |   void WaitForLock() { | 
 |     while (!called()) { | 
 |       run_loop_.Run(); | 
 |     } | 
 |   } | 
 |  | 
 |   void GotEndpointLock() override { | 
 |     FakeWaiter::GotEndpointLock(); | 
 |     run_loop_.Quit(); | 
 |   } | 
 |  | 
 |  private: | 
 |   base::RunLoop run_loop_; | 
 | }; | 
 |  | 
 | class WebSocketEndpointLockManagerTest : public TestWithTaskEnvironment { | 
 |  protected: | 
 |   WebSocketEndpointLockManagerTest() { | 
 |     websocket_endpoint_lock_manager_.SetUnlockDelayForTesting( | 
 |         base::TimeDelta()); | 
 |   } | 
 |  | 
 |   ~WebSocketEndpointLockManagerTest() override { | 
 |     // Permit any pending asynchronous unlock operations to complete. | 
 |     RunUntilIdle(); | 
 |     // If this check fails then subsequent tests may fail. | 
 |     CHECK(websocket_endpoint_lock_manager_.IsEmpty()); | 
 |   } | 
 |  | 
 |   IPEndPoint DummyEndpoint() { | 
 |     return IPEndPoint(IPAddress::IPv4Localhost(), 80); | 
 |   } | 
 |  | 
 |   void UnlockDummyEndpoint(int times) { | 
 |     for (int i = 0; i < times; ++i) { | 
 |       websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |       RunUntilIdle(); | 
 |     } | 
 |   } | 
 |  | 
 |   static void RunUntilIdle() { base::RunLoop().RunUntilIdle(); } | 
 |  | 
 |   WebSocketEndpointLockManager websocket_endpoint_lock_manager_; | 
 | }; | 
 |  | 
 | TEST_F(WebSocketEndpointLockManagerTest, LockEndpointReturnsOkOnce) { | 
 |   FakeWaiter waiters[2]; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |  | 
 |   UnlockDummyEndpoint(2); | 
 | } | 
 |  | 
 | TEST_F(WebSocketEndpointLockManagerTest, GotEndpointLockNotCalledOnOk) { | 
 |   FakeWaiter waiter; | 
 |   EXPECT_THAT( | 
 |       websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), &waiter), | 
 |       IsOk()); | 
 |   RunUntilIdle(); | 
 |   EXPECT_FALSE(waiter.called()); | 
 |  | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | TEST_F(WebSocketEndpointLockManagerTest, GotEndpointLockNotCalledImmediately) { | 
 |   FakeWaiter waiters[2]; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |   RunUntilIdle(); | 
 |   EXPECT_FALSE(waiters[1].called()); | 
 |  | 
 |   UnlockDummyEndpoint(2); | 
 | } | 
 |  | 
 | TEST_F(WebSocketEndpointLockManagerTest, GotEndpointLockCalledWhenUnlocked) { | 
 |   FakeWaiter waiters[2]; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |   websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   RunUntilIdle(); | 
 |   EXPECT_TRUE(waiters[1].called()); | 
 |  | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | TEST_F(WebSocketEndpointLockManagerTest, | 
 |        EndpointUnlockedIfWaiterAlreadyDeleted) { | 
 |   FakeWaiter first_lock_holder; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &first_lock_holder), | 
 |               IsOk()); | 
 |  | 
 |   { | 
 |     FakeWaiter short_lived_waiter; | 
 |     EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                   DummyEndpoint(), &short_lived_waiter)); | 
 |   } | 
 |  | 
 |   websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   RunUntilIdle(); | 
 |  | 
 |   FakeWaiter second_lock_holder; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                   DummyEndpoint(), &second_lock_holder), | 
 |               IsOk()); | 
 |  | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | TEST_F(WebSocketEndpointLockManagerTest, LockReleaserWorks) { | 
 |   FakeWaiter waiters[2]; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |  | 
 |   { | 
 |     WebSocketEndpointLockManager::LockReleaser releaser( | 
 |         &websocket_endpoint_lock_manager_, DummyEndpoint()); | 
 |   } | 
 |   RunUntilIdle(); | 
 |   EXPECT_TRUE(waiters[1].called()); | 
 |  | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | // UnlockEndpoint() should cause any LockReleasers for this endpoint to be | 
 | // unregistered. | 
 | TEST_F(WebSocketEndpointLockManagerTest, LockReleaserForgottenOnUnlock) { | 
 |   FakeWaiter waiter; | 
 |  | 
 |   EXPECT_THAT( | 
 |       websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), &waiter), | 
 |       IsOk()); | 
 |   WebSocketEndpointLockManager::LockReleaser releaser( | 
 |       &websocket_endpoint_lock_manager_, DummyEndpoint()); | 
 |   websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   RunUntilIdle(); | 
 |   EXPECT_TRUE(websocket_endpoint_lock_manager_.IsEmpty()); | 
 | } | 
 |  | 
 | // When ownership of the endpoint is passed to a new waiter, the new waiter can | 
 | // construct another LockReleaser. | 
 | TEST_F(WebSocketEndpointLockManagerTest, NextWaiterCanCreateLockReleaserAgain) { | 
 |   FakeWaiter waiters[2]; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |  | 
 |   WebSocketEndpointLockManager::LockReleaser releaser1( | 
 |       &websocket_endpoint_lock_manager_, DummyEndpoint()); | 
 |   websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   RunUntilIdle(); | 
 |   EXPECT_TRUE(waiters[1].called()); | 
 |   WebSocketEndpointLockManager::LockReleaser releaser2( | 
 |       &websocket_endpoint_lock_manager_, DummyEndpoint()); | 
 |  | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | // Destroying LockReleaser after UnlockEndpoint() does nothing. | 
 | TEST_F(WebSocketEndpointLockManagerTest, | 
 |        DestroyLockReleaserAfterUnlockEndpointDoesNothing) { | 
 |   FakeWaiter waiters[3]; | 
 |  | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[2])); | 
 |   { | 
 |     WebSocketEndpointLockManager::LockReleaser releaser( | 
 |         &websocket_endpoint_lock_manager_, DummyEndpoint()); | 
 |     websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   } | 
 |   RunUntilIdle(); | 
 |   EXPECT_TRUE(waiters[1].called()); | 
 |   EXPECT_FALSE(waiters[2].called()); | 
 |  | 
 |   UnlockDummyEndpoint(2); | 
 | } | 
 |  | 
 | // UnlockEndpoint() should always be asynchronous. | 
 | TEST_F(WebSocketEndpointLockManagerTest, UnlockEndpointIsAsynchronous) { | 
 |   FakeWaiter waiters[2]; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &waiters[0]), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &waiters[1])); | 
 |  | 
 |   websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   EXPECT_FALSE(waiters[1].called()); | 
 |   RunUntilIdle(); | 
 |   EXPECT_TRUE(waiters[1].called()); | 
 |  | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | // UnlockEndpoint() should normally have a delay. | 
 | TEST_F(WebSocketEndpointLockManagerTest, UnlockEndpointIsDelayed) { | 
 |   using base::TimeTicks; | 
 |  | 
 |   // This 1ms delay is too short for very slow environments (usually those | 
 |   // running memory checkers). In those environments, the code takes >1ms to run | 
 |   // and no delay is needed. Rather than increase the delay and slow down the | 
 |   // test everywhere, the test doesn't explicitly verify that a delay has been | 
 |   // applied. Instead it just verifies that the whole thing took >=1ms. 1ms is | 
 |   // easily enough for normal compiles even on Android, so the fact that there | 
 |   // is a delay is still checked on every platform. | 
 |   const base::TimeDelta unlock_delay = base::Milliseconds(1); | 
 |   websocket_endpoint_lock_manager_.SetUnlockDelayForTesting(unlock_delay); | 
 |   FakeWaiter fake_waiter; | 
 |   BlockingWaiter blocking_waiter; | 
 |   EXPECT_THAT(websocket_endpoint_lock_manager_.LockEndpoint(DummyEndpoint(), | 
 |                                                             &fake_waiter), | 
 |               IsOk()); | 
 |   EXPECT_EQ(ERR_IO_PENDING, websocket_endpoint_lock_manager_.LockEndpoint( | 
 |                                 DummyEndpoint(), &blocking_waiter)); | 
 |  | 
 |   TimeTicks before_unlock = TimeTicks::Now(); | 
 |   websocket_endpoint_lock_manager_.UnlockEndpoint(DummyEndpoint()); | 
 |   blocking_waiter.WaitForLock(); | 
 |   TimeTicks after_unlock = TimeTicks::Now(); | 
 |   EXPECT_GE(after_unlock - before_unlock, unlock_delay); | 
 |   websocket_endpoint_lock_manager_.SetUnlockDelayForTesting(base::TimeDelta()); | 
 |   UnlockDummyEndpoint(1); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | }  // namespace net |