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"
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
;
35 using testing::InSequence
;
36 using testing::Invoke
;
37 using testing::WithoutArgs
;
45 class TestServerSession
: public QuicServerSession
{
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
*());
56 DISALLOW_COPY_AND_ASSIGN(TestServerSession
);
59 class TestDispatcher
: public QuicDispatcher
{
61 explicit TestDispatcher(const QuicConfig
& config
,
62 const QuicCryptoServerConfig
* crypto_config
,
64 : QuicDispatcher(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
{
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
);
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(
111 connection
, &MockServerConnection::UnregisterOnConnectionClosed
)));
112 EXPECT_CALL(*reinterpret_cast<MockConnection
*>((*session
)->connection()),
113 ProcessUdpPacket(_
, client_address
, _
));
118 class QuicDispatcherTest
: public ::testing::Test
{
122 crypto_config_(QuicCryptoServerConfig::TESTING
,
123 QuicRandom::GetInstance()),
124 dispatcher_(config_
, &crypto_config_
, &eps_
),
125 time_wait_list_manager_(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
,
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
,
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_
);
187 QuicEpollConnectionHelper helper_
;
189 QuicCryptoServerConfig crypto_config_
;
190 IPEndPoint server_address_
;
191 TestDispatcher dispatcher_
;
192 MockTimeWaitListManager
* time_wait_list_manager_
;
193 TestServerSession
* session1_
;
194 TestServerSession
* session2_
;
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(_
, _
, _
))
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(_
, _
, _
))
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
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(_
, _
, _
))
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(_
, _
, _
))
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
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
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(_
, _
, _
))
335 // A packet whose sequence number is one to large to be allowed to start a
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
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(_
, _
, _
))
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
{
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
,
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();
394 class QuicDispatcherWriteBlockedListTest
: public QuicDispatcherTest
{
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();
424 writer_
->write_blocked_
= true;
427 void BlockConnection2() {
428 writer_
->write_blocked_
= true;
429 dispatcher_
.OnWriteBlocked(connection2());
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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());