| Index: net/quic/chromium/quic_network_transaction_unittest.cc
|
| diff --git a/net/quic/chromium/quic_network_transaction_unittest.cc b/net/quic/chromium/quic_network_transaction_unittest.cc
|
| index 3f3654e58af8e37830928fdf834af53fcbffa962..a5b893ede38757066172e8da33b25cde3cc36094 100644
|
| --- a/net/quic/chromium/quic_network_transaction_unittest.cc
|
| +++ b/net/quic/chromium/quic_network_transaction_unittest.cc
|
| @@ -15,6 +15,7 @@
|
| #include "base/strings/stringprintf.h"
|
| #include "net/base/chunked_upload_data_stream.h"
|
| #include "net/base/test_completion_callback.h"
|
| +#include "net/base/test_proxy_delegate.h"
|
| #include "net/cert/ct_policy_enforcer.h"
|
| #include "net/cert/mock_cert_verifier.h"
|
| #include "net/cert/multi_log_ct_verifier.h"
|
| @@ -572,6 +573,21 @@ class QuicNetworkTransactionTest
|
| CheckResponseData(&trans, expected);
|
| }
|
|
|
| + void SendRequestAndExpectHttpResponseFromProxy(const std::string& expected,
|
| + bool used_proxy,
|
| + uint16_t port) {
|
| + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
|
| + HeadersHandler headers_handler;
|
| + trans.SetBeforeHeadersSentCallback(
|
| + base::Bind(&HeadersHandler::OnBeforeHeadersSent,
|
| + base::Unretained(&headers_handler)));
|
| + RunTransaction(&trans);
|
| + CheckWasHttpResponse(&trans);
|
| + CheckResponsePort(&trans, port);
|
| + CheckResponseData(&trans, expected);
|
| + EXPECT_EQ(used_proxy, headers_handler.was_proxied());
|
| + }
|
| +
|
| void SendRequestAndExpectQuicResponse(const std::string& expected) {
|
| SendRequestAndExpectQuicResponseMaybeFromProxy(expected, false, 443);
|
| }
|
| @@ -631,6 +647,70 @@ class QuicNetworkTransactionTest
|
| socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
|
| }
|
|
|
| + // Fetches two non-cryptographic URL requests via a HTTPS proxy with a QUIC
|
| + // alternative proxy. Verifies that if the alternative proxy job returns
|
| + // |error_code|, the request is fetched successfully by the main job.
|
| + void TestAlternativeProxy(int error_code) {
|
| + // Use a non-cryptographic scheme for the request URL since this request
|
| + // will be fetched via proxy with QUIC as the alternative service.
|
| + request_.url = GURL("http://example.org/");
|
| + // Data for the alternative proxy server job.
|
| + MockRead quic_reads[] = {
|
| + MockRead(SYNCHRONOUS, error_code),
|
| + };
|
| +
|
| + StaticSocketDataProvider quic_data(quic_reads, arraysize(quic_reads),
|
| + nullptr, 0);
|
| + socket_factory_.AddSocketDataProvider(&quic_data);
|
| +
|
| + // Main job succeeds and the alternative job fails.
|
| + // Add data for two requests that will be read by the main job.
|
| + MockRead http_reads_1[] = {
|
| + MockRead("HTTP/1.1 200 OK\r\n\r\n"), MockRead("hello from http"),
|
| + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
|
| + MockRead(ASYNC, OK)};
|
| +
|
| + MockRead http_reads_2[] = {
|
| + MockRead("HTTP/1.1 200 OK\r\n\r\n"), MockRead("hello from http"),
|
| + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
|
| + MockRead(ASYNC, OK)};
|
| +
|
| + StaticSocketDataProvider http_data_1(http_reads_1, arraysize(http_reads_1),
|
| + nullptr, 0);
|
| + StaticSocketDataProvider http_data_2(http_reads_2, arraysize(http_reads_2),
|
| + nullptr, 0);
|
| + socket_factory_.AddSocketDataProvider(&http_data_1);
|
| + socket_factory_.AddSocketDataProvider(&http_data_2);
|
| + socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
|
| + socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
|
| +
|
| + TestProxyDelegate test_proxy_delegate;
|
| + // Proxy URL is different from the request URL.
|
| + test_proxy_delegate.SetAlternativeProxy(
|
| + ProxyServer::FromPacString("QUIC myproxy.org:443"));
|
| +
|
| + params_.proxy_delegate = &test_proxy_delegate;
|
| + proxy_service_ =
|
| + ProxyService::CreateFixedFromPacResult("HTTPS myproxy.org:443");
|
| +
|
| + CreateSession();
|
| + EXPECT_TRUE(test_proxy_delegate.alternative_proxy_server().is_valid());
|
| +
|
| + // The first request should be fetched via the HTTPS proxy.
|
| + SendRequestAndExpectHttpResponseFromProxy("hello from http", true, 443);
|
| +
|
| + // Even through the alternative proxy server job failed, the proxy should
|
| + // not be marked as bad since the main job succeeded.
|
| + EXPECT_TRUE(session_->proxy_service()->proxy_retry_info().empty());
|
| +
|
| + // The alternative proxy server should no longer be in use.
|
| + EXPECT_FALSE(test_proxy_delegate.alternative_proxy_server().is_valid());
|
| +
|
| + // Verify that the second request completes successfully, and the
|
| + // alternative proxy server job is not started.
|
| + SendRequestAndExpectHttpResponseFromProxy("hello from http", true, 443);
|
| + }
|
| +
|
| MockClock* clock_; // Owned by QuicStreamFactory after CreateSession.
|
| QuicTestPacketMaker client_maker_;
|
| QuicTestPacketMaker server_maker_;
|
| @@ -1588,6 +1668,52 @@ TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceForQuicForHttps) {
|
| SendRequestAndExpectHttpResponse("hello world");
|
| }
|
|
|
| +// Tests that the connection to an HTTPS proxy is raced with an available
|
| +// alternative proxy server.
|
| +TEST_P(QuicNetworkTransactionTest, QuicProxyWithRacing) {
|
| + proxy_service_ =
|
| + ProxyService::CreateFixedFromPacResult("HTTPS mail.example.org:443");
|
| +
|
| + MockQuicData mock_quic_data;
|
| + mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
|
| + 1, kClientDataStreamId1, true, true,
|
| + GetRequestHeaders("GET", "http", "/")));
|
| + mock_quic_data.AddRead(ConstructServerResponseHeadersPacket(
|
| + 1, kClientDataStreamId1, false, false, GetResponseHeaders("200 OK")));
|
| + mock_quic_data.AddRead(ConstructServerDataPacket(2, kClientDataStreamId1,
|
| + false, true, 0, "hello!"));
|
| + mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
|
| + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read
|
| + mock_quic_data.AddRead(ASYNC, 0); // EOF
|
| +
|
| + mock_quic_data.AddSocketDataToFactory(&socket_factory_);
|
| +
|
| + // There is no need to set up main job, because no attempt will be made to
|
| + // speak to the proxy over TCP.
|
| + request_.url = GURL("http://mail.example.org/");
|
| + params_.enable_quic_alternative_service_with_different_host = false;
|
| + TestProxyDelegate test_proxy_delegate;
|
| + const HostPortPair host_port_pair("mail.example.org", 443);
|
| +
|
| + test_proxy_delegate.SetAlternativeProxy(
|
| + ProxyServer::FromPacString("QUIC mail.example.org:443"));
|
| + params_.proxy_delegate = &test_proxy_delegate;
|
| + CreateSession();
|
| + EXPECT_TRUE(test_proxy_delegate.alternative_proxy_server().is_quic());
|
| +
|
| + // The main job needs to hang in order to guarantee that the alternative
|
| + // proxy server job will "win".
|
| + AddHangingNonAlternateProtocolSocketData();
|
| +
|
| + SendRequestAndExpectQuicResponseFromProxyOnPort("hello!", 443);
|
| +
|
| + // Verify that the alternative proxy server is not marked as broken.
|
| + EXPECT_TRUE(test_proxy_delegate.alternative_proxy_server().is_quic());
|
| +
|
| + // Verify that the proxy server is not marked as broken.
|
| + EXPECT_TRUE(session_->proxy_service()->proxy_retry_info().empty());
|
| +}
|
| +
|
| TEST_P(QuicNetworkTransactionTest, HungAlternativeService) {
|
| crypto_client_stream_factory_.set_handshake_mode(
|
| MockCryptoClientStream::COLD_START);
|
| @@ -2201,6 +2327,83 @@ TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnect) {
|
| SendRequestAndExpectHttpResponse("hello world");
|
| }
|
|
|
| +// For an alternative proxy that supports QUIC, test that the request is
|
| +// successfully fetched by the main job when the alternate proxy job encounters
|
| +// an error.
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxySocketNotConnected) {
|
| + TestAlternativeProxy(ERR_SOCKET_NOT_CONNECTED);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyConnectionFailed) {
|
| + TestAlternativeProxy(ERR_CONNECTION_FAILED);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyConnectionTimedOut) {
|
| + TestAlternativeProxy(ERR_CONNECTION_TIMED_OUT);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyConnectionRefused) {
|
| + TestAlternativeProxy(ERR_CONNECTION_REFUSED);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyQuicHandshakeFailed) {
|
| + TestAlternativeProxy(ERR_QUIC_HANDSHAKE_FAILED);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyQuicProtocolError) {
|
| + TestAlternativeProxy(ERR_QUIC_PROTOCOL_ERROR);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyIOPending) {
|
| + TestAlternativeProxy(ERR_IO_PENDING);
|
| +}
|
| +TEST_P(QuicNetworkTransactionTest, BrokenAlternativeProxyAddressUnreachable) {
|
| + TestAlternativeProxy(ERR_ADDRESS_UNREACHABLE);
|
| +}
|
| +
|
| +TEST_P(QuicNetworkTransactionTest, ConnectionCloseDuringConnectProxy) {
|
| + MockQuicData mock_quic_data;
|
| + mock_quic_data.AddSynchronousRead(ConstructServerConnectionClosePacket(1));
|
| + mock_quic_data.AddWrite(ConstructClientRequestHeadersPacket(
|
| + 1, kClientDataStreamId1, true, true,
|
| + GetRequestHeaders("GET", "https", "/")));
|
| + mock_quic_data.AddWrite(ConstructClientAckPacket(2, 1));
|
| + mock_quic_data.AddSocketDataToFactory(&socket_factory_);
|
| +
|
| + // When the QUIC connection fails, we will try the request again over HTTP.
|
| + MockRead http_reads[] = {
|
| + MockRead("HTTP/1.1 200 OK\r\n"), MockRead(kQuicAlternativeServiceHeader),
|
| + MockRead("hello world"),
|
| + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ),
|
| + MockRead(ASYNC, OK)};
|
| +
|
| + StaticSocketDataProvider http_data(http_reads, arraysize(http_reads), nullptr,
|
| + 0);
|
| + socket_factory_.AddSocketDataProvider(&http_data);
|
| + socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
|
| +
|
| + TestProxyDelegate test_proxy_delegate;
|
| + const HostPortPair host_port_pair("myproxy.org", 443);
|
| + test_proxy_delegate.SetAlternativeProxy(
|
| + ProxyServer::FromPacString("QUIC myproxy.org:443"));
|
| + EXPECT_TRUE(test_proxy_delegate.alternative_proxy_server().is_quic());
|
| +
|
| + params_.proxy_delegate = &test_proxy_delegate;
|
| + proxy_service_ =
|
| + ProxyService::CreateFixedFromPacResult("HTTPS myproxy.org:443");
|
| + request_.url = GURL("http://mail.example.org/");
|
| +
|
| + // In order for a new QUIC session to be established via alternate-protocol
|
| + // without racing an HTTP connection, we need the host resolution to happen
|
| + // synchronously.
|
| + host_resolver_.set_synchronous_mode(true);
|
| + host_resolver_.rules()->AddIPLiteralRule("myproxy.org", "192.168.0.1", "");
|
| + HostResolver::RequestInfo info(HostPortPair("myproxy.org", 443));
|
| + AddressList address;
|
| + std::unique_ptr<HostResolver::Request> request;
|
| + host_resolver_.Resolve(info, DEFAULT_PRIORITY, &address, CompletionCallback(),
|
| + &request, net_log_.bound());
|
| +
|
| + CreateSession();
|
| + SendRequestAndExpectHttpResponseFromProxy("hello world", true, 443);
|
| + EXPECT_FALSE(test_proxy_delegate.alternative_proxy_server().is_valid());
|
| + EXPECT_TRUE(session_->proxy_service()->proxy_retry_info().empty());
|
| +}
|
| +
|
| TEST_P(QuicNetworkTransactionTest, SecureResourceOverSecureQuic) {
|
| client_maker_.set_hostname("www.example.org");
|
| EXPECT_FALSE(
|
| @@ -2226,6 +2429,49 @@ TEST_P(QuicNetworkTransactionTest, SecureResourceOverSecureQuic) {
|
| test_socket_performance_watcher_factory_.rtt_notification_received());
|
| }
|
|
|
| +TEST_P(QuicNetworkTransactionTest, QuicUploadToAlternativeProxyServer) {
|
| + proxy_service_ =
|
| + ProxyService::CreateFixedFromPacResult("HTTPS mail.example.org:443");
|
| +
|
| + TestProxyDelegate test_proxy_delegate;
|
| + const HostPortPair host_port_pair("mail.example.org", 443);
|
| +
|
| + test_proxy_delegate.SetAlternativeProxy(
|
| + ProxyServer::FromPacString("QUIC mail.example.org:443"));
|
| + params_.proxy_delegate = &test_proxy_delegate;
|
| +
|
| + request_.url = GURL("http://mail.example.org/");
|
| +
|
| + MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
|
| + MockWrite writes[] = {MockWrite(SYNCHRONOUS, ERR_FAILED, 1)};
|
| + SequencedSocketData socket_data(reads, arraysize(reads), writes,
|
| + arraysize(writes));
|
| + socket_factory_.AddSocketDataProvider(&socket_data);
|
| +
|
| + // The non-alternate protocol job needs to hang in order to guarantee that
|
| + // the alternate-protocol job will "win".
|
| + AddHangingNonAlternateProtocolSocketData();
|
| +
|
| + CreateSession();
|
| + request_.method = "POST";
|
| + ChunkedUploadDataStream upload_data(0);
|
| + upload_data.AppendData("1", 1, true);
|
| +
|
| + request_.upload_data_stream = &upload_data;
|
| +
|
| + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get());
|
| + TestCompletionCallback callback;
|
| + int rv = trans.Start(&request_, callback.callback(), net_log_.bound());
|
| + EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
|
| + EXPECT_NE(OK, callback.WaitForResult());
|
| +
|
| + // Verify that the alternative proxy server is not marked as broken.
|
| + EXPECT_TRUE(test_proxy_delegate.alternative_proxy_server().is_quic());
|
| +
|
| + // Verify that the proxy server is not marked as broken.
|
| + EXPECT_TRUE(session_->proxy_service()->proxy_retry_info().empty());
|
| +}
|
| +
|
| TEST_P(QuicNetworkTransactionTest, QuicUpload) {
|
| params_.origins_to_force_quic_on.insert(
|
| HostPortPair::FromString("mail.example.org:443"));
|
|
|