Land Recent QUIC Changes until 04/30/2015.
[chromium-blink-merge.git] / net / tools / quic / quic_dispatcher_test.cc
blob0e3fdffaaadea988138676aabd0179d320eafdec
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/tools/quic/quic_dispatcher.h"
7 #include <string>
9 #include "base/strings/string_piece.h"
10 #include "net/quic/crypto/crypto_handshake.h"
11 #include "net/quic/crypto/quic_crypto_server_config.h"
12 #include "net/quic/crypto/quic_random.h"
13 #include "net/quic/quic_connection_helper.h"
14 #include "net/quic/quic_crypto_stream.h"
15 #include "net/quic/quic_flags.h"
16 #include "net/quic/quic_utils.h"
17 #include "net/quic/test_tools/quic_test_utils.h"
18 #include "net/tools/epoll_server/epoll_server.h"
19 #include "net/tools/quic/quic_epoll_connection_helper.h"
20 #include "net/tools/quic/quic_packet_writer_wrapper.h"
21 #include "net/tools/quic/quic_time_wait_list_manager.h"
22 #include "net/tools/quic/test_tools/quic_dispatcher_peer.h"
23 #include "net/tools/quic/test_tools/quic_test_utils.h"
24 #include "testing/gmock/include/gmock/gmock.h"
25 #include "testing/gtest/include/gtest/gtest.h"
27 using base::StringPiece;
28 using net::EpollServer;
29 using net::test::ConstructEncryptedPacket;
30 using net::test::MockConnection;
31 using net::test::MockSession;
32 using net::test::ValueRestore;
33 using std::string;
34 using testing::DoAll;
35 using testing::InSequence;
36 using testing::Invoke;
37 using testing::WithoutArgs;
38 using testing::_;
40 namespace net {
41 namespace tools {
42 namespace test {
43 namespace {
45 class TestServerSession : public QuicServerSession {
46 public:
47 TestServerSession(const QuicConfig& config, QuicConnection* connection)
48 : QuicServerSession(config, connection, nullptr) {}
49 ~TestServerSession() override{};
51 MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer));
52 MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id));
53 MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*());
55 private:
56 DISALLOW_COPY_AND_ASSIGN(TestServerSession);
59 class TestDispatcher : public QuicDispatcher {
60 public:
61 explicit TestDispatcher(const QuicConfig& config,
62 const QuicCryptoServerConfig* crypto_config,
63 EpollServer* eps)
64 : QuicDispatcher(config,
65 crypto_config,
66 QuicSupportedVersions(),
67 new QuicDispatcher::DefaultPacketWriterFactory(),
68 new QuicEpollConnectionHelper(eps)) {
71 MOCK_METHOD3(CreateQuicSession,
72 QuicServerSession*(QuicConnectionId connection_id,
73 const IPEndPoint& server_address,
74 const IPEndPoint& client_address));
76 using QuicDispatcher::current_server_address;
77 using QuicDispatcher::current_client_address;
80 // A Connection class which unregisters the session from the dispatcher
81 // when sending connection close.
82 // It'd be slightly more realistic to do this from the Session but it would
83 // involve a lot more mocking.
84 class MockServerConnection : public MockConnection {
85 public:
86 MockServerConnection(QuicConnectionId connection_id,
87 QuicDispatcher* dispatcher)
88 : MockConnection(connection_id, Perspective::IS_SERVER),
89 dispatcher_(dispatcher) {}
91 void UnregisterOnConnectionClosed() {
92 LOG(ERROR) << "Unregistering " << connection_id();
93 dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR);
96 private:
97 QuicDispatcher* dispatcher_;
100 QuicServerSession* CreateSession(QuicDispatcher* dispatcher,
101 const QuicConfig& config,
102 QuicConnectionId connection_id,
103 const IPEndPoint& client_address,
104 TestServerSession** session) {
105 MockServerConnection* connection =
106 new MockServerConnection(connection_id, dispatcher);
107 *session = new TestServerSession(config, connection);
108 connection->set_visitor(*session);
109 ON_CALL(*connection, SendConnectionClose(_)).WillByDefault(
110 WithoutArgs(Invoke(
111 connection, &MockServerConnection::UnregisterOnConnectionClosed)));
112 EXPECT_CALL(*reinterpret_cast<MockConnection*>((*session)->connection()),
113 ProcessUdpPacket(_, client_address, _));
115 return *session;
118 class QuicDispatcherTest : public ::testing::Test {
119 public:
120 QuicDispatcherTest()
121 : helper_(&eps_),
122 crypto_config_(QuicCryptoServerConfig::TESTING,
123 QuicRandom::GetInstance()),
124 dispatcher_(config_, &crypto_config_, &eps_),
125 time_wait_list_manager_(nullptr),
126 session1_(nullptr),
127 session2_(nullptr) {
128 dispatcher_.InitializeWithWriter(new QuicDefaultPacketWriter(1));
131 ~QuicDispatcherTest() override {}
133 MockConnection* connection1() {
134 return reinterpret_cast<MockConnection*>(session1_->connection());
137 MockConnection* connection2() {
138 return reinterpret_cast<MockConnection*>(session2_->connection());
141 void ProcessPacket(IPEndPoint client_address,
142 QuicConnectionId connection_id,
143 bool has_version_flag,
144 const string& data) {
145 ProcessPacket(client_address, connection_id, has_version_flag, data,
146 PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_SEQUENCE_NUMBER);
149 void ProcessPacket(IPEndPoint client_address,
150 QuicConnectionId connection_id,
151 bool has_version_flag,
152 const string& data,
153 QuicConnectionIdLength connection_id_length,
154 QuicSequenceNumberLength sequence_number_length) {
155 ProcessPacket(client_address, connection_id, has_version_flag, data,
156 connection_id_length, sequence_number_length, 1);
159 void ProcessPacket(IPEndPoint client_address,
160 QuicConnectionId connection_id,
161 bool has_version_flag,
162 const string& data,
163 QuicConnectionIdLength connection_id_length,
164 QuicSequenceNumberLength sequence_number_length,
165 QuicPacketSequenceNumber sequence_number) {
166 scoped_ptr<QuicEncryptedPacket> packet(ConstructEncryptedPacket(
167 connection_id, has_version_flag, false, sequence_number, data,
168 connection_id_length, sequence_number_length));
169 data_ = string(packet->data(), packet->length());
170 dispatcher_.ProcessPacket(server_address_, client_address, *packet);
173 void ValidatePacket(const QuicEncryptedPacket& packet) {
174 EXPECT_EQ(data_.length(), packet.AsStringPiece().length());
175 EXPECT_EQ(data_, packet.AsStringPiece());
178 void CreateTimeWaitListManager() {
179 time_wait_list_manager_ = new MockTimeWaitListManager(
180 QuicDispatcherPeer::GetWriter(&dispatcher_), &dispatcher_, &helper_);
181 // dispatcher_ takes the ownership of time_wait_list_manager_.
182 QuicDispatcherPeer::SetTimeWaitListManager(&dispatcher_,
183 time_wait_list_manager_);
186 EpollServer eps_;
187 QuicEpollConnectionHelper helper_;
188 QuicConfig config_;
189 QuicCryptoServerConfig crypto_config_;
190 IPEndPoint server_address_;
191 TestDispatcher dispatcher_;
192 MockTimeWaitListManager* time_wait_list_manager_;
193 TestServerSession* session1_;
194 TestServerSession* session2_;
195 string data_;
198 TEST_F(QuicDispatcherTest, ProcessPackets) {
199 IPEndPoint client_address(net::test::Loopback4(), 1);
200 server_address_ = IPEndPoint(net::test::Any4(), 5);
202 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address))
203 .WillOnce(testing::Return(
204 CreateSession(&dispatcher_, config_, 1, client_address, &session1_)));
205 ProcessPacket(client_address, 1, true, "foo");
206 EXPECT_EQ(client_address, dispatcher_.current_client_address());
207 EXPECT_EQ(server_address_, dispatcher_.current_server_address());
209 EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, client_address))
210 .WillOnce(testing::Return(
211 CreateSession(&dispatcher_, config_, 2, client_address, &session2_)));
212 ProcessPacket(client_address, 2, true, "bar");
214 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
215 ProcessUdpPacket(_, _, _)).Times(1).
216 WillOnce(testing::WithArgs<2>(Invoke(
217 this, &QuicDispatcherTest::ValidatePacket)));
218 ProcessPacket(client_address, 1, false, "eep");
221 TEST_F(QuicDispatcherTest, Shutdown) {
222 IPEndPoint client_address(net::test::Loopback4(), 1);
224 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
225 .WillOnce(testing::Return(
226 CreateSession(&dispatcher_, config_, 1, client_address, &session1_)));
228 ProcessPacket(client_address, 1, true, "foo");
230 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
231 SendConnectionClose(QUIC_PEER_GOING_AWAY));
233 dispatcher_.Shutdown();
236 TEST_F(QuicDispatcherTest, TimeWaitListManager) {
237 CreateTimeWaitListManager();
239 // Create a new session.
240 IPEndPoint client_address(net::test::Loopback4(), 1);
241 QuicConnectionId connection_id = 1;
242 EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address))
243 .WillOnce(testing::Return(CreateSession(
244 &dispatcher_, config_, connection_id, client_address, &session1_)));
245 ProcessPacket(client_address, connection_id, true, "foo");
247 // Close the connection by sending public reset packet.
248 QuicPublicResetPacket packet;
249 packet.public_header.connection_id = connection_id;
250 packet.public_header.reset_flag = true;
251 packet.public_header.version_flag = false;
252 packet.rejected_sequence_number = 19191;
253 packet.nonce_proof = 132232;
254 scoped_ptr<QuicEncryptedPacket> encrypted(
255 QuicFramer::BuildPublicResetPacket(packet));
256 EXPECT_CALL(*session1_, OnConnectionClosed(QUIC_PUBLIC_RESET, true)).Times(1)
257 .WillOnce(WithoutArgs(Invoke(
258 reinterpret_cast<MockServerConnection*>(session1_->connection()),
259 &MockServerConnection::UnregisterOnConnectionClosed)));
260 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()),
261 ProcessUdpPacket(_, _, _))
262 .WillOnce(Invoke(
263 reinterpret_cast<MockConnection*>(session1_->connection()),
264 &MockConnection::ReallyProcessUdpPacket));
265 dispatcher_.ProcessPacket(IPEndPoint(), client_address, *encrypted);
266 EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id));
268 // Dispatcher forwards subsequent packets for this connection_id to the time
269 // wait list manager.
270 EXPECT_CALL(*time_wait_list_manager_,
271 ProcessPacket(_, _, connection_id, _, _)).Times(1);
272 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
273 .Times(0);
274 ProcessPacket(client_address, connection_id, true, "foo");
277 TEST_F(QuicDispatcherTest, NoVersionPacketToTimeWaitListManager) {
278 CreateTimeWaitListManager();
280 IPEndPoint client_address(net::test::Loopback4(), 1);
281 QuicConnectionId connection_id = 1;
282 // Dispatcher forwards all packets for this connection_id to the time wait
283 // list manager.
284 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0);
285 EXPECT_CALL(*time_wait_list_manager_,
286 ProcessPacket(_, _, connection_id, _, _)).Times(1);
287 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
288 .Times(1);
289 ProcessPacket(client_address, connection_id, false, "data");
292 TEST_F(QuicDispatcherTest, ProcessPacketWithZeroPort) {
293 CreateTimeWaitListManager();
295 IPEndPoint client_address(net::test::Loopback4(), 0);
296 server_address_ = IPEndPoint(net::test::Any4(), 5);
298 // dispatcher_ should drop this packet.
299 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)).Times(0);
300 EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0);
301 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
302 .Times(0);
303 ProcessPacket(client_address, 1, true, "foo");
306 TEST_F(QuicDispatcherTest, OKSeqNoPacketProcessed) {
307 IPEndPoint client_address(net::test::Loopback4(), 1);
308 QuicConnectionId connection_id = 1;
309 server_address_ = IPEndPoint(net::test::Any4(), 5);
311 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address))
312 .WillOnce(testing::Return(
313 CreateSession(&dispatcher_, config_, 1, client_address, &session1_)));
314 // A packet whose sequence number is the largest that is allowed to start a
315 // connection.
316 ProcessPacket(client_address, connection_id, true, "data",
317 PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_SEQUENCE_NUMBER,
318 QuicDispatcher::kMaxReasonableInitialSequenceNumber);
319 EXPECT_EQ(client_address, dispatcher_.current_client_address());
320 EXPECT_EQ(server_address_, dispatcher_.current_server_address());
323 TEST_F(QuicDispatcherTest, TooBigSeqNoPacketToTimeWaitListManager) {
324 CreateTimeWaitListManager();
326 IPEndPoint client_address(net::test::Loopback4(), 1);
327 QuicConnectionId connection_id = 1;
328 // Dispatcher forwards this packet for this connection_id to the time wait
329 // list manager.
330 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0);
331 EXPECT_CALL(*time_wait_list_manager_,
332 ProcessPacket(_, _, connection_id, _, _)).Times(1);
333 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
334 .Times(1);
335 // A packet whose sequence number is one to large to be allowed to start a
336 // connection.
337 ProcessPacket(client_address, connection_id, true, "data",
338 PACKET_8BYTE_CONNECTION_ID, PACKET_6BYTE_SEQUENCE_NUMBER,
339 QuicDispatcher::kMaxReasonableInitialSequenceNumber + 1);
342 // Verify the stopgap test: Packets with truncated connection IDs should be
343 // dropped.
344 class QuicDispatcherTestStrayPacketConnectionId
345 : public QuicDispatcherTest,
346 public ::testing::WithParamInterface<QuicConnectionIdLength> {};
348 // Packets with truncated connection IDs should be dropped.
349 TEST_P(QuicDispatcherTestStrayPacketConnectionId,
350 StrayPacketTruncatedConnectionId) {
351 const QuicConnectionIdLength connection_id_length = GetParam();
353 CreateTimeWaitListManager();
355 IPEndPoint client_address(net::test::Loopback4(), 1);
356 QuicConnectionId connection_id = 1;
357 // Dispatcher drops this packet.
358 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0);
359 EXPECT_CALL(*time_wait_list_manager_,
360 ProcessPacket(_, _, connection_id, _, _)).Times(0);
361 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _))
362 .Times(0);
363 ProcessPacket(client_address, connection_id, true, "data",
364 connection_id_length, PACKET_6BYTE_SEQUENCE_NUMBER);
367 INSTANTIATE_TEST_CASE_P(ConnectionIdLength,
368 QuicDispatcherTestStrayPacketConnectionId,
369 ::testing::Values(PACKET_0BYTE_CONNECTION_ID,
370 PACKET_1BYTE_CONNECTION_ID,
371 PACKET_4BYTE_CONNECTION_ID));
373 class BlockingWriter : public QuicPacketWriterWrapper {
374 public:
375 BlockingWriter() : write_blocked_(false) {}
377 bool IsWriteBlocked() const override { return write_blocked_; }
378 void SetWritable() override { write_blocked_ = false; }
380 WriteResult WritePacket(const char* buffer,
381 size_t buf_len,
382 const IPAddressNumber& self_client_address,
383 const IPEndPoint& peer_client_address) override {
384 // It would be quite possible to actually implement this method here with
385 // the fake blocked status, but it would be significantly more work in
386 // Chromium, and since it's not called anyway, don't bother.
387 LOG(DFATAL) << "Not supported";
388 return WriteResult();
391 bool write_blocked_;
394 class QuicDispatcherWriteBlockedListTest : public QuicDispatcherTest {
395 public:
396 void SetUp() override {
397 writer_ = new BlockingWriter;
398 QuicDispatcherPeer::SetPacketWriterFactory(&dispatcher_,
399 new TestWriterFactory());
400 QuicDispatcherPeer::UseWriter(&dispatcher_, writer_);
402 IPEndPoint client_address(net::test::Loopback4(), 1);
404 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
405 .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 1,
406 client_address, &session1_)));
407 ProcessPacket(client_address, 1, true, "foo");
409 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address))
410 .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 2,
411 client_address, &session2_)));
412 ProcessPacket(client_address, 2, true, "bar");
414 blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(&dispatcher_);
417 void TearDown() override {
418 EXPECT_CALL(*connection1(), SendConnectionClose(QUIC_PEER_GOING_AWAY));
419 EXPECT_CALL(*connection2(), SendConnectionClose(QUIC_PEER_GOING_AWAY));
420 dispatcher_.Shutdown();
423 void SetBlocked() {
424 writer_->write_blocked_ = true;
427 void BlockConnection2() {
428 writer_->write_blocked_ = true;
429 dispatcher_.OnWriteBlocked(connection2());
432 protected:
433 BlockingWriter* writer_;
434 QuicDispatcher::WriteBlockedList* blocked_list_;
437 TEST_F(QuicDispatcherWriteBlockedListTest, BasicOnCanWrite) {
438 // No OnCanWrite calls because no connections are blocked.
439 dispatcher_.OnCanWrite();
441 // Register connection 1 for events, and make sure it's notified.
442 SetBlocked();
443 dispatcher_.OnWriteBlocked(connection1());
444 EXPECT_CALL(*connection1(), OnCanWrite());
445 dispatcher_.OnCanWrite();
447 // It should get only one notification.
448 EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
449 dispatcher_.OnCanWrite();
450 EXPECT_FALSE(dispatcher_.HasPendingWrites());
453 TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteOrder) {
454 // Make sure we handle events in order.
455 InSequence s;
456 SetBlocked();
457 dispatcher_.OnWriteBlocked(connection1());
458 dispatcher_.OnWriteBlocked(connection2());
459 EXPECT_CALL(*connection1(), OnCanWrite());
460 EXPECT_CALL(*connection2(), OnCanWrite());
461 dispatcher_.OnCanWrite();
463 // Check the other ordering.
464 SetBlocked();
465 dispatcher_.OnWriteBlocked(connection2());
466 dispatcher_.OnWriteBlocked(connection1());
467 EXPECT_CALL(*connection2(), OnCanWrite());
468 EXPECT_CALL(*connection1(), OnCanWrite());
469 dispatcher_.OnCanWrite();
472 TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteRemove) {
473 // Add and remove one connction.
474 SetBlocked();
475 dispatcher_.OnWriteBlocked(connection1());
476 blocked_list_->erase(connection1());
477 EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
478 dispatcher_.OnCanWrite();
480 // Add and remove one connction and make sure it doesn't affect others.
481 SetBlocked();
482 dispatcher_.OnWriteBlocked(connection1());
483 dispatcher_.OnWriteBlocked(connection2());
484 blocked_list_->erase(connection1());
485 EXPECT_CALL(*connection2(), OnCanWrite());
486 dispatcher_.OnCanWrite();
488 // Add it, remove it, and add it back and make sure things are OK.
489 SetBlocked();
490 dispatcher_.OnWriteBlocked(connection1());
491 blocked_list_->erase(connection1());
492 dispatcher_.OnWriteBlocked(connection1());
493 EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
494 dispatcher_.OnCanWrite();
497 TEST_F(QuicDispatcherWriteBlockedListTest, DoubleAdd) {
498 // Make sure a double add does not necessitate a double remove.
499 SetBlocked();
500 dispatcher_.OnWriteBlocked(connection1());
501 dispatcher_.OnWriteBlocked(connection1());
502 blocked_list_->erase(connection1());
503 EXPECT_CALL(*connection1(), OnCanWrite()).Times(0);
504 dispatcher_.OnCanWrite();
506 // Make sure a double add does not result in two OnCanWrite calls.
507 SetBlocked();
508 dispatcher_.OnWriteBlocked(connection1());
509 dispatcher_.OnWriteBlocked(connection1());
510 EXPECT_CALL(*connection1(), OnCanWrite()).Times(1);
511 dispatcher_.OnCanWrite();
514 TEST_F(QuicDispatcherWriteBlockedListTest, OnCanWriteHandleBlock) {
515 // Finally make sure if we write block on a write call, we stop calling.
516 InSequence s;
517 SetBlocked();
518 dispatcher_.OnWriteBlocked(connection1());
519 dispatcher_.OnWriteBlocked(connection2());
520 EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(
521 Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked));
522 EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
523 dispatcher_.OnCanWrite();
525 // And we'll resume where we left off when we get another call.
526 EXPECT_CALL(*connection2(), OnCanWrite());
527 dispatcher_.OnCanWrite();
530 TEST_F(QuicDispatcherWriteBlockedListTest, LimitedWrites) {
531 // Make sure we call both writers. The first will register for more writing
532 // but should not be immediately called due to limits.
533 InSequence s;
534 SetBlocked();
535 dispatcher_.OnWriteBlocked(connection1());
536 dispatcher_.OnWriteBlocked(connection2());
537 EXPECT_CALL(*connection1(), OnCanWrite());
538 EXPECT_CALL(*connection2(), OnCanWrite()).WillOnce(
539 Invoke(this, &QuicDispatcherWriteBlockedListTest::BlockConnection2));
540 dispatcher_.OnCanWrite();
541 EXPECT_TRUE(dispatcher_.HasPendingWrites());
543 // Now call OnCanWrite again, and connection1 should get its second chance
544 EXPECT_CALL(*connection2(), OnCanWrite());
545 dispatcher_.OnCanWrite();
546 EXPECT_FALSE(dispatcher_.HasPendingWrites());
549 TEST_F(QuicDispatcherWriteBlockedListTest, TestWriteLimits) {
550 // Finally make sure if we write block on a write call, we stop calling.
551 InSequence s;
552 SetBlocked();
553 dispatcher_.OnWriteBlocked(connection1());
554 dispatcher_.OnWriteBlocked(connection2());
555 EXPECT_CALL(*connection1(), OnCanWrite()).WillOnce(
556 Invoke(this, &QuicDispatcherWriteBlockedListTest::SetBlocked));
557 EXPECT_CALL(*connection2(), OnCanWrite()).Times(0);
558 dispatcher_.OnCanWrite();
559 EXPECT_TRUE(dispatcher_.HasPendingWrites());
561 // And we'll resume where we left off when we get another call.
562 EXPECT_CALL(*connection2(), OnCanWrite());
563 dispatcher_.OnCanWrite();
564 EXPECT_FALSE(dispatcher_.HasPendingWrites());
567 } // namespace
568 } // namespace test
569 } // namespace tools
570 } // namespace net