blob: 8f3bdde566bcc6d54825d8a85a7be14ad9bb21f3 [file] [log] [blame]
[email protected]e13201d82012-12-12 05:00:321// 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.
4
5#include "net/quic/quic_stream_factory.h"
6
[email protected]e13201d82012-12-12 05:00:327#include "base/run_loop.h"
[email protected]fc9be5802013-06-11 10:56:518#include "base/strings/string_util.h"
[email protected]eed749f92013-12-23 18:57:389#include "net/base/test_data_directory.h"
[email protected]6d1b4ed2013-07-10 03:57:5410#include "net/cert/cert_verifier.h"
[email protected]f2cb3cf2013-03-21 01:40:5311#include "net/dns/mock_host_resolver.h"
[email protected]e13201d82012-12-12 05:00:3212#include "net/http/http_response_headers.h"
13#include "net/http/http_response_info.h"
14#include "net/http/http_util.h"
[email protected]c49ff182013-09-28 08:33:2615#include "net/quic/crypto/crypto_handshake.h"
[email protected]4df69842013-02-27 06:32:1616#include "net/quic/crypto/quic_decrypter.h"
17#include "net/quic/crypto/quic_encrypter.h"
[email protected]e13201d82012-12-12 05:00:3218#include "net/quic/quic_http_stream.h"
19#include "net/quic/test_tools/mock_clock.h"
[email protected]e8ff26842013-03-22 21:02:0520#include "net/quic/test_tools/mock_crypto_client_stream_factory.h"
[email protected]9558c5d32012-12-22 00:08:1421#include "net/quic/test_tools/mock_random.h"
[email protected]1e960032013-12-20 19:00:2022#include "net/quic/test_tools/quic_test_packet_maker.h"
[email protected]e13201d82012-12-12 05:00:3223#include "net/quic/test_tools/quic_test_utils.h"
24#include "net/socket/socket_test_util.h"
[email protected]eed749f92013-12-23 18:57:3825#include "net/test/cert_test_util.h"
[email protected]e13201d82012-12-12 05:00:3226#include "testing/gtest/include/gtest/gtest.h"
27
[email protected]6e12d702013-11-13 00:17:1728using base::StringPiece;
29using std::string;
30using std::vector;
31
[email protected]e13201d82012-12-12 05:00:3232namespace net {
[email protected]e13201d82012-12-12 05:00:3233namespace test {
34
[email protected]3c772402013-12-18 21:38:1135namespace {
36const char kDefaultServerHostName[] = "www.google.com";
37const int kDefaultServerPort = 443;
38} // namespace anonymous
39
[email protected]c49ff182013-09-28 08:33:2640class QuicStreamFactoryPeer {
41 public:
42 static QuicCryptoClientConfig* GetOrCreateCryptoConfig(
43 QuicStreamFactory* factory,
44 const HostPortProxyPair& host_port_proxy_pair) {
45 return factory->GetOrCreateCryptoConfig(host_port_proxy_pair);
46 }
[email protected]4d283b32013-10-17 12:57:2747
48 static bool HasActiveSession(QuicStreamFactory* factory,
49 const HostPortProxyPair& host_port_proxy_pair) {
50 return factory->HasActiveSession(host_port_proxy_pair);
51 }
52
53 static QuicClientSession* GetActiveSession(
54 QuicStreamFactory* factory,
55 const HostPortProxyPair& host_port_proxy_pair) {
56 DCHECK(factory->HasActiveSession(host_port_proxy_pair));
57 return factory->active_sessions_[host_port_proxy_pair];
58 }
59
60 static bool IsLiveSession(QuicStreamFactory* factory,
61 QuicClientSession* session) {
62 for (QuicStreamFactory::SessionSet::iterator it =
63 factory->all_sessions_.begin();
64 it != factory->all_sessions_.end(); ++it) {
65 if (*it == session)
66 return true;
67 }
68 return false;
69 }
[email protected]c49ff182013-09-28 08:33:2670};
71
[email protected]1e960032013-12-20 19:00:2072class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
[email protected]e13201d82012-12-12 05:00:3273 protected:
74 QuicStreamFactoryTest()
[email protected]457d6952013-12-13 09:24:5875 : random_generator_(0),
[email protected]1e960032013-12-20 19:00:2076 maker_(GetParam(), 0),
[email protected]457d6952013-12-13 09:24:5877 clock_(new MockClock()),
[email protected]872edd9e2013-01-16 08:51:1578 factory_(&host_resolver_, &socket_factory_,
[email protected]77c6c162013-08-17 02:57:4579 base::WeakPtr<HttpServerProperties>(),
[email protected]e8ff26842013-03-22 21:02:0580 &crypto_client_stream_factory_,
[email protected]1e960032013-12-20 19:00:2081 &random_generator_, clock_, kDefaultMaxPacketSize,
82 SupportedVersions(GetParam())),
[email protected]3c772402013-12-18 21:38:1183 host_port_proxy_pair_(HostPortPair(kDefaultServerHostName,
84 kDefaultServerPort),
[email protected]6d1b4ed2013-07-10 03:57:5485 ProxyServer::Direct()),
86 is_https_(false),
87 cert_verifier_(CertVerifier::CreateDefault()) {
[email protected]11c05872013-08-20 02:04:1288 factory_.set_require_confirmation(false);
[email protected]e13201d82012-12-12 05:00:3289 }
90
[email protected]e13201d82012-12-12 05:00:3291
[email protected]3c772402013-12-18 21:38:1192 int GetSourcePortForNewSession(const HostPortProxyPair& destination) {
93 // Should only be called if there is no active session for this destination.
94 EXPECT_EQ(NULL, factory_.CreateIfSessionExists(destination,
95 net_log_).get());
96 size_t socket_count = socket_factory_.udp_client_sockets().size();
97
98 MockRead reads[] = {
99 MockRead(ASYNC, OK, 0) // EOF
100 };
101 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
102 socket_data.StopAfter(1);
103 socket_factory_.AddSocketDataProvider(&socket_data);
104
105 QuicStreamRequest request(&factory_);
106 EXPECT_EQ(ERR_IO_PENDING, request.Request(destination, is_https_,
107 cert_verifier_.get(), net_log_,
108 callback_.callback()));
109
110 EXPECT_EQ(OK, callback_.WaitForResult());
111 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
112 EXPECT_TRUE(stream.get());
113 stream.reset();
114
115 QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
116 &factory_, destination);
117
118 if (socket_count + 1 != socket_factory_.udp_client_sockets().size()) {
119 EXPECT_TRUE(false);
120 return 0;
121 }
122
123 IPEndPoint endpoint;
124 socket_factory_.
125 udp_client_sockets()[socket_count]->GetLocalAddress(&endpoint);
126 int port = endpoint.port();
127
128 factory_.OnSessionClosed(session);
129 EXPECT_EQ(NULL, factory_.CreateIfSessionExists(destination,
130 net_log_).get());
131 EXPECT_TRUE(socket_data.at_read_eof());
132 EXPECT_TRUE(socket_data.at_write_eof());
133 return port;
134 }
135
[email protected]e13201d82012-12-12 05:00:32136 MockHostResolver host_resolver_;
[email protected]0edce6a2013-05-08 18:02:40137 DeterministicMockClientSocketFactory socket_factory_;
[email protected]e8ff26842013-03-22 21:02:05138 MockCryptoClientStreamFactory crypto_client_stream_factory_;
[email protected]9558c5d32012-12-22 00:08:14139 MockRandom random_generator_;
[email protected]1e960032013-12-20 19:00:20140 QuicTestPacketMaker maker_;
[email protected]872edd9e2013-01-16 08:51:15141 MockClock* clock_; // Owned by factory_.
[email protected]e13201d82012-12-12 05:00:32142 QuicStreamFactory factory_;
143 HostPortProxyPair host_port_proxy_pair_;
[email protected]6d1b4ed2013-07-10 03:57:54144 bool is_https_;
[email protected]ce542d52013-07-11 01:08:39145 scoped_ptr<CertVerifier> cert_verifier_;
[email protected]e13201d82012-12-12 05:00:32146 BoundNetLog net_log_;
147 TestCompletionCallback callback_;
[email protected]e13201d82012-12-12 05:00:32148};
149
[email protected]1e960032013-12-20 19:00:20150INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest,
151 ::testing::ValuesIn(QuicSupportedVersions()));
152
153TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) {
[email protected]e13201d82012-12-12 05:00:32154 EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
155 net_log_).get());
156}
157
[email protected]1e960032013-12-20 19:00:20158TEST_P(QuicStreamFactoryTest, Create) {
[email protected]69dfd1b2013-06-04 22:20:12159 MockRead reads[] = {
[email protected]25c31dc2013-06-05 17:56:04160 MockRead(ASYNC, OK, 0) // EOF
[email protected]69dfd1b2013-06-04 22:20:12161 };
[email protected]25c31dc2013-06-05 17:56:04162 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
[email protected]e13201d82012-12-12 05:00:32163 socket_factory_.AddSocketDataProvider(&socket_data);
[email protected]25c31dc2013-06-05 17:56:04164 socket_data.StopAfter(1);
[email protected]e13201d82012-12-12 05:00:32165
166 QuicStreamRequest request(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54167 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39168 cert_verifier_.get(), net_log_,
[email protected]e13201d82012-12-12 05:00:32169 callback_.callback()));
170
171 EXPECT_EQ(OK, callback_.WaitForResult());
172 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
[email protected]0edce6a2013-05-08 18:02:40173 EXPECT_TRUE(stream.get());
[email protected]e13201d82012-12-12 05:00:32174
175 // Will reset stream 3.
176 stream = factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_);
177 EXPECT_TRUE(stream.get());
178
[email protected]6d1b4ed2013-07-10 03:57:54179 // TODO(rtenneti): We should probably have a tests that HTTP and HTTPS result
180 // in streams on different sessions.
[email protected]e13201d82012-12-12 05:00:32181 QuicStreamRequest request2(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54182 EXPECT_EQ(OK, request2.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39183 cert_verifier_.get(), net_log_,
[email protected]e13201d82012-12-12 05:00:32184 callback_.callback()));
185 stream = request2.ReleaseStream(); // Will reset stream 5.
186 stream.reset(); // Will reset stream 7.
187
188 EXPECT_TRUE(socket_data.at_read_eof());
189 EXPECT_TRUE(socket_data.at_write_eof());
190}
191
[email protected]eed749f92013-12-23 18:57:38192TEST_P(QuicStreamFactoryTest, Pooling) {
193 MockRead reads[] = {
194 MockRead(ASYNC, OK, 0) // EOF
195 };
196 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
197 socket_factory_.AddSocketDataProvider(&socket_data);
198 socket_data.StopAfter(1);
199
200 HostPortProxyPair server2 = HostPortProxyPair(
201 HostPortPair("mail.google.com", kDefaultServerPort),
202 host_port_proxy_pair_.second);
203
204 host_resolver_.set_synchronous_mode(true);
205 host_resolver_.rules()->AddIPLiteralRule(
206 kDefaultServerHostName, "192.168.0.1", "");
207 host_resolver_.rules()->AddIPLiteralRule(
208 "mail.google.com", "192.168.0.1", "");
209
210 QuicStreamRequest request(&factory_);
211 EXPECT_EQ(OK, request.Request(host_port_proxy_pair_, is_https_,
212 cert_verifier_.get(), net_log_,
213 callback_.callback()));
214 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
215 EXPECT_TRUE(stream.get());
216
217 TestCompletionCallback callback;
218 QuicStreamRequest request2(&factory_);
219 EXPECT_EQ(OK, request2.Request(server2, is_https_,
220 cert_verifier_.get(), net_log_,
221 callback.callback()));
222 scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
223 EXPECT_TRUE(stream2.get());
224
225 EXPECT_EQ(
226 QuicStreamFactoryPeer::GetActiveSession(&factory_, host_port_proxy_pair_),
227 QuicStreamFactoryPeer::GetActiveSession(&factory_, server2));
228
229 EXPECT_TRUE(socket_data.at_read_eof());
230 EXPECT_TRUE(socket_data.at_write_eof());
231}
232
233TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) {
234 MockRead reads[] = {
235 MockRead(ASYNC, OK, 0) // EOF
236 };
237 DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
238 DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
239 socket_factory_.AddSocketDataProvider(&socket_data1);
240 socket_factory_.AddSocketDataProvider(&socket_data2);
241 socket_data1.StopAfter(1);
242 socket_data2.StopAfter(1);
243
244 HostPortProxyPair server2 = HostPortProxyPair(
245 HostPortPair("mail.google.com", kDefaultServerPort),
246 host_port_proxy_pair_.second);
247
248 host_resolver_.set_synchronous_mode(true);
249 host_resolver_.rules()->AddIPLiteralRule(
250 kDefaultServerHostName, "192.168.0.1", "");
251 host_resolver_.rules()->AddIPLiteralRule(
252 "mail.google.com", "192.168.0.1", "");
253
254 QuicStreamRequest request(&factory_);
255 EXPECT_EQ(OK, request.Request(host_port_proxy_pair_, is_https_,
256 cert_verifier_.get(), net_log_,
257 callback_.callback()));
258 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
259 EXPECT_TRUE(stream.get());
260
261 TestCompletionCallback callback;
262 QuicStreamRequest request2(&factory_);
263 EXPECT_EQ(OK, request2.Request(server2, is_https_,
264 cert_verifier_.get(), net_log_,
265 callback.callback()));
266 scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
267 EXPECT_TRUE(stream2.get());
268
269 factory_.OnSessionGoingAway(
270 QuicStreamFactoryPeer::GetActiveSession(&factory_,
271 host_port_proxy_pair_));
272 EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
273 host_port_proxy_pair_));
274 EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_, server2));
275
276 TestCompletionCallback callback3;
277 QuicStreamRequest request3(&factory_);
278 EXPECT_EQ(OK, request3.Request(server2, is_https_,
279 cert_verifier_.get(), net_log_,
280 callback3.callback()));
281 scoped_ptr<QuicHttpStream> stream3 = request3.ReleaseStream();
282 EXPECT_TRUE(stream3.get());
283
284 EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_, server2));
285
286 EXPECT_TRUE(socket_data1.at_read_eof());
287 EXPECT_TRUE(socket_data1.at_write_eof());
288 EXPECT_TRUE(socket_data2.at_read_eof());
289 EXPECT_TRUE(socket_data2.at_write_eof());
290}
291
292TEST_P(QuicStreamFactoryTest, HttpsPooling) {
293 MockRead reads[] = {
294 MockRead(ASYNC, OK, 0) // EOF
295 };
296 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
297 socket_factory_.AddSocketDataProvider(&socket_data);
298 socket_data.StopAfter(1);
299
300 HostPortProxyPair server1(HostPortPair("www.example.org", 443),
301 ProxyServer::Direct());
302 HostPortProxyPair server2(HostPortPair("mail.example.org", 443),
303 ProxyServer::Direct());
304
305 // Load a cert that is valid for:
306 // www.example.org (server1)
307 // mail.example.org (server2)
308 // www.example.com
309 base::FilePath certs_dir = GetTestCertsDirectory();
310 scoped_refptr<X509Certificate> test_cert(
311 ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
312 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
313 SSLInfo ssl_info;
314 ssl_info.cert = test_cert.get();
315 crypto_client_stream_factory_.set_ssl_info(&ssl_info);
316
317 host_resolver_.set_synchronous_mode(true);
318 host_resolver_.rules()->AddIPLiteralRule(
319 server1.first.host(), "192.168.0.1", "");
320 host_resolver_.rules()->AddIPLiteralRule(
321 server2.first.host(), "192.168.0.1", "");
322
323 QuicStreamRequest request(&factory_);
324 is_https_ = true;
325 EXPECT_EQ(OK, request.Request(server1, is_https_, cert_verifier_.get(),
326 net_log_, callback_.callback()));
327 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
328 EXPECT_TRUE(stream.get());
329
330 TestCompletionCallback callback;
331 QuicStreamRequest request2(&factory_);
332 EXPECT_EQ(OK, request2.Request(server2, is_https_, cert_verifier_.get(),
333 net_log_, callback_.callback()));
334 scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
335 EXPECT_TRUE(stream2.get());
336
337 EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession(&factory_, server1),
338 QuicStreamFactoryPeer::GetActiveSession(&factory_, server2));
339
340 EXPECT_TRUE(socket_data.at_read_eof());
341 EXPECT_TRUE(socket_data.at_write_eof());
342}
343
344TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) {
345 MockRead reads[] = {
346 MockRead(ASYNC, OK, 0) // EOF
347 };
348 DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0);
349 DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
350 socket_factory_.AddSocketDataProvider(&socket_data1);
351 socket_factory_.AddSocketDataProvider(&socket_data2);
352 socket_data1.StopAfter(1);
353 socket_data2.StopAfter(1);
354
355 HostPortProxyPair server1(HostPortPair("www.example.org", 443),
356 ProxyServer::Direct());
357 HostPortProxyPair server2(HostPortPair("mail.google.com", 443),
358 ProxyServer::Direct());
359
360 // Load a cert that is valid for:
361 // www.example.org (server1)
362 // mail.example.org
363 // www.example.com
364 // But is not valid for mail.google.com (server2).
365 base::FilePath certs_dir = GetTestCertsDirectory();
366 scoped_refptr<X509Certificate> test_cert(
367 ImportCertFromFile(certs_dir, "spdy_pooling.pem"));
368 ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert);
369 SSLInfo ssl_info;
370 ssl_info.cert = test_cert.get();
371 crypto_client_stream_factory_.set_ssl_info(&ssl_info);
372
373 host_resolver_.set_synchronous_mode(true);
374 host_resolver_.rules()->AddIPLiteralRule(
375 server1.first.host(), "192.168.0.1", "");
376 host_resolver_.rules()->AddIPLiteralRule(
377 server2.first.host(), "192.168.0.1", "");
378
379 QuicStreamRequest request(&factory_);
380 is_https_ = true;
381 EXPECT_EQ(OK, request.Request(server1, is_https_, cert_verifier_.get(),
382 net_log_, callback_.callback()));
383 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
384 EXPECT_TRUE(stream.get());
385
386 TestCompletionCallback callback;
387 QuicStreamRequest request2(&factory_);
388 EXPECT_EQ(OK, request2.Request(server2, is_https_, cert_verifier_.get(),
389 net_log_, callback_.callback()));
390 scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
391 EXPECT_TRUE(stream2.get());
392
393 EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession(&factory_, server1),
394 QuicStreamFactoryPeer::GetActiveSession(&factory_, server2));
395
396 EXPECT_TRUE(socket_data1.at_read_eof());
397 EXPECT_TRUE(socket_data1.at_write_eof());
398 EXPECT_TRUE(socket_data2.at_read_eof());
399 EXPECT_TRUE(socket_data2.at_write_eof());
400}
401
[email protected]1e960032013-12-20 19:00:20402TEST_P(QuicStreamFactoryTest, Goaway) {
[email protected]4d283b32013-10-17 12:57:27403 MockRead reads[] = {
404 MockRead(ASYNC, OK, 0) // EOF
405 };
406 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
407 socket_data.StopAfter(1);
408 socket_factory_.AddSocketDataProvider(&socket_data);
409 DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0);
410 socket_data2.StopAfter(1);
411 socket_factory_.AddSocketDataProvider(&socket_data2);
412
413 QuicStreamRequest request(&factory_);
414 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
415 cert_verifier_.get(), net_log_,
416 callback_.callback()));
417
418 EXPECT_EQ(OK, callback_.WaitForResult());
419 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
420 EXPECT_TRUE(stream.get());
421
422 // Mark the session as going away. Ensure that while it is still alive
423 // that it is no longer active.
424 QuicClientSession* session = QuicStreamFactoryPeer::GetActiveSession(
425 &factory_, host_port_proxy_pair_);
426 factory_.OnSessionGoingAway(session);
427 EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
428 EXPECT_FALSE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
429 host_port_proxy_pair_));
430 EXPECT_EQ(NULL, factory_.CreateIfSessionExists(host_port_proxy_pair_,
431 net_log_).get());
432
433 // Create a new request for the same destination and verify that a
434 // new session is created.
435 QuicStreamRequest request2(&factory_);
436 EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
437 cert_verifier_.get(), net_log_,
438 callback_.callback()));
439 EXPECT_EQ(OK, callback_.WaitForResult());
440 scoped_ptr<QuicHttpStream> stream2 = request2.ReleaseStream();
441 EXPECT_TRUE(stream2.get());
442
443 EXPECT_TRUE(QuicStreamFactoryPeer::HasActiveSession(&factory_,
444 host_port_proxy_pair_));
445 EXPECT_NE(session,
446 QuicStreamFactoryPeer::GetActiveSession(
447 &factory_, host_port_proxy_pair_));
448 EXPECT_EQ(true, QuicStreamFactoryPeer::IsLiveSession(&factory_, session));
449
450 stream2.reset();
451 stream.reset();
452
453 EXPECT_TRUE(socket_data.at_read_eof());
454 EXPECT_TRUE(socket_data.at_write_eof());
[email protected]3c772402013-12-18 21:38:11455 EXPECT_TRUE(socket_data2.at_read_eof());
456 EXPECT_TRUE(socket_data2.at_write_eof());
[email protected]4d283b32013-10-17 12:57:27457}
458
[email protected]1e960032013-12-20 19:00:20459TEST_P(QuicStreamFactoryTest, MaxOpenStream) {
[email protected]0b2294d32013-08-02 00:46:36460 MockRead reads[] = {
461 MockRead(ASYNC, OK, 0) // EOF
462 };
[email protected]1e960032013-12-20 19:00:20463 QuicStreamId stream_id = GetParam() > QUIC_VERSION_12 ? 5 : 3;
464 scoped_ptr<QuicEncryptedPacket> rst(
465 maker_.MakeRstPacket(1, true, stream_id, QUIC_STREAM_CANCELLED));
[email protected]06ff5152013-08-29 01:03:05466 MockWrite writes[] = {
467 MockWrite(ASYNC, rst->data(), rst->length(), 1),
468 };
469 DeterministicSocketData socket_data(reads, arraysize(reads),
470 writes, arraysize(writes));
[email protected]0b2294d32013-08-02 00:46:36471 socket_factory_.AddSocketDataProvider(&socket_data);
472 socket_data.StopAfter(1);
473
474 HttpRequestInfo request_info;
475 std::vector<QuicHttpStream*> streams;
476 // The MockCryptoClientStream sets max_open_streams to be
477 // 2 * kDefaultMaxStreamsPerConnection.
478 for (size_t i = 0; i < 2 * kDefaultMaxStreamsPerConnection; i++) {
479 QuicStreamRequest request(&factory_);
480 int rv = request.Request(host_port_proxy_pair_, is_https_,
481 cert_verifier_.get(), net_log_,
482 callback_.callback());
483 if (i == 0) {
484 EXPECT_EQ(ERR_IO_PENDING, rv);
485 EXPECT_EQ(OK, callback_.WaitForResult());
486 } else {
487 EXPECT_EQ(OK, rv);
488 }
489 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
490 EXPECT_TRUE(stream);
491 EXPECT_EQ(OK, stream->InitializeStream(
492 &request_info, DEFAULT_PRIORITY, net_log_, CompletionCallback()));
493 streams.push_back(stream.release());
494 }
495
496 QuicStreamRequest request(&factory_);
497 EXPECT_EQ(OK, request.Request(host_port_proxy_pair_, is_https_,
498 cert_verifier_.get(), net_log_,
499 CompletionCallback()));
500 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
501 EXPECT_TRUE(stream);
502 EXPECT_EQ(ERR_IO_PENDING, stream->InitializeStream(
503 &request_info, DEFAULT_PRIORITY, net_log_, callback_.callback()));
504
505 // Close the first stream.
506 streams.front()->Close(false);
507
508 ASSERT_TRUE(callback_.have_result());
509
510 EXPECT_EQ(OK, callback_.WaitForResult());
511
512 EXPECT_TRUE(socket_data.at_read_eof());
513 EXPECT_TRUE(socket_data.at_write_eof());
514 STLDeleteElements(&streams);
515}
516
[email protected]1e960032013-12-20 19:00:20517TEST_P(QuicStreamFactoryTest, ResolutionErrorInCreate) {
[email protected]0edce6a2013-05-08 18:02:40518 DeterministicSocketData socket_data(NULL, 0, NULL, 0);
[email protected]e13201d82012-12-12 05:00:32519 socket_factory_.AddSocketDataProvider(&socket_data);
520
[email protected]3c772402013-12-18 21:38:11521 host_resolver_.rules()->AddSimulatedFailure(kDefaultServerHostName);
[email protected]e13201d82012-12-12 05:00:32522
523 QuicStreamRequest request(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54524 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39525 cert_verifier_.get(), net_log_,
[email protected]e13201d82012-12-12 05:00:32526 callback_.callback()));
527
528 EXPECT_EQ(ERR_NAME_NOT_RESOLVED, callback_.WaitForResult());
529
530 EXPECT_TRUE(socket_data.at_read_eof());
531 EXPECT_TRUE(socket_data.at_write_eof());
532}
533
[email protected]1e960032013-12-20 19:00:20534TEST_P(QuicStreamFactoryTest, ConnectErrorInCreate) {
[email protected]3c772402013-12-18 21:38:11535 MockConnect connect(SYNCHRONOUS, ERR_ADDRESS_IN_USE);
536 DeterministicSocketData socket_data(NULL, 0, NULL, 0);
537 socket_data.set_connect_data(connect);
538 socket_factory_.AddSocketDataProvider(&socket_data);
539 socket_data.StopAfter(1);
540
541 QuicStreamRequest request(&factory_);
542 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
543 cert_verifier_.get(), net_log_,
544 callback_.callback()));
545
546 EXPECT_EQ(ERR_ADDRESS_IN_USE, callback_.WaitForResult());
547
548 EXPECT_TRUE(socket_data.at_read_eof());
549 EXPECT_TRUE(socket_data.at_write_eof());
550}
551
[email protected]1e960032013-12-20 19:00:20552TEST_P(QuicStreamFactoryTest, CancelCreate) {
[email protected]69dfd1b2013-06-04 22:20:12553 MockRead reads[] = {
[email protected]25c31dc2013-06-05 17:56:04554 MockRead(ASYNC, OK, 0) // EOF
[email protected]69dfd1b2013-06-04 22:20:12555 };
[email protected]25c31dc2013-06-05 17:56:04556 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
[email protected]e13201d82012-12-12 05:00:32557 socket_factory_.AddSocketDataProvider(&socket_data);
558 {
559 QuicStreamRequest request(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54560 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39561 cert_verifier_.get(), net_log_,
[email protected]e13201d82012-12-12 05:00:32562 callback_.callback()));
563 }
564
[email protected]25c31dc2013-06-05 17:56:04565 socket_data.StopAfter(1);
[email protected]e13201d82012-12-12 05:00:32566 base::RunLoop run_loop;
567 run_loop.RunUntilIdle();
568
569 scoped_ptr<QuicHttpStream> stream(
570 factory_.CreateIfSessionExists(host_port_proxy_pair_, net_log_));
571 EXPECT_TRUE(stream.get());
572 stream.reset();
573
574 EXPECT_TRUE(socket_data.at_read_eof());
575 EXPECT_TRUE(socket_data.at_write_eof());
576}
577
[email protected]1e960032013-12-20 19:00:20578TEST_P(QuicStreamFactoryTest, CreateConsistentEphemeralPort) {
[email protected]3c772402013-12-18 21:38:11579 // Sequentially connect to the default host, then another host, and then the
580 // default host. Verify that the default host gets a consistent ephemeral
581 // port, that is different from the other host's connection.
582
583 std::string other_server_name = "other.google.com";
584 EXPECT_NE(kDefaultServerHostName, other_server_name);
585 HostPortPair host_port_pair2(other_server_name, kDefaultServerPort);
586 HostPortProxyPair host_port_proxy_pair2(host_port_pair2,
587 host_port_proxy_pair_.second);
588
589 int original_port = GetSourcePortForNewSession(host_port_proxy_pair_);
590 EXPECT_NE(original_port, GetSourcePortForNewSession(host_port_proxy_pair2));
591 EXPECT_EQ(original_port, GetSourcePortForNewSession(host_port_proxy_pair_));
592}
593
[email protected]1e960032013-12-20 19:00:20594TEST_P(QuicStreamFactoryTest, CloseAllSessions) {
[email protected]56dfb902013-01-03 23:17:55595 MockRead reads[] = {
[email protected]0edce6a2013-05-08 18:02:40596 MockRead(ASYNC, 0, 0) // EOF
[email protected]56dfb902013-01-03 23:17:55597 };
[email protected]25c31dc2013-06-05 17:56:04598 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
[email protected]56dfb902013-01-03 23:17:55599 socket_factory_.AddSocketDataProvider(&socket_data);
[email protected]0edce6a2013-05-08 18:02:40600 socket_data.StopAfter(1);
[email protected]56dfb902013-01-03 23:17:55601
[email protected]69dfd1b2013-06-04 22:20:12602 MockRead reads2[] = {
[email protected]25c31dc2013-06-05 17:56:04603 MockRead(ASYNC, 0, 0) // EOF
[email protected]69dfd1b2013-06-04 22:20:12604 };
[email protected]25c31dc2013-06-05 17:56:04605 DeterministicSocketData socket_data2(reads2, arraysize(reads2), NULL, 0);
[email protected]56dfb902013-01-03 23:17:55606 socket_factory_.AddSocketDataProvider(&socket_data2);
[email protected]0edce6a2013-05-08 18:02:40607 socket_data2.StopAfter(1);
[email protected]56dfb902013-01-03 23:17:55608
609 QuicStreamRequest request(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54610 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39611 cert_verifier_.get(), net_log_,
[email protected]56dfb902013-01-03 23:17:55612 callback_.callback()));
613
614 EXPECT_EQ(OK, callback_.WaitForResult());
615 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
[email protected]0b2294d32013-08-02 00:46:36616 HttpRequestInfo request_info;
617 EXPECT_EQ(OK, stream->InitializeStream(&request_info,
618 DEFAULT_PRIORITY,
619 net_log_, CompletionCallback()));
[email protected]56dfb902013-01-03 23:17:55620
621 // Close the session and verify that stream saw the error.
622 factory_.CloseAllSessions(ERR_INTERNET_DISCONNECTED);
623 EXPECT_EQ(ERR_INTERNET_DISCONNECTED,
624 stream->ReadResponseHeaders(callback_.callback()));
625
626 // Now attempting to request a stream to the same origin should create
627 // a new session.
628
629 QuicStreamRequest request2(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54630 EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39631 cert_verifier_.get(), net_log_,
[email protected]56dfb902013-01-03 23:17:55632 callback_.callback()));
633
634 EXPECT_EQ(OK, callback_.WaitForResult());
635 stream = request2.ReleaseStream();
636 stream.reset(); // Will reset stream 3.
637
638 EXPECT_TRUE(socket_data.at_read_eof());
639 EXPECT_TRUE(socket_data.at_write_eof());
640 EXPECT_TRUE(socket_data2.at_read_eof());
641 EXPECT_TRUE(socket_data2.at_write_eof());
642}
643
[email protected]1e960032013-12-20 19:00:20644TEST_P(QuicStreamFactoryTest, OnIPAddressChanged) {
[email protected]f698a012013-05-06 20:18:59645 MockRead reads[] = {
[email protected]0edce6a2013-05-08 18:02:40646 MockRead(ASYNC, 0, 0) // EOF
[email protected]f698a012013-05-06 20:18:59647 };
[email protected]25c31dc2013-06-05 17:56:04648 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
[email protected]f698a012013-05-06 20:18:59649 socket_factory_.AddSocketDataProvider(&socket_data);
[email protected]0edce6a2013-05-08 18:02:40650 socket_data.StopAfter(1);
[email protected]f698a012013-05-06 20:18:59651
[email protected]69dfd1b2013-06-04 22:20:12652 MockRead reads2[] = {
[email protected]25c31dc2013-06-05 17:56:04653 MockRead(ASYNC, 0, 0) // EOF
[email protected]69dfd1b2013-06-04 22:20:12654 };
[email protected]25c31dc2013-06-05 17:56:04655 DeterministicSocketData socket_data2(reads2, arraysize(reads2), NULL, 0);
[email protected]f698a012013-05-06 20:18:59656 socket_factory_.AddSocketDataProvider(&socket_data2);
[email protected]0edce6a2013-05-08 18:02:40657 socket_data2.StopAfter(1);
[email protected]f698a012013-05-06 20:18:59658
659 QuicStreamRequest request(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54660 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39661 cert_verifier_.get(), net_log_,
[email protected]f698a012013-05-06 20:18:59662 callback_.callback()));
663
664 EXPECT_EQ(OK, callback_.WaitForResult());
665 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
[email protected]0b2294d32013-08-02 00:46:36666 HttpRequestInfo request_info;
667 EXPECT_EQ(OK, stream->InitializeStream(&request_info,
668 DEFAULT_PRIORITY,
669 net_log_, CompletionCallback()));
[email protected]f698a012013-05-06 20:18:59670
671 // Change the IP address and verify that stream saw the error.
672 factory_.OnIPAddressChanged();
673 EXPECT_EQ(ERR_NETWORK_CHANGED,
674 stream->ReadResponseHeaders(callback_.callback()));
[email protected]11c05872013-08-20 02:04:12675 EXPECT_TRUE(factory_.require_confirmation());
[email protected]f698a012013-05-06 20:18:59676
677 // Now attempting to request a stream to the same origin should create
678 // a new session.
679
680 QuicStreamRequest request2(&factory_);
[email protected]6d1b4ed2013-07-10 03:57:54681 EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
[email protected]ce542d52013-07-11 01:08:39682 cert_verifier_.get(), net_log_,
[email protected]f698a012013-05-06 20:18:59683 callback_.callback()));
684
685 EXPECT_EQ(OK, callback_.WaitForResult());
686 stream = request2.ReleaseStream();
687 stream.reset(); // Will reset stream 3.
688
689 EXPECT_TRUE(socket_data.at_read_eof());
690 EXPECT_TRUE(socket_data.at_write_eof());
691 EXPECT_TRUE(socket_data2.at_read_eof());
692 EXPECT_TRUE(socket_data2.at_write_eof());
693}
694
[email protected]1e960032013-12-20 19:00:20695TEST_P(QuicStreamFactoryTest, OnCertAdded) {
[email protected]d7d1e50b2013-11-25 22:08:09696 MockRead reads[] = {
697 MockRead(ASYNC, 0, 0) // EOF
698 };
699 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
700 socket_factory_.AddSocketDataProvider(&socket_data);
701 socket_data.StopAfter(1);
702
703 MockRead reads2[] = {
704 MockRead(ASYNC, 0, 0) // EOF
705 };
706 DeterministicSocketData socket_data2(reads2, arraysize(reads2), NULL, 0);
707 socket_factory_.AddSocketDataProvider(&socket_data2);
708 socket_data2.StopAfter(1);
709
710 QuicStreamRequest request(&factory_);
711 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
712 cert_verifier_.get(), net_log_,
713 callback_.callback()));
714
715 EXPECT_EQ(OK, callback_.WaitForResult());
716 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
717 HttpRequestInfo request_info;
718 EXPECT_EQ(OK, stream->InitializeStream(&request_info,
719 DEFAULT_PRIORITY,
720 net_log_, CompletionCallback()));
721
722 // Add a cert and verify that stream saw the event.
723 factory_.OnCertAdded(NULL);
724 EXPECT_EQ(ERR_CERT_DATABASE_CHANGED,
725 stream->ReadResponseHeaders(callback_.callback()));
726 EXPECT_FALSE(factory_.require_confirmation());
727
728 // Now attempting to request a stream to the same origin should create
729 // a new session.
730
731 QuicStreamRequest request2(&factory_);
732 EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
733 cert_verifier_.get(), net_log_,
734 callback_.callback()));
735
736 EXPECT_EQ(OK, callback_.WaitForResult());
737 stream = request2.ReleaseStream();
738 stream.reset(); // Will reset stream 3.
739
740 EXPECT_TRUE(socket_data.at_read_eof());
741 EXPECT_TRUE(socket_data.at_write_eof());
742 EXPECT_TRUE(socket_data2.at_read_eof());
743 EXPECT_TRUE(socket_data2.at_write_eof());
744}
745
[email protected]1e960032013-12-20 19:00:20746TEST_P(QuicStreamFactoryTest, OnCACertChanged) {
[email protected]d7d1e50b2013-11-25 22:08:09747 MockRead reads[] = {
748 MockRead(ASYNC, 0, 0) // EOF
749 };
750 DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0);
751 socket_factory_.AddSocketDataProvider(&socket_data);
752 socket_data.StopAfter(1);
753
754 MockRead reads2[] = {
755 MockRead(ASYNC, 0, 0) // EOF
756 };
757 DeterministicSocketData socket_data2(reads2, arraysize(reads2), NULL, 0);
758 socket_factory_.AddSocketDataProvider(&socket_data2);
759 socket_data2.StopAfter(1);
760
761 QuicStreamRequest request(&factory_);
762 EXPECT_EQ(ERR_IO_PENDING, request.Request(host_port_proxy_pair_, is_https_,
763 cert_verifier_.get(), net_log_,
764 callback_.callback()));
765
766 EXPECT_EQ(OK, callback_.WaitForResult());
767 scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
768 HttpRequestInfo request_info;
769 EXPECT_EQ(OK, stream->InitializeStream(&request_info,
770 DEFAULT_PRIORITY,
771 net_log_, CompletionCallback()));
772
773 // Change the CA cert and verify that stream saw the event.
774 factory_.OnCACertChanged(NULL);
775 EXPECT_EQ(ERR_CERT_DATABASE_CHANGED,
776 stream->ReadResponseHeaders(callback_.callback()));
777 EXPECT_FALSE(factory_.require_confirmation());
778
779 // Now attempting to request a stream to the same origin should create
780 // a new session.
781
782 QuicStreamRequest request2(&factory_);
783 EXPECT_EQ(ERR_IO_PENDING, request2.Request(host_port_proxy_pair_, is_https_,
784 cert_verifier_.get(), net_log_,
785 callback_.callback()));
786
787 EXPECT_EQ(OK, callback_.WaitForResult());
788 stream = request2.ReleaseStream();
789 stream.reset(); // Will reset stream 3.
790
791 EXPECT_TRUE(socket_data.at_read_eof());
792 EXPECT_TRUE(socket_data.at_write_eof());
793 EXPECT_TRUE(socket_data2.at_read_eof());
794 EXPECT_TRUE(socket_data2.at_write_eof());
795}
796
[email protected]1e960032013-12-20 19:00:20797TEST_P(QuicStreamFactoryTest, SharedCryptoConfig) {
[email protected]6e12d702013-11-13 00:17:17798 vector<string> cannoncial_suffixes;
799 cannoncial_suffixes.push_back(string(".c.youtube.com"));
800 cannoncial_suffixes.push_back(string(".googlevideo.com"));
[email protected]c49ff182013-09-28 08:33:26801
[email protected]6e12d702013-11-13 00:17:17802 for (unsigned i = 0; i < cannoncial_suffixes.size(); ++i) {
803 string r1_host_name("r1");
804 string r2_host_name("r2");
805 r1_host_name.append(cannoncial_suffixes[i]);
806 r2_host_name.append(cannoncial_suffixes[i]);
[email protected]b70fdb792013-10-25 19:04:14807
[email protected]6e12d702013-11-13 00:17:17808 HostPortProxyPair host_port_proxy_pair1(HostPortPair(r1_host_name, 80),
809 ProxyServer::Direct());
[email protected]c49ff182013-09-28 08:33:26810
[email protected]6e12d702013-11-13 00:17:17811 QuicCryptoClientConfig* crypto_config1 =
812 QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
813 host_port_proxy_pair1);
814 DCHECK(crypto_config1);
815 QuicCryptoClientConfig::CachedState* cached1 =
816 crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host());
817 EXPECT_FALSE(cached1->proof_valid());
818 EXPECT_TRUE(cached1->source_address_token().empty());
819
820 // Mutate the cached1 to have different data.
821 // TODO(rtenneti): mutate other members of CachedState.
822 cached1->set_source_address_token(r1_host_name);
823 cached1->SetProofValid();
824
825 HostPortProxyPair host_port_proxy_pair2(HostPortPair(r2_host_name, 80),
826 ProxyServer::Direct());
827 QuicCryptoClientConfig* crypto_config2 =
828 QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
829 host_port_proxy_pair2);
830 DCHECK(crypto_config2);
831 QuicCryptoClientConfig::CachedState* cached2 =
832 crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host());
833 EXPECT_EQ(cached1->source_address_token(), cached2->source_address_token());
834 EXPECT_TRUE(cached2->proof_valid());
835 }
[email protected]b70fdb792013-10-25 19:04:14836}
837
[email protected]1e960032013-12-20 19:00:20838TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
[email protected]6e12d702013-11-13 00:17:17839 vector<string> cannoncial_suffixes;
840 cannoncial_suffixes.push_back(string(".c.youtube.com"));
841 cannoncial_suffixes.push_back(string(".googlevideo.com"));
[email protected]b70fdb792013-10-25 19:04:14842
[email protected]6e12d702013-11-13 00:17:17843 for (unsigned i = 0; i < cannoncial_suffixes.size(); ++i) {
844 string r3_host_name("r3");
845 string r4_host_name("r4");
846 r3_host_name.append(cannoncial_suffixes[i]);
847 r4_host_name.append(cannoncial_suffixes[i]);
[email protected]b70fdb792013-10-25 19:04:14848
[email protected]6e12d702013-11-13 00:17:17849 HostPortProxyPair host_port_proxy_pair1(HostPortPair(r3_host_name, 80),
850 ProxyServer::Direct());
[email protected]b70fdb792013-10-25 19:04:14851
[email protected]6e12d702013-11-13 00:17:17852 QuicCryptoClientConfig* crypto_config1 =
853 QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
854 host_port_proxy_pair1);
855 DCHECK(crypto_config1);
856 QuicCryptoClientConfig::CachedState* cached1 =
857 crypto_config1->LookupOrCreate(host_port_proxy_pair1.first.host());
858 EXPECT_FALSE(cached1->proof_valid());
859 EXPECT_TRUE(cached1->source_address_token().empty());
860
861 // Mutate the cached1 to have different data.
862 // TODO(rtenneti): mutate other members of CachedState.
863 cached1->set_source_address_token(r3_host_name);
864 cached1->SetProofInvalid();
865
866 HostPortProxyPair host_port_proxy_pair2(HostPortPair(r4_host_name, 80),
867 ProxyServer::Direct());
868 QuicCryptoClientConfig* crypto_config2 =
869 QuicStreamFactoryPeer::GetOrCreateCryptoConfig(&factory_,
870 host_port_proxy_pair2);
871 DCHECK(crypto_config2);
872 QuicCryptoClientConfig::CachedState* cached2 =
873 crypto_config2->LookupOrCreate(host_port_proxy_pair2.first.host());
874 EXPECT_NE(cached1->source_address_token(), cached2->source_address_token());
875 EXPECT_TRUE(cached2->source_address_token().empty());
876 EXPECT_FALSE(cached2->proof_valid());
877 }
[email protected]c49ff182013-09-28 08:33:26878}
879
[email protected]e13201d82012-12-12 05:00:32880} // namespace test
[email protected]e13201d82012-12-12 05:00:32881} // namespace net