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 <memory> | 5 #include <memory> |
6 #include <ostream> | 6 #include <ostream> |
7 #include <string> | 7 #include <string> |
8 #include <utility> | 8 #include <utility> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 1721 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1732 trans.reset(); | 1732 trans.reset(); |
1733 | 1733 |
1734 // Run the QUIC session to completion. | 1734 // Run the QUIC session to completion. |
1735 quic_task_runner_->RunUntilIdle(); | 1735 quic_task_runner_->RunUntilIdle(); |
1736 | 1736 |
1737 ExpectQuicAlternateProtocolMapping(); | 1737 ExpectQuicAlternateProtocolMapping(); |
1738 | 1738 |
1739 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | 1739 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); |
1740 } | 1740 } |
1741 | 1741 |
1742 // Verify that if a QUIC protocol error occurs after the handshake is confirmed | |
1743 // the request fails with QUIC_PROTOCOL_ERROR. | |
1744 TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmed) { | |
1745 // The request will initially go out over QUIC. | |
1746 MockQuicData quic_data; | |
1747 QuicStreamOffset header_stream_offset = 0; | |
1748 quic_data.AddWrite(ConstructClientRequestHeadersPacket( | |
1749 1, kClientDataStreamId1, true, true, | |
1750 GetRequestHeaders("GET", "https", "/"), &header_stream_offset)); | |
1751 quic_data.AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE, | |
1752 kDefaultMaxUncompressedHeaderSize, | |
1753 &header_stream_offset)); | |
1754 // Peer sending data from an non-existing stream causes this end to raise | |
1755 // error and close connection. | |
1756 quic_data.AddRead( | |
1757 ConstructServerRstPacket(1, false, 99, QUIC_STREAM_LAST_ERROR)); | |
1758 std::string quic_error_details = "Data for nonexistent stream"; | |
1759 quic_data.AddWrite(ConstructClientAckAndConnectionClosePacket( | |
1760 3, QuicTime::Delta::Zero(), 1, 1, QUIC_INVALID_STREAM_ID, | |
1761 quic_error_details)); | |
1762 quic_data.AddSocketDataToFactory(&socket_factory_); | |
1763 | |
1764 // In order for a new QUIC session to be established via alternate-protocol | |
1765 // without racing an HTTP connection, we need the host resolution to happen | |
1766 // synchronously. Of course, even though QUIC *could* perform a 0-RTT | |
1767 // connection to the the server, in this test we require confirmation | |
1768 // before encrypting so the HTTP job will still start. | |
1769 host_resolver_.set_synchronous_mode(true); | |
1770 host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1", | |
1771 ""); | |
1772 HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443)); | |
1773 AddressList address; | |
1774 std::unique_ptr<HostResolver::Request> request; | |
1775 host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address, CompletionCallback(), | |
1776 &request, net_log_.bound()); | |
1777 | |
1778 CreateSession(); | |
1779 | |
1780 AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); | |
1781 | |
1782 HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get()); | |
1783 TestCompletionCallback callback; | |
1784 int rv = trans.Start(&request_, callback.callback(), net_log_.bound()); | |
1785 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
1786 | |
1787 // Pump the message loop to get the request started. | |
1788 base::RunLoop().RunUntilIdle(); | |
1789 // Explicitly confirm the handshake. | |
1790 crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent( | |
1791 QuicSession::HANDSHAKE_CONFIRMED); | |
1792 | |
1793 ASSERT_FALSE(quic_data.AllReadDataConsumed()); | |
1794 | |
1795 // Run the QUIC session to completion. | |
1796 base::RunLoop().RunUntilIdle(); | |
1797 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | |
1798 ASSERT_TRUE(quic_data.AllReadDataConsumed()); | |
1799 | |
1800 EXPECT_THAT(callback.WaitForResult(), IsError(ERR_QUIC_PROTOCOL_ERROR)); | |
1801 ExpectQuicAlternateProtocolMapping(); | |
1802 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | |
1803 } | |
1804 | |
1805 // Verify that with mark_quic_broken_when_network_blackholes enabled, if a QUIC | 1742 // Verify that with mark_quic_broken_when_network_blackholes enabled, if a QUIC |
1806 // connection times out, then QUIC will be marked as broken and the request | 1743 // connection times out, then QUIC will be marked as broken and the request |
1807 // retried over TCP. | 1744 // retried over TCP. |
1808 TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmedThenBroken) { | 1745 TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmedThenBroken) { |
1809 params_.mark_quic_broken_when_network_blackholes = true; | 1746 params_.mark_quic_broken_when_network_blackholes = true; |
1810 params_.quic_idle_connection_timeout_seconds = 5; | 1747 params_.quic_idle_connection_timeout_seconds = 5; |
1811 | 1748 |
1812 // The request will initially go out over QUIC. | 1749 // The request will initially go out over QUIC. |
1813 MockQuicData quic_data; | 1750 MockQuicData quic_data; |
1814 QuicStreamOffset header_stream_offset = 0; | 1751 QuicStreamOffset header_stream_offset = 0; |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1912 | 1849 |
1913 ExpectBrokenAlternateProtocolMapping(); | 1850 ExpectBrokenAlternateProtocolMapping(); |
1914 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | 1851 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); |
1915 ASSERT_FALSE(http_data.AllReadDataConsumed()); | 1852 ASSERT_FALSE(http_data.AllReadDataConsumed()); |
1916 | 1853 |
1917 // Read the response body over TCP. | 1854 // Read the response body over TCP. |
1918 CheckResponseData(&trans, "hello world"); | 1855 CheckResponseData(&trans, "hello world"); |
1919 ASSERT_TRUE(http_data.AllWriteDataConsumed()); | 1856 ASSERT_TRUE(http_data.AllWriteDataConsumed()); |
1920 ASSERT_TRUE(http_data.AllReadDataConsumed()); | 1857 ASSERT_TRUE(http_data.AllReadDataConsumed()); |
1921 } | 1858 } |
1922 | |
1923 // Verify that with retry_without_alt_svc_on_quic_errors enabled, if a QUIC | |
1924 // connection times out, then QUIC will be marked as broken and the request | |
1925 // retried over TCP. | |
1926 TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmedThenBroken2) { | |
1927 params_.retry_without_alt_svc_on_quic_errors = true; | |
1928 params_.quic_idle_connection_timeout_seconds = 5; | |
1929 | |
1930 // The request will initially go out over QUIC. | |
1931 MockQuicData quic_data; | |
1932 QuicStreamOffset header_stream_offset = 0; | |
1933 SpdyPriority priority = | |
1934 ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY); | |
1935 | |
1936 std::string request_data; | |
1937 quic_data.AddWrite(client_maker_.MakeRequestHeadersPacketAndSaveData( | |
1938 1, kClientDataStreamId1, true, true, priority, | |
1939 GetRequestHeaders("GET", "https", "/"), nullptr, &header_stream_offset, | |
1940 &request_data)); | |
1941 | |
1942 std::string settings_data; | |
1943 QuicStreamOffset settings_offset = header_stream_offset; | |
1944 quic_data.AddWrite(client_maker_.MakeSettingsPacketAndSaveData( | |
1945 2, SETTINGS_MAX_HEADER_LIST_SIZE, kDefaultMaxUncompressedHeaderSize, true, | |
1946 &header_stream_offset, &settings_data)); | |
1947 // TLP 1 | |
1948 quic_data.AddWrite(client_maker_.MakeDataPacket(3, kHeadersStreamId, true, | |
1949 false, 0, request_data)); | |
1950 // TLP 2 | |
1951 quic_data.AddWrite(client_maker_.MakeDataPacket( | |
1952 4, kHeadersStreamId, true, false, settings_offset, settings_data)); | |
1953 // RTO 1 | |
1954 quic_data.AddWrite(client_maker_.MakeDataPacket(5, kHeadersStreamId, true, | |
1955 false, 0, request_data)); | |
1956 quic_data.AddWrite(client_maker_.MakeDataPacket( | |
1957 6, kHeadersStreamId, true, false, settings_offset, settings_data)); | |
1958 // RTO 2 | |
1959 quic_data.AddWrite(client_maker_.MakeDataPacket(7, kHeadersStreamId, true, | |
1960 false, 0, request_data)); | |
1961 quic_data.AddWrite(client_maker_.MakeDataPacket( | |
1962 8, kHeadersStreamId, true, false, settings_offset, settings_data)); | |
1963 // RTO 3 | |
1964 quic_data.AddWrite(client_maker_.MakeDataPacket(9, kHeadersStreamId, true, | |
1965 false, 0, request_data)); | |
1966 quic_data.AddWrite(client_maker_.MakeDataPacket( | |
1967 10, kHeadersStreamId, true, false, settings_offset, settings_data)); | |
1968 | |
1969 quic_data.AddRead(ASYNC, ERR_IO_PENDING); | |
1970 quic_data.AddRead(ASYNC, OK); | |
1971 quic_data.AddSocketDataToFactory(&socket_factory_); | |
1972 | |
1973 // After that fails, it will be resent via TCP. | |
1974 MockWrite http_writes[] = { | |
1975 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"), | |
1976 MockWrite(SYNCHRONOUS, 1, "Host: mail.example.org\r\n"), | |
1977 MockWrite(SYNCHRONOUS, 2, "Connection: keep-alive\r\n\r\n")}; | |
1978 | |
1979 MockRead http_reads[] = { | |
1980 MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"), | |
1981 MockRead(SYNCHRONOUS, 4, kQuicAlternativeServiceHeader), | |
1982 MockRead(SYNCHRONOUS, 5, "hello world"), MockRead(SYNCHRONOUS, OK, 6)}; | |
1983 SequencedSocketData http_data(http_reads, arraysize(http_reads), http_writes, | |
1984 arraysize(http_writes)); | |
1985 socket_factory_.AddSocketDataProvider(&http_data); | |
1986 socket_factory_.AddSSLSocketDataProvider(&ssl_data_); | |
1987 | |
1988 // In order for a new QUIC session to be established via alternate-protocol | |
1989 // without racing an HTTP connection, we need the host resolution to happen | |
1990 // synchronously. Of course, even though QUIC *could* perform a 0-RTT | |
1991 // connection to the the server, in this test we require confirmation | |
1992 // before encrypting so the HTTP job will still start. | |
1993 host_resolver_.set_synchronous_mode(true); | |
1994 host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1", | |
1995 ""); | |
1996 HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443)); | |
1997 AddressList address; | |
1998 std::unique_ptr<HostResolver::Request> request; | |
1999 host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address, CompletionCallback(), | |
2000 &request, net_log_.bound()); | |
2001 | |
2002 CreateSession(); | |
2003 // Use a TestTaskRunner to avoid waiting in real time for timeouts. | |
2004 scoped_refptr<TestTaskRunner> quic_task_runner_(new TestTaskRunner(clock_)); | |
2005 QuicStreamFactoryPeer::SetAlarmFactory( | |
2006 session_->quic_stream_factory(), | |
2007 base::MakeUnique<QuicChromiumAlarmFactory>(quic_task_runner_.get(), | |
2008 clock_)); | |
2009 | |
2010 AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); | |
2011 | |
2012 HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get()); | |
2013 TestCompletionCallback callback; | |
2014 int rv = trans.Start(&request_, callback.callback(), net_log_.bound()); | |
2015 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2016 | |
2017 // Pump the message loop to get the request started. | |
2018 base::RunLoop().RunUntilIdle(); | |
2019 // Explicitly confirm the handshake. | |
2020 crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent( | |
2021 QuicSession::HANDSHAKE_CONFIRMED); | |
2022 | |
2023 // Run the QUIC session to completion. | |
2024 quic_task_runner_->RunUntilIdle(); | |
2025 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | |
2026 | |
2027 ExpectQuicAlternateProtocolMapping(); | |
2028 | |
2029 // Let the transaction proceed which will result in QUIC being marked | |
2030 // as broken and the request falling back to TCP. | |
2031 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
2032 | |
2033 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | |
2034 ASSERT_FALSE(http_data.AllReadDataConsumed()); | |
2035 | |
2036 // Read the response body over TCP. | |
2037 CheckResponseData(&trans, "hello world"); | |
2038 ExpectBrokenAlternateProtocolMapping(); | |
2039 ASSERT_TRUE(http_data.AllWriteDataConsumed()); | |
2040 ASSERT_TRUE(http_data.AllReadDataConsumed()); | |
2041 } | |
2042 | 1859 |
2043 // Verify that with mark_quic_broken_when_network_blackholes enabled, if a QUIC | 1860 // Verify that with mark_quic_broken_when_network_blackholes enabled, if a QUIC |
2044 // connection times out, then QUIC will be marked as broken but the request | 1861 // connection times out, then QUIC will be marked as broken but the request |
2045 // will not be retried over TCP. | 1862 // will not be retried over TCP. |
2046 TEST_P(QuicNetworkTransactionTest, | 1863 TEST_P(QuicNetworkTransactionTest, |
2047 TimeoutAfterHandshakeConfirmedAndHeadersThenBrokenNotRetried) { | 1864 TimeoutAfterHandshakeConfirmedAndHeadersThenBrokenNotRetried) { |
2048 params_.mark_quic_broken_when_network_blackholes = true; | 1865 params_.mark_quic_broken_when_network_blackholes = true; |
2049 params_.quic_idle_connection_timeout_seconds = 5; | 1866 params_.quic_idle_connection_timeout_seconds = 5; |
2050 | 1867 |
2051 // The request will initially go out over QUIC. | 1868 // The request will initially go out over QUIC. |
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2380 trans.reset(); | 2197 trans.reset(); |
2381 | 2198 |
2382 // Run the QUIC session to completion. | 2199 // Run the QUIC session to completion. |
2383 quic_task_runner_->RunUntilIdle(); | 2200 quic_task_runner_->RunUntilIdle(); |
2384 | 2201 |
2385 ExpectBrokenAlternateProtocolMapping(); | 2202 ExpectBrokenAlternateProtocolMapping(); |
2386 | 2203 |
2387 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | 2204 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); |
2388 } | 2205 } |
2389 | 2206 |
2390 // Verify that with retry_without_alt_svc_on_quic_errors enabled, if a QUIC | |
2391 // protocol error occurs after the handshake is confirmed, the request | |
2392 // retried over TCP and the QUIC will be marked as broken. | |
2393 TEST_P(QuicNetworkTransactionTest, | |
2394 ProtocolErrorAfterHandshakeConfirmedThenBroken) { | |
2395 params_.retry_without_alt_svc_on_quic_errors = true; | |
2396 params_.quic_idle_connection_timeout_seconds = 5; | |
2397 | |
2398 // The request will initially go out over QUIC. | |
2399 MockQuicData quic_data; | |
2400 QuicStreamOffset header_stream_offset = 0; | |
2401 quic_data.AddWrite(ConstructClientRequestHeadersPacket( | |
2402 1, kClientDataStreamId1, true, true, | |
2403 GetRequestHeaders("GET", "https", "/"), &header_stream_offset)); | |
2404 quic_data.AddWrite(ConstructSettingsPacket(2, SETTINGS_MAX_HEADER_LIST_SIZE, | |
2405 kDefaultMaxUncompressedHeaderSize, | |
2406 &header_stream_offset)); | |
2407 // Peer sending data from an non-existing stream causes this end to raise | |
2408 // error and close connection. | |
2409 quic_data.AddRead( | |
2410 ConstructServerRstPacket(1, false, 99, QUIC_STREAM_LAST_ERROR)); | |
2411 std::string quic_error_details = "Data for nonexistent stream"; | |
2412 quic_data.AddWrite(ConstructClientAckAndConnectionClosePacket( | |
2413 3, QuicTime::Delta::Zero(), 1, 1, QUIC_INVALID_STREAM_ID, | |
2414 quic_error_details)); | |
2415 quic_data.AddSocketDataToFactory(&socket_factory_); | |
2416 | |
2417 // After that fails, it will be resent via TCP. | |
2418 MockWrite http_writes[] = { | |
2419 MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"), | |
2420 MockWrite(SYNCHRONOUS, 1, "Host: mail.example.org\r\n"), | |
2421 MockWrite(SYNCHRONOUS, 2, "Connection: keep-alive\r\n\r\n")}; | |
2422 | |
2423 MockRead http_reads[] = { | |
2424 MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"), | |
2425 MockRead(SYNCHRONOUS, 4, kQuicAlternativeServiceHeader), | |
2426 MockRead(SYNCHRONOUS, 5, "hello world"), MockRead(SYNCHRONOUS, OK, 6)}; | |
2427 SequencedSocketData http_data(http_reads, arraysize(http_reads), http_writes, | |
2428 arraysize(http_writes)); | |
2429 socket_factory_.AddSocketDataProvider(&http_data); | |
2430 socket_factory_.AddSSLSocketDataProvider(&ssl_data_); | |
2431 | |
2432 // In order for a new QUIC session to be established via alternate-protocol | |
2433 // without racing an HTTP connection, we need the host resolution to happen | |
2434 // synchronously. Of course, even though QUIC *could* perform a 0-RTT | |
2435 // connection to the the server, in this test we require confirmation | |
2436 // before encrypting so the HTTP job will still start. | |
2437 host_resolver_.set_synchronous_mode(true); | |
2438 host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1", | |
2439 ""); | |
2440 HostResolver::RequestInfo info(HostPortPair("mail.example.org", 443)); | |
2441 AddressList address; | |
2442 std::unique_ptr<HostResolver::Request> request; | |
2443 host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address, CompletionCallback(), | |
2444 &request, net_log_.bound()); | |
2445 | |
2446 CreateSession(); | |
2447 | |
2448 AddQuicAlternateProtocolMapping(MockCryptoClientStream::ZERO_RTT); | |
2449 | |
2450 HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get()); | |
2451 TestCompletionCallback callback; | |
2452 int rv = trans.Start(&request_, callback.callback(), net_log_.bound()); | |
2453 EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); | |
2454 | |
2455 // Pump the message loop to get the request started. | |
2456 base::RunLoop().RunUntilIdle(); | |
2457 // Explicitly confirm the handshake. | |
2458 crypto_client_stream_factory_.last_stream()->SendOnCryptoHandshakeEvent( | |
2459 QuicSession::HANDSHAKE_CONFIRMED); | |
2460 | |
2461 // Run the QUIC session to completion. | |
2462 base::RunLoop().RunUntilIdle(); | |
2463 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | |
2464 | |
2465 ExpectQuicAlternateProtocolMapping(); | |
2466 | |
2467 // Let the transaction proceed which will result in QUIC being marked | |
2468 // as broken and the request falling back to TCP. | |
2469 EXPECT_THAT(callback.WaitForResult(), IsOk()); | |
2470 | |
2471 ASSERT_TRUE(quic_data.AllWriteDataConsumed()); | |
2472 ASSERT_FALSE(http_data.AllReadDataConsumed()); | |
2473 | |
2474 // Read the response body over TCP. | |
2475 CheckResponseData(&trans, "hello world"); | |
2476 ExpectBrokenAlternateProtocolMapping(); | |
2477 ASSERT_TRUE(http_data.AllWriteDataConsumed()); | |
2478 ASSERT_TRUE(http_data.AllReadDataConsumed()); | |
2479 } | |
2480 | |
2481 TEST_P(QuicNetworkTransactionTest, | 2207 TEST_P(QuicNetworkTransactionTest, |
2482 DoNotUseAlternativeServiceQuicUnsupportedVersion) { | 2208 DoNotUseAlternativeServiceQuicUnsupportedVersion) { |
2483 std::string altsvc_header = base::StringPrintf( | 2209 std::string altsvc_header = base::StringPrintf( |
2484 "Alt-Svc: quic=\":443\"; v=\"%u\"\r\n\r\n", version_ - 1); | 2210 "Alt-Svc: quic=\":443\"; v=\"%u\"\r\n\r\n", version_ - 1); |
2485 MockRead http_reads[] = { | 2211 MockRead http_reads[] = { |
2486 MockRead("HTTP/1.1 200 OK\r\n"), MockRead(altsvc_header.c_str()), | 2212 MockRead("HTTP/1.1 200 OK\r\n"), MockRead(altsvc_header.c_str()), |
2487 MockRead("hello world"), | 2213 MockRead("hello world"), |
2488 MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), | 2214 MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), |
2489 MockRead(ASYNC, OK)}; | 2215 MockRead(ASYNC, OK)}; |
2490 | 2216 |
(...skipping 2219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4710 | 4436 |
4711 request_.url = GURL("https://mail.example.org/pushed.jpg"); | 4437 request_.url = GURL("https://mail.example.org/pushed.jpg"); |
4712 ChunkedUploadDataStream upload_data(0); | 4438 ChunkedUploadDataStream upload_data(0); |
4713 upload_data.AppendData("1", 1, true); | 4439 upload_data.AppendData("1", 1, true); |
4714 request_.upload_data_stream = &upload_data; | 4440 request_.upload_data_stream = &upload_data; |
4715 SendRequestAndExpectQuicResponse("and hello!"); | 4441 SendRequestAndExpectQuicResponse("and hello!"); |
4716 } | 4442 } |
4717 | 4443 |
4718 } // namespace test | 4444 } // namespace test |
4719 } // namespace net | 4445 } // namespace net |
OLD | NEW |