Index: net/quic/chromium/quic_stream_factory.cc |
diff --git a/net/quic/chromium/quic_stream_factory.cc b/net/quic/chromium/quic_stream_factory.cc |
index 426f1ca8c3581bd5bffe3dd0c2d9dcb106726c07..51befea2739094f8916a2c5d7ee3b0007b773787 100644 |
--- a/net/quic/chromium/quic_stream_factory.cc |
+++ b/net/quic/chromium/quic_stream_factory.cc |
@@ -331,7 +331,15 @@ |
const QuicSessionKey& key, |
bool was_alternative_service_recently_broken, |
int cert_verify_flags, |
+ std::unique_ptr<QuicServerInfo> server_info, |
const NetLogWithSource& net_log); |
+ |
+ // Creates a new job to handle the resumption of for connecting an |
+ // existing session. |
+ Job(QuicStreamFactory* factory, |
+ HostResolver* host_resolver, |
+ QuicChromiumClientSession* session, |
+ const QuicSessionKey& key); |
~Job(); |
@@ -340,12 +348,18 @@ |
int DoLoop(int rv); |
int DoResolveHost(); |
int DoResolveHostComplete(int rv); |
+ int DoLoadServerInfo(); |
+ int DoLoadServerInfoComplete(int rv); |
int DoConnect(); |
int DoConnectComplete(int rv); |
void OnIOComplete(int rv); |
+ void RunAuxilaryJob(); |
+ |
void Cancel(); |
+ |
+ void CancelWaitForDataReadyCallback(); |
const QuicSessionKey& key() const { return key_; } |
@@ -361,6 +375,8 @@ |
STATE_NONE, |
STATE_RESOLVE_HOST, |
STATE_RESOLVE_HOST_COMPLETE, |
+ STATE_LOAD_SERVER_INFO, |
+ STATE_LOAD_SERVER_INFO_COMPLETE, |
STATE_CONNECT, |
STATE_CONNECT_COMPLETE, |
}; |
@@ -372,6 +388,8 @@ |
const QuicSessionKey key_; |
const int cert_verify_flags_; |
const bool was_alternative_service_recently_broken_; |
+ std::unique_ptr<QuicServerInfo> server_info_; |
+ bool started_another_job_; |
const NetLogWithSource net_log_; |
int num_sent_client_hellos_; |
QuicChromiumClientSession* session_; |
@@ -388,6 +406,7 @@ |
const QuicSessionKey& key, |
bool was_alternative_service_recently_broken, |
int cert_verify_flags, |
+ std::unique_ptr<QuicServerInfo> server_info, |
const NetLogWithSource& net_log) |
: io_state_(STATE_RESOLVE_HOST), |
factory_(factory), |
@@ -396,6 +415,8 @@ |
cert_verify_flags_(cert_verify_flags), |
was_alternative_service_recently_broken_( |
was_alternative_service_recently_broken), |
+ server_info_(std::move(server_info)), |
+ started_another_job_(false), |
net_log_( |
NetLogWithSource::Make(net_log.net_log(), |
NetLogSourceType::QUIC_STREAM_FACTORY_JOB)), |
@@ -417,6 +438,10 @@ |
QuicStreamFactory::Job::~Job() { |
net_log_.EndEvent(NetLogEventType::QUIC_STREAM_FACTORY_JOB); |
DCHECK(callback_.is_null()); |
+ |
+ // If disk cache has a pending WaitForDataReadyCallback, cancel that callback. |
+ if (server_info_) |
+ server_info_->ResetWaitForDataReadyCallback(); |
} |
int QuicStreamFactory::Job::Run(const CompletionCallback& callback) { |
@@ -440,6 +465,13 @@ |
case STATE_RESOLVE_HOST_COMPLETE: |
rv = DoResolveHostComplete(rv); |
break; |
+ case STATE_LOAD_SERVER_INFO: |
+ CHECK_EQ(OK, rv); |
+ rv = DoLoadServerInfo(); |
+ break; |
+ case STATE_LOAD_SERVER_INFO_COMPLETE: |
+ rv = DoLoadServerInfoComplete(rv); |
+ break; |
case STATE_CONNECT: |
CHECK_EQ(OK, rv); |
rv = DoConnect(); |
@@ -461,6 +493,13 @@ |
base::ResetAndReturn(&callback_).Run(rv); |
} |
+void QuicStreamFactory::Job::RunAuxilaryJob() { |
+ int rv = Run(base::Bind(&QuicStreamFactory::OnJobComplete, |
+ base::Unretained(factory_), this)); |
+ if (rv != ERR_IO_PENDING) |
+ factory_->OnJobComplete(this, rv); |
+} |
+ |
void QuicStreamFactory::Job::Cancel() { |
callback_.Reset(); |
if (session_) |
@@ -469,12 +508,24 @@ |
ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); |
} |
+void QuicStreamFactory::Job::CancelWaitForDataReadyCallback() { |
+ // If we are waiting for WaitForDataReadyCallback, then cancel the callback. |
+ if (io_state_ != STATE_LOAD_SERVER_INFO_COMPLETE) |
+ return; |
+ server_info_->CancelWaitForDataReadyCallback(); |
+ OnIOComplete(OK); |
+} |
+ |
size_t QuicStreamFactory::Job::EstimateMemoryUsage() const { |
- return base::trace_event::EstimateMemoryUsage(key_); |
+ return base::trace_event::EstimateMemoryUsage(key_) + |
+ base::trace_event::EstimateMemoryUsage(server_info_); |
} |
int QuicStreamFactory::Job::DoResolveHost() { |
dns_resolution_start_time_ = base::TimeTicks::Now(); |
+ // Start loading the data now, and wait for it after we resolve the host. |
+ if (server_info_) |
+ server_info_->Start(); |
io_state_ = STATE_RESOLVE_HOST_COMPLETE; |
return host_resolver_->Resolve( |
@@ -496,6 +547,71 @@ |
if (factory_->OnResolution(key_, address_list_)) |
return OK; |
+ if (server_info_) |
+ io_state_ = STATE_LOAD_SERVER_INFO; |
+ else |
+ io_state_ = STATE_CONNECT; |
+ return OK; |
+} |
+ |
+int QuicStreamFactory::Job::DoLoadServerInfo() { |
+ net_log_.BeginEvent( |
+ NetLogEventType::QUIC_STREAM_FACTORY_JOB_LOAD_SERVER_INFO); |
+ |
+ io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE; |
+ |
+ DCHECK(server_info_); |
+ |
+ // To mitigate the effects of disk cache taking too long to load QUIC server |
+ // information, set up a timer to cancel WaitForDataReady's callback. |
+ if (factory_->load_server_info_timeout_srtt_multiplier_ > 0) { |
+ const int kMaxLoadServerInfoTimeoutMs = 50; |
+ // Wait for DiskCache a maximum of 50ms. |
+ int64_t load_server_info_timeout_ms = |
+ std::min(static_cast<int>( |
+ (factory_->load_server_info_timeout_srtt_multiplier_ * |
+ factory_->GetServerNetworkStatsSmoothedRttInMicroseconds( |
+ key_.server_id())) / |
+ 1000), |
+ kMaxLoadServerInfoTimeoutMs); |
+ if (load_server_info_timeout_ms > 0) { |
+ factory_->task_runner_->PostDelayedTask( |
+ FROM_HERE, |
+ base::Bind(&QuicStreamFactory::Job::CancelWaitForDataReadyCallback, |
+ GetWeakPtr()), |
+ base::TimeDelta::FromMilliseconds(load_server_info_timeout_ms)); |
+ } |
+ } |
+ |
+ int rv = server_info_->WaitForDataReady( |
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr())); |
+ if (rv == ERR_IO_PENDING && factory_->enable_connection_racing()) { |
+ // If we are waiting to load server config from the disk cache, then start |
+ // another job. |
+ started_another_job_ = true; |
+ factory_->CreateAuxilaryJob(key_, cert_verify_flags_, net_log_); |
+ } |
+ return rv; |
+} |
+ |
+int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) { |
+ net_log_.EndEvent(NetLogEventType::QUIC_STREAM_FACTORY_JOB_LOAD_SERVER_INFO); |
+ UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheWaitForDataReadyTime", |
+ base::TimeTicks::Now() - dns_resolution_end_time_); |
+ |
+ if (rv != OK) |
+ server_info_.reset(); |
+ |
+ if (started_another_job_ && |
+ (!server_info_ || server_info_->state().server_config.empty() || |
+ !factory_->CryptoConfigCacheIsEmpty(key_.server_id()))) { |
+ // If we have started another job and if we didn't load the server config |
+ // from the disk cache or if we have received a new server config from the |
+ // server, then cancel the current job. |
+ io_state_ = STATE_NONE; |
+ return ERR_CONNECTION_CLOSED; |
+ } |
+ |
io_state_ = STATE_CONNECT; |
return OK; |
} |
@@ -509,10 +625,10 @@ |
NetLogEventType::QUIC_STREAM_FACTORY_JOB_CONNECT, |
NetLog::BoolCallback("require_confirmation", require_confirmation)); |
- int rv = |
- factory_->CreateSession(key_, cert_verify_flags_, require_confirmation, |
- address_list_, dns_resolution_start_time_, |
- dns_resolution_end_time_, net_log_, &session_); |
+ int rv = factory_->CreateSession( |
+ key_, cert_verify_flags_, std::move(server_info_), require_confirmation, |
+ address_list_, dns_resolution_start_time_, dns_resolution_end_time_, |
+ net_log_, &session_); |
if (rv != OK) { |
DCHECK(rv != ERR_IO_PENDING); |
DCHECK(!session_); |
@@ -655,8 +771,11 @@ |
size_t max_packet_length, |
const std::string& user_agent_id, |
const QuicVersionVector& supported_versions, |
+ float load_server_info_timeout_srtt_multiplier, |
+ bool enable_connection_racing, |
bool enable_non_blocking_io, |
- bool store_server_configs_in_properties, |
+ bool disable_disk_cache, |
+ int max_server_configs_stored_in_properties, |
bool close_sessions_on_ip_change, |
bool mark_quic_broken_when_network_blackholes, |
int idle_connection_timeout_seconds, |
@@ -694,10 +813,13 @@ |
transport_security_state, |
cert_transparency_verifier))), |
supported_versions_(supported_versions), |
+ load_server_info_timeout_srtt_multiplier_( |
+ load_server_info_timeout_srtt_multiplier), |
+ enable_connection_racing_(enable_connection_racing), |
enable_non_blocking_io_(enable_non_blocking_io), |
+ disable_disk_cache_(disable_disk_cache), |
mark_quic_broken_when_network_blackholes_( |
mark_quic_broken_when_network_blackholes), |
- store_server_configs_in_properties_(store_server_configs_in_properties), |
ping_timeout_(QuicTime::Delta::FromSeconds(kPingTimeoutSecs)), |
reduced_ping_timeout_( |
QuicTime::Delta::FromSeconds(reduced_ping_timeout_seconds)), |
@@ -716,6 +838,7 @@ |
do_not_fragment_(do_not_fragment), |
estimate_initial_rtt(estimate_initial_rtt), |
check_persisted_supports_quic_(true), |
+ has_initialized_data_(false), |
num_push_streams_created_(0), |
task_runner_(nullptr), |
ssl_config_service_(ssl_config_service), |
@@ -743,6 +866,13 @@ |
has_aes_hardware_support); |
if (has_aes_hardware_support) |
crypto_config_.PreferAesGcm(); |
+ // When disk cache is used to store the server configs, HttpCache code calls |
+ // |set_quic_server_info_factory| if |quic_server_info_factory_| wasn't |
+ // created. |
+ if (max_server_configs_stored_in_properties > 0) { |
+ quic_server_info_factory_.reset( |
+ new PropertiesBasedQuicServerInfoFactory(http_server_properties_)); |
+ } |
// migrate_sessions_early should only be set to true if |
// migrate_sessions_on_network_change is set to true. |
@@ -798,6 +928,11 @@ |
if (!srtt) |
srtt = kDefaultRTT; |
return base::TimeDelta::FromMicroseconds(srtt); |
+} |
+ |
+void QuicStreamFactory::set_quic_server_info_factory( |
+ QuicServerInfoFactory* quic_server_info_factory) { |
+ quic_server_info_factory_.reset(quic_server_info_factory); |
} |
void QuicStreamFactory::DumpMemoryStats( |
@@ -934,12 +1069,25 @@ |
if (!task_runner_) |
task_runner_ = base::ThreadTaskRunnerHandle::Get().get(); |
+ std::unique_ptr<QuicServerInfo> quic_server_info; |
+ if (quic_server_info_factory_.get()) { |
+ bool load_from_disk_cache = !disable_disk_cache_; |
+ MaybeInitialize(); |
+ if (!base::ContainsKey(quic_supported_servers_at_startup_, destination)) { |
+ // If there is no entry for QUIC, consider that as a new server and |
+ // don't wait for Cache thread to load the data for that server. |
+ load_from_disk_cache = false; |
+ } |
+ if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) |
+ quic_server_info = quic_server_info_factory_->GetForServer(server_id); |
+ } |
+ |
ignore_result(StartCertVerifyJob(server_id, cert_verify_flags, net_log)); |
QuicSessionKey key(destination, server_id); |
std::unique_ptr<Job> job = base::MakeUnique<Job>( |
this, host_resolver_, key, WasQuicRecentlyBroken(server_id), |
- cert_verify_flags, net_log); |
+ cert_verify_flags, std::move(quic_server_info), net_log); |
int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete, |
base::Unretained(this), job.get())); |
if (rv == ERR_IO_PENDING) { |
@@ -983,6 +1131,18 @@ |
size_t QuicStreamFactory::QuicSessionKey::EstimateMemoryUsage() const { |
return base::trace_event::EstimateMemoryUsage(destination_) + |
EstimateServerIdMemoryUsage(server_id_); |
+} |
+ |
+void QuicStreamFactory::CreateAuxilaryJob(const QuicSessionKey& key, |
+ int cert_verify_flags, |
+ const NetLogWithSource& net_log) { |
+ Job* aux_job = |
+ new Job(this, host_resolver_, key, WasQuicRecentlyBroken(key.server_id()), |
+ cert_verify_flags, nullptr, net_log); |
+ active_jobs_[key.server_id()][aux_job] = base::WrapUnique(aux_job); |
+ task_runner_->PostTask(FROM_HERE, |
+ base::Bind(&QuicStreamFactory::Job::RunAuxilaryJob, |
+ aux_job->GetWeakPtr())); |
} |
bool QuicStreamFactory::OnResolution(const QuicSessionKey& key, |
@@ -1483,6 +1643,7 @@ |
int QuicStreamFactory::CreateSession( |
const QuicSessionKey& key, |
int cert_verify_flags, |
+ std::unique_ptr<QuicServerInfo> server_info, |
bool require_confirmation, |
const AddressList& address_list, |
base::TimeTicks dns_resolution_start_time, |
@@ -1511,13 +1672,7 @@ |
alarm_factory_.reset(new QuicChromiumAlarmFactory( |
base::ThreadTaskRunnerHandle::Get().get(), clock_)); |
} |
- |
QuicConnectionId connection_id = random_generator_->RandUint64(); |
- std::unique_ptr<QuicServerInfo> server_info; |
- if (store_server_configs_in_properties_) { |
- server_info = base::MakeUnique<PropertiesBasedQuicServerInfo>( |
- server_id, http_server_properties_); |
- } |
InitializeCachedStateInCryptoConfig(server_id, server_info, &connection_id); |
QuicChromiumPacketWriter* writer = new QuicChromiumPacketWriter(socket.get()); |
@@ -1539,6 +1694,14 @@ |
if (force_hol_blocking_) |
config.SetForceHolBlocking(); |
+ |
+ if (quic_server_info_factory_.get() && !server_info) { |
+ // Start the disk cache loading so that we can persist the newer QUIC server |
+ // information and/or inform the disk cache that we have reused |
+ // |server_info|. |
+ server_info = quic_server_info_factory_->GetForServer(server_id); |
+ server_info->Start(); |
+ } |
// Use the factory to create a new socket performance watcher, and pass the |
// ownership to QuicChromiumClientSession. |
@@ -1682,8 +1845,19 @@ |
if (!cached->IsEmpty()) |
return; |
- if (!server_info || !server_info->Load()) |
+ // |server_info| will be NULL, if a non-empty server config already exists in |
+ // the memory cache. |
+ if (!server_info) |
return; |
+ |
+ // TODO(rtenneti): Delete the following histogram after collecting stats. |
+ // If the AlternativeServiceMap contained an entry for this host, check if |
+ // the disk cache contained an entry for it. |
+ if (base::ContainsKey(quic_supported_servers_at_startup_, |
+ server_id.host_port_pair())) { |
+ UMA_HISTOGRAM_BOOLEAN("Net.QuicServerInfo.ExpectConfigMissingFromDiskCache", |
+ server_info->state().server_config.empty()); |
+ } |
cached->Initialize(server_info->state().server_config, |
server_info->state().source_address_token, |
@@ -1691,6 +1865,60 @@ |
server_info->state().chlo_hash, |
server_info->state().server_config_sig, clock_->WallNow(), |
QuicWallTime::Zero()); |
+} |
+ |
+void QuicStreamFactory::MaybeInitialize() { |
+ // We don't initialize data from HttpServerProperties in the constructor |
+ // because HttpServerProperties has not yet initialized. We're guaranteed |
+ // HttpServerProperties has been initialized by the first time a request is |
+ // made. |
+ if (has_initialized_data_) |
+ return; |
+ |
+ has_initialized_data_ = true; |
+ |
+ // Query the proxy delegate for the default alternative proxy server. |
+ ProxyServer default_alternative_proxy_server = |
+ proxy_delegate_ ? proxy_delegate_->GetDefaultAlternativeProxy() |
+ : ProxyServer(); |
+ if (default_alternative_proxy_server.is_quic()) { |
+ quic_supported_servers_at_startup_.insert( |
+ default_alternative_proxy_server.host_port_pair()); |
+ } |
+ |
+ for (const std::pair<const url::SchemeHostPort, AlternativeServiceInfoVector>& |
+ key_value : http_server_properties_->alternative_service_map()) { |
+ HostPortPair host_port_pair(key_value.first.host(), key_value.first.port()); |
+ for (const AlternativeServiceInfo& alternative_service_info : |
+ key_value.second) { |
+ if (alternative_service_info.alternative_service.protocol == kProtoQUIC) { |
+ quic_supported_servers_at_startup_.insert(host_port_pair); |
+ break; |
+ } |
+ } |
+ } |
+ |
+ if (http_server_properties_->max_server_configs_stored_in_properties() == 0) |
+ return; |
+ // Create a temporary QuicServerInfo object to deserialize and to populate the |
+ // in-memory crypto server config cache in the MRU order. |
+ std::unique_ptr<QuicServerInfo> server_info; |
+ CompletionCallback callback; |
+ // Get the list of servers to be deserialized first because WaitForDataReady |
+ // touches quic_server_info_map. |
+ const QuicServerInfoMap& quic_server_info_map = |
+ http_server_properties_->quic_server_info_map(); |
+ std::vector<QuicServerId> server_list; |
+ for (const auto& key_value : quic_server_info_map) |
+ server_list.push_back(key_value.first); |
+ for (auto it = server_list.rbegin(); it != server_list.rend(); ++it) { |
+ const QuicServerId& server_id = *it; |
+ server_info = quic_server_info_factory_->GetForServer(server_id); |
+ if (server_info->WaitForDataReady(callback) == OK) { |
+ DVLOG(1) << "Initialized server config for: " << server_id.ToString(); |
+ InitializeCachedStateInCryptoConfig(server_id, server_info, nullptr); |
+ } |
+ } |
} |
void QuicStreamFactory::ProcessGoingAwaySession( |