Chromium Code Reviews| Index: content/browser/renderer_host/websocket_dispatcher_host_unittest.cc |
| diff --git a/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc b/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc |
| index 0d2e7057bbcef5c0d90dadebc580843114e208bd..816b5093a259b7d4d23f77c3ad33ba8a9231d859 100644 |
| --- a/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc |
| +++ b/content/browser/renderer_host/websocket_dispatcher_host_unittest.cc |
| @@ -11,10 +11,12 @@ |
| #include "base/bind_helpers.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| +#include "base/message_loop/message_loop.h" |
| #include "content/browser/renderer_host/websocket_host.h" |
| #include "content/common/websocket.h" |
| #include "content/common/websocket_messages.h" |
| #include "ipc/ipc_message.h" |
| +#include "net/websockets/websocket_errors.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| @@ -33,26 +35,62 @@ class MockWebSocketHost : public WebSocketHost { |
| MockWebSocketHost(int routing_id, |
| WebSocketDispatcherHost* dispatcher, |
| net::URLRequestContext* url_request_context, |
| + base::TimeDelta delay, |
| WebSocketDispatcherHostTest* owner); |
| ~MockWebSocketHost() override {} |
| bool OnMessageReceived(const IPC::Message& message) override { |
| received_messages_.push_back(message); |
| - return true; |
| + switch (message.type()) { |
| + case WebSocketMsg_DropChannel::ID: |
| + // Needed for PerRendererThrottlingFailedHandshakes, because without |
| + // calling WebSocketHost::OnMessageReceived() (and thus |
| + // WebSocketHost::OnDropChannel()), the connection stays pending and |
| + // we cannot test per-renderer throttling with failed connections. |
| + return WebSocketHost::OnMessageReceived(message); |
| + |
| + default: |
| + return true; |
| + } |
| } |
| void GoAway() override; |
| std::vector<IPC::Message> received_messages_; |
| base::WeakPtr<WebSocketDispatcherHostTest> owner_; |
| + base::TimeDelta delay_; |
| +}; |
| + |
| +class TestingWebSocketDispatcherHost : public WebSocketDispatcherHost { |
| + public: |
| + TestingWebSocketDispatcherHost( |
| + int process_id, |
| + const GetRequestContextCallback& get_context_callback, |
| + const WebSocketHostFactory& websocket_host_factory) |
| + : WebSocketDispatcherHost(process_id, |
| + get_context_callback, |
| + websocket_host_factory) {} |
| + |
| + bool Send(IPC::Message* message) override { |
|
Adam Rice
2015/03/11 07:20:50
Add a comment like
// This is needed because Brow
hiroshige
2015/03/11 10:40:01
Done.
|
| + delete message; |
| + return true; |
| + } |
| + |
| + using WebSocketDispatcherHost::num_pending_connections; |
| + using WebSocketDispatcherHost::num_failed_connections; |
| + using WebSocketDispatcherHost::num_succeeded_connections; |
| + |
| + private: |
| + ~TestingWebSocketDispatcherHost() override {} |
| }; |
| class WebSocketDispatcherHostTest : public ::testing::Test { |
| public: |
| WebSocketDispatcherHostTest() |
| - : weak_ptr_factory_(this) { |
| - dispatcher_host_ = new WebSocketDispatcherHost( |
| + : next_routing_id_(123), |
| + weak_ptr_factory_(this) { |
| + dispatcher_host_ = new TestingWebSocketDispatcherHost( |
| kMagicRenderProcessId, |
| base::Bind(&WebSocketDispatcherHostTest::OnGetRequestContext, |
| base::Unretained(this)), |
| @@ -75,35 +113,90 @@ class WebSocketDispatcherHostTest : public ::testing::Test { |
| } |
| protected: |
| - scoped_refptr<WebSocketDispatcherHost> dispatcher_host_; |
| + // Adds |n| connections. Returns true if succeeded. |
| + bool AddMultipleChannels(int number_of_channels) { |
| + GURL socket_url("ws://example.com/test"); |
| + std::vector<std::string> requested_protocols; |
| + url::Origin origin("http://example.com"); |
| + int render_frame_id = -3; |
| + |
| + for (int i = 0; i < number_of_channels; ++i) { |
| + int routing_id = next_routing_id_++; |
| + WebSocketHostMsg_AddChannelRequest message( |
| + routing_id, |
| + socket_url, |
| + requested_protocols, |
| + origin, |
| + render_frame_id); |
| + if (!dispatcher_host_->OnMessageReceived(message)) |
| + return false; |
| + } |
| + |
| + return true; |
| + } |
| + |
| + // Adds and cancels |n| connections. Returns true if succeeded. |
| + bool AddAndCancelMultipleChannels(int number_of_channels) { |
| + GURL socket_url("ws://example.com/test"); |
| + std::vector<std::string> requested_protocols; |
| + url::Origin origin("http://example.com"); |
| + int render_frame_id = -3; |
| + |
| + for (int i = 0; i < number_of_channels; ++i) { |
| + int routing_id = next_routing_id_++; |
| + WebSocketHostMsg_AddChannelRequest messageAddChannelRequest( |
| + routing_id, |
| + socket_url, |
| + requested_protocols, |
| + origin, |
| + render_frame_id); |
| + if (!dispatcher_host_->OnMessageReceived(messageAddChannelRequest)) |
| + return false; |
| + |
| + WebSocketMsg_DropChannel messageDropChannel( |
| + routing_id, false, net::kWebSocketErrorAbnormalClosure, ""); |
| + if (!dispatcher_host_->OnMessageReceived(messageDropChannel)) |
| + return false; |
| + } |
| + |
| + return true; |
| + } |
| + |
| + scoped_refptr<TestingWebSocketDispatcherHost> dispatcher_host_; |
| // Stores allocated MockWebSocketHost instances. Doesn't take ownership of |
| // them. |
| std::vector<MockWebSocketHost*> mock_hosts_; |
| std::vector<int> gone_hosts_; |
| - base::WeakPtrFactory<WebSocketDispatcherHostTest> weak_ptr_factory_; |
| - |
| private: |
| net::URLRequestContext* OnGetRequestContext() { |
| return NULL; |
| } |
| - WebSocketHost* CreateWebSocketHost(int routing_id) { |
| - MockWebSocketHost* host = |
| - new MockWebSocketHost(routing_id, dispatcher_host_.get(), NULL, this); |
| + WebSocketHost* CreateWebSocketHost(int routing_id, base::TimeDelta delay) { |
| + MockWebSocketHost* host = new MockWebSocketHost( |
| + routing_id, dispatcher_host_.get(), NULL, delay, this); |
| mock_hosts_.push_back(host); |
| return host; |
| } |
| + |
| + base::MessageLoop message_loop_; |
| + |
| + int next_routing_id_; |
| + |
| + base::WeakPtrFactory<WebSocketDispatcherHostTest> weak_ptr_factory_; |
| }; |
| MockWebSocketHost::MockWebSocketHost( |
| int routing_id, |
| WebSocketDispatcherHost* dispatcher, |
| net::URLRequestContext* url_request_context, |
| + base::TimeDelta delay, |
| WebSocketDispatcherHostTest* owner) |
| - : WebSocketHost(routing_id, dispatcher, url_request_context), |
| - owner_(owner->GetWeakPtr()) {} |
| + : WebSocketHost(routing_id, dispatcher, url_request_context, delay), |
| + owner_(owner->GetWeakPtr()), |
| + delay_(delay) {} |
| void MockWebSocketHost::GoAway() { |
| if (owner_) |
| @@ -128,7 +221,7 @@ TEST_F(WebSocketDispatcherHostTest, AddChannelRequest) { |
| GURL socket_url("ws://example.com/test"); |
| std::vector<std::string> requested_protocols; |
| requested_protocols.push_back("hello"); |
| - url::Origin origin("http://example.com/test"); |
| + url::Origin origin("http://example.com"); |
| int render_frame_id = -2; |
| WebSocketHostMsg_AddChannelRequest message( |
| routing_id, socket_url, requested_protocols, origin, render_frame_id); |
| @@ -162,7 +255,7 @@ TEST_F(WebSocketDispatcherHostTest, SendFrame) { |
| GURL socket_url("ws://example.com/test"); |
| std::vector<std::string> requested_protocols; |
| requested_protocols.push_back("hello"); |
| - url::Origin origin("http://example.com/test"); |
| + url::Origin origin("http://example.com"); |
| int render_frame_id = -2; |
| WebSocketHostMsg_AddChannelRequest add_channel_message( |
| routing_id, socket_url, requested_protocols, origin, render_frame_id); |
| @@ -215,5 +308,105 @@ TEST_F(WebSocketDispatcherHostTest, Destruct) { |
| EXPECT_EQ(456, gone_hosts_[1]); |
| } |
| +TEST_F(WebSocketDispatcherHostTest, DelayFor4thPendingConnectionIsZero) { |
| + ASSERT_TRUE(AddMultipleChannels(4)); |
| + |
| + EXPECT_EQ(4, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_EQ(4U, mock_hosts_.size()); |
| + EXPECT_EQ(base::TimeDelta(), mock_hosts_[3]->delay_); |
| +} |
| + |
| +TEST_F(WebSocketDispatcherHostTest, DelayFor8thPendingConnectionIsNonZero) { |
| + ASSERT_TRUE(AddMultipleChannels(8)); |
| + |
| + EXPECT_EQ(8, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_EQ(8U, mock_hosts_.size()); |
| + EXPECT_LT(base::TimeDelta(), mock_hosts_[7]->delay_); |
| +} |
| + |
| +TEST_F(WebSocketDispatcherHostTest, DelayFor17thPendingConnection) { |
| + ASSERT_TRUE(AddMultipleChannels(17)); |
| + |
| + EXPECT_EQ(17, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_EQ(17U, mock_hosts_.size()); |
| + EXPECT_LE(base::TimeDelta::FromMilliseconds(1000), mock_hosts_[16]->delay_); |
| + EXPECT_GE(base::TimeDelta::FromMilliseconds(5000), mock_hosts_[16]->delay_); |
| +} |
| + |
| +// The 256th connection is rejected by per-renderer WebSocket throttling. |
| +// This is not counted as a failure. |
| +TEST_F(WebSocketDispatcherHostTest, Rejects256thPendingConnection) { |
| + ASSERT_TRUE(AddMultipleChannels(256)); |
| + |
| + EXPECT_EQ(255, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_EQ(255U, mock_hosts_.size()); |
| +} |
| + |
| +TEST_F(WebSocketDispatcherHostTest, DelayIsZeroAfter3FailedConnections) { |
| + ASSERT_TRUE(AddAndCancelMultipleChannels(3)); |
| + |
| + EXPECT_EQ(0, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(3, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_TRUE(AddMultipleChannels(1)); |
| + |
| + ASSERT_EQ(4U, mock_hosts_.size()); |
| + EXPECT_EQ(base::TimeDelta(), mock_hosts_[3]->delay_); |
| +} |
| + |
| +TEST_F(WebSocketDispatcherHostTest, DelayIsNonZeroAfter7FailedConnections) { |
| + ASSERT_TRUE(AddAndCancelMultipleChannels(7)); |
| + |
| + EXPECT_EQ(0, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(7, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_TRUE(AddMultipleChannels(1)); |
| + |
| + ASSERT_EQ(8U, mock_hosts_.size()); |
| + EXPECT_LT(base::TimeDelta(), mock_hosts_[7]->delay_); |
| +} |
| + |
| +TEST_F(WebSocketDispatcherHostTest, DelayAfter16FailedConnections) { |
| + ASSERT_TRUE(AddAndCancelMultipleChannels(16)); |
| + |
| + EXPECT_EQ(0, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(16, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_TRUE(AddMultipleChannels(1)); |
| + |
| + ASSERT_EQ(17U, mock_hosts_.size()); |
| + EXPECT_LE(base::TimeDelta::FromMilliseconds(1000), mock_hosts_[16]->delay_); |
| + EXPECT_GE(base::TimeDelta::FromMilliseconds(5000), mock_hosts_[16]->delay_); |
| +} |
| + |
| +TEST_F(WebSocketDispatcherHostTest, NotRejectedAfter255FailedConnections) { |
| + ASSERT_TRUE(AddAndCancelMultipleChannels(255)); |
| + |
| + EXPECT_EQ(0, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(255, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| + |
| + ASSERT_TRUE(AddMultipleChannels(1)); |
| + |
| + EXPECT_EQ(1, dispatcher_host_->num_pending_connections()); |
| + EXPECT_EQ(255, dispatcher_host_->num_failed_connections()); |
| + EXPECT_EQ(0, dispatcher_host_->num_succeeded_connections()); |
| +} |
| + |
| } // namespace |
| } // namespace content |