summaryrefslogtreecommitdiff
path: root/chromium/net/http/http_stream_factory_job_controller_unittest.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-24 12:15:48 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-08-28 13:30:04 +0000
commitb014812705fc80bff0a5c120dfcef88f349816dc (patch)
tree25a2e2d9fa285f1add86aa333389a839f81a39ae /chromium/net/http/http_stream_factory_job_controller_unittest.cc
parent9f4560b1027ae06fdb497023cdcaf91b8511fa74 (diff)
downloadqtwebengine-chromium-b014812705fc80bff0a5c120dfcef88f349816dc.tar.gz
BASELINE: Update Chromium to 68.0.3440.125
Change-Id: I23f19369e01f688e496f5bf179abb521ad73874f Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/http/http_stream_factory_job_controller_unittest.cc')
-rw-r--r--chromium/net/http/http_stream_factory_job_controller_unittest.cc2648
1 files changed, 2648 insertions, 0 deletions
diff --git a/chromium/net/http/http_stream_factory_job_controller_unittest.cc b/chromium/net/http/http_stream_factory_job_controller_unittest.cc
new file mode 100644
index 00000000000..b499032ed80
--- /dev/null
+++ b/chromium/net/http/http_stream_factory_job_controller_unittest.cc
@@ -0,0 +1,2648 @@
+// Copyright (c) 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/http/http_stream_factory_job_controller.h"
+
+#include <algorithm>
+#include <list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/memory/ptr_util.h"
+#include "base/memory/scoped_refptr.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/test/histogram_tester.h"
+#include "base/test/scoped_feature_list.h"
+#include "base/test/scoped_task_environment.h"
+#include "base/test/test_mock_time_task_runner.h"
+#include "base/threading/platform_thread.h"
+#include "net/base/test_proxy_delegate.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/http/http_basic_stream.h"
+#include "net/http/http_network_session_peer.h"
+#include "net/http/http_stream_factory.h"
+#include "net/http/http_stream_factory_job.h"
+#include "net/http/http_stream_factory_test_util.h"
+#include "net/log/net_log_with_source.h"
+#include "net/log/test_net_log.h"
+#include "net/log/test_net_log_entry.h"
+#include "net/log/test_net_log_util.h"
+#include "net/proxy_resolution/mock_proxy_resolver.h"
+#include "net/proxy_resolution/proxy_config_service_fixed.h"
+#include "net/proxy_resolution/proxy_info.h"
+#include "net/proxy_resolution/proxy_resolution_service.h"
+#include "net/quic/chromium/mock_crypto_client_stream_factory.h"
+#include "net/quic/chromium/mock_quic_data.h"
+#include "net/quic/chromium/quic_stream_factory.h"
+#include "net/quic/chromium/quic_stream_factory_peer.h"
+#include "net/quic/chromium/quic_test_packet_maker.h"
+#include "net/socket/socket_test_util.h"
+#include "net/spdy/spdy_test_util_common.h"
+#include "net/test/test_with_scoped_task_environment.h"
+#include "net/third_party/quic/test_tools/mock_random.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::Contains;
+using ::testing::ElementsAre;
+using ::testing::Invoke;
+using ::testing::IsEmpty;
+using ::testing::Key;
+using ::testing::SizeIs;
+
+namespace net {
+
+namespace test {
+
+namespace {
+
+const char kServerHostname[] = "www.example.com";
+
+// List of errors for which fallback is expected on an HTTPS proxy.
+const int proxy_test_mock_errors[] = {
+ ERR_PROXY_CONNECTION_FAILED,
+ ERR_NAME_NOT_RESOLVED,
+ ERR_ADDRESS_UNREACHABLE,
+ ERR_CONNECTION_CLOSED,
+ ERR_CONNECTION_TIMED_OUT,
+ ERR_CONNECTION_RESET,
+ ERR_CONNECTION_REFUSED,
+ ERR_CONNECTION_ABORTED,
+ ERR_TIMED_OUT,
+ ERR_SOCKS_CONNECTION_FAILED,
+ ERR_PROXY_CERTIFICATE_INVALID,
+ ERR_SSL_PROTOCOL_ERROR,
+};
+
+class FailingProxyResolverFactory : public ProxyResolverFactory {
+ public:
+ FailingProxyResolverFactory() : ProxyResolverFactory(false) {}
+
+ // ProxyResolverFactory override.
+ int CreateProxyResolver(const scoped_refptr<PacFileData>& script_data,
+ std::unique_ptr<ProxyResolver>* result,
+ const CompletionCallback& callback,
+ std::unique_ptr<Request>* request) override {
+ return ERR_PAC_SCRIPT_FAILED;
+ }
+};
+
+class FailingHostResolver : public MockHostResolverBase {
+ public:
+ FailingHostResolver() : MockHostResolverBase(false /*use_caching*/) {}
+ ~FailingHostResolver() override = default;
+
+ int Resolve(const RequestInfo& info,
+ RequestPriority priority,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ std::unique_ptr<Request>* out_req,
+ const NetLogWithSource& net_log) override {
+ return ERR_NAME_NOT_RESOLVED;
+ }
+};
+
+// TODO(xunjieli): This should just use HangingHostResolver from
+// mock_host_resolver.h
+class HangingResolver : public MockHostResolverBase {
+ public:
+ HangingResolver() : MockHostResolverBase(false /*use_caching*/) {}
+ ~HangingResolver() override = default;
+
+ int Resolve(const RequestInfo& info,
+ RequestPriority priority,
+ AddressList* addresses,
+ const CompletionCallback& callback,
+ std::unique_ptr<Request>* out_req,
+ const NetLogWithSource& net_log) override {
+ return ERR_IO_PENDING;
+ }
+};
+
+// A mock HttpServerProperties that always returns false for IsInitialized().
+class MockHttpServerProperties : public HttpServerPropertiesImpl {
+ public:
+ MockHttpServerProperties() = default;
+ ~MockHttpServerProperties() override = default;
+ bool IsInitialized() const override { return false; }
+};
+
+} // anonymous namespace
+
+class HttpStreamFactoryJobPeer {
+ public:
+ static void Start(HttpStreamFactory::Job* job,
+ HttpStreamRequest::StreamType stream_type) {
+ // Start() is mocked for MockHttpStreamFactoryJob.
+ // This is the alternative method to invoke real Start() method on Job.
+ job->stream_type_ = stream_type;
+ job->StartInternal();
+ }
+
+ // Returns |num_streams_| of |job|. It should be 0 for non-preconnect Jobs.
+ static int GetNumStreams(const HttpStreamFactory::Job* job) {
+ return job->num_streams_;
+ }
+
+ // Return SpdySessionKey of |job|.
+ static const SpdySessionKey GetSpdySessionKey(
+ const HttpStreamFactory::Job* job) {
+ return job->spdy_session_key_;
+ }
+
+ static void SetShouldReconsiderProxy(HttpStreamFactory::Job* job) {
+ job->should_reconsider_proxy_ = true;
+ }
+
+ static void SetStream(HttpStreamFactory::Job* job,
+ std::unique_ptr<HttpStream> http_stream) {
+ job->stream_ = std::move(http_stream);
+ }
+};
+
+class JobControllerPeer {
+ public:
+ static bool main_job_is_blocked(
+ HttpStreamFactory::JobController* job_controller) {
+ return job_controller->main_job_is_blocked_;
+ }
+
+ static bool main_job_is_resumed(
+ HttpStreamFactory::JobController* job_controller) {
+ return job_controller->main_job_is_resumed_;
+ }
+
+ static AlternativeServiceInfo GetAlternativeServiceInfoFor(
+ HttpStreamFactory::JobController* job_controller,
+ const HttpRequestInfo& request_info,
+ HttpStreamRequest::Delegate* delegate,
+ HttpStreamRequest::StreamType stream_type) {
+ return job_controller->GetAlternativeServiceInfoFor(request_info, delegate,
+ stream_type);
+ }
+};
+
+class HttpStreamFactoryJobControllerTest
+ : public TestWithScopedTaskEnvironment {
+ public:
+ HttpStreamFactoryJobControllerTest()
+ : TestWithScopedTaskEnvironment(
+ base::test::ScopedTaskEnvironment::MainThreadType::MOCK_TIME) {
+ session_deps_.enable_quic = true;
+ }
+
+ void UseAlternativeProxy() {
+ ASSERT_FALSE(test_proxy_delegate_);
+ use_alternative_proxy_ = true;
+ }
+
+ void SetPreconnect() {
+ ASSERT_FALSE(test_proxy_delegate_);
+ is_preconnect_ = true;
+ }
+
+ void DisableIPBasedPooling() {
+ ASSERT_FALSE(test_proxy_delegate_);
+ enable_ip_based_pooling_ = false;
+ }
+
+ void DisableAlternativeServices() {
+ ASSERT_FALSE(test_proxy_delegate_);
+ enable_alternative_services_ = false;
+ }
+
+ void SkipCreatingJobController() {
+ ASSERT_FALSE(job_controller_);
+ create_job_controller_ = false;
+ }
+
+ void Initialize(const HttpRequestInfo& request_info) {
+ ASSERT_FALSE(test_proxy_delegate_);
+ auto test_proxy_delegate = std::make_unique<TestProxyDelegate>();
+ test_proxy_delegate_ = test_proxy_delegate.get();
+
+ test_proxy_delegate->set_alternative_proxy_server(
+ ProxyServer::FromPacString("QUIC myproxy.org:443"));
+ EXPECT_TRUE(test_proxy_delegate->alternative_proxy_server().is_quic());
+ session_deps_.proxy_delegate = std::move(test_proxy_delegate);
+
+ if (quic_data_)
+ quic_data_->AddSocketDataToFactory(session_deps_.socket_factory.get());
+ if (tcp_data_)
+ session_deps_.socket_factory->AddSocketDataProvider(tcp_data_.get());
+
+ if (use_alternative_proxy_) {
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "HTTPS myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS);
+ session_deps_.proxy_resolution_service =
+ std::move(proxy_resolution_service);
+ }
+ session_deps_.net_log = net_log_.bound().net_log();
+ HttpNetworkSession::Params params =
+ SpdySessionDependencies::CreateSessionParams(&session_deps_);
+ HttpNetworkSession::Context session_context =
+ SpdySessionDependencies::CreateSessionContext(&session_deps_);
+
+ session_context.quic_crypto_client_stream_factory =
+ &crypto_client_stream_factory_;
+ session_context.quic_random = &random_generator_;
+ session_ = std::make_unique<HttpNetworkSession>(params, session_context);
+ factory_ = static_cast<HttpStreamFactory*>(session_->http_stream_factory());
+ if (create_job_controller_) {
+ job_controller_ = new HttpStreamFactory::JobController(
+ factory_, &request_delegate_, session_.get(), &job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller_);
+ }
+ }
+
+ TestProxyDelegate* test_proxy_delegate() const {
+ return test_proxy_delegate_;
+ }
+
+ ~HttpStreamFactoryJobControllerTest() override {
+ if (quic_data_) {
+ EXPECT_TRUE(quic_data_->AllReadDataConsumed());
+ EXPECT_TRUE(quic_data_->AllWriteDataConsumed());
+ }
+ if (tcp_data_) {
+ EXPECT_TRUE(tcp_data_->AllReadDataConsumed());
+ EXPECT_TRUE(tcp_data_->AllWriteDataConsumed());
+ }
+ }
+
+ void SetAlternativeService(const HttpRequestInfo& request_info,
+ AlternativeService alternative_service) {
+ url::SchemeHostPort server(request_info.url);
+ base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+ if (alternative_service.protocol == kProtoQUIC) {
+ session_->http_server_properties()->SetQuicAlternativeService(
+ server, alternative_service, expiration,
+ session_->params().quic_supported_versions);
+ } else {
+ session_->http_server_properties()->SetHttp2AlternativeService(
+ server, alternative_service, expiration);
+ }
+ }
+
+ void VerifyBrokenAlternateProtocolMapping(const HttpRequestInfo& request_info,
+ bool should_mark_broken) {
+ const url::SchemeHostPort server(request_info.url);
+ const AlternativeServiceInfoVector alternative_service_info_vector =
+ session_->http_server_properties()->GetAlternativeServiceInfos(server);
+ EXPECT_EQ(1u, alternative_service_info_vector.size());
+ EXPECT_EQ(should_mark_broken,
+ session_->http_server_properties()->IsAlternativeServiceBroken(
+ alternative_service_info_vector[0].alternative_service()));
+ }
+
+ TestJobFactory job_factory_;
+ MockHttpStreamRequestDelegate request_delegate_;
+ SpdySessionDependencies session_deps_{ProxyResolutionService::CreateDirect()};
+ std::unique_ptr<HttpNetworkSession> session_;
+ HttpStreamFactory* factory_ = nullptr;
+ HttpStreamFactory::JobController* job_controller_ = nullptr;
+ std::unique_ptr<HttpStreamRequest> request_;
+ std::unique_ptr<SequencedSocketData> tcp_data_;
+ std::unique_ptr<MockQuicData> quic_data_;
+ MockCryptoClientStreamFactory crypto_client_stream_factory_;
+ MockClock clock_;
+ MockRandom random_generator_{0};
+ QuicTestPacketMaker client_maker_{
+ HttpNetworkSession::Params().quic_supported_versions[0],
+ 0,
+ &clock_,
+ kServerHostname,
+ Perspective::IS_CLIENT,
+ false};
+
+ protected:
+ BoundTestNetLog net_log_;
+ bool use_alternative_proxy_ = false;
+ bool is_preconnect_ = false;
+ bool enable_ip_based_pooling_ = true;
+ bool enable_alternative_services_ = true;
+
+ private:
+ // Not owned by |this|.
+ TestProxyDelegate* test_proxy_delegate_ = nullptr;
+ bool create_job_controller_ = true;
+
+ DISALLOW_COPY_AND_ASSIGN(HttpStreamFactoryJobControllerTest);
+};
+
+TEST_F(HttpStreamFactoryJobControllerTest, ProxyResolutionFailsSync) {
+ ProxyConfig proxy_config;
+ proxy_config.set_pac_url(GURL("http://fooproxyurl"));
+ proxy_config.set_pac_mandatory(true);
+ session_deps_.proxy_resolution_service.reset(new ProxyResolutionService(
+ std::make_unique<ProxyConfigServiceFixed>(ProxyConfigWithAnnotation(
+ proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS)),
+ std::make_unique<FailingProxyResolverFactory>(), nullptr));
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.google.com");
+
+ Initialize(request_info);
+
+ EXPECT_CALL(request_delegate_,
+ OnStreamFailed(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED, _, _))
+ .Times(1);
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_FALSE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ // Make sure calling GetLoadState() when before job creation does not crash.
+ // Regression test for crbug.com/723920.
+ EXPECT_EQ(LOAD_STATE_IDLE, job_controller_->GetLoadState());
+
+ base::RunLoop().RunUntilIdle();
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, ProxyResolutionFailsAsync) {
+ ProxyConfig proxy_config;
+ proxy_config.set_pac_url(GURL("http://fooproxyurl"));
+ proxy_config.set_pac_mandatory(true);
+ MockAsyncProxyResolverFactory* proxy_resolver_factory =
+ new MockAsyncProxyResolverFactory(false);
+ MockAsyncProxyResolver resolver;
+ session_deps_.proxy_resolution_service.reset(new ProxyResolutionService(
+ std::make_unique<ProxyConfigServiceFixed>(ProxyConfigWithAnnotation(
+ proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS)),
+ base::WrapUnique(proxy_resolver_factory), nullptr));
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.google.com");
+
+ Initialize(request_info);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_FALSE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ EXPECT_EQ(LOAD_STATE_RESOLVING_PROXY_FOR_URL,
+ job_controller_->GetLoadState());
+
+ EXPECT_CALL(request_delegate_,
+ OnStreamFailed(ERR_MANDATORY_PROXY_CONFIGURATION_FAILED, _, _))
+ .Times(1);
+ proxy_resolver_factory->pending_requests()[0]->CompleteNowWithForwarder(
+ ERR_FAILED, &resolver);
+ base::RunLoop().RunUntilIdle();
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, NoSupportedProxies) {
+ session_deps_.proxy_resolution_service =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "QUIC myproxy.org:443", TRAFFIC_ANNOTATION_FOR_TESTS);
+ session_deps_.enable_quic = false;
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.google.com");
+
+ Initialize(request_info);
+
+ EXPECT_CALL(request_delegate_, OnStreamFailed(ERR_NO_SUPPORTED_PROXIES, _, _))
+ .Times(1);
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_FALSE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ base::RunLoop().RunUntilIdle();
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+class JobControllerReconsiderProxyAfterErrorTest
+ : public HttpStreamFactoryJobControllerTest,
+ public ::testing::WithParamInterface<::testing::tuple<bool, int>> {
+ public:
+ void Initialize(
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service,
+ std::unique_ptr<ProxyDelegate> proxy_delegate) {
+ session_deps_.proxy_delegate = std::move(proxy_delegate);
+ session_deps_.proxy_resolution_service =
+ std::move(proxy_resolution_service);
+ session_ = std::make_unique<HttpNetworkSession>(
+ SpdySessionDependencies::CreateSessionParams(&session_deps_),
+ SpdySessionDependencies::CreateSessionContext(&session_deps_));
+ factory_ = session_->http_stream_factory();
+ }
+
+ std::unique_ptr<HttpStreamRequest> CreateJobController(
+ const HttpRequestInfo& request_info) {
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, &request_delegate_, session_.get(), &default_job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ return job_controller->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM,
+ DEFAULT_PRIORITY);
+ }
+
+ private:
+ // Use real Jobs so that Job::Resume() is not mocked out. When main job is
+ // resumed it will use mock socket data.
+ HttpStreamFactory::JobFactory default_job_factory_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ JobControllerReconsiderProxyAfterErrorTest,
+ ::testing::Combine(::testing::Bool(),
+ testing::ValuesIn(proxy_test_mock_errors)));
+
+// TODO(eroman): The testing should be expanded to test cases where proxy
+// fallback is NOT supposed to occur, and also vary across all of
+// the proxy types.
+TEST_P(JobControllerReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) {
+ // Use mock proxy client sockets to test the fallback behavior of error codes
+ // returned by HttpProxyClientSocketWrapper. Errors returned by transport
+ // sockets usually get re-written by the wrapper class. crbug.com/826570.
+ session_deps_.socket_factory->UseMockProxyClientSockets();
+
+ const bool set_alternative_proxy_server = ::testing::get<0>(GetParam());
+ const int mock_error = ::testing::get<1>(GetParam());
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT",
+ TRAFFIC_ANNOTATION_FOR_TESTS);
+ auto test_proxy_delegate = std::make_unique<TestProxyDelegate>();
+ TestProxyDelegate* test_proxy_delegate_raw = test_proxy_delegate.get();
+
+ // Before starting the test, verify that there are no proxies marked as bad.
+ ASSERT_TRUE(proxy_resolution_service->proxy_retry_info().empty())
+ << mock_error;
+
+ // Alternative Proxy job is given preference over the main job, so populate
+ // the socket provider first.
+ StaticSocketDataProvider socket_data_proxy_alternate_job;
+ if (set_alternative_proxy_server) {
+ // Mock data for QUIC proxy socket.
+ socket_data_proxy_alternate_job.set_connect_data(
+ MockConnect(ASYNC, mock_error));
+ session_deps_.socket_factory->AddSocketDataProvider(
+ &socket_data_proxy_alternate_job);
+ test_proxy_delegate->set_alternative_proxy_server(
+ ProxyServer::FromPacString("QUIC badproxy:99"));
+ }
+
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ ProxyClientSocketDataProvider proxy_data(ASYNC, mock_error);
+
+ StaticSocketDataProvider socket_data_proxy_main_job;
+ socket_data_proxy_main_job.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(
+ &socket_data_proxy_main_job);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+ session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
+
+ // When retrying the job using the second proxy (badfallback:98),
+ // alternative job must not be created. So, socket data for only the
+ // main job is needed.
+ StaticSocketDataProvider socket_data_proxy_main_job_2;
+ socket_data_proxy_main_job_2.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(
+ &socket_data_proxy_main_job_2);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+ session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
+
+ // First request would use DIRECT, and succeed.
+ StaticSocketDataProvider socket_data_direct_first_request;
+ socket_data_direct_first_request.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(
+ &socket_data_direct_first_request);
+
+ // Second request would use DIRECT, and succeed.
+ StaticSocketDataProvider socket_data_direct_second_request;
+ socket_data_direct_second_request.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(
+ &socket_data_direct_second_request);
+
+ // Now request a stream. It should succeed using the DIRECT.
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.example.com");
+
+ Initialize(std::move(proxy_resolution_service),
+ std::move(test_proxy_delegate));
+ EXPECT_EQ(set_alternative_proxy_server,
+ test_proxy_delegate_raw->alternative_proxy_server().is_quic());
+
+ // Start two requests. The first request should consume data from
+ // |socket_data_proxy_main_job|,
+ // |socket_data_proxy_alternate_job| and
+ // |socket_data_direct_first_request|. The second request should consume
+ // data from |socket_data_direct_second_request|.
+
+ for (size_t i = 0; i < 2; ++i) {
+ ProxyInfo used_proxy_info;
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _))
+ .Times(1)
+ .WillOnce(::testing::SaveArg<1>(&used_proxy_info));
+
+ std::unique_ptr<HttpStreamRequest> request =
+ CreateJobController(request_info);
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that request was fetched without proxy.
+ EXPECT_TRUE(used_proxy_info.is_direct());
+
+ // The proxies that failed should now be known to the proxy service as
+ // bad.
+ const ProxyRetryInfoMap& retry_info =
+ session_->proxy_resolution_service()->proxy_retry_info();
+ EXPECT_THAT(retry_info, SizeIs(set_alternative_proxy_server ? 3 : 2));
+ EXPECT_THAT(retry_info, Contains(Key("https://badproxy:99")));
+ EXPECT_THAT(retry_info, Contains(Key("https://badfallbackproxy:98")));
+
+ if (set_alternative_proxy_server)
+ EXPECT_THAT(retry_info, Contains(Key("quic://badproxy:99")));
+ }
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Tests that ERR_MSG_TOO_BIG is retryable for QUIC proxy.
+TEST_F(JobControllerReconsiderProxyAfterErrorTest, ReconsiderErrMsgTooBig) {
+ session_deps_.socket_factory->UseMockProxyClientSockets();
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "QUIC badproxy:99; DIRECT", TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ // Before starting the test, verify that there are no proxies marked as bad.
+ ASSERT_TRUE(proxy_resolution_service->proxy_retry_info().empty());
+
+ // Mock data for the QUIC proxy socket.
+ StaticSocketDataProvider quic_proxy_socket;
+ quic_proxy_socket.set_connect_data(MockConnect(ASYNC, ERR_MSG_TOO_BIG));
+ session_deps_.socket_factory->AddSocketDataProvider(&quic_proxy_socket);
+
+ // Mock data for DIRECT.
+ StaticSocketDataProvider socket_data_direct;
+ socket_data_direct.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(&socket_data_direct);
+
+ // Now request a stream. It should fallback to DIRECT on ERR_MSG_TOO_BIG.
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.example.com");
+
+ Initialize(std::move(proxy_resolution_service),
+ std::make_unique<TestProxyDelegate>());
+
+ ProxyInfo used_proxy_info;
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _))
+ .Times(1)
+ .WillOnce(::testing::SaveArg<1>(&used_proxy_info));
+
+ std::unique_ptr<HttpStreamRequest> request =
+ CreateJobController(request_info);
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(used_proxy_info.is_direct());
+ const ProxyRetryInfoMap& retry_info =
+ session_->proxy_resolution_service()->proxy_retry_info();
+ EXPECT_THAT(retry_info, SizeIs(1));
+ EXPECT_THAT(retry_info, Contains(Key("quic://badproxy:99")));
+
+ request.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Same as test above except that this is testing the retry behavior for
+// non-QUIC proxy on ERR_MSG_TOO_BIG.
+TEST_F(JobControllerReconsiderProxyAfterErrorTest,
+ DoNotReconsiderErrMsgTooBig) {
+ session_deps_.socket_factory->UseMockProxyClientSockets();
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "HTTPS badproxy:99; DIRECT", TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ // Before starting the test, verify that there are no proxies marked as bad.
+ ASSERT_TRUE(proxy_resolution_service->proxy_retry_info().empty());
+
+ // Mock data for the HTTPS proxy socket.
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ ProxyClientSocketDataProvider proxy_data(ASYNC, ERR_MSG_TOO_BIG);
+ StaticSocketDataProvider https_proxy_socket;
+ https_proxy_socket.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(&https_proxy_socket);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+ session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
+
+ // Now request a stream. It should not fallback to DIRECT on ERR_MSG_TOO_BIG.
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.example.com");
+
+ Initialize(std::move(proxy_resolution_service),
+ std::make_unique<TestProxyDelegate>());
+
+ ProxyInfo used_proxy_info;
+ EXPECT_CALL(request_delegate_, OnStreamFailed(ERR_MSG_TOO_BIG, _, _))
+ .Times(1);
+
+ std::unique_ptr<HttpStreamRequest> request =
+ CreateJobController(request_info);
+ base::RunLoop().RunUntilIdle();
+
+ const ProxyRetryInfoMap& retry_info =
+ session_->proxy_resolution_service()->proxy_retry_info();
+ EXPECT_THAT(retry_info, SizeIs(0));
+
+ request.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Tests that the main (HTTP) job is started after the alternative
+// proxy server job has failed. There are 3 jobs in total that are run
+// in the following sequence: alternative proxy server job,
+// delayed HTTP job with the first proxy server, HTTP job with
+// the second proxy configuration. The result of the last job (OK)
+// should be returned to the delegate.
+TEST_F(JobControllerReconsiderProxyAfterErrorTest,
+ SecondMainJobIsStartedAfterAltProxyServerJobFailed) {
+ // Configure the proxies and initialize the test.
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
+ ProxyResolutionService::CreateFixedFromPacResult(
+ "HTTPS myproxy.org:443; DIRECT", TRAFFIC_ANNOTATION_FOR_TESTS);
+
+ auto test_proxy_delegate = std::make_unique<TestProxyDelegate>();
+ test_proxy_delegate->set_alternative_proxy_server(
+ ProxyServer::FromPacString("QUIC myproxy.org:443"));
+
+ Initialize(std::move(proxy_resolution_service),
+ std::move(test_proxy_delegate));
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromSeconds(100);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("http://www.example.com")), stats1);
+
+ // Prepare the mocked data.
+ MockQuicData quic_data;
+ quic_data.AddRead(ASYNC, ERR_QUIC_PROTOCOL_ERROR);
+ quic_data.AddWrite(ASYNC, OK);
+ quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
+
+ StaticSocketDataProvider tcp_data_1;
+ tcp_data_1.set_connect_data(MockConnect(SYNCHRONOUS, ERR_CONNECTION_REFUSED));
+ session_deps_.socket_factory->AddSocketDataProvider(&tcp_data_1);
+
+ StaticSocketDataProvider tcp_data_2;
+ tcp_data_2.set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(&tcp_data_2);
+ SSLSocketDataProvider ssl_data(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ // Create a request.
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.example.com");
+ AlternativeService alternative_service(kProtoQUIC, "www.example.com", 80);
+ SetAlternativeService(request_info, alternative_service);
+
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _)).Times(1);
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+
+ // Create the job controller.
+ std::unique_ptr<HttpStreamRequest> request =
+ CreateJobController(request_info);
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(quic_data.AllReadDataConsumed());
+ EXPECT_TRUE(quic_data.AllWriteDataConsumed());
+ EXPECT_TRUE(tcp_data_1.AllReadDataConsumed());
+ EXPECT_TRUE(tcp_data_1.AllWriteDataConsumed());
+ EXPECT_TRUE(tcp_data_2.AllReadDataConsumed());
+ EXPECT_TRUE(tcp_data_2.AllWriteDataConsumed());
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, OnStreamFailedWithNoAlternativeJob) {
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, ERR_FAILED));
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.google.com");
+
+ Initialize(request_info);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ // There's no other alternative job. Thus when stream failed, it should
+ // notify Request of the stream failure.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(ERR_FAILED, _, _)).Times(1);
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, OnStreamReadyWithNoAlternativeJob) {
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.google.com");
+
+ Initialize(request_info);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ // There's no other alternative job. Thus when a stream is ready, it should
+ // notify Request.
+ EXPECT_TRUE(job_controller_->main_job());
+
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+ base::RunLoop().RunUntilIdle();
+}
+
+// Test we cancel Jobs correctly when the Request is explicitly canceled
+// before any Job is bound to Request.
+TEST_F(HttpStreamFactoryJobControllerTest, CancelJobsBeforeBinding) {
+ // Use COLD_START to make the alt job pending.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, OK);
+
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // Reset the Request will cancel all the Jobs since there's no Job determined
+ // to serve Request yet and JobController will notify the factory to delete
+ // itself upon completion.
+ request_.reset();
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Test that the controller does not create alternative job when the advertised
+// versions in AlternativeServiceInfo do not contain any version that is
+// supported.
+TEST_F(HttpStreamFactoryJobControllerTest,
+ DoNotCreateAltJobIfQuicVersionsUnsupported) {
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+ session_->http_server_properties()->SetQuicAlternativeService(
+ server, alternative_service, expiration, {QUIC_VERSION_UNSUPPORTED});
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ request_.reset();
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, OnStreamFailedForBothJobs) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddConnect(ASYNC, ERR_FAILED);
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, ERR_FAILED));
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // The failure of second Job should be reported to Request as there's no more
+ // pending Job to serve the Request.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(1);
+ base::RunLoop().RunUntilIdle();
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, AltJobFailsAfterMainJobSucceeds) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(ASYNC, ERR_FAILED);
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // Main job succeeds, starts serving Request and it should report status
+ // to Request. The alternative job will mark the main job complete and gets
+ // orphaned.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+ // JobController shouldn't report the status of second job as request
+ // is already successfully served.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+
+ base::RunLoop().RunUntilIdle();
+
+ VerifyBrokenAlternateProtocolMapping(request_info, true);
+ // Reset the request as it's been successfully served.
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Tests that when alt job succeeds, main job is destroyed.
+TEST_F(HttpStreamFactoryJobControllerTest, AltJobSucceedsMainJobDestroyed) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ // Use cold start and complete alt job manually.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+
+ // Make |alternative_job| succeed.
+ auto http_stream = std::make_unique<HttpBasicStream>(
+ std::make_unique<ClientSocketHandle>(), false, false);
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, http_stream.get()));
+
+ HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(),
+ std::move(http_stream));
+ job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig());
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ request_.reset();
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Tests that if alt job succeeds and main job is blocked, main job should be
+// cancelled immediately. |request_| completion will clean up the JobController.
+// Regression test for crbug.com/678768.
+TEST_F(HttpStreamFactoryJobControllerTest,
+ AltJobSucceedsMainJobBlockedControllerDestroyed) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddWrite(SYNCHRONOUS,
+ client_maker_.MakeInitialSettingsPacket(1, nullptr));
+ quic_data_->AddRead(ASYNC, OK);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(JobControllerPeer::main_job_is_blocked(job_controller_));
+
+ // |alternative_job| succeeds and should report status to |request_delegate_|.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // Invoke OnRequestComplete() which should delete |job_controller_| from
+ // |factory_|.
+ request_.reset();
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ // This fails without the fix for crbug.com/678768.
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest,
+ SpdySessionKeyHasOriginHostPortPair) {
+ session_deps_.enable_http2_alternative_service = true;
+
+ const char origin_host[] = "www.example.org";
+ const uint16_t origin_port = 443;
+ const char alternative_host[] = "mail.example.org";
+ const uint16_t alternative_port = 123;
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url =
+ GURL(base::StringPrintf("https://%s:%u", origin_host, origin_port));
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoHTTP2, alternative_host,
+ alternative_port);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ HostPortPair main_host_port_pair =
+ HttpStreamFactoryJobPeer::GetSpdySessionKey(job_controller_->main_job())
+ .host_port_pair();
+ EXPECT_EQ(origin_host, main_host_port_pair.host());
+ EXPECT_EQ(origin_port, main_host_port_pair.port());
+
+ HostPortPair alternative_host_port_pair =
+ HttpStreamFactoryJobPeer::GetSpdySessionKey(
+ job_controller_->alternative_job())
+ .host_port_pair();
+ EXPECT_EQ(origin_host, alternative_host_port_pair.host());
+ EXPECT_EQ(origin_port, alternative_host_port_pair.port());
+}
+
+// Tests that if an orphaned job completes after |request_| is gone,
+// JobController will be cleaned up.
+TEST_F(HttpStreamFactoryJobControllerTest,
+ OrphanedJobCompletesControllerDestroyed) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ // Use cold start and complete alt job manually.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ // main job should not be blocked because alt job returned ERR_IO_PENDING.
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ // Complete main job now.
+ base::RunLoop().RunUntilIdle();
+
+ // Invoke OnRequestComplete() which should not delete |job_controller_| from
+ // |factory_| because alt job is yet to finish.
+ request_.reset();
+ ASSERT_FALSE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+ EXPECT_FALSE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // Make |alternative_job| succeed.
+ auto http_stream = std::make_unique<HttpBasicStream>(
+ std::make_unique<ClientSocketHandle>(), false, false);
+ HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(),
+ std::move(http_stream));
+ // This should not call request_delegate_::OnStreamReady.
+ job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig());
+ // Make sure that controller does not leak.
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, AltJobSucceedsAfterMainJobFailed) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ // Use cold start and complete alt job manually.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ // One failed TCP connect.
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, ERR_FAILED));
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // |main_job| fails but should not report status to Request.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ base::RunLoop().RunUntilIdle();
+
+ // Make |alternative_job| succeed.
+ auto http_stream = std::make_unique<HttpBasicStream>(
+ std::make_unique<ClientSocketHandle>(), false, false);
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, http_stream.get()));
+
+ HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(),
+ std::move(http_stream));
+ job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig());
+
+ // |alternative_job| succeeds and should report status to Request.
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, MainJobSucceedsAfterAltJobFailed) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddConnect(SYNCHRONOUS, ERR_FAILED);
+
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ base::HistogramTester histogram_tester;
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // |alternative_job| fails but should not report status to Request.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+ // |main_job| succeeds and should report status to Request.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the alternate protocol is marked as broken.
+ VerifyBrokenAlternateProtocolMapping(request_info, true);
+ histogram_tester.ExpectUniqueSample("Net.AlternateServiceFailed", -ERR_FAILED,
+ 1);
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Verifies that if the alternative job fails due to a connection change event,
+// then the alternative service is not marked as broken.
+TEST_F(HttpStreamFactoryJobControllerTest,
+ MainJobSucceedsAfterConnectionChanged) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddConnect(SYNCHRONOUS, ERR_NETWORK_CHANGED);
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ base::HistogramTester histogram_tester;
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // |alternative_job| fails but should not report status to Request.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+ // |main_job| succeeds and should report status to Request.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+ base::RunLoop().RunUntilIdle();
+
+ // Verify that the alternate protocol is not marked as broken.
+ VerifyBrokenAlternateProtocolMapping(request_info, false);
+ histogram_tester.ExpectUniqueSample("Net.AlternateServiceFailed",
+ -ERR_NETWORK_CHANGED, 1);
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Regression test for crbug/621069.
+// Get load state after main job fails and before alternative job succeeds.
+TEST_F(HttpStreamFactoryJobControllerTest, GetLoadStateAfterMainJobFailed) {
+ // Use COLD_START to complete alt job manually.
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, ERR_FAILED));
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // |main_job| fails but should not report status to Request.
+ // The alternative job will mark the main job complete.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+
+ base::RunLoop().RunUntilIdle();
+
+ // Controller should use alternative job to get load state.
+ job_controller_->GetLoadState();
+
+ // |alternative_job| succeeds and should report status to Request.
+ auto http_stream = std::make_unique<HttpBasicStream>(
+ std::make_unique<ClientSocketHandle>(), false, false);
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, http_stream.get()));
+
+ HttpStreamFactoryJobPeer::SetStream(job_factory_.alternative_job(),
+ std::move(http_stream));
+ job_controller_->OnStreamReady(job_factory_.alternative_job(), SSLConfig());
+
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, ResumeMainJobWhenAltJobStalls) {
+ // Use COLD_START to stall alt job.
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // Alt job is stalled and main job should complete successfully.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ base::RunLoop().RunUntilIdle();
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, InvalidPortForQuic) {
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ // Using a restricted port 101 for QUIC should fail and the alternative job
+ // should post OnStreamFailedCall on the controller to resume the main job.
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 101);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_factory_.main_job()->is_waiting());
+
+ // Wait until OnStreamFailedCallback is executed on the alternative job.
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1);
+ base::RunLoop().RunUntilIdle();
+}
+
+// Verifies that the main job is not resumed until after the alt job completes
+// host resolution.
+TEST_F(HttpStreamFactoryJobControllerTest, HostResolutionHang) {
+ auto hanging_resolver = std::make_unique<MockHostResolver>();
+ hanging_resolver->set_ondemand_mode(true);
+ hanging_resolver->set_synchronous_mode(false);
+ session_deps_.host_resolver = std::move(hanging_resolver);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ // handshake will fail asynchronously after mock data is unpaused.
+ MockQuicData quic_data;
+ quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Pause
+ quic_data.AddRead(ASYNC, ERR_FAILED);
+ quic_data.AddWrite(ASYNC, ERR_FAILED);
+ quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(10);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://www.google.com")), stats1);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // This prevents handshake from immediately succeeding.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(JobControllerPeer::main_job_is_blocked(job_controller_));
+
+ // Since the alt job has not finished host resolution, there should be no
+ // delayed task posted to resume the main job.
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(50));
+ EXPECT_TRUE(JobControllerPeer::main_job_is_blocked(job_controller_));
+
+ // Allow alt job host resolution to complete.
+ session_deps_.host_resolver->ResolveAllPending();
+
+ // Task to resume main job in 15 microseconds should be posted.
+ EXPECT_TRUE(MainThreadHasPendingTask());
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(14));
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(1));
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+ EXPECT_TRUE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Unpause mock quic data.
+ // Will cause |alternative_job| to fail, but its failure should not be
+ // reported to Request.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+ // OnStreamFailed will post a task to resume the main job immediately but
+ // won't call Resume() on the main job since it's been resumed already.
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ quic_data.GetSequencedSocketData()->Resume();
+ FastForwardUntilNoTasksRemain();
+ // Alt job should be cleaned up
+ EXPECT_FALSE(job_controller_->alternative_job());
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, DelayedTCP) {
+ auto immediate_resolver = std::make_unique<MockHostResolver>();
+ immediate_resolver->set_synchronous_mode(true);
+ session_deps_.host_resolver = std::move(immediate_resolver);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ // Handshake will fail asynchronously after mock data is unpaused.
+ MockQuicData quic_data;
+ quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Pause
+ quic_data.AddRead(ASYNC, ERR_FAILED);
+ quic_data.AddWrite(ASYNC, ERR_FAILED);
+ quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(10);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://www.google.com")), stats1);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // This prevents handshake from immediately succeeding.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(job_controller_->main_job()->is_waiting());
+ // Main job is not blocked but hasn't resumed yet; it should resume in 15us.
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+ EXPECT_FALSE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Task to resume main job in 15us should be posted.
+ EXPECT_TRUE(MainThreadHasPendingTask());
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(14));
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(1));
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Unpause mock quic data and run all remaining tasks. Alt-job should fail
+ // and be cleaned up.
+ quic_data.GetSequencedSocketData()->Resume();
+ FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(job_controller_->alternative_job());
+}
+
+// Regression test for crbug.com/789560.
+TEST_F(HttpStreamFactoryJobControllerTest, ResumeMainJobLaterCanceled) {
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service =
+ ProxyResolutionService::CreateDirect();
+ ProxyResolutionService* proxy_resolution_service_raw =
+ proxy_resolution_service.get();
+ session_deps_.proxy_resolution_service = std::move(proxy_resolution_service);
+
+ // Using hanging resolver will cause the alternative job to hang indefinitely.
+ session_deps_.host_resolver = std::make_unique<HangingResolver>();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(10);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://www.google.com")), stats1);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(job_controller_->main_job()->is_waiting());
+
+ base::RunLoop run_loop;
+ // The main job should be resumed without delay when alt job fails.
+ EXPECT_CALL(*job_factory_.main_job(), Resume())
+ .Times(1)
+ .WillOnce(Invoke([&run_loop]() { run_loop.Quit(); }));
+ job_controller_->OnStreamFailed(job_factory_.alternative_job(),
+ ERR_QUIC_PROTOCOL_ERROR, SSLConfig());
+ FastForwardBy(base::TimeDelta::FromMicroseconds(0));
+ run_loop.Run();
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ // Calling ForceReloadProxyConfig will cause the proxy configuration to
+ // change. It will still be the direct connection but the configuration
+ // version will be bumped. That is enough for the job controller to restart
+ // the jobs.
+ proxy_resolution_service_raw->ForceReloadProxyConfig();
+ HttpStreamFactoryJobPeer::SetShouldReconsiderProxy(job_factory_.main_job());
+ // Now the alt service is marked as broken (e.g. through a different request),
+ // so only non-alt job is restarted.
+ session_->http_server_properties()->MarkAlternativeServiceBroken(
+ alternative_service);
+
+ job_controller_->OnStreamFailed(job_factory_.main_job(), ERR_FAILED,
+ SSLConfig());
+ // Jobs are restarted.
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ // There shouldn't be any ResumeMainJobLater() delayed tasks.
+ // This EXPECT_CALL will fail before crbug.com/789560 fix.
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(15));
+
+ EXPECT_TRUE(job_controller_->main_job());
+ request_.reset();
+}
+
+// Test that main job is blocked for kMaxDelayTimeForMainJob(3s) if
+// http_server_properties cached an inappropriate large srtt for the server,
+// which would potentially delay the main job for a extremely long time in
+// delayed tcp case.
+TEST_F(HttpStreamFactoryJobControllerTest, DelayedTCPWithLargeSrtt) {
+ // The max delay time should be in sync with .cc file.
+ base::TimeDelta kMaxDelayTimeForMainJob = base::TimeDelta::FromSeconds(3);
+
+ auto immediate_resolver = std::make_unique<MockHostResolver>();
+ immediate_resolver->set_synchronous_mode(true);
+ session_deps_.host_resolver = std::move(immediate_resolver);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ // handshake will fail asynchronously after mock data is unpaused.
+ MockQuicData quic_data;
+ quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Pause
+ quic_data.AddRead(ASYNC, ERR_FAILED);
+ quic_data.AddWrite(ASYNC, ERR_FAILED);
+ quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromSeconds(100);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://www.google.com")), stats1);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // This prevents handshake from immediately succeeding.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ // Main job is not blocked but hasn't resumed yet; it should resume in 3s.
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+ EXPECT_FALSE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Task to resume main job in 3 seconds should be posted.
+ EXPECT_TRUE(MainThreadHasPendingTask());
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(kMaxDelayTimeForMainJob - base::TimeDelta::FromMicroseconds(1));
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(1));
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Unpause mock quic data and run all remaining tasks. Alt-job should fail
+ // and be cleaned up.
+ quic_data.GetSequencedSocketData()->Resume();
+ FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(job_controller_->alternative_job());
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest,
+ ResumeMainJobImmediatelyOnStreamFailed) {
+ auto immediate_resolver = std::make_unique<MockHostResolver>();
+ immediate_resolver->set_synchronous_mode(true);
+ session_deps_.host_resolver = std::move(immediate_resolver);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ // handshake will fail asynchronously after mock data is unpaused.
+ MockQuicData quic_data;
+ quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Pause
+ quic_data.AddRead(ASYNC, ERR_FAILED);
+ quic_data.AddWrite(ASYNC, ERR_FAILED);
+ quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(10);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://www.google.com")), stats1);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // This prevents handshake from immediately succeeding.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ // Main job is not blocked but hasn't resumed yet; it's scheduled to resume
+ // in 15us.
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+ EXPECT_FALSE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Task to resume main job in 15us should be posted.
+ EXPECT_TRUE(MainThreadHasPendingTask());
+
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(1));
+
+ // Now unpause the mock quic data to fail the alt job. This should immediately
+ // resume the main job.
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1);
+ quic_data.GetSequencedSocketData()->Resume();
+ FastForwardBy(base::TimeDelta());
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+ EXPECT_TRUE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Verify there is another task to resume main job with delay but should
+ // not call Resume() on the main job as main job has been resumed.
+ EXPECT_TRUE(MainThreadHasPendingTask());
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(15));
+
+ FastForwardUntilNoTasksRemain();
+}
+
+// Verifies that the alternative proxy server job is not created if the URL
+// scheme is HTTPS.
+TEST_F(HttpStreamFactoryJobControllerTest, HttpsURL) {
+ // Using hanging resolver will cause the alternative job to hang indefinitely.
+ session_deps_.host_resolver = std::make_unique<HangingResolver>();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://mail.example.org/");
+ Initialize(request_info);
+ EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_quic());
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->main_job()->is_waiting());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ base::RunLoop().RunUntilIdle();
+}
+
+// Verifies that the alternative proxy server job is not created if the main job
+// does not fetch the resource through a proxy.
+TEST_F(HttpStreamFactoryJobControllerTest, HttpURLWithNoProxy) {
+ // Using hanging resolver will cause the alternative job to hang indefinitely.
+ session_deps_.host_resolver = std::make_unique<HangingResolver>();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://mail.example.org/");
+
+ Initialize(request_info);
+ EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_quic());
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->main_job()->is_waiting());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ base::RunLoop().RunUntilIdle();
+}
+
+// Verifies that the main job is resumed properly after a delay when the
+// alternative proxy server job hangs.
+TEST_F(HttpStreamFactoryJobControllerTest, DelayedTCPAlternativeProxy) {
+ auto immediate_resolver = std::make_unique<MockHostResolver>();
+ immediate_resolver->set_synchronous_mode(true);
+ session_deps_.host_resolver = std::move(immediate_resolver);
+
+ UseAlternativeProxy();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.mail.example.org/");
+
+ Initialize(request_info);
+
+ EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_quic());
+
+ // Handshake will fail asynchronously after mock data is unpaused.
+ MockQuicData quic_data;
+ quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Pause
+ quic_data.AddRead(ASYNC, ERR_FAILED);
+ quic_data.AddWrite(ASYNC, ERR_FAILED);
+ quic_data.AddSocketDataToFactory(session_deps_.socket_factory.get());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(10);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://myproxy.org")), stats1);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // This prevents handshake from immediately succeeding.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(job_controller_->main_job()->is_waiting());
+ // Main job is not blocked but hasn't resumed yet; it should resume in 15us.
+ EXPECT_FALSE(JobControllerPeer::main_job_is_blocked(job_controller_));
+ EXPECT_FALSE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Task to resume main job in 15us should be posted.
+ EXPECT_TRUE(MainThreadHasPendingTask());
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(0);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(14));
+ EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1);
+ FastForwardBy(base::TimeDelta::FromMicroseconds(1));
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+ EXPECT_TRUE(JobControllerPeer::main_job_is_resumed(job_controller_));
+
+ // Unpause mock quic data and run all remaining tasks. Alt-job should fail
+ // and be cleaned up.
+ quic_data.GetSequencedSocketData()->Resume();
+ FastForwardUntilNoTasksRemain();
+ EXPECT_FALSE(job_controller_->alternative_job());
+}
+
+// Verifies that if the alternative proxy server job fails immediately, the
+// main job is not blocked.
+TEST_F(HttpStreamFactoryJobControllerTest, FailAlternativeProxy) {
+ session_deps_.socket_factory->UseMockProxyClientSockets();
+ ProxyClientSocketDataProvider proxy_data(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
+
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddConnect(SYNCHRONOUS, ERR_FAILED);
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ UseAlternativeProxy();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://mail.example.org/");
+ Initialize(request_info);
+ EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_quic());
+ EXPECT_THAT(session_->proxy_resolution_service()->proxy_retry_info(),
+ IsEmpty());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(300 * 1000);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://myproxy.org")), stats1);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(job_controller_->alternative_job());
+ EXPECT_TRUE(job_controller_->main_job());
+
+ // The alternative proxy server should be marked as bad.
+ EXPECT_THAT(session_->proxy_resolution_service()->proxy_retry_info(),
+ ElementsAre(Key("quic://myproxy.org:443")));
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Verifies that if the alternative proxy server job fails due to network
+// disconnection, then the proxy delegate is not notified.
+TEST_F(HttpStreamFactoryJobControllerTest,
+ InternetDisconnectedAlternativeProxy) {
+ session_deps_.socket_factory->UseMockProxyClientSockets();
+ ProxyClientSocketDataProvider proxy_data(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
+
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddConnect(SYNCHRONOUS, ERR_INTERNET_DISCONNECTED);
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ UseAlternativeProxy();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://mail.example.org/");
+ Initialize(request_info);
+ EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_quic());
+
+ // Enable delayed TCP and set time delay for waiting job.
+ QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory();
+ quic_stream_factory->set_require_confirmation(false);
+ ServerNetworkStats stats1;
+ stats1.srtt = base::TimeDelta::FromMicroseconds(300 * 1000);
+ session_->http_server_properties()->SetServerNetworkStats(
+ url::SchemeHostPort(GURL("https://myproxy.org")), stats1);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(job_controller_->alternative_job());
+ EXPECT_TRUE(job_controller_->main_job());
+
+ // The alternative proxy server should not be marked as bad.
+ EXPECT_TRUE(test_proxy_delegate()->alternative_proxy_server().is_valid());
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest,
+ AlternativeProxyServerJobFailsAfterMainJobSucceeds) {
+ base::HistogramTester histogram_tester;
+
+ session_deps_.socket_factory->UseMockProxyClientSockets();
+ ProxyClientSocketDataProvider proxy_data(SYNCHRONOUS, OK);
+ session_deps_.socket_factory->AddProxyClientSocketDataProvider(&proxy_data);
+
+ // Use COLD_START to make the alt job pending.
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ UseAlternativeProxy();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.google.com");
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // Main job succeeds, starts serving Request and it should report status
+ // to Request. The alternative job will mark the main job complete and gets
+ // orphaned.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_TRUE(job_controller_->alternative_job());
+
+ // JobController shouldn't report the status of alternative server job as
+ // request is already successfully served.
+ EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0);
+ job_controller_->OnStreamFailed(job_factory_.alternative_job(), ERR_FAILED,
+ SSLConfig());
+
+ // Reset the request as it's been successfully served.
+ request_.reset();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+
+ histogram_tester.ExpectUniqueSample("Net.QuicAlternativeProxy.Usage",
+ 2 /* ALTERNATIVE_PROXY_USAGE_LOST_RACE */,
+ 1);
+}
+
+TEST_F(HttpStreamFactoryJobControllerTest, PreconnectToHostWithValidAltSvc) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddWrite(SYNCHRONOUS,
+ client_maker_.MakeInitialSettingsPacket(1, nullptr));
+ quic_data_->AddRead(ASYNC, OK);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.example.com");
+ SetPreconnect();
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ job_controller_->Preconnect(1);
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_EQ(HttpStreamFactory::PRECONNECT,
+ job_controller_->main_job()->job_type());
+ EXPECT_FALSE(job_controller_->alternative_job());
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// When preconnect to a H2 supported server, only 1 connection is opened.
+TEST_F(HttpStreamFactoryJobControllerTest,
+ PreconnectMultipleStreamsToH2Server) {
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+ SetPreconnect();
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("http://www.example.com");
+ Initialize(request_info);
+
+ // Sets server support HTTP/2.
+ url::SchemeHostPort server(request_info.url);
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ job_controller_->Preconnect(/*num_streams=*/5);
+ // Only one job is started.
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+ EXPECT_EQ(HttpStreamFactory::PRECONNECT,
+ job_controller_->main_job()->job_type());
+ // There is only 1 connect even though multiple streams were requested.
+ EXPECT_EQ(
+ 1, HttpStreamFactoryJobPeer::GetNumStreams(job_controller_->main_job()));
+
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+class JobControllerLimitMultipleH2Requests
+ : public HttpStreamFactoryJobControllerTest {
+ protected:
+ const int kNumRequests = 5;
+ void SetUp() override { SkipCreatingJobController(); }
+};
+
+TEST_F(JobControllerLimitMultipleH2Requests, MultipleRequests) {
+ // Make sure there is only one socket connect.
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+ tcp_data_ =
+ std::make_unique<SequencedSocketData>(reads, base::span<MockWrite>());
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ ssl_data.next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.example.com");
+ Initialize(request_info);
+ SpdySessionPoolPeer pool_peer(session_->spdy_session_pool());
+ pool_peer.SetEnableSendingInitialData(false);
+
+ // Sets server support HTTP/2.
+ url::SchemeHostPort server(request_info.url);
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ std::vector<std::unique_ptr<MockHttpStreamRequestDelegate>> request_delegates;
+ std::vector<std::unique_ptr<HttpStreamRequest>> requests;
+ for (int i = 0; i < kNumRequests; ++i) {
+ request_delegates.emplace_back(
+ std::make_unique<MockHttpStreamRequestDelegate>());
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, request_delegates[i].get(), session_.get(), &job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ auto request = job_controller->Start(
+ request_delegates[i].get(), nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller->main_job());
+ EXPECT_FALSE(job_controller->alternative_job());
+ requests.push_back(std::move(request));
+ }
+
+ for (int i = 0; i < kNumRequests; ++i) {
+ EXPECT_CALL(*request_delegates[i].get(), OnStreamReadyImpl(_, _, _));
+ }
+
+ base::RunLoop().RunUntilIdle();
+ requests.clear();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+ TestNetLogEntry::List entries;
+ size_t log_position = 0;
+ for (int i = 0; i < kNumRequests - 1; ++i) {
+ net_log_.GetEntries(&entries);
+ log_position = ExpectLogContainsSomewhereAfter(
+ entries, log_position, NetLogEventType::HTTP_STREAM_JOB_THROTTLED,
+ NetLogEventPhase::NONE);
+ }
+}
+
+TEST_F(JobControllerLimitMultipleH2Requests, MultipleRequestsFirstRequestHang) {
+ // First socket connect hang.
+ SequencedSocketData hangdata;
+ hangdata.set_connect_data(MockConnect(SYNCHRONOUS, ERR_IO_PENDING));
+ session_deps_.socket_factory->AddSocketDataProvider(&hangdata);
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+ std::list<SequencedSocketData> socket_data;
+ std::list<SSLSocketDataProvider> ssl_socket_data;
+ // kNumRequests - 1 will resume themselves after a delay. There will be
+ // kNumRequests - 1 sockets opened.
+ for (int i = 0; i < kNumRequests - 1; i++) {
+ // Only the first one needs a MockRead because subsequent sockets are
+ // not used to establish a SpdySession.
+ if (i == 0) {
+ socket_data.emplace_back(reads, base::span<MockWrite>());
+ } else {
+ socket_data.emplace_back();
+ }
+ socket_data.back().set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(&socket_data.back());
+ ssl_socket_data.emplace_back(ASYNC, OK);
+ ssl_socket_data.back().next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSSLSocketDataProvider(
+ &ssl_socket_data.back());
+ }
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.example.com");
+ Initialize(request_info);
+ SpdySessionPoolPeer pool_peer(session_->spdy_session_pool());
+ pool_peer.SetEnableSendingInitialData(false);
+
+ // Sets server support HTTP/2.
+ url::SchemeHostPort server(request_info.url);
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ std::vector<std::unique_ptr<MockHttpStreamRequestDelegate>> request_delegates;
+ std::vector<std::unique_ptr<HttpStreamRequest>> requests;
+ for (int i = 0; i < kNumRequests; ++i) {
+ request_delegates.push_back(
+ std::make_unique<MockHttpStreamRequestDelegate>());
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, request_delegates[i].get(), session_.get(), &job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ auto request = job_controller->Start(
+ request_delegates[i].get(), nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller->main_job());
+ EXPECT_FALSE(job_controller->alternative_job());
+ requests.push_back(std::move(request));
+ }
+
+ for (int i = 0; i < kNumRequests; ++i) {
+ EXPECT_CALL(*request_delegates[i].get(), OnStreamReadyImpl(_, _, _));
+ }
+
+ EXPECT_TRUE(MainThreadHasPendingTask());
+ FastForwardBy(base::TimeDelta::FromMilliseconds(
+ HttpStreamFactory::Job::kHTTP2ThrottleMs));
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+ requests.clear();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+
+ EXPECT_TRUE(hangdata.AllReadDataConsumed());
+ for (const auto& data : socket_data) {
+ EXPECT_TRUE(data.AllReadDataConsumed());
+ EXPECT_TRUE(data.AllWriteDataConsumed());
+ }
+}
+
+TEST_F(JobControllerLimitMultipleH2Requests,
+ MultipleRequestsFirstRequestCanceled) {
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+ SequencedSocketData first_socket(reads, base::span<MockWrite>());
+ first_socket.set_connect_data(MockConnect(ASYNC, OK));
+ SSLSocketDataProvider first_ssl_data(ASYNC, OK);
+ first_ssl_data.next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSocketDataProvider(&first_socket);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&first_ssl_data);
+ std::list<SequencedSocketData> socket_data;
+ std::list<SSLSocketDataProvider> ssl_socket_data;
+ // kNumRequests - 1 will be resumed when the first request is canceled.
+ for (int i = 0; i < kNumRequests - 1; i++) {
+ socket_data.emplace_back();
+ socket_data.back().set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(&socket_data.back());
+ ssl_socket_data.emplace_back(ASYNC, OK);
+ ssl_socket_data.back().next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSSLSocketDataProvider(
+ &ssl_socket_data.back());
+ }
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.example.com");
+ Initialize(request_info);
+ SpdySessionPoolPeer pool_peer(session_->spdy_session_pool());
+ pool_peer.SetEnableSendingInitialData(false);
+
+ // Sets server support HTTP/2.
+ url::SchemeHostPort server(request_info.url);
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ std::vector<std::unique_ptr<MockHttpStreamRequestDelegate>> request_delegates;
+ std::vector<std::unique_ptr<HttpStreamRequest>> requests;
+ for (int i = 0; i < kNumRequests; ++i) {
+ request_delegates.emplace_back(
+ std::make_unique<MockHttpStreamRequestDelegate>());
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, request_delegates[i].get(), session_.get(), &job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ auto request = job_controller->Start(
+ request_delegates[i].get(), nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller->main_job());
+ EXPECT_FALSE(job_controller->alternative_job());
+ requests.push_back(std::move(request));
+ }
+ // Cancel the first one.
+ requests[0].reset();
+
+ for (int i = 1; i < kNumRequests; ++i) {
+ EXPECT_CALL(*request_delegates[i].get(), OnStreamReadyImpl(_, _, _));
+ }
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+ requests.clear();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+
+ EXPECT_TRUE(first_socket.AllReadDataConsumed());
+ for (const auto& data : socket_data) {
+ EXPECT_TRUE(data.AllReadDataConsumed());
+ EXPECT_TRUE(data.AllWriteDataConsumed());
+ }
+}
+
+TEST_F(JobControllerLimitMultipleH2Requests, MultiplePreconnects) {
+ // Make sure there is only one socket connect.
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ ssl_data.next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.example.com");
+ SetPreconnect();
+ Initialize(request_info);
+
+ // Sets server support HTTP/2.
+ url::SchemeHostPort server(request_info.url);
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ std::vector<std::unique_ptr<MockHttpStreamRequestDelegate>> request_delegates;
+ for (int i = 0; i < kNumRequests; ++i) {
+ request_delegates.emplace_back(
+ std::make_unique<MockHttpStreamRequestDelegate>());
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, request_delegates[i].get(), session_.get(), &job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ job_controller->Preconnect(1);
+ EXPECT_TRUE(job_controller->main_job());
+ EXPECT_FALSE(job_controller->alternative_job());
+ }
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+TEST_F(JobControllerLimitMultipleH2Requests, H1NegotiatedForFirstRequest) {
+ // First socket is an HTTP/1.1 socket.
+ SequencedSocketData first_socket;
+ first_socket.set_connect_data(MockConnect(ASYNC, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSocketDataProvider(&first_socket);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+ // Second socket is an HTTP/2 socket.
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+ SequencedSocketData second_socket(reads, base::span<MockWrite>());
+ second_socket.set_connect_data(MockConnect(ASYNC, OK));
+ session_deps_.socket_factory->AddSocketDataProvider(&second_socket);
+ SSLSocketDataProvider second_ssl_data(ASYNC, OK);
+ second_ssl_data.next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&second_ssl_data);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.example.com");
+ Initialize(request_info);
+ SpdySessionPoolPeer pool_peer(session_->spdy_session_pool());
+ pool_peer.SetEnableSendingInitialData(false);
+
+ // Sets server support HTTP/2.
+ url::SchemeHostPort server(request_info.url);
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ std::vector<std::unique_ptr<MockHttpStreamRequestDelegate>> request_delegates;
+ std::vector<std::unique_ptr<HttpStreamRequest>> requests;
+ for (int i = 0; i < 2; ++i) {
+ request_delegates.emplace_back(
+ std::make_unique<MockHttpStreamRequestDelegate>());
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, request_delegates[i].get(), session_.get(), &job_factory_,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ auto request = job_controller->Start(
+ request_delegates[i].get(), nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller->main_job());
+ EXPECT_FALSE(job_controller->alternative_job());
+ requests.push_back(std::move(request));
+ }
+
+ for (int i = 0; i < 2; ++i) {
+ EXPECT_CALL(*request_delegates[i].get(), OnStreamReadyImpl(_, _, _));
+ }
+ base::RunLoop().RunUntilIdle();
+
+ EXPECT_FALSE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+ requests.clear();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+
+ EXPECT_TRUE(first_socket.AllReadDataConsumed());
+ EXPECT_FALSE(second_socket.AllReadDataConsumed());
+}
+
+// Tests that HTTP/2 throttling logic only applies to non-QUIC jobs.
+TEST_F(JobControllerLimitMultipleH2Requests, QuicJobNotThrottled) {
+ crypto_client_stream_factory_.set_handshake_mode(
+ MockCryptoClientStream::COLD_START);
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddRead(SYNCHRONOUS, ERR_IO_PENDING);
+ MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING)};
+ tcp_data_ =
+ std::make_unique<SequencedSocketData>(reads, base::span<MockWrite>());
+
+ tcp_data_->set_connect_data(MockConnect(ASYNC, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ ssl_data.next_proto = kProtoHTTP2;
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ SpdySessionPoolPeer pool_peer(session_->spdy_session_pool());
+ pool_peer.SetEnableSendingInitialData(false);
+
+ url::SchemeHostPort server(request_info.url);
+ // Sets server supports QUIC.
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ // Sets server support HTTP/2.
+ session_->http_server_properties()->SetSupportsSpdy(server, true);
+
+ // Use default job factory so that Resume() is not mocked out.
+ HttpStreamFactory::JobFactory default_job_factory;
+ HttpStreamFactory::JobController* job_controller =
+ new HttpStreamFactory::JobController(
+ factory_, &request_delegate_, session_.get(), &default_job_factory,
+ request_info, is_preconnect_, false /* is_websocket */,
+ enable_ip_based_pooling_, enable_alternative_services_, SSLConfig(),
+ SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller);
+ request_ =
+ job_controller->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+
+ EXPECT_TRUE(job_controller->main_job());
+ EXPECT_TRUE(job_controller->alternative_job());
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+ base::RunLoop().RunUntilIdle();
+ TestNetLogEntry::List entries;
+ net_log_.GetEntries(&entries);
+ for (auto entry : entries) {
+ ASSERT_NE(NetLogEventType::HTTP_STREAM_JOB_THROTTLED, entry.type);
+ }
+}
+
+class HttpStreamFactoryJobControllerMisdirectedRequestRetry
+ : public HttpStreamFactoryJobControllerTest,
+ public ::testing::WithParamInterface<::testing::tuple<bool, bool>> {};
+
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ HttpStreamFactoryJobControllerMisdirectedRequestRetry,
+ ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
+TEST_P(HttpStreamFactoryJobControllerMisdirectedRequestRetry,
+ DisableIPBasedPoolingAndAlternativeServices) {
+ const bool enable_ip_based_pooling = ::testing::get<0>(GetParam());
+ const bool enable_alternative_services = ::testing::get<1>(GetParam());
+ if (enable_alternative_services) {
+ quic_data_ = std::make_unique<MockQuicData>();
+ quic_data_->AddConnect(SYNCHRONOUS, OK);
+ quic_data_->AddWrite(SYNCHRONOUS,
+ client_maker_.MakeInitialSettingsPacket(1, nullptr));
+ quic_data_->AddRead(ASYNC, OK);
+ }
+ tcp_data_ = std::make_unique<SequencedSocketData>();
+ tcp_data_->set_connect_data(MockConnect(SYNCHRONOUS, OK));
+ SSLSocketDataProvider ssl_data(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(&ssl_data);
+
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ if (!enable_ip_based_pooling)
+ DisableIPBasedPooling();
+ if (!enable_alternative_services)
+ DisableAlternativeServices();
+
+ Initialize(request_info);
+
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ SetAlternativeService(request_info, alternative_service);
+
+ request_ =
+ job_controller_->Start(&request_delegate_, nullptr, net_log_.bound(),
+ HttpStreamRequest::HTTP_STREAM, DEFAULT_PRIORITY);
+ EXPECT_TRUE(job_controller_->main_job());
+ if (enable_alternative_services) {
+ EXPECT_TRUE(job_controller_->alternative_job());
+ } else {
+ EXPECT_FALSE(job_controller_->alternative_job());
+ }
+
+ // |main_job| succeeds and should report status to Request.
+ EXPECT_CALL(request_delegate_, OnStreamReadyImpl(_, _, _));
+ base::RunLoop().RunUntilIdle();
+}
+
+class HttpStreamFactoryJobControllerPreconnectTest
+ : public HttpStreamFactoryJobControllerTest,
+ public ::testing::WithParamInterface<bool> {
+ protected:
+ void SetUp() override {
+ if (!GetParam()) {
+ scoped_feature_list_.InitFromCommandLine(std::string(),
+ "LimitEarlyPreconnects");
+ }
+ }
+
+ void Initialize() {
+ session_deps_.http_server_properties =
+ std::make_unique<MockHttpServerProperties>();
+ session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_);
+ factory_ = session_->http_stream_factory();
+ request_info_.method = "GET";
+ request_info_.url = GURL("https://www.example.com");
+ job_controller_ = new HttpStreamFactory::JobController(
+ factory_, &request_delegate_, session_.get(), &job_factory_,
+ request_info_, /* is_preconnect = */ true,
+ /* is_websocket = */ false,
+ /* enable_ip_based_pooling = */ true,
+ /* enable_alternative_services = */ true, SSLConfig(), SSLConfig());
+ HttpStreamFactoryPeer::AddJobController(factory_, job_controller_);
+ }
+
+ protected:
+ void Preconnect(int num_streams) {
+ job_controller_->Preconnect(num_streams);
+ // Only one job is started.
+ EXPECT_TRUE(job_controller_->main_job());
+ EXPECT_FALSE(job_controller_->alternative_job());
+ }
+
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_;
+ HttpRequestInfo request_info_;
+};
+
+INSTANTIATE_TEST_CASE_P(
+ /* no prefix */,
+ HttpStreamFactoryJobControllerPreconnectTest,
+ ::testing::Bool());
+
+TEST_P(HttpStreamFactoryJobControllerPreconnectTest, LimitEarlyPreconnects) {
+ std::list<SequencedSocketData> providers;
+ std::list<SSLSocketDataProvider> ssl_providers;
+ const int kNumPreconects = 5;
+ MockRead reads[] = {MockRead(ASYNC, OK)};
+ // If experiment is not enabled, there are 5 socket connects.
+ const size_t actual_num_connects = GetParam() ? 1 : kNumPreconects;
+ for (size_t i = 0; i < actual_num_connects; ++i) {
+ providers.emplace_back(reads, base::span<MockWrite>());
+ session_deps_.socket_factory->AddSocketDataProvider(&providers.back());
+ ssl_providers.emplace_back(ASYNC, OK);
+ session_deps_.socket_factory->AddSSLSocketDataProvider(
+ &ssl_providers.back());
+ }
+ Initialize();
+ Preconnect(kNumPreconects);
+ // If experiment is enabled, only 1 stream is requested.
+ EXPECT_EQ((int)actual_num_connects, HttpStreamFactoryJobPeer::GetNumStreams(
+ job_controller_->main_job()));
+ base::RunLoop().RunUntilIdle();
+ EXPECT_TRUE(HttpStreamFactoryPeer::IsJobControllerDeleted(factory_));
+}
+
+// Test that GetAlternativeServiceInfoFor will include a list of advertised
+// versions, which contains a version that is supported. Returns an empty list
+// if advertised versions are missing in HttpServerProperties.
+TEST_F(HttpStreamFactoryJobControllerTest, GetAlternativeServiceInfoFor) {
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+ url::SchemeHostPort server(request_info.url);
+ AlternativeService alternative_service(kProtoQUIC, server.host(), 443);
+ base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+
+ // Set alternative service with no advertised version.
+ session_->http_server_properties()->SetQuicAlternativeService(
+ server, alternative_service, expiration, QuicTransportVersionVector());
+
+ AlternativeServiceInfo alt_svc_info =
+ JobControllerPeer::GetAlternativeServiceInfoFor(
+ job_controller_, request_info, &request_delegate_,
+ HttpStreamRequest::HTTP_STREAM);
+ // Verify that JobController get an empty list of supported QUIC versions.
+ EXPECT_TRUE(alt_svc_info.advertised_versions().empty());
+
+ // Set alternative service for the same server with the same list of versions
+ // that is supported.
+ QuicTransportVersionVector supported_versions =
+ session_->params().quic_supported_versions;
+ ASSERT_TRUE(session_->http_server_properties()->SetQuicAlternativeService(
+ server, alternative_service, expiration, supported_versions));
+
+ alt_svc_info = JobControllerPeer::GetAlternativeServiceInfoFor(
+ job_controller_, request_info, &request_delegate_,
+ HttpStreamRequest::HTTP_STREAM);
+ std::sort(supported_versions.begin(), supported_versions.end());
+ EXPECT_EQ(supported_versions, alt_svc_info.advertised_versions());
+
+ QuicTransportVersion unsupported_version_1(QUIC_VERSION_UNSUPPORTED);
+ QuicTransportVersion unsupported_version_2(QUIC_VERSION_UNSUPPORTED);
+ for (const QuicTransportVersion& version : AllSupportedTransportVersions()) {
+ if (std::find(supported_versions.begin(), supported_versions.end(),
+ version) != supported_versions.end())
+ continue;
+ if (unsupported_version_1 == QUIC_VERSION_UNSUPPORTED) {
+ unsupported_version_1 = version;
+ continue;
+ }
+ unsupported_version_2 = version;
+ break;
+ }
+
+ // Set alternative service for the same server with two QUIC versions:
+ // - one unsupported version: |unsupported_version_1|,
+ // - one supported version: session_->params().quic_supported_versions[0].
+ QuicTransportVersionVector mixed_quic_versions = {
+ unsupported_version_1, session_->params().quic_supported_versions[0]};
+ ASSERT_TRUE(session_->http_server_properties()->SetQuicAlternativeService(
+ server, alternative_service, expiration, mixed_quic_versions));
+
+ alt_svc_info = JobControllerPeer::GetAlternativeServiceInfoFor(
+ job_controller_, request_info, &request_delegate_,
+ HttpStreamRequest::HTTP_STREAM);
+ EXPECT_EQ(2u, alt_svc_info.advertised_versions().size());
+ // Verify that JobController returns the list of versions specified in set.
+ std::sort(mixed_quic_versions.begin(), mixed_quic_versions.end());
+ EXPECT_EQ(mixed_quic_versions, alt_svc_info.advertised_versions());
+
+ // Set alternative service for the same server with two unsupported QUIC
+ // versions: |unsupported_version_1|, |unsupported_version_2|.
+ ASSERT_TRUE(session_->http_server_properties()->SetQuicAlternativeService(
+ server, alternative_service, expiration,
+ {unsupported_version_1, unsupported_version_2}));
+
+ alt_svc_info = JobControllerPeer::GetAlternativeServiceInfoFor(
+ job_controller_, request_info, &request_delegate_,
+ HttpStreamRequest::HTTP_STREAM);
+ // Verify that JobController returns no valid alternative service.
+ EXPECT_EQ(kProtoUnknown, alt_svc_info.alternative_service().protocol);
+ EXPECT_EQ(0u, alt_svc_info.advertised_versions().size());
+}
+
+// Tests that if HttpNetworkSession has a non-empty QUIC host whitelist,
+// then GetAlternativeServiceFor() will not return any QUIC alternative service
+// that's not on the whitelist.
+TEST_F(HttpStreamFactoryJobControllerTest, QuicHostWhitelist) {
+ HttpRequestInfo request_info;
+ request_info.method = "GET";
+ request_info.url = GURL("https://www.google.com");
+
+ Initialize(request_info);
+
+ // Set HttpNetworkSession's QUIC host whitelist to only have www.example.com
+ HttpNetworkSessionPeer session_peer(session_.get());
+ session_peer.params()->quic_host_whitelist.insert("www.example.com");
+ session_peer.params()->quic_allow_remote_alt_svc = true;
+
+ // Set alternative service for www.google.com to be www.example.com over QUIC.
+ url::SchemeHostPort server(request_info.url);
+ base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1);
+ QuicTransportVersionVector supported_versions =
+ session_->params().quic_supported_versions;
+ session_->http_server_properties()->SetQuicAlternativeService(
+ server, AlternativeService(kProtoQUIC, "www.example.com", 443),
+ expiration, supported_versions);
+
+ AlternativeServiceInfo alt_svc_info =
+ JobControllerPeer::GetAlternativeServiceInfoFor(
+ job_controller_, request_info, &request_delegate_,
+ HttpStreamRequest::HTTP_STREAM);
+
+ std::sort(supported_versions.begin(), supported_versions.end());
+ EXPECT_EQ(kProtoQUIC, alt_svc_info.alternative_service().protocol);
+ EXPECT_EQ(supported_versions, alt_svc_info.advertised_versions());
+
+ session_->http_server_properties()->SetQuicAlternativeService(
+ server, AlternativeService(kProtoQUIC, "www.example.org", 443),
+ expiration, supported_versions);
+
+ alt_svc_info = JobControllerPeer::GetAlternativeServiceInfoFor(
+ job_controller_, request_info, &request_delegate_,
+ HttpStreamRequest::HTTP_STREAM);
+
+ EXPECT_EQ(kProtoUnknown, alt_svc_info.alternative_service().protocol);
+ EXPECT_EQ(0u, alt_svc_info.advertised_versions().size());
+}
+
+} // namespace test
+
+} // namespace net