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 |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c2a94c7f128bd47209626499fc972701aa7d1224 |
| --- /dev/null |
| +++ b/net/http/http_stream_factory_impl_job_controller_unittest.cc |
| @@ -0,0 +1,345 @@ |
| +// 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_impl_job_controller.h" |
| + |
| +#include <memory> |
| + |
| +#include "net/http/http_basic_stream.h" |
| +#include "net/http/http_stream_factory_impl_job.h" |
| +#include "net/http/http_stream_factory_impl_request.h" |
| +#include "net/http/http_stream_factory_test_util.h" |
| +#include "net/proxy/proxy_info.h" |
| +#include "net/proxy/proxy_service.h" |
| +#include "net/spdy/spdy_test_util_common.h" |
| +#include "net/ssl/ssl_failure_state.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| + |
| +using ::testing::_; |
| + |
| +namespace net { |
| + |
| +class TestJobControllerPeer { |
| + public: |
| + TestJobControllerPeer(NextProto protocol, |
| + TestHttpStreamRequestDelegate* request_delegate) |
| + : session_deps_(protocol, ProxyService::CreateDirect()) { |
| + session_deps_.enable_quic = true; |
| + session_ = SpdySessionDependencies::SpdyCreateSession(&session_deps_); |
| + factory_ = |
| + static_cast<HttpStreamFactoryImpl*>(session_->http_stream_factory()); |
| + job_controller_ = new HttpStreamFactoryImpl::JobController( |
| + factory_, request_delegate, session_.get(), &job_factory_); |
| + factory_->job_controller_set_.insert(base::WrapUnique(job_controller_)); |
| + } |
| + |
| + ~TestJobControllerPeer() {} |
| + |
| + TestHttpStreamFactoryImplJob* AddMainJobToFactory( |
| + const HttpRequestInfo& request_info) { |
| + HostPortPair host_port_pair = HostPortPair::FromURL(request_info.url); |
| + GURL original_url = job_controller_->ApplyHostMappingRules(request_info.url, |
| + &host_port_pair); |
| + TestHttpStreamFactoryImplJob* main_job = new TestHttpStreamFactoryImplJob( |
| + job_controller_, HttpStreamFactoryImpl::MAIN, session_.get(), |
| + request_info, DEFAULT_PRIORITY, SSLConfig(), SSLConfig(), |
| + host_port_pair, original_url, nullptr); |
| + job_factory_.SetMainJob(main_job); |
| + return main_job; |
| + } |
| + |
| + TestHttpStreamFactoryImplJob* AddAlternativeJobToFactory( |
| + const HttpRequestInfo& request_info, |
| + AlternativeService alternative_service) { |
| + HostPortPair host_port_pair = HostPortPair::FromURL(request_info.url); |
| + GURL original_url = job_controller_->ApplyHostMappingRules(request_info.url, |
| + &host_port_pair); |
| + TestHttpStreamFactoryImplJob* alternative_job = |
| + new TestHttpStreamFactoryImplJob( |
| + job_controller_, HttpStreamFactoryImpl::ALTERNATIVE, session_.get(), |
| + request_info, DEFAULT_PRIORITY, SSLConfig(), SSLConfig(), |
| + host_port_pair, original_url, alternative_service, nullptr); |
| + job_factory_.SetAlternativeJob(alternative_job); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + base::Time expiration = base::Time::Now() + base::TimeDelta::FromDays(1); |
| + session_->http_server_properties()->SetAlternativeService( |
| + server, alternative_service, expiration); |
| + |
| + return alternative_job; |
| + } |
| + |
| + void Start(const HttpRequestInfo& request_info, |
| + HttpStreamRequest::Delegate* delegate, |
| + WebSocketHandshakeStreamBase::CreateHelper* |
| + websocket_handshake_stream_create_helper, |
| + const BoundNetLog& net_log, |
| + RequestPriority priority, |
| + const SSLConfig& server_ssl_config, |
| + const SSLConfig& proxy_ssl_config) { |
| + request_.reset(job_controller_->Start( |
| + request_info, delegate, websocket_handshake_stream_create_helper, |
| + net_log, HttpStreamFactoryImpl::Request::HTTP_STREAM, priority, |
| + server_ssl_config, proxy_ssl_config)); |
| + } |
| + |
| + void CancelRequest() { request_.reset(); } |
| + |
| + void OnStreamFailed(HttpStreamFactoryImpl::Job* job, |
| + int status, |
| + const SSLConfig& used_ssl_config, |
| + SSLFailureState ssl_failure_state) { |
| + job_controller_->OnStreamFailed(job, status, used_ssl_config, |
| + ssl_failure_state); |
| + } |
| + |
| + void OnStreamReady(HttpStreamFactoryImpl::Job* job, |
| + const SSLConfig& used_ssl_config, |
| + const ProxyInfo& used_proxy_info) { |
| + job_controller_->OnStreamReady(job, used_ssl_config, used_proxy_info); |
| + } |
| + |
| + bool IsJobControllerDeleted() { |
| + return factory_->job_controller_set_.empty(); |
| + } |
| + |
| + HttpStreamFactoryImpl::Job* alternative_job() { |
| + return job_controller_->alternative_job_.get(); |
| + } |
| + |
| + HttpStreamFactoryImpl::Job* main_job() { |
| + return job_controller_->main_job_.get(); |
| + } |
| + |
| + private: |
| + SpdySessionDependencies session_deps_; |
| + std::unique_ptr<HttpNetworkSession> session_; |
| + HttpStreamFactoryImpl* factory_; |
| + TestJobFactory job_factory_; |
| + HttpStreamFactoryImpl::JobController* job_controller_; |
| + std::unique_ptr<HttpStreamFactoryImpl::Request> request_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(TestJobControllerPeer); |
|
Ryan Hamilton
2016/06/03 00:11:24
As discussed offline, I think you can eliminate th
Zhongyi Shi
2016/06/03 23:09:21
Done.
|
| +}; |
| + |
| +class HttpStreamFactoryImplJobControllerTest |
| + : public ::testing::Test, |
| + public ::testing::WithParamInterface<NextProto> { |
| + public: |
| + HttpStreamFactoryImplJobControllerTest() |
| + : job_controller_peer_(GetParam(), &request_delegate_) {} |
| + |
| + ~HttpStreamFactoryImplJobControllerTest() {} |
| + |
| + TestJobControllerPeer job_controller_peer_; |
| + TestHttpStreamRequestDelegate request_delegate_; |
| +}; |
| + |
| +INSTANTIATE_TEST_CASE_P(NextProto, |
| + HttpStreamFactoryImplJobControllerTest, |
| + testing::Values(kProtoSPDY31, kProtoHTTP2)); |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, |
| + OnStreamFailedWithNoAlternativeJob) { |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("http://www.google.com"); |
| + TestHttpStreamFactoryImplJob* main_job = |
| + job_controller_peer_.AddMainJobToFactory(request_info); |
| + |
| + EXPECT_CALL(*main_job, Start(_)).Times(1); |
| + job_controller_peer_.Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), DEFAULT_PRIORITY, SSLConfig(), |
| + SSLConfig()); |
| + EXPECT_TRUE(job_controller_peer_.main_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, _, SSL_FAILURE_NONE)) |
| + .Times(1); |
| + job_controller_peer_.OnStreamFailed(main_job, ERR_FAILED, SSLConfig(), |
| + SSL_FAILURE_NONE); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, |
| + OnStreamReadyWithNoAlternativeJob) { |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("http://www.google.com"); |
| + TestHttpStreamFactoryImplJob* main_job = |
| + job_controller_peer_.AddMainJobToFactory(request_info); |
| + |
| + EXPECT_CALL(*main_job, Start(_)).Times(1); |
| + job_controller_peer_.Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), DEFAULT_PRIORITY, SSLConfig(), |
| + SSLConfig()); |
| + EXPECT_TRUE(job_controller_peer_.main_job()); |
| + |
| + // There's no other alternative job. Thus when stream is ready, it should |
| + // notify Request. |
| + HttpStream* http_stream = |
| + new HttpBasicStream(new ClientSocketHandle(), false); |
| + main_job->SetStream(http_stream); |
| + |
| + EXPECT_CALL(request_delegate_, OnStreamReady(_, _, _)).Times(1); |
| + job_controller_peer_.OnStreamReady(main_job, SSLConfig(), ProxyInfo()); |
| +} |
| + |
| +// Test we cancel Jobs correctly when the Request is explicitly canceled |
| +// before any Job is bound to Request. |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, CancelJobsBeforeBinding) { |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("https://www.google.com"); |
| + TestHttpStreamFactoryImplJob* main_job = |
| + job_controller_peer_.AddMainJobToFactory(request_info); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(QUIC, server.host(), 443); |
| + TestHttpStreamFactoryImplJob* alternative_job = |
| + job_controller_peer_.AddAlternativeJobToFactory(request_info, |
| + alternative_service); |
| + |
| + EXPECT_CALL(*main_job, Start(_)).Times(1); |
| + EXPECT_CALL(*alternative_job, Start(_)).Times(1); |
| + job_controller_peer_.Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), DEFAULT_PRIORITY, SSLConfig(), |
| + SSLConfig()); |
| + EXPECT_TRUE(job_controller_peer_.main_job()); |
| + EXPECT_TRUE(job_controller_peer_.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. |
| + job_controller_peer_.CancelRequest(); |
| + EXPECT_TRUE(job_controller_peer_.IsJobControllerDeleted()); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, OnStreamFailedForBothJobs) { |
| + 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); |
| + |
| + TestHttpStreamFactoryImplJob* main_job = |
| + job_controller_peer_.AddMainJobToFactory(request_info); |
| + |
| + TestHttpStreamFactoryImplJob* alternative_job = |
| + job_controller_peer_.AddAlternativeJobToFactory(request_info, |
| + alternative_service); |
| + |
| + EXPECT_CALL(*main_job, Start(_)).Times(1); |
| + EXPECT_CALL(*alternative_job, Start(_)).Times(1); |
| + |
| + job_controller_peer_.Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), DEFAULT_PRIORITY, SSLConfig(), |
| + SSLConfig()); |
| + EXPECT_TRUE(job_controller_peer_.main_job()); |
| + EXPECT_TRUE(job_controller_peer_.alternative_job()); |
| + |
| + // We have |main_job| with unknown status when |alternative_job| is failed |
| + // thus should not notify Request of |alternative_job|'s failure. But should |
| + // notify |main_job| to mark |alternative_job| failed. |
| + EXPECT_CALL(request_delegate_, OnStreamFailed(_, _, _)).Times(0); |
| + EXPECT_CALL(*main_job, MarkOtherJobComplete(_)).Times(1); |
| + job_controller_peer_.OnStreamFailed(alternative_job, ERR_FAILED, SSLConfig(), |
| + SSL_FAILURE_NONE); |
| + EXPECT_TRUE(!job_controller_peer_.alternative_job()); |
| + EXPECT_TRUE(job_controller_peer_.main_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); |
| + job_controller_peer_.OnStreamFailed(main_job, ERR_FAILED, SSLConfig(), |
| + SSL_FAILURE_NONE); |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, |
| + SecondJobFailsAfterFirstJobSucceeds) { |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("https://www.google.com"); |
| + TestHttpStreamFactoryImplJob* main_job = |
| + job_controller_peer_.AddMainJobToFactory(request_info); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(QUIC, server.host(), 443); |
| + TestHttpStreamFactoryImplJob* alternative_job = |
| + job_controller_peer_.AddAlternativeJobToFactory(request_info, |
| + alternative_service); |
| + |
| + EXPECT_CALL(*main_job, Start(_)).Times(1); |
| + EXPECT_CALL(*alternative_job, Start(_)).Times(1); |
| + job_controller_peer_.Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), DEFAULT_PRIORITY, SSLConfig(), |
| + SSLConfig()); |
| + EXPECT_TRUE(job_controller_peer_.main_job()); |
| + EXPECT_TRUE(job_controller_peer_.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. |
| + HttpStream* http_stream = |
| + new HttpBasicStream(new ClientSocketHandle(), false); |
| + main_job->SetStream(http_stream); |
| + |
| + EXPECT_CALL(request_delegate_, OnStreamReady(_, _, _)).Times(1); |
| + EXPECT_CALL(*alternative_job, MarkOtherJobComplete(_)).Times(1); |
| + job_controller_peer_.OnStreamReady(main_job, SSLConfig(), ProxyInfo()); |
| + |
| + // Reset the request as it's been successfully served. |
| + job_controller_peer_.CancelRequest(); |
| + |
| + // JobController shouldn't report the status of second job as request |
| + // is gone served. |
| + job_controller_peer_.OnStreamFailed(alternative_job, ERR_FAILED, SSLConfig(), |
| + SSL_FAILURE_NONE); |
| + EXPECT_TRUE(job_controller_peer_.IsJobControllerDeleted()); |
| + delete http_stream; |
| +} |
| + |
| +TEST_P(HttpStreamFactoryImplJobControllerTest, |
| + SecondJobSucceedsAfterFirstJobFailed) { |
| + HttpRequestInfo request_info; |
| + request_info.method = "GET"; |
| + request_info.url = GURL("https://www.google.com"); |
| + TestHttpStreamFactoryImplJob* main_job = |
| + job_controller_peer_.AddMainJobToFactory(request_info); |
| + |
| + url::SchemeHostPort server(request_info.url); |
| + AlternativeService alternative_service(QUIC, server.host(), 443); |
| + TestHttpStreamFactoryImplJob* alternative_job = |
| + job_controller_peer_.AddAlternativeJobToFactory(request_info, |
| + alternative_service); |
| + |
| + EXPECT_CALL(*main_job, Start(_)).Times(1); |
| + EXPECT_CALL(*alternative_job, Start(_)).Times(1); |
| + job_controller_peer_.Start(request_info, &request_delegate_, nullptr, |
| + BoundNetLog(), DEFAULT_PRIORITY, SSLConfig(), |
| + SSLConfig()); |
| + EXPECT_TRUE(job_controller_peer_.main_job()); |
| + EXPECT_TRUE(job_controller_peer_.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); |
| + EXPECT_CALL(*alternative_job, MarkOtherJobComplete(_)).Times(1); |
| + |
| + job_controller_peer_.OnStreamFailed(main_job, ERR_FAILED, SSLConfig(), |
| + SSL_FAILURE_NONE); |
| + |
| + // |alternative_job| succeeds and should report status to Request. |
| + HttpStream* http_stream = |
| + new HttpBasicStream(new ClientSocketHandle(), false); |
| + alternative_job->SetStream(http_stream); |
| + |
| + EXPECT_CALL(request_delegate_, OnStreamReady(_, _, _)).Times(1); |
| + job_controller_peer_.OnStreamReady(alternative_job, SSLConfig(), ProxyInfo()); |
| + |
| + delete http_stream; |
| +} |
| + |
| +} // namespace net |