Chromium Code Reviews| Index: net/http/http_stream_factory_impl_job_controller_unittest.cc |
| diff --git a/net/http/http_stream_factory_impl_job_controller_unittest.cc b/net/http/http_stream_factory_impl_job_controller_unittest.cc |
| index bd94d7ddc3fa89cdb49f8f5847f58e79e3994577..999fdb9b32771822c05245447c854d4dec1c8b96 100644 |
| --- a/net/http/http_stream_factory_impl_job_controller_unittest.cc |
| +++ b/net/http/http_stream_factory_impl_job_controller_unittest.cc |
| @@ -6,13 +6,19 @@ |
| #include <memory> |
| +#include "base/run_loop.cc" |
| +#include "net/dns/mock_host_resolver.h" |
| #include "net/http/http_basic_stream.h" |
| #include "net/http/http_stream_factory_impl_request.h" |
| #include "net/http/http_stream_factory_test_util.h" |
| +#include "net/proxy/mock_proxy_resolver.h" |
| +#include "net/proxy/proxy_config_service_fixed.h" |
| #include "net/proxy/proxy_info.h" |
| #include "net/proxy/proxy_service.h" |
| +#include "net/quic/test_tools/quic_stream_factory_peer.h" |
| #include "net/spdy/spdy_test_util_common.h" |
| #include "net/ssl/ssl_failure_state.h" |
| +#include "testing/gmock_mutant.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| using ::testing::_; |
| @@ -28,8 +34,116 @@ void DeleteHttpStreamPointer(const SSLConfig& used_ssl_config, |
| delete stream; |
| } |
| +class HangingProxyResolver : public ProxyResolver { |
| + public: |
| + HangingProxyResolver() {} |
| + ~HangingProxyResolver() override {} |
| + |
| + int GetProxyForURL(const GURL& url, |
| + ProxyInfo* results, |
| + const CompletionCallback& callback, |
| + RequestHandle* request, |
| + const BoundNetLog& net_log) override { |
| + return ERR_IO_PENDING; |
| + } |
| + |
| + void CancelRequest(RequestHandle request) override { NOTREACHED(); } |
| + |
| + LoadState GetLoadState(RequestHandle request) const override { |
| + NOTREACHED(); |
| + return LOAD_STATE_IDLE; |
| + } |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(HangingProxyResolver); |
| +}; |
| + |
| +class HangingProxyResolverFactory : public ProxyResolverFactory { |
| + public: |
| + explicit HangingProxyResolverFactory(HangingProxyResolver* resolver) |
| + : ProxyResolverFactory(false), resolver_(resolver) {} |
| + |
| + // ProxyResolverFactory override. |
| + int CreateProxyResolver( |
| + const scoped_refptr<ProxyResolverScriptData>& pac_script, |
| + std::unique_ptr<ProxyResolver>* resolver, |
| + const net::CompletionCallback& callback, |
| + std::unique_ptr<Request>* request) override { |
| + resolver->reset(new ForwardingProxyResolver(resolver_)); |
| + return OK; |
| + } |
| + |
| + private: |
| + HangingProxyResolver* resolver_; |
| +}; |
| + |
| +class FailingProxyResolverFactory : public ProxyResolverFactory { |
| + public: |
| + FailingProxyResolverFactory() : ProxyResolverFactory(false) {} |
| + |
| + // ProxyResolverFactory override. |
| + int CreateProxyResolver( |
| + const scoped_refptr<ProxyResolverScriptData>& 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 {} |
| + |
| + int Resolve(const RequestInfo& info, |
| + RequestPriority priority, |
| + AddressList* addresses, |
| + const CompletionCallback& callback, |
| + RequestHandle* out_req, |
| + const BoundNetLog& net_log) override { |
| + return ERR_NAME_NOT_RESOLVED; |
| + } |
| +}; |
| + |
| +class HangingResolver : public MockHostResolverBase { |
| + public: |
| + HangingResolver() : MockHostResolverBase(false /*use_caching*/) {} |
| + ~HangingResolver() override {} |
| + |
| + int Resolve(const RequestInfo& info, |
| + RequestPriority priority, |
| + AddressList* addresses, |
| + const CompletionCallback& callback, |
| + RequestHandle* out_req, |
| + const BoundNetLog& net_log) override { |
| + return ERR_IO_PENDING; |
| + } |
| + |
| + void CancelRequest(RequestHandle req) override {} |
| +}; |
| } // anonymous namespace |
| +class HttpStreamFactoryImplJobPeer { |
| + public: |
| + static void Start(HttpStreamFactoryImpl::Job* job, |
| + HttpStreamRequest::StreamType stream_type) { |
| + // Start() is mocked for MockHttpStreamFactoryImplJob. |
| + // This is the alternative method to invoke real Start() method on Job. |
| + job->stream_type_ = stream_type; |
| + job->StartInternal(); |
| + } |
| +}; |
| + |
| +class JobControllerPeer { |
| + public: |
| + static void VerifyWaitingTimeForMainJob( |
| + HttpStreamFactoryImpl::JobController* job_controller, |
| + const base::TimeDelta& delay) { |
| + EXPECT_EQ(delay, job_controller->main_job_wait_time_); |
| + } |
| +}; |
| + |
| class HttpStreamFactoryImplJobControllerTest |
| : public ::testing::Test, |
| public ::testing::WithParamInterface<NextProto> { |
| @@ -37,6 +151,9 @@ class HttpStreamFactoryImplJobControllerTest |
| HttpStreamFactoryImplJobControllerTest() |
| : session_deps_(GetParam(), ProxyService::CreateDirect()) { |
| session_deps_.enable_quic = true; |
| + } |
| + |
| + void Initialize() { |
| session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); |
| factory_ = |
| static_cast<HttpStreamFactoryImpl*>(session_->http_stream_factory()); |
| @@ -73,6 +190,8 @@ INSTANTIATE_TEST_CASE_P(NextProto, |
| TEST_P(HttpStreamFactoryImplJobControllerTest, |
| OnStreamFailedWithNoAlternativeJob) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("http://www.google.com"); |
| @@ -95,6 +214,8 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, |
| TEST_P(HttpStreamFactoryImplJobControllerTest, |
| OnStreamReadyWithNoAlternativeJob) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("http://www.google.com"); |
| @@ -120,6 +241,8 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, |
| // Test we cancel Jobs correctly when the Request is explicitly canceled |
| // before any Job is bound to Request. |
| TEST_P(HttpStreamFactoryImplJobControllerTest, CancelJobsBeforeBinding) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("https://www.google.com"); |
| @@ -143,6 +266,8 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, CancelJobsBeforeBinding) { |
| } |
| TEST_P(HttpStreamFactoryImplJobControllerTest, OnStreamFailedForBothJobs) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("https://www.google.com"); |
| @@ -178,6 +303,8 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, OnStreamFailedForBothJobs) { |
| TEST_P(HttpStreamFactoryImplJobControllerTest, |
| SecondJobFailsAfterFirstJobSucceeds) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("https://www.google.com"); |
| @@ -220,6 +347,8 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, |
| TEST_P(HttpStreamFactoryImplJobControllerTest, |
| SecondJobSucceedsAfterFirstJobFailed) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("https://www.google.com"); |
| @@ -258,6 +387,8 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, |
| // Regression test for crbug/621069. |
| // Get load state after main job fails and before alternative job succeeds. |
| TEST_P(HttpStreamFactoryImplJobControllerTest, GetLoadStateAfterMainJobFailed) { |
| + Initialize(); |
| + |
| HttpRequestInfo request_info; |
| request_info.method = "GET"; |
| request_info.url = GURL("https://www.google.com"); |
| @@ -296,4 +427,243 @@ TEST_P(HttpStreamFactoryImplJobControllerTest, GetLoadStateAfterMainJobFailed) { |
| ProxyInfo()); |
| } |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, DoNotResumeMainJobBeforeWait) { |
| + // Use failing ProxyResolverFactory which is unable to create ProxyResolver |
| + // to stall the alternative job and report to controller to maybe resume the |
| + // main job. |
| + ProxyConfig proxy_config; |
| + proxy_config.set_auto_detect(true); |
| + proxy_config.set_pac_mandatory(true); |
| + session_deps_.proxy_service.reset(new ProxyService( |
| + base::WrapUnique(new ProxyConfigServiceFixed(proxy_config)), |
| + base::WrapUnique(new FailingProxyResolverFactory), nullptr)); |
| + |
| + Initialize(); |
| + |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("https://www.google.com"); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(QUIC, server.host(), 443); |
| + SetAlternativeService(request_info, alternative_service); |
| + job_factory_.DisableMockStartForJobs(); |
| + |
| + request_.reset( |
| + job_controller_->Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), HttpStreamRequest::HTTP_STREAM, |
| + DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); |
| + EXPECT_TRUE(job_controller_->main_job()); |
| + EXPECT_TRUE(job_controller_->alternative_job()); |
| + |
| + // Wait until OnStreamFailedCallback is executed on the alternative job. |
| + EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, SSL_FAILURE_NONE)) |
| + .Times(1); |
| + EXPECT_CALL(*job_factory_.main_job(), MarkOtherJobComplete(_)).Times(1); |
| + base::RunLoop().RunUntilIdle(); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, InvalidPortForQuic) { |
| + // 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(); |
| + |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("https://www.google.com"); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(QUIC, server.host(), 101); |
| + SetAlternativeService(request_info, alternative_service); |
| + job_factory_.DisableMockStartForJobs(); |
| + |
| + request_.reset( |
| + job_controller_->Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), HttpStreamRequest::HTTP_STREAM, |
| + DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); |
| + |
| + 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); |
| + EXPECT_CALL(*job_factory_.main_job(), MarkOtherJobComplete(_)).Times(1); |
| + base::RunLoop().RunUntilIdle(); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, |
| + NoAvailableSpdySessionToResumeMainJob) { |
| + // Test the alternative job is not resumed when the alternative job is |
| + // IO_PENDING for proxy resolution. Once all the proxy resolution succeeds, |
| + // the latter part of this test tests controller resumes the main job |
| + // when there's no SPDY session for the alternative job. |
| + ProxyConfig proxy_config; |
| + proxy_config.set_auto_detect(true); |
| + // Use asynchronous proxy resolver. |
| + MockAsyncProxyResolverFactory* proxy_resolver_factory = |
| + new MockAsyncProxyResolverFactory(false); |
| + session_deps_.proxy_service.reset(new ProxyService( |
| + base::WrapUnique(new ProxyConfigServiceFixed(proxy_config)), |
| + base::WrapUnique(proxy_resolver_factory), nullptr)); |
| + |
| + HangingResolver* host_resolver = new HangingResolver(); |
| + session_deps_.host_resolver.reset(host_resolver); |
| + session_deps_.host_resolver->set_synchronous_mode(false); |
| + |
| + Initialize(); |
| + |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("http://www.google.com"); |
| + |
| + // Set a SPDY alternative service for the server. |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(NPN_HTTP_2, server.host(), 443); |
| + SetAlternativeService(request_info, alternative_service); |
| + job_factory_.DisableMockStartForJobs(); |
|
Ryan Hamilton
2016/07/19 17:48:15
Discussed offline, let's move to using the 'normal
|
| + |
| + request_.reset( |
| + job_controller_->Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), HttpStreamRequest::HTTP_STREAM, |
| + DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); |
| + // Both jobs should be created but stalled as proxy resolution not completed. |
| + EXPECT_TRUE(job_controller_->main_job()); |
| + EXPECT_TRUE(job_controller_->alternative_job()); |
| + |
| + MockAsyncProxyResolver resolver; |
| + proxy_resolver_factory->pending_requests()[0]->CompleteNowWithForwarder( |
| + net::OK, &resolver); |
| + |
| + // Resolve proxy for the main job which then proceed to wait for the |
| + // alternative job which is IO_PENDING. |
| + int main_job_request_id = |
| + resolver.pending_requests()[0]->url().SchemeIs("http") ? 0 : 1; |
| + |
| + resolver.pending_requests()[main_job_request_id]->results()->UseNamedProxy( |
| + "result1:80"); |
| + resolver.pending_requests()[main_job_request_id]->CompleteNow(net::OK); |
| + EXPECT_TRUE(job_controller_->main_job()->is_waiting()); |
| + |
| + // Resolve proxy for the alternative job to proceed to create a connection. |
| + // Use hanging HostResolver to fail creation of a SPDY session for the |
| + // alternative job. The alternative job will be IO_PENDING thus should resume |
| + // the main job. |
| + resolver.pending_requests()[0]->CompleteNow(net::OK); |
| + EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0); |
| + EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1); |
| + EXPECT_CALL(*job_factory_.main_job(), MarkOtherJobComplete(_)).Times(1); |
| + |
| + base::RunLoop().RunUntilIdle(); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, |
| + NoAvailableQuicSessionToResumeMainJob) { |
| + // Use failing HostResolver which is unable to resolve the host name for QUIC. |
| + // No QUIC session is created and thus should resume the main job. |
| + FailingHostResolver* host_resolver = new FailingHostResolver(); |
| + session_deps_.host_resolver.reset(host_resolver); |
| + |
| + ProxyConfig proxy_config; |
| + proxy_config.set_auto_detect(true); |
| + // Use asynchronous proxy resolver. |
| + MockAsyncProxyResolverFactory* proxy_resolver_factory = |
| + new MockAsyncProxyResolverFactory(false); |
| + session_deps_.proxy_service.reset(new ProxyService( |
| + base::WrapUnique(new ProxyConfigServiceFixed(proxy_config)), |
| + base::WrapUnique(proxy_resolver_factory), nullptr)); |
| + |
| + Initialize(); |
| + |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("https://www.google.com"); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(QUIC, server.host(), 443); |
| + SetAlternativeService(request_info, alternative_service); |
| + job_factory_.DisableMockStartForJobs(); |
| + // Hack to use different URL for the main job to help differentiate the proxy |
| + // requests. |
| + job_factory_.UseDifferentURLForMainJob(GURL("http://www.google.com")); |
| + |
|
Zhongyi Shi
2016/07/15 18:42:49
The original url has to use HTTPS so as to racing
eroman
2016/07/15 19:30:33
Does this test specifically need to test using PAC
|
| + request_.reset( |
| + job_controller_->Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), HttpStreamRequest::HTTP_STREAM, |
| + DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); |
| + EXPECT_TRUE(job_controller_->main_job()); |
| + EXPECT_TRUE(job_controller_->alternative_job()); |
| + |
| + MockAsyncProxyResolver resolver; |
| + proxy_resolver_factory->pending_requests()[0]->CompleteNowWithForwarder( |
| + net::OK, &resolver); |
| + |
| + // Resolve proxy for the main job which then proceed to wait for the |
| + // alternative job which is IO_PENDING. |
| + int main_job_request_id = |
| + resolver.pending_requests()[0]->url().SchemeIs("http") ? 0 : 1; |
| + |
| + resolver.pending_requests()[main_job_request_id]->results()->UseNamedProxy( |
| + "result1:80"); |
| + resolver.pending_requests()[main_job_request_id]->CompleteNow(net::OK); |
| + EXPECT_TRUE(job_controller_->main_job()->is_waiting()); |
| + |
| + // Resolve proxy for the alternative job to proceed to create a connection. |
| + // Use failing HostResolver to fail creation of a QUIC session for the |
| + // alternative job. The alternative job will thus resume the main job. |
| + resolver.pending_requests()[0]->results()->UseNamedProxy("result1:80"); |
| + resolver.pending_requests()[0]->CompleteNow(net::OK); |
| + |
| + // Wait until OnStreamFailedCallback is executed on the alternative job. |
| + // Request shouldn't be notified as the main job is still pending status. |
| + EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0); |
| + EXPECT_CALL(*job_factory_.main_job(), Resume()).Times(1); |
| + EXPECT_CALL(*job_factory_.main_job(), MarkOtherJobComplete(_)).Times(1); |
| + |
| + base::RunLoop().RunUntilIdle(); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, DelayedTCP) { |
| + session_deps_.proxy_service.reset( |
| + ProxyService::CreateFixedFromPacResult("QUIC mail.example.org:70") |
| + .release()); |
| + HangingResolver* resolver = new HangingResolver(); |
| + session_deps_.host_resolver.reset(resolver); |
| + |
| + Initialize(); |
| + |
| + // Enable delayed TCP and set time delay for waiting job. |
| + QuicStreamFactory* quic_stream_factory = session_->quic_stream_factory(); |
| + test::QuicStreamFactoryPeer::SetDelayTcpRace(quic_stream_factory, true); |
| + quic_stream_factory->set_require_confirmation(false); |
| + ServerNetworkStats stats1; |
| + stats1.srtt = base::TimeDelta::FromMicroseconds(10); |
| + session_->http_server_properties()->SetServerNetworkStats( |
| + url::SchemeHostPort(GURL("https://mail.example.org:70/")), stats1); |
| + |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("http://www.google.com"); |
| + |
| + // Set a SPDY alternative service for the server. |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(NPN_HTTP_2, server.host(), 443); |
| + SetAlternativeService(request_info, alternative_service); |
| + job_factory_.DisableMockStartForJobs(); |
| + |
| + request_.reset( |
| + job_controller_->Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), HttpStreamRequest::HTTP_STREAM, |
| + DEFAULT_PRIORITY, SSLConfig(), SSLConfig())); |
| + EXPECT_TRUE(job_controller_->main_job()); |
| + EXPECT_TRUE(job_controller_->alternative_job()); |
| + |
| + // The alternative job stalls as host resolution hangs when creating the QUIC |
| + // request and controller should resume the main job after delay. |
| + // Verify the waiting time for delayed main job. |
| + EXPECT_CALL(*job_factory_.main_job(), Resume()) |
| + .WillOnce(Invoke(testing::CreateFunctor( |
| + &JobControllerPeer::VerifyWaitingTimeForMainJob, job_controller_, |
| + base::TimeDelta::FromMicroseconds(15)))); |
| + |
| + base::RunLoop().RunUntilIdle(); |
| +} |
| } // namespace net |