OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/tools/quic/quic_dispatcher.h" | 5 #include "net/tools/quic/quic_dispatcher.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
9 #include "base/strings/string_piece.h" | 9 #include "base/strings/string_piece.h" |
10 #include "net/quic/crypto/crypto_handshake.h" | 10 #include "net/quic/crypto/crypto_handshake.h" |
(...skipping 24 matching lines...) Expand all Loading... |
35 using testing::InSequence; | 35 using testing::InSequence; |
36 using testing::Invoke; | 36 using testing::Invoke; |
37 using testing::WithoutArgs; | 37 using testing::WithoutArgs; |
38 using testing::_; | 38 using testing::_; |
39 | 39 |
40 namespace net { | 40 namespace net { |
41 namespace tools { | 41 namespace tools { |
42 namespace test { | 42 namespace test { |
43 namespace { | 43 namespace { |
44 | 44 |
| 45 class TestServerSession : public QuicServerSession { |
| 46 public: |
| 47 TestServerSession(const QuicConfig& config, QuicConnection* connection) |
| 48 : QuicServerSession(config, connection, nullptr) {} |
| 49 ~TestServerSession() override{}; |
| 50 |
| 51 MOCK_METHOD2(OnConnectionClosed, void(QuicErrorCode error, bool from_peer)); |
| 52 MOCK_METHOD1(CreateIncomingDataStream, QuicDataStream*(QuicStreamId id)); |
| 53 MOCK_METHOD0(CreateOutgoingDataStream, QuicDataStream*()); |
| 54 |
| 55 private: |
| 56 DISALLOW_COPY_AND_ASSIGN(TestServerSession); |
| 57 }; |
| 58 |
45 class TestDispatcher : public QuicDispatcher { | 59 class TestDispatcher : public QuicDispatcher { |
46 public: | 60 public: |
47 explicit TestDispatcher(const QuicConfig& config, | 61 explicit TestDispatcher(const QuicConfig& config, |
48 const QuicCryptoServerConfig& crypto_config, | 62 const QuicCryptoServerConfig& crypto_config, |
49 EpollServer* eps) | 63 EpollServer* eps) |
50 : QuicDispatcher(config, | 64 : QuicDispatcher(config, |
51 crypto_config, | 65 crypto_config, |
52 QuicSupportedVersions(), | 66 QuicSupportedVersions(), |
53 new QuicDispatcher::DefaultPacketWriterFactory(), | 67 new QuicDispatcher::DefaultPacketWriterFactory(), |
54 new QuicEpollConnectionHelper(eps)) { | 68 new QuicEpollConnectionHelper(eps)) { |
55 } | 69 } |
56 | 70 |
57 MOCK_METHOD3(CreateQuicSession, QuicSession*( | 71 MOCK_METHOD3(CreateQuicSession, |
58 QuicConnectionId connection_id, | 72 QuicServerSession*(QuicConnectionId connection_id, |
59 const IPEndPoint& server_address, | 73 const IPEndPoint& server_address, |
60 const IPEndPoint& client_address)); | 74 const IPEndPoint& client_address)); |
61 | 75 |
62 using QuicDispatcher::current_server_address; | 76 using QuicDispatcher::current_server_address; |
63 using QuicDispatcher::current_client_address; | 77 using QuicDispatcher::current_client_address; |
64 }; | 78 }; |
65 | 79 |
66 // A Connection class which unregisters the session from the dispatcher | 80 // A Connection class which unregisters the session from the dispatcher |
67 // when sending connection close. | 81 // when sending connection close. |
68 // It'd be slightly more realistic to do this from the Session but it would | 82 // It'd be slightly more realistic to do this from the Session but it would |
69 // involve a lot more mocking. | 83 // involve a lot more mocking. |
70 class MockServerConnection : public MockConnection { | 84 class MockServerConnection : public MockConnection { |
71 public: | 85 public: |
72 MockServerConnection(QuicConnectionId connection_id, | 86 MockServerConnection(QuicConnectionId connection_id, |
73 QuicDispatcher* dispatcher) | 87 QuicDispatcher* dispatcher) |
74 : MockConnection(connection_id, Perspective::IS_SERVER), | 88 : MockConnection(connection_id, Perspective::IS_SERVER), |
75 dispatcher_(dispatcher) {} | 89 dispatcher_(dispatcher) {} |
76 | 90 |
77 void UnregisterOnConnectionClosed() { | 91 void UnregisterOnConnectionClosed() { |
78 LOG(ERROR) << "Unregistering " << connection_id(); | 92 LOG(ERROR) << "Unregistering " << connection_id(); |
79 dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR); | 93 dispatcher_->OnConnectionClosed(connection_id(), QUIC_NO_ERROR); |
80 } | 94 } |
| 95 |
81 private: | 96 private: |
82 QuicDispatcher* dispatcher_; | 97 QuicDispatcher* dispatcher_; |
83 }; | 98 }; |
84 | 99 |
85 QuicSession* CreateSession(QuicDispatcher* dispatcher, | 100 QuicServerSession* CreateSession(QuicDispatcher* dispatcher, |
86 QuicConnectionId connection_id, | 101 const QuicConfig& config, |
87 const IPEndPoint& client_address, | 102 QuicConnectionId connection_id, |
88 MockSession** session) { | 103 const IPEndPoint& client_address, |
| 104 TestServerSession** session) { |
89 MockServerConnection* connection = | 105 MockServerConnection* connection = |
90 new MockServerConnection(connection_id, dispatcher); | 106 new MockServerConnection(connection_id, dispatcher); |
91 *session = new MockSession(connection); | 107 *session = new TestServerSession(config, connection); |
| 108 connection->set_visitor(*session); |
92 ON_CALL(*connection, SendConnectionClose(_)).WillByDefault( | 109 ON_CALL(*connection, SendConnectionClose(_)).WillByDefault( |
93 WithoutArgs(Invoke( | 110 WithoutArgs(Invoke( |
94 connection, &MockServerConnection::UnregisterOnConnectionClosed))); | 111 connection, &MockServerConnection::UnregisterOnConnectionClosed))); |
95 EXPECT_CALL(*reinterpret_cast<MockConnection*>((*session)->connection()), | 112 EXPECT_CALL(*reinterpret_cast<MockConnection*>((*session)->connection()), |
96 ProcessUdpPacket(_, client_address, _)); | 113 ProcessUdpPacket(_, client_address, _)); |
97 | 114 |
98 return *session; | 115 return *session; |
99 } | 116 } |
100 | 117 |
101 class QuicDispatcherTest : public ::testing::Test { | 118 class QuicDispatcherTest : public ::testing::Test { |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
155 time_wait_list_manager_); | 172 time_wait_list_manager_); |
156 } | 173 } |
157 | 174 |
158 EpollServer eps_; | 175 EpollServer eps_; |
159 QuicEpollConnectionHelper helper_; | 176 QuicEpollConnectionHelper helper_; |
160 QuicConfig config_; | 177 QuicConfig config_; |
161 QuicCryptoServerConfig crypto_config_; | 178 QuicCryptoServerConfig crypto_config_; |
162 IPEndPoint server_address_; | 179 IPEndPoint server_address_; |
163 TestDispatcher dispatcher_; | 180 TestDispatcher dispatcher_; |
164 MockTimeWaitListManager* time_wait_list_manager_; | 181 MockTimeWaitListManager* time_wait_list_manager_; |
165 MockSession* session1_; | 182 TestServerSession* session1_; |
166 MockSession* session2_; | 183 TestServerSession* session2_; |
167 string data_; | 184 string data_; |
168 }; | 185 }; |
169 | 186 |
170 TEST_F(QuicDispatcherTest, ProcessPackets) { | 187 TEST_F(QuicDispatcherTest, ProcessPackets) { |
171 IPEndPoint client_address(net::test::Loopback4(), 1); | 188 IPEndPoint client_address(net::test::Loopback4(), 1); |
172 IPAddressNumber any4; | 189 IPAddressNumber any4; |
173 CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4)); | 190 CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4)); |
174 server_address_ = IPEndPoint(any4, 5); | 191 server_address_ = IPEndPoint(any4, 5); |
175 | 192 |
176 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)) | 193 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)) |
177 .WillOnce(testing::Return(CreateSession( | 194 .WillOnce(testing::Return( |
178 &dispatcher_, 1, client_address, &session1_))); | 195 CreateSession(&dispatcher_, config_, 1, client_address, &session1_))); |
179 ProcessPacket(client_address, 1, true, "foo"); | 196 ProcessPacket(client_address, 1, true, "foo"); |
180 EXPECT_EQ(client_address, dispatcher_.current_client_address()); | 197 EXPECT_EQ(client_address, dispatcher_.current_client_address()); |
181 EXPECT_EQ(server_address_, dispatcher_.current_server_address()); | 198 EXPECT_EQ(server_address_, dispatcher_.current_server_address()); |
182 | 199 |
183 | |
184 EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, client_address)) | 200 EXPECT_CALL(dispatcher_, CreateQuicSession(2, _, client_address)) |
185 .WillOnce(testing::Return(CreateSession( | 201 .WillOnce(testing::Return( |
186 &dispatcher_, 2, client_address, &session2_))); | 202 CreateSession(&dispatcher_, config_, 2, client_address, &session2_))); |
187 ProcessPacket(client_address, 2, true, "bar"); | 203 ProcessPacket(client_address, 2, true, "bar"); |
188 | 204 |
189 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), | 205 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), |
190 ProcessUdpPacket(_, _, _)).Times(1). | 206 ProcessUdpPacket(_, _, _)).Times(1). |
191 WillOnce(testing::WithArgs<2>(Invoke( | 207 WillOnce(testing::WithArgs<2>(Invoke( |
192 this, &QuicDispatcherTest::ValidatePacket))); | 208 this, &QuicDispatcherTest::ValidatePacket))); |
193 ProcessPacket(client_address, 1, false, "eep"); | 209 ProcessPacket(client_address, 1, false, "eep"); |
194 } | 210 } |
195 | 211 |
196 TEST_F(QuicDispatcherTest, Shutdown) { | 212 TEST_F(QuicDispatcherTest, Shutdown) { |
197 IPEndPoint client_address(net::test::Loopback4(), 1); | 213 IPEndPoint client_address(net::test::Loopback4(), 1); |
198 | 214 |
199 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) | 215 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) |
200 .WillOnce(testing::Return(CreateSession( | 216 .WillOnce(testing::Return( |
201 &dispatcher_, 1, client_address, &session1_))); | 217 CreateSession(&dispatcher_, config_, 1, client_address, &session1_))); |
202 | 218 |
203 ProcessPacket(client_address, 1, true, "foo"); | 219 ProcessPacket(client_address, 1, true, "foo"); |
204 | 220 |
205 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), | 221 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), |
206 SendConnectionClose(QUIC_PEER_GOING_AWAY)); | 222 SendConnectionClose(QUIC_PEER_GOING_AWAY)); |
207 | 223 |
208 dispatcher_.Shutdown(); | 224 dispatcher_.Shutdown(); |
209 } | 225 } |
210 | 226 |
211 TEST_F(QuicDispatcherTest, TimeWaitListManager) { | 227 TEST_F(QuicDispatcherTest, TimeWaitListManager) { |
212 CreateTimeWaitListManager(); | 228 CreateTimeWaitListManager(); |
213 | 229 |
214 // Create a new session. | 230 // Create a new session. |
215 IPEndPoint client_address(net::test::Loopback4(), 1); | 231 IPEndPoint client_address(net::test::Loopback4(), 1); |
216 QuicConnectionId connection_id = 1; | 232 QuicConnectionId connection_id = 1; |
217 EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address)) | 233 EXPECT_CALL(dispatcher_, CreateQuicSession(connection_id, _, client_address)) |
218 .WillOnce(testing::Return(CreateSession( | 234 .WillOnce(testing::Return(CreateSession( |
219 &dispatcher_, connection_id, client_address, &session1_))); | 235 &dispatcher_, config_, connection_id, client_address, &session1_))); |
220 ProcessPacket(client_address, connection_id, true, "foo"); | 236 ProcessPacket(client_address, connection_id, true, "foo"); |
221 | 237 |
222 // Close the connection by sending public reset packet. | 238 // Close the connection by sending public reset packet. |
223 QuicPublicResetPacket packet; | 239 QuicPublicResetPacket packet; |
224 packet.public_header.connection_id = connection_id; | 240 packet.public_header.connection_id = connection_id; |
225 packet.public_header.reset_flag = true; | 241 packet.public_header.reset_flag = true; |
226 packet.public_header.version_flag = false; | 242 packet.public_header.version_flag = false; |
227 packet.rejected_sequence_number = 19191; | 243 packet.rejected_sequence_number = 19191; |
228 packet.nonce_proof = 132232; | 244 packet.nonce_proof = 132232; |
229 scoped_ptr<QuicEncryptedPacket> encrypted( | 245 scoped_ptr<QuicEncryptedPacket> encrypted( |
230 QuicFramer::BuildPublicResetPacket(packet)); | 246 QuicFramer::BuildPublicResetPacket(packet)); |
231 EXPECT_CALL(*session1_, OnConnectionClosed(QUIC_PUBLIC_RESET, true)).Times(1) | 247 EXPECT_CALL(*session1_, OnConnectionClosed(QUIC_PUBLIC_RESET, true)).Times(1) |
232 .WillOnce(WithoutArgs(Invoke( | 248 .WillOnce(WithoutArgs(Invoke( |
233 reinterpret_cast<MockServerConnection*>(session1_->connection()), | 249 reinterpret_cast<MockServerConnection*>(session1_->connection()), |
234 &MockServerConnection::UnregisterOnConnectionClosed))); | 250 &MockServerConnection::UnregisterOnConnectionClosed))); |
235 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), | 251 EXPECT_CALL(*reinterpret_cast<MockConnection*>(session1_->connection()), |
236 ProcessUdpPacket(_, _, _)) | 252 ProcessUdpPacket(_, _, _)) |
237 .WillOnce(Invoke( | 253 .WillOnce(Invoke( |
238 reinterpret_cast<MockConnection*>(session1_->connection()), | 254 reinterpret_cast<MockConnection*>(session1_->connection()), |
239 &MockConnection::ReallyProcessUdpPacket)); | 255 &MockConnection::ReallyProcessUdpPacket)); |
240 dispatcher_.ProcessPacket(IPEndPoint(), client_address, *encrypted); | 256 dispatcher_.ProcessPacket(IPEndPoint(), client_address, *encrypted); |
241 EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); | 257 EXPECT_TRUE(time_wait_list_manager_->IsConnectionIdInTimeWait(connection_id)); |
242 | 258 |
243 // Dispatcher forwards subsequent packets for this connection_id to the time | 259 // Dispatcher forwards subsequent packets for this connection_id to the time |
244 // wait list manager. | 260 // wait list manager. |
245 EXPECT_CALL(*time_wait_list_manager_, | 261 EXPECT_CALL(*time_wait_list_manager_, |
246 ProcessPacket(_, _, connection_id, _, _)).Times(1); | 262 ProcessPacket(_, _, connection_id, _, _)).Times(1); |
247 EXPECT_CALL(*time_wait_list_manager_, | 263 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) |
248 AddConnectionIdToTimeWait(_, _, _)).Times(0); | 264 .Times(0); |
249 ProcessPacket(client_address, connection_id, true, "foo"); | 265 ProcessPacket(client_address, connection_id, true, "foo"); |
250 } | 266 } |
251 | 267 |
252 TEST_F(QuicDispatcherTest, StrayPacketToTimeWaitListManager) { | 268 TEST_F(QuicDispatcherTest, StrayPacketToTimeWaitListManager) { |
253 CreateTimeWaitListManager(); | 269 CreateTimeWaitListManager(); |
254 | 270 |
255 IPEndPoint client_address(net::test::Loopback4(), 1); | 271 IPEndPoint client_address(net::test::Loopback4(), 1); |
256 QuicConnectionId connection_id = 1; | 272 QuicConnectionId connection_id = 1; |
257 // Dispatcher forwards all packets for this connection_id to the time wait | 273 // Dispatcher forwards all packets for this connection_id to the time wait |
258 // list manager. | 274 // list manager. |
259 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0); | 275 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, _)).Times(0); |
260 EXPECT_CALL(*time_wait_list_manager_, | 276 EXPECT_CALL(*time_wait_list_manager_, |
261 ProcessPacket(_, _, connection_id, _, _)).Times(1); | 277 ProcessPacket(_, _, connection_id, _, _)).Times(1); |
262 EXPECT_CALL(*time_wait_list_manager_, | 278 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) |
263 AddConnectionIdToTimeWait(_, _, _)).Times(1); | 279 .Times(1); |
264 ProcessPacket(client_address, connection_id, false, "data"); | 280 ProcessPacket(client_address, connection_id, false, "data"); |
265 } | 281 } |
266 | 282 |
267 TEST_F(QuicDispatcherTest, ProcessPacketWithBogusPort) { | 283 TEST_F(QuicDispatcherTest, ProcessPacketWithBogusPort) { |
268 CreateTimeWaitListManager(); | 284 CreateTimeWaitListManager(); |
269 | 285 |
270 IPEndPoint client_address(net::test::Loopback4(), 0); | 286 IPEndPoint client_address(net::test::Loopback4(), 0); |
271 IPAddressNumber any4; | 287 IPAddressNumber any4; |
272 CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4)); | 288 CHECK(net::ParseIPLiteralToNumber("0.0.0.0", &any4)); |
273 server_address_ = IPEndPoint(any4, 5); | 289 server_address_ = IPEndPoint(any4, 5); |
274 | 290 |
275 // dispatcher_ should drop this packet. | 291 // dispatcher_ should drop this packet. |
276 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)).Times(0); | 292 EXPECT_CALL(dispatcher_, CreateQuicSession(1, _, client_address)).Times(0); |
277 EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); | 293 EXPECT_CALL(*time_wait_list_manager_, ProcessPacket(_, _, _, _, _)).Times(0); |
278 EXPECT_CALL(*time_wait_list_manager_, | 294 EXPECT_CALL(*time_wait_list_manager_, AddConnectionIdToTimeWait(_, _, _)) |
279 AddConnectionIdToTimeWait(_, _, _)).Times(0); | 295 .Times(0); |
280 ProcessPacket(client_address, 1, true, "foo"); | 296 ProcessPacket(client_address, 1, true, "foo"); |
281 EXPECT_EQ(client_address, dispatcher_.current_client_address()); | 297 EXPECT_EQ(client_address, dispatcher_.current_client_address()); |
282 EXPECT_EQ(server_address_, dispatcher_.current_server_address()); | 298 EXPECT_EQ(server_address_, dispatcher_.current_server_address()); |
283 } | 299 } |
284 | 300 |
285 class BlockingWriter : public QuicPacketWriterWrapper { | 301 class BlockingWriter : public QuicPacketWriterWrapper { |
286 public: | 302 public: |
287 BlockingWriter() : write_blocked_(false) {} | 303 BlockingWriter() : write_blocked_(false) {} |
288 | 304 |
289 bool IsWriteBlocked() const override { return write_blocked_; } | 305 bool IsWriteBlocked() const override { return write_blocked_; } |
(...skipping 17 matching lines...) Expand all Loading... |
307 public: | 323 public: |
308 void SetUp() override { | 324 void SetUp() override { |
309 writer_ = new BlockingWriter; | 325 writer_ = new BlockingWriter; |
310 QuicDispatcherPeer::SetPacketWriterFactory(&dispatcher_, | 326 QuicDispatcherPeer::SetPacketWriterFactory(&dispatcher_, |
311 new TestWriterFactory()); | 327 new TestWriterFactory()); |
312 QuicDispatcherPeer::UseWriter(&dispatcher_, writer_); | 328 QuicDispatcherPeer::UseWriter(&dispatcher_, writer_); |
313 | 329 |
314 IPEndPoint client_address(net::test::Loopback4(), 1); | 330 IPEndPoint client_address(net::test::Loopback4(), 1); |
315 | 331 |
316 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) | 332 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) |
317 .WillOnce(testing::Return(CreateSession( | 333 .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 1, |
318 &dispatcher_, 1, client_address, &session1_))); | 334 client_address, &session1_))); |
319 ProcessPacket(client_address, 1, true, "foo"); | 335 ProcessPacket(client_address, 1, true, "foo"); |
320 | 336 |
321 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) | 337 EXPECT_CALL(dispatcher_, CreateQuicSession(_, _, client_address)) |
322 .WillOnce(testing::Return(CreateSession( | 338 .WillOnce(testing::Return(CreateSession(&dispatcher_, config_, 2, |
323 &dispatcher_, 2, client_address, &session2_))); | 339 client_address, &session2_))); |
324 ProcessPacket(client_address, 2, true, "bar"); | 340 ProcessPacket(client_address, 2, true, "bar"); |
325 | 341 |
326 blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(&dispatcher_); | 342 blocked_list_ = QuicDispatcherPeer::GetWriteBlockedList(&dispatcher_); |
327 } | 343 } |
328 | 344 |
329 void TearDown() override { | 345 void TearDown() override { |
330 EXPECT_CALL(*connection1(), SendConnectionClose(QUIC_PEER_GOING_AWAY)); | 346 EXPECT_CALL(*connection1(), SendConnectionClose(QUIC_PEER_GOING_AWAY)); |
331 EXPECT_CALL(*connection2(), SendConnectionClose(QUIC_PEER_GOING_AWAY)); | 347 EXPECT_CALL(*connection2(), SendConnectionClose(QUIC_PEER_GOING_AWAY)); |
332 dispatcher_.Shutdown(); | 348 dispatcher_.Shutdown(); |
333 } | 349 } |
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 // And we'll resume where we left off when we get another call. | 489 // And we'll resume where we left off when we get another call. |
474 EXPECT_CALL(*connection2(), OnCanWrite()); | 490 EXPECT_CALL(*connection2(), OnCanWrite()); |
475 dispatcher_.OnCanWrite(); | 491 dispatcher_.OnCanWrite(); |
476 EXPECT_FALSE(dispatcher_.HasPendingWrites()); | 492 EXPECT_FALSE(dispatcher_.HasPendingWrites()); |
477 } | 493 } |
478 | 494 |
479 } // namespace | 495 } // namespace |
480 } // namespace test | 496 } // namespace test |
481 } // namespace tools | 497 } // namespace tools |
482 } // namespace net | 498 } // namespace net |
OLD | NEW |