| Index: net/http/http_stream_factory_impl.cc
|
| diff --git a/net/http/http_stream_factory_impl.cc b/net/http/http_stream_factory_impl.cc
|
| index ced9ce3d1dbac1f0bbd7d077a61d2d2facb25e7f..744b9807785c3bd325be7ae85c8e5a2a9c009ae6 100644
|
| --- a/net/http/http_stream_factory_impl.cc
|
| +++ b/net/http/http_stream_factory_impl.cc
|
| @@ -7,13 +7,12 @@
|
| #include <string>
|
|
|
| #include "base/logging.h"
|
| -#include "base/memory/ptr_util.h"
|
| +#include "base/metrics/histogram_macros.h"
|
| #include "base/stl_util.h"
|
| #include "base/strings/string_util.h"
|
| #include "net/http/http_network_session.h"
|
| #include "net/http/http_server_properties.h"
|
| #include "net/http/http_stream_factory_impl_job.h"
|
| -#include "net/http/http_stream_factory_impl_job_controller.h"
|
| #include "net/http/http_stream_factory_impl_request.h"
|
| #include "net/http/transport_security_state.h"
|
| #include "net/log/net_log.h"
|
| @@ -24,59 +23,24 @@
|
|
|
| namespace net {
|
|
|
| -namespace {
|
| -// Default JobFactory for creating HttpStreamFactoryImpl::Jobs.
|
| -class DefaultJobFactory : public HttpStreamFactoryImpl::JobFactory {
|
| - public:
|
| - DefaultJobFactory() {}
|
| -
|
| - ~DefaultJobFactory() override {}
|
| -
|
| - HttpStreamFactoryImpl::Job* CreateJob(
|
| - HttpStreamFactoryImpl::Job::Delegate* delegate,
|
| - HttpStreamFactoryImpl::JobType job_type,
|
| - HttpNetworkSession* session,
|
| - const HttpRequestInfo& request_info,
|
| - RequestPriority priority,
|
| - const SSLConfig& server_ssl_config,
|
| - const SSLConfig& proxy_ssl_config,
|
| - HostPortPair destination,
|
| - GURL origin_url,
|
| - NetLog* net_log) override {
|
| - return new HttpStreamFactoryImpl::Job(
|
| - delegate, job_type, session, request_info, priority, server_ssl_config,
|
| - proxy_ssl_config, destination, origin_url, net_log);
|
| - }
|
| -
|
| - HttpStreamFactoryImpl::Job* CreateJob(
|
| - HttpStreamFactoryImpl::Job::Delegate* delegate,
|
| - HttpStreamFactoryImpl::JobType job_type,
|
| - HttpNetworkSession* session,
|
| - const HttpRequestInfo& request_info,
|
| - RequestPriority priority,
|
| - const SSLConfig& server_ssl_config,
|
| - const SSLConfig& proxy_ssl_config,
|
| - HostPortPair destination,
|
| - GURL origin_url,
|
| - AlternativeService alternative_service,
|
| - NetLog* net_log) override {
|
| - return new HttpStreamFactoryImpl::Job(
|
| - delegate, job_type, session, request_info, priority, server_ssl_config,
|
| - proxy_ssl_config, destination, origin_url, alternative_service,
|
| - net_log);
|
| - }
|
| -};
|
| -} // anonymous namespace
|
| -
|
| HttpStreamFactoryImpl::HttpStreamFactoryImpl(HttpNetworkSession* session,
|
| bool for_websockets)
|
| : session_(session),
|
| - job_factory_(new DefaultJobFactory()),
|
| for_websockets_(for_websockets) {}
|
|
|
| HttpStreamFactoryImpl::~HttpStreamFactoryImpl() {
|
| DCHECK(request_map_.empty());
|
| DCHECK(spdy_session_request_map_.empty());
|
| +
|
| + std::set<const Job*> tmp_job_set;
|
| + tmp_job_set.swap(orphaned_job_set_);
|
| + STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
|
| + DCHECK(orphaned_job_set_.empty());
|
| +
|
| + tmp_job_set.clear();
|
| + tmp_job_set.swap(preconnect_job_set_);
|
| + STLDeleteContainerPointers(tmp_job_set.begin(), tmp_job_set.end());
|
| + DCHECK(preconnect_job_set_.empty());
|
| }
|
|
|
| HttpStreamRequest* HttpStreamFactoryImpl::RequestStream(
|
| @@ -132,14 +96,48 @@
|
| websocket_handshake_stream_create_helper,
|
| HttpStreamRequest::StreamType stream_type,
|
| const BoundNetLog& net_log) {
|
| - JobController* job_controller =
|
| - new JobController(this, delegate, session_, job_factory_.get());
|
| - job_controller_set_.insert(base::WrapUnique(job_controller));
|
| -
|
| - Request* request = job_controller->Start(
|
| - request_info, delegate, websocket_handshake_stream_create_helper, net_log,
|
| - stream_type, priority, server_ssl_config, proxy_ssl_config);
|
| -
|
| + Request* request = new Request(request_info.url, this, delegate,
|
| + websocket_handshake_stream_create_helper,
|
| + net_log, stream_type);
|
| + HostPortPair destination(HostPortPair::FromURL(request_info.url));
|
| + GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
|
| +
|
| + Job* job =
|
| + new Job(this, session_, request_info, priority, server_ssl_config,
|
| + proxy_ssl_config, destination, origin_url, net_log.net_log());
|
| + request->AttachJob(job);
|
| +
|
| + const AlternativeService alternative_service =
|
| + GetAlternativeServiceFor(request_info, delegate, stream_type);
|
| +
|
| + if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
|
| + // Never share connection with other jobs for FTP requests.
|
| + DVLOG(1) << "Selected alternative service (host: "
|
| + << alternative_service.host_port_pair().host()
|
| + << " port: " << alternative_service.host_port_pair().port() << ")";
|
| +
|
| + DCHECK(!request_info.url.SchemeIs("ftp"));
|
| + HostPortPair alternative_destination(alternative_service.host_port_pair());
|
| + ignore_result(
|
| + ApplyHostMappingRules(request_info.url, &alternative_destination));
|
| +
|
| + Job* alternative_job =
|
| + new Job(this, session_, request_info, priority, server_ssl_config,
|
| + proxy_ssl_config, alternative_destination, origin_url,
|
| + alternative_service, net_log.net_log());
|
| + request->AttachJob(alternative_job);
|
| +
|
| + job->WaitFor(alternative_job);
|
| + // Make sure to wait until we call WaitFor(), before starting
|
| + // |alternative_job|, otherwise |alternative_job| will not notify |job|
|
| + // appropriately.
|
| + alternative_job->Start(request);
|
| + }
|
| +
|
| + // Even if |alternative_job| has already finished, it will not have notified
|
| + // the request yet, since we defer that to the next iteration of the
|
| + // MessageLoop, so starting |job| is always safe.
|
| + job->Start(request);
|
| return request;
|
| }
|
|
|
| @@ -154,16 +152,188 @@
|
| proxy_ssl_config.verify_ev_cert = true;
|
|
|
| DCHECK(!for_websockets_);
|
| -
|
| - JobController* job_controller =
|
| - new JobController(this, nullptr, session_, job_factory_.get());
|
| - job_controller_set_.insert(base::WrapUnique(job_controller));
|
| - job_controller->Preconnect(num_streams, request_info, server_ssl_config,
|
| - proxy_ssl_config);
|
| + AlternativeService alternative_service = GetAlternativeServiceFor(
|
| + request_info, nullptr, HttpStreamRequest::HTTP_STREAM);
|
| + HostPortPair destination(HostPortPair::FromURL(request_info.url));
|
| + GURL origin_url = ApplyHostMappingRules(request_info.url, &destination);
|
| + if (alternative_service.protocol != UNINITIALIZED_ALTERNATE_PROTOCOL) {
|
| + if (session_->params().quic_disable_preconnect_if_0rtt &&
|
| + alternative_service.protocol == QUIC &&
|
| + session_->quic_stream_factory()->ZeroRTTEnabledFor(QuicServerId(
|
| + alternative_service.host_port_pair(), request_info.privacy_mode))) {
|
| + return;
|
| + }
|
| + destination = alternative_service.host_port_pair();
|
| + ignore_result(ApplyHostMappingRules(request_info.url, &destination));
|
| + }
|
| + // Due to how the socket pools handle priorities and idle sockets, only IDLE
|
| + // priority currently makes sense for preconnects. The priority for
|
| + // preconnects is currently ignored (see RequestSocketsForPool()), but could
|
| + // be used at some point for proxy resolution or something.
|
| + Job* job = new Job(this, session_, request_info, IDLE, server_ssl_config,
|
| + proxy_ssl_config, destination, origin_url,
|
| + alternative_service, session_->net_log());
|
| + preconnect_job_set_.insert(job);
|
| + job->Preconnect(num_streams);
|
| }
|
|
|
| const HostMappingRules* HttpStreamFactoryImpl::GetHostMappingRules() const {
|
| return session_->params().host_mapping_rules;
|
| +}
|
| +
|
| +AlternativeService HttpStreamFactoryImpl::GetAlternativeServiceFor(
|
| + const HttpRequestInfo& request_info,
|
| + HttpStreamRequest::Delegate* delegate,
|
| + HttpStreamRequest::StreamType stream_type) {
|
| + AlternativeService alternative_service =
|
| + GetAlternativeServiceForInternal(request_info, delegate, stream_type);
|
| + AlternativeServiceType type;
|
| + if (alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL) {
|
| + type = NO_ALTERNATIVE_SERVICE;
|
| + } else if (alternative_service.protocol == QUIC) {
|
| + if (request_info.url.host() == alternative_service.host) {
|
| + type = QUIC_SAME_DESTINATION;
|
| + } else {
|
| + type = QUIC_DIFFERENT_DESTINATION;
|
| + }
|
| + } else {
|
| + if (request_info.url.host() == alternative_service.host) {
|
| + type = NOT_QUIC_SAME_DESTINATION;
|
| + } else {
|
| + type = NOT_QUIC_DIFFERENT_DESTINATION;
|
| + }
|
| + }
|
| + UMA_HISTOGRAM_ENUMERATION("Net.AlternativeServiceTypeForRequest", type,
|
| + MAX_ALTERNATIVE_SERVICE_TYPE);
|
| + return alternative_service;
|
| +}
|
| +
|
| +AlternativeService HttpStreamFactoryImpl::GetAlternativeServiceForInternal(
|
| + const HttpRequestInfo& request_info,
|
| + HttpStreamRequest::Delegate* delegate,
|
| + HttpStreamRequest::StreamType stream_type) {
|
| + GURL original_url = request_info.url;
|
| +
|
| + if (original_url.SchemeIs("ftp"))
|
| + return AlternativeService();
|
| +
|
| + if (!session_->params().enable_alternative_service_for_insecure_origins &&
|
| + !original_url.SchemeIs("https"))
|
| + return AlternativeService();
|
| +
|
| + url::SchemeHostPort origin(original_url);
|
| + HttpServerProperties& http_server_properties =
|
| + *session_->http_server_properties();
|
| + const AlternativeServiceVector alternative_service_vector =
|
| + http_server_properties.GetAlternativeServices(origin);
|
| + if (alternative_service_vector.empty())
|
| + return AlternativeService();
|
| +
|
| + bool quic_advertised = false;
|
| + bool quic_all_broken = true;
|
| +
|
| + const bool enable_different_host =
|
| + session_->params().enable_alternative_service_with_different_host;
|
| +
|
| + // First Alt-Svc that is not marked as broken.
|
| + AlternativeService first_alternative_service;
|
| +
|
| + for (const AlternativeService& alternative_service :
|
| + alternative_service_vector) {
|
| + DCHECK(IsAlternateProtocolValid(alternative_service.protocol));
|
| + if (!quic_advertised && alternative_service.protocol == QUIC)
|
| + quic_advertised = true;
|
| + if (http_server_properties.IsAlternativeServiceBroken(
|
| + alternative_service)) {
|
| + HistogramAlternateProtocolUsage(ALTERNATE_PROTOCOL_USAGE_BROKEN);
|
| + continue;
|
| + }
|
| +
|
| + if (origin.host() != alternative_service.host && !enable_different_host)
|
| + continue;
|
| +
|
| + // Some shared unix systems may have user home directories (like
|
| + // http://foo.com/~mike) which allow users to emit headers. This is a bad
|
| + // idea already, but with Alternate-Protocol, it provides the ability for a
|
| + // single user on a multi-user system to hijack the alternate protocol.
|
| + // These systems also enforce ports <1024 as restricted ports. So don't
|
| + // allow protocol upgrades to user-controllable ports.
|
| + const int kUnrestrictedPort = 1024;
|
| + if (!session_->params().enable_user_alternate_protocol_ports &&
|
| + (alternative_service.port >= kUnrestrictedPort &&
|
| + origin.port() < kUnrestrictedPort))
|
| + continue;
|
| +
|
| + if (alternative_service.protocol >= NPN_SPDY_MINIMUM_VERSION &&
|
| + alternative_service.protocol <= NPN_SPDY_MAXIMUM_VERSION) {
|
| + if (!HttpStreamFactory::spdy_enabled())
|
| + continue;
|
| +
|
| + // TODO(bnc): Re-enable when https://crbug.com/615413 is fixed.
|
| + if (origin.host() != alternative_service.host)
|
| + continue;
|
| +
|
| + // Cache this entry if we don't have a non-broken Alt-Svc yet.
|
| + if (first_alternative_service.protocol ==
|
| + UNINITIALIZED_ALTERNATE_PROTOCOL)
|
| + first_alternative_service = alternative_service;
|
| + continue;
|
| + }
|
| +
|
| + DCHECK_EQ(QUIC, alternative_service.protocol);
|
| + quic_all_broken = false;
|
| + if (!session_->params().enable_quic)
|
| + continue;
|
| +
|
| + if (!IsQuicWhitelistedForHost(origin.host()))
|
| + continue;
|
| +
|
| + if (stream_type == HttpStreamRequest::BIDIRECTIONAL_STREAM &&
|
| + session_->params().quic_disable_bidirectional_streams) {
|
| + continue;
|
| + }
|
| +
|
| + if (session_->quic_stream_factory()->IsQuicDisabled(
|
| + alternative_service.port))
|
| + continue;
|
| +
|
| + if (!original_url.SchemeIs("https"))
|
| + continue;
|
| +
|
| + // Check whether there is an existing QUIC session to use for this origin.
|
| + HostPortPair mapped_origin(origin.host(), origin.port());
|
| + ignore_result(ApplyHostMappingRules(original_url, &mapped_origin));
|
| + QuicServerId server_id(mapped_origin, request_info.privacy_mode);
|
| +
|
| + HostPortPair destination(alternative_service.host_port_pair());
|
| + ignore_result(ApplyHostMappingRules(original_url, &destination));
|
| +
|
| + if (session_->quic_stream_factory()->CanUseExistingSession(server_id,
|
| + destination)) {
|
| + return alternative_service;
|
| + }
|
| +
|
| + // Cache this entry if we don't have a non-broken Alt-Svc yet.
|
| + if (first_alternative_service.protocol == UNINITIALIZED_ALTERNATE_PROTOCOL)
|
| + first_alternative_service = alternative_service;
|
| + }
|
| +
|
| + // Ask delegate to mark QUIC as broken for the origin.
|
| + if (quic_advertised && quic_all_broken && delegate != nullptr)
|
| + delegate->OnQuicBroken();
|
| +
|
| + return first_alternative_service;
|
| +}
|
| +
|
| +void HttpStreamFactoryImpl::OrphanJob(Job* job, const Request* request) {
|
| + DCHECK(ContainsKey(request_map_, job));
|
| + DCHECK_EQ(request_map_[job], request);
|
| + DCHECK(!ContainsKey(orphaned_job_set_, job));
|
| +
|
| + request_map_.erase(job);
|
| +
|
| + orphaned_job_set_.insert(job);
|
| + job->Orphan(request);
|
| }
|
|
|
| void HttpStreamFactoryImpl::OnNewSpdySessionReady(
|
| @@ -197,27 +367,47 @@
|
| } else if (request->stream_type() ==
|
| HttpStreamRequest::BIDIRECTIONAL_STREAM) {
|
| request->OnBidirectionalStreamImplReady(
|
| - used_ssl_config, used_proxy_info,
|
| + nullptr, used_ssl_config, used_proxy_info,
|
| new BidirectionalStreamSpdyImpl(spdy_session));
|
| } else {
|
| bool use_relative_url = direct || request->url().SchemeIs("https");
|
| request->OnStreamReady(
|
| - used_ssl_config, used_proxy_info,
|
| + nullptr, used_ssl_config, used_proxy_info,
|
| new SpdyHttpStream(spdy_session, use_relative_url));
|
| }
|
| }
|
| // TODO(mbelshe): Alert other valid requests.
|
| }
|
|
|
| -void HttpStreamFactoryImpl::OnJobControllerComplete(JobController* controller) {
|
| - for (auto it = job_controller_set_.begin(); it != job_controller_set_.end();
|
| - ++it) {
|
| - if (it->get() == controller) {
|
| - job_controller_set_.erase(it);
|
| - return;
|
| - }
|
| - }
|
| - NOTREACHED();
|
| +void HttpStreamFactoryImpl::OnOrphanedJobComplete(const Job* job) {
|
| + orphaned_job_set_.erase(job);
|
| + delete job;
|
| +}
|
| +
|
| +void HttpStreamFactoryImpl::OnPreconnectsComplete(const Job* job) {
|
| + preconnect_job_set_.erase(job);
|
| + delete job;
|
| + OnPreconnectsCompleteInternal();
|
| +}
|
| +
|
| +bool HttpStreamFactoryImpl::IsQuicWhitelistedForHost(const std::string& host) {
|
| + bool whitelist_needed = false;
|
| + for (QuicVersion version : session_->params().quic_supported_versions) {
|
| + if (version <= QUIC_VERSION_30) {
|
| + whitelist_needed = true;
|
| + break;
|
| + }
|
| + }
|
| +
|
| + // The QUIC whitelist is not needed in QUIC versions after 30.
|
| + if (!whitelist_needed)
|
| + return true;
|
| +
|
| + if (session_->params().transport_security_state->IsGooglePinnedHost(host))
|
| + return true;
|
| +
|
| + return ContainsKey(session_->params().quic_host_whitelist,
|
| + base::ToLowerASCII(host));
|
| }
|
|
|
| } // namespace net
|
|
|