Commit 14abd31d authored by rtenneti's avatar rtenneti Committed by Commit bot

QUIC - Race two connections. One connection which loads data from disk

cache sends a CHLO and another connection that doesn't wait to load
server config from disk cache and sends INCHOATE_HELLO.

This is not enabled. QuicStreamFactory tests with this flag enabled
and disabled. Tested chrome and all unit tests with this flag enabled.

R=rch@chromium.org

Review URL: https://codereview.chromium.org/881133004

Cr-Commit-Position: refs/heads/master@{#315117}
parent 1ef7ee1e
...@@ -196,7 +196,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( ...@@ -196,7 +196,7 @@ void QuicCryptoClientStream::DoHandshakeLoop(
break; break;
case STATE_SEND_CHLO: case STATE_SEND_CHLO:
DoSendCHLO(in, cached); DoSendCHLO(in, cached);
return; return; // return waiting to hear from server.
case STATE_RECV_REJ: case STATE_RECV_REJ:
DoReceiveREJ(in, cached); DoReceiveREJ(in, cached);
break; break;
...@@ -638,7 +638,7 @@ bool QuicCryptoClientStream::RequiresChannelID( ...@@ -638,7 +638,7 @@ bool QuicCryptoClientStream::RequiresChannelID(
return false; return false;
} }
const CryptoHandshakeMessage* scfg = cached->GetServerConfig(); const CryptoHandshakeMessage* scfg = cached->GetServerConfig();
if (!scfg) { // scfg may be null when we send an inchoate CHLO. if (!scfg) { // scfg may be null then we send an inchoate CHLO.
return false; return false;
} }
const QuicTag* their_proof_demands; const QuicTag* their_proof_demands;
......
...@@ -513,6 +513,8 @@ enum QuicErrorCode { ...@@ -513,6 +513,8 @@ enum QuicErrorCode {
QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS = 68, QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS = 68,
// The connection has too many outstanding received packets. // The connection has too many outstanding received packets.
QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS = 69, QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS = 69,
// The quic connection job to load server config is cancelled.
QUIC_CONNECTION_CANCELLED = 70,
// Crypto errors. // Crypto errors.
...@@ -570,7 +572,7 @@ enum QuicErrorCode { ...@@ -570,7 +572,7 @@ enum QuicErrorCode {
QUIC_VERSION_NEGOTIATION_MISMATCH = 55, QUIC_VERSION_NEGOTIATION_MISMATCH = 55,
// No error. Used as bound while iterating. // No error. Used as bound while iterating.
QUIC_LAST_ERROR = 70, QUIC_LAST_ERROR = 71,
}; };
struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { struct NET_EXPORT_PRIVATE QuicPacketPublicHeader {
......
...@@ -141,7 +141,7 @@ class QuicStreamFactory::Job { ...@@ -141,7 +141,7 @@ class QuicStreamFactory::Job {
bool is_https, bool is_https,
bool was_alternate_protocol_recently_broken, bool was_alternate_protocol_recently_broken,
PrivacyMode privacy_mode, PrivacyMode privacy_mode,
base::StringPiece method, bool is_post,
QuicServerInfo* server_info, QuicServerInfo* server_info,
const BoundNetLog& net_log); const BoundNetLog& net_log);
...@@ -167,15 +167,15 @@ class QuicStreamFactory::Job { ...@@ -167,15 +167,15 @@ class QuicStreamFactory::Job {
void OnIOComplete(int rv); void OnIOComplete(int rv);
void RunAuxilaryJob();
void Cancel();
void CancelWaitForDataReadyCallback(); void CancelWaitForDataReadyCallback();
CompletionCallback callback() { const QuicServerId server_id() const { return server_id_; }
return callback_;
}
const QuicServerId server_id() const { base::WeakPtr<Job> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
return server_id_;
}
private: private:
enum IoState { enum IoState {
...@@ -196,6 +196,7 @@ class QuicStreamFactory::Job { ...@@ -196,6 +196,7 @@ class QuicStreamFactory::Job {
bool is_post_; bool is_post_;
bool was_alternate_protocol_recently_broken_; bool was_alternate_protocol_recently_broken_;
scoped_ptr<QuicServerInfo> server_info_; scoped_ptr<QuicServerInfo> server_info_;
bool started_another_job_;
const BoundNetLog net_log_; const BoundNetLog net_log_;
QuicClientSession* session_; QuicClientSession* session_;
CompletionCallback callback_; CompletionCallback callback_;
...@@ -212,20 +213,22 @@ QuicStreamFactory::Job::Job(QuicStreamFactory* factory, ...@@ -212,20 +213,22 @@ QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
bool is_https, bool is_https,
bool was_alternate_protocol_recently_broken, bool was_alternate_protocol_recently_broken,
PrivacyMode privacy_mode, PrivacyMode privacy_mode,
base::StringPiece method, bool is_post,
QuicServerInfo* server_info, QuicServerInfo* server_info,
const BoundNetLog& net_log) const BoundNetLog& net_log)
: io_state_(STATE_RESOLVE_HOST), : io_state_(STATE_RESOLVE_HOST),
factory_(factory), factory_(factory),
host_resolver_(host_resolver), host_resolver_(host_resolver),
server_id_(host_port_pair, is_https, privacy_mode), server_id_(host_port_pair, is_https, privacy_mode),
is_post_(method == "POST"), is_post_(is_post),
was_alternate_protocol_recently_broken_( was_alternate_protocol_recently_broken_(
was_alternate_protocol_recently_broken), was_alternate_protocol_recently_broken),
server_info_(server_info), server_info_(server_info),
started_another_job_(false),
net_log_(net_log), net_log_(net_log),
session_(nullptr), session_(nullptr),
weak_factory_(this) {} weak_factory_(this) {
}
QuicStreamFactory::Job::Job(QuicStreamFactory* factory, QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
HostResolver* host_resolver, HostResolver* host_resolver,
...@@ -235,13 +238,18 @@ QuicStreamFactory::Job::Job(QuicStreamFactory* factory, ...@@ -235,13 +238,18 @@ QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
factory_(factory), factory_(factory),
host_resolver_(host_resolver), // unused host_resolver_(host_resolver), // unused
server_id_(server_id), server_id_(server_id),
is_post_(false), // unused is_post_(false), // unused
was_alternate_protocol_recently_broken_(false), // unused was_alternate_protocol_recently_broken_(false), // unused
net_log_(session->net_log()), // unused started_another_job_(false), // unused
net_log_(session->net_log()), // unused
session_(session), session_(session),
weak_factory_(this) {} weak_factory_(this) {
}
QuicStreamFactory::Job::~Job() { QuicStreamFactory::Job::~Job() {
// If disk cache has a pending WaitForDataReadyCallback, cancel that callback.
if (server_info_)
server_info_->CancelWaitForDataReadyCallback();
} }
int QuicStreamFactory::Job::Run(const CompletionCallback& callback) { int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
...@@ -307,6 +315,19 @@ void QuicStreamFactory::Job::OnIOComplete(int rv) { ...@@ -307,6 +315,19 @@ void QuicStreamFactory::Job::OnIOComplete(int 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_)
session_->connection()->SendConnectionClose(QUIC_CONNECTION_CANCELLED);
}
void QuicStreamFactory::Job::CancelWaitForDataReadyCallback() { void QuicStreamFactory::Job::CancelWaitForDataReadyCallback() {
// If we are waiting for WaitForDataReadyCallback, then cancel the callback. // If we are waiting for WaitForDataReadyCallback, then cancel the callback.
if (io_state_ != STATE_LOAD_SERVER_INFO_COMPLETE) if (io_state_ != STATE_LOAD_SERVER_INFO_COMPLETE)
...@@ -329,11 +350,9 @@ int QuicStreamFactory::Job::DoResolveHost() { ...@@ -329,11 +350,9 @@ int QuicStreamFactory::Job::DoResolveHost() {
io_state_ = STATE_RESOLVE_HOST_COMPLETE; io_state_ = STATE_RESOLVE_HOST_COMPLETE;
dns_resolution_start_time_ = base::TimeTicks::Now(); dns_resolution_start_time_ = base::TimeTicks::Now();
return host_resolver_.Resolve( return host_resolver_.Resolve(
HostResolver::RequestInfo(server_id_.host_port_pair()), HostResolver::RequestInfo(server_id_.host_port_pair()), DEFAULT_PRIORITY,
DEFAULT_PRIORITY,
&address_list_, &address_list_,
base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()),
weak_factory_.GetWeakPtr()),
net_log_); net_log_);
} }
...@@ -356,7 +375,10 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) { ...@@ -356,7 +375,10 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
return OK; return OK;
} }
io_state_ = STATE_LOAD_SERVER_INFO; if (server_info_)
io_state_ = STATE_LOAD_SERVER_INFO;
else
io_state_ = STATE_CONNECT;
return OK; return OK;
} }
...@@ -368,8 +390,7 @@ int QuicStreamFactory::Job::DoLoadServerInfo() { ...@@ -368,8 +390,7 @@ int QuicStreamFactory::Job::DoLoadServerInfo() {
io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE; io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE;
if (!server_info_) DCHECK(server_info_);
return OK;
// To mitigate the effects of disk cache taking too long to load QUIC server // To mitigate the effects of disk cache taking too long to load QUIC server
// information, set up a timer to cancel WaitForDataReady's callback. // information, set up a timer to cancel WaitForDataReady's callback.
...@@ -385,14 +406,20 @@ int QuicStreamFactory::Job::DoLoadServerInfo() { ...@@ -385,14 +406,20 @@ int QuicStreamFactory::Job::DoLoadServerInfo() {
factory_->task_runner_->PostDelayedTask( factory_->task_runner_->PostDelayedTask(
FROM_HERE, FROM_HERE,
base::Bind(&QuicStreamFactory::Job::CancelWaitForDataReadyCallback, base::Bind(&QuicStreamFactory::Job::CancelWaitForDataReadyCallback,
weak_factory_.GetWeakPtr()), GetWeakPtr()),
base::TimeDelta::FromMilliseconds(load_server_info_timeout_ms)); base::TimeDelta::FromMilliseconds(load_server_info_timeout_ms));
} }
disk_cache_load_start_time_ = base::TimeTicks::Now(); disk_cache_load_start_time_ = base::TimeTicks::Now();
return server_info_->WaitForDataReady( int rv = server_info_->WaitForDataReady(
base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()));
weak_factory_.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(server_id_, is_post_, net_log_);
}
return rv;
} }
int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) { int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) {
...@@ -401,13 +428,20 @@ int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) { ...@@ -401,13 +428,20 @@ int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) {
FROM_HERE_WITH_EXPLICIT_FUNCTION( FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422516 QuicStreamFactory::Job::DoLoadServerInfoComplete")); "422516 QuicStreamFactory::Job::DoLoadServerInfoComplete"));
if (server_info_) { UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheWaitForDataReadyTime",
UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheWaitForDataReadyTime", base::TimeTicks::Now() - disk_cache_load_start_time_);
base::TimeTicks::Now() - disk_cache_load_start_time_);
}
if (rv != OK) { if (rv != OK)
server_info_.reset(); server_info_.reset();
if (started_another_job_ &&
(!server_info_ || server_info_->state().server_config.empty() ||
!factory_->CryptoConfigCacheIsEmpty(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; io_state_ = STATE_CONNECT;
...@@ -454,8 +488,7 @@ int QuicStreamFactory::Job::DoConnect() { ...@@ -454,8 +488,7 @@ int QuicStreamFactory::Job::DoConnect() {
rv = session_->CryptoConnect( rv = session_->CryptoConnect(
require_confirmation, require_confirmation,
base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()));
base::Unretained(this)));
return rv; return rv;
} }
...@@ -468,8 +501,7 @@ int QuicStreamFactory::Job::DoResumeConnect() { ...@@ -468,8 +501,7 @@ int QuicStreamFactory::Job::DoResumeConnect() {
io_state_ = STATE_CONNECT_COMPLETE; io_state_ = STATE_CONNECT_COMPLETE;
int rv = session_->ResumeCryptoConnect( int rv = session_->ResumeCryptoConnect(
base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()));
base::Unretained(this)));
return rv; return rv;
} }
...@@ -585,6 +617,7 @@ QuicStreamFactory::QuicStreamFactory( ...@@ -585,6 +617,7 @@ QuicStreamFactory::QuicStreamFactory(
load_server_info_timeout_srtt_multiplier_( load_server_info_timeout_srtt_multiplier_(
load_server_info_timeout_srtt_multiplier), load_server_info_timeout_srtt_multiplier),
enable_truncated_connection_ids_(enable_truncated_connection_ids), enable_truncated_connection_ids_(enable_truncated_connection_ids),
enable_connection_racing_(false),
port_seed_(random_generator_->RandUint64()), port_seed_(random_generator_->RandUint64()),
check_persisted_supports_quic_(true), check_persisted_supports_quic_(true),
task_runner_(nullptr), task_runner_(nullptr),
...@@ -610,7 +643,11 @@ QuicStreamFactory::~QuicStreamFactory() { ...@@ -610,7 +643,11 @@ QuicStreamFactory::~QuicStreamFactory() {
delete all_sessions_.begin()->first; delete all_sessions_.begin()->first;
all_sessions_.erase(all_sessions_.begin()); all_sessions_.erase(all_sessions_.begin());
} }
STLDeleteValues(&active_jobs_); while (!active_jobs_.empty()) {
const QuicServerId server_id = active_jobs_.begin()->first;
STLDeleteElements(&(active_jobs_[server_id]));
active_jobs_.erase(server_id);
}
} }
void QuicStreamFactory::set_require_confirmation(bool require_confirmation) { void QuicStreamFactory::set_require_confirmation(bool require_confirmation) {
...@@ -634,12 +671,16 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, ...@@ -634,12 +671,16 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
} }
if (HasActiveJob(server_id)) { if (HasActiveJob(server_id)) {
Job* job = active_jobs_[server_id]; active_requests_[request] = server_id;
active_requests_[request] = job; job_requests_map_[server_id].insert(request);
job_requests_map_[job].insert(request);
return ERR_IO_PENDING; return ERR_IO_PENDING;
} }
// TODO(rtenneti): |task_runner_| is used by the Job. Initialize task_runner_
// in the constructor after WebRequestActionWithThreadsTest.* tests are fixed.
if (!task_runner_)
task_runner_ = base::MessageLoop::current()->message_loop_proxy().get();
QuicServerInfo* quic_server_info = nullptr; QuicServerInfo* quic_server_info = nullptr;
if (quic_server_info_factory_) { if (quic_server_info_factory_) {
bool load_from_disk_cache = true; bool load_from_disk_cache = true;
...@@ -654,34 +695,22 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, ...@@ -654,34 +695,22 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
load_from_disk_cache = false; load_from_disk_cache = false;
} }
} }
if (load_from_disk_cache) { if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) {
QuicCryptoClientConfig::CachedState* cached = quic_server_info = quic_server_info_factory_->GetForServer(server_id);
crypto_config_.LookupOrCreate(server_id);
DCHECK(cached);
if (cached->IsEmpty()) {
quic_server_info = quic_server_info_factory_->GetForServer(server_id);
}
} }
} }
// TODO(rtenneti): Initialize task_runner_ in the constructor after
// WebRequestActionWithThreadsTest.* tests are fixed.
if (!task_runner_)
task_runner_ = base::MessageLoop::current()->message_loop_proxy().get();
bool was_alternate_protocol_recently_broken =
http_server_properties_ &&
http_server_properties_->WasAlternateProtocolRecentlyBroken(
server_id.host_port_pair());
scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https, scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_pair, is_https,
was_alternate_protocol_recently_broken, WasAlternateProtocolRecentlyBroken(server_id),
privacy_mode, method, quic_server_info, net_log)); privacy_mode, method == "POST" /* is_post */,
quic_server_info, net_log));
int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete, int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
base::Unretained(this), job.get())); base::Unretained(this), job.get()));
if (rv == ERR_IO_PENDING) { if (rv == ERR_IO_PENDING) {
active_requests_[request] = job.get(); active_requests_[request] = server_id;
job_requests_map_[job.get()].insert(request); job_requests_map_[server_id].insert(request);
active_jobs_[server_id] = job.release(); active_jobs_[server_id].insert(job.release());
return rv;
} }
if (rv == OK) { if (rv == OK) {
DCHECK(HasActiveSession(server_id)); DCHECK(HasActiveSession(server_id));
...@@ -690,6 +719,19 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair, ...@@ -690,6 +719,19 @@ int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
return rv; return rv;
} }
void QuicStreamFactory::CreateAuxilaryJob(const QuicServerId server_id,
bool is_post,
const BoundNetLog& net_log) {
Job* aux_job = new Job(this, host_resolver_, server_id.host_port_pair(),
server_id.is_https(),
WasAlternateProtocolRecentlyBroken(server_id),
server_id.privacy_mode(), is_post, nullptr, net_log);
active_jobs_[server_id].insert(aux_job);
task_runner_->PostTask(FROM_HERE,
base::Bind(&QuicStreamFactory::Job::RunAuxilaryJob,
aux_job->GetWeakPtr()));
}
bool QuicStreamFactory::OnResolution( bool QuicStreamFactory::OnResolution(
const QuicServerId& server_id, const QuicServerId& server_id,
const AddressList& address_list) { const AddressList& address_list) {
...@@ -715,6 +757,19 @@ bool QuicStreamFactory::OnResolution( ...@@ -715,6 +757,19 @@ bool QuicStreamFactory::OnResolution(
} }
void QuicStreamFactory::OnJobComplete(Job* job, int rv) { void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
QuicServerId server_id = job->server_id();
if (rv != OK) {
JobSet* jobs = &(active_jobs_[server_id]);
if (jobs->size() > 1) {
// If there is another pending job, then we can delete this job and let
// the other job handle the request.
job->Cancel();
jobs->erase(job);
delete job;
return;
}
}
if (rv == OK) { if (rv == OK) {
// TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
tracked_objects::ScopedTracker tracking_profile1( tracked_objects::ScopedTracker tracking_profile1(
...@@ -725,11 +780,9 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { ...@@ -725,11 +780,9 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
set_require_confirmation(false); set_require_confirmation(false);
// Create all the streams, but do not notify them yet. // Create all the streams, but do not notify them yet.
for (RequestSet::iterator it = job_requests_map_[job].begin(); for (QuicStreamRequest* request : job_requests_map_[server_id]) {
it != job_requests_map_[job].end() ; ++it) { DCHECK(HasActiveSession(server_id));
DCHECK(HasActiveSession(job->server_id())); request->set_stream(CreateIfSessionExists(server_id, request->net_log()));
(*it)->set_stream(CreateIfSessionExists(job->server_id(),
(*it)->net_log()));
} }
} }
...@@ -738,10 +791,10 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { ...@@ -738,10 +791,10 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
FROM_HERE_WITH_EXPLICIT_FUNCTION( FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422516 QuicStreamFactory::OnJobComplete2")); "422516 QuicStreamFactory::OnJobComplete2"));
while (!job_requests_map_[job].empty()) { while (!job_requests_map_[server_id].empty()) {
RequestSet::iterator it = job_requests_map_[job].begin(); RequestSet::iterator it = job_requests_map_[server_id].begin();
QuicStreamRequest* request = *it; QuicStreamRequest* request = *it;
job_requests_map_[job].erase(it); job_requests_map_[server_id].erase(it);
active_requests_.erase(request); active_requests_.erase(request);
// Even though we're invoking callbacks here, we don't need to worry // Even though we're invoking callbacks here, we don't need to worry
// about |this| being deleted, because the factory is owned by the // about |this| being deleted, because the factory is owned by the
...@@ -754,10 +807,14 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) { ...@@ -754,10 +807,14 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
FROM_HERE_WITH_EXPLICIT_FUNCTION( FROM_HERE_WITH_EXPLICIT_FUNCTION(
"422516 QuicStreamFactory::OnJobComplete3")); "422516 QuicStreamFactory::OnJobComplete3"));
active_jobs_.erase(job->server_id()); for (Job* other_job : active_jobs_[server_id]) {
job_requests_map_.erase(job); if (other_job != job)
delete job; other_job->Cancel();
return; }
STLDeleteElements(&(active_jobs_[server_id]));
active_jobs_.erase(server_id);
job_requests_map_.erase(server_id);
} }
// Returns a newly created QuicHttpStream owned by the caller, if a // Returns a newly created QuicHttpStream owned by the caller, if a
...@@ -836,7 +893,7 @@ void QuicStreamFactory::OnSessionConnectTimeout( ...@@ -836,7 +893,7 @@ void QuicStreamFactory::OnSessionConnectTimeout(
QuicServerId server_id = *aliases.begin(); QuicServerId server_id = *aliases.begin();
session_aliases_.erase(session); session_aliases_.erase(session);
Job* job = new Job(this, host_resolver_, session, server_id); Job* job = new Job(this, host_resolver_, session, server_id);
active_jobs_[server_id] = job; active_jobs_[server_id].insert(job);
int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete, int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
base::Unretained(this), job)); base::Unretained(this), job));
DCHECK_EQ(ERR_IO_PENDING, rv); DCHECK_EQ(ERR_IO_PENDING, rv);
...@@ -844,8 +901,8 @@ void QuicStreamFactory::OnSessionConnectTimeout( ...@@ -844,8 +901,8 @@ void QuicStreamFactory::OnSessionConnectTimeout(
void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) { void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
DCHECK(ContainsKey(active_requests_, request)); DCHECK(ContainsKey(active_requests_, request));
Job* job = active_requests_[request]; QuicServerId server_id = active_requests_[request];
job_requests_map_[job].erase(request); job_requests_map_[server_id].erase(request);
active_requests_.erase(request); active_requests_.erase(request);
} }
...@@ -915,6 +972,10 @@ bool QuicStreamFactory::HasActiveSession( ...@@ -915,6 +972,10 @@ bool QuicStreamFactory::HasActiveSession(
return ContainsKey(active_sessions_, server_id); return ContainsKey(active_sessions_, server_id);
} }
bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
return ContainsKey(active_jobs_, key);
}
int QuicStreamFactory::CreateSession( int QuicStreamFactory::CreateSession(
const QuicServerId& server_id, const QuicServerId& server_id,
scoped_ptr<QuicServerInfo> server_info, scoped_ptr<QuicServerInfo> server_info,
...@@ -1129,10 +1190,6 @@ int QuicStreamFactory::CreateSession( ...@@ -1129,10 +1190,6 @@ int QuicStreamFactory::CreateSession(
return OK; return OK;
} }
bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
return ContainsKey(active_jobs_, key);
}
void QuicStreamFactory::ActivateSession( void QuicStreamFactory::ActivateSession(
const QuicServerId& server_id, const QuicServerId& server_id,
QuicClientSession* session) { QuicClientSession* session) {
...@@ -1158,6 +1215,20 @@ int64 QuicStreamFactory::GetServerNetworkStatsSmoothedRttInMicroseconds( ...@@ -1158,6 +1215,20 @@ int64 QuicStreamFactory::GetServerNetworkStatsSmoothedRttInMicroseconds(
return stats->srtt.InMicroseconds(); return stats->srtt.InMicroseconds();
} }
bool QuicStreamFactory::WasAlternateProtocolRecentlyBroken(
const QuicServerId& server_id) const {
return http_server_properties_ &&
http_server_properties_->WasAlternateProtocolRecentlyBroken(
server_id.host_port_pair());
}
bool QuicStreamFactory::CryptoConfigCacheIsEmpty(
const QuicServerId& server_id) {
QuicCryptoClientConfig::CachedState* cached =
crypto_config_.LookupOrCreate(server_id);
return cached->IsEmpty();
}
void QuicStreamFactory::InitializeCachedStateInCryptoConfig( void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
const QuicServerId& server_id, const QuicServerId& server_id,
const scoped_ptr<QuicServerInfo>& server_info) { const scoped_ptr<QuicServerInfo>& server_info) {
......
...@@ -178,6 +178,11 @@ class NET_EXPORT_PRIVATE QuicStreamFactory ...@@ -178,6 +178,11 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
quic_server_info_factory_ = quic_server_info_factory; quic_server_info_factory_ = quic_server_info_factory;
} }
bool enable_connection_racing() const { return enable_connection_racing_; }
void set_enable_connection_racing(bool enable_connection_racing) {
enable_connection_racing_ = enable_connection_racing;
}
private: private:
class Job; class Job;
friend class test::QuicStreamFactoryPeer; friend class test::QuicStreamFactoryPeer;
...@@ -204,10 +209,17 @@ class NET_EXPORT_PRIVATE QuicStreamFactory ...@@ -204,10 +209,17 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
typedef std::set<QuicClientSession*> SessionSet; typedef std::set<QuicClientSession*> SessionSet;
typedef std::map<IpAliasKey, SessionSet> IPAliasMap; typedef std::map<IpAliasKey, SessionSet> IPAliasMap;
typedef std::map<QuicServerId, QuicCryptoClientConfig*> CryptoConfigMap; typedef std::map<QuicServerId, QuicCryptoClientConfig*> CryptoConfigMap;
typedef std::map<QuicServerId, Job*> JobMap; typedef std::set<Job*> JobSet;
typedef std::map<QuicStreamRequest*, Job*> RequestMap; typedef std::map<QuicServerId, JobSet> JobMap;
typedef std::map<QuicStreamRequest*, QuicServerId> RequestMap;
typedef std::set<QuicStreamRequest*> RequestSet; typedef std::set<QuicStreamRequest*> RequestSet;
typedef std::map<Job*, RequestSet> JobRequestsMap; typedef std::map<QuicServerId, RequestSet> ServerIDRequestsMap;
// Creates a job which doesn't wait for server config to be loaded from the
// disk cache. This job is started via a PostTask.
void CreateAuxilaryJob(const QuicServerId server_id,
bool is_post,
const BoundNetLog& net_log);
// Returns a newly created QuicHttpStream owned by the caller, if a // Returns a newly created QuicHttpStream owned by the caller, if a
// matching session already exists. Returns NULL otherwise. // matching session already exists. Returns NULL otherwise.
...@@ -233,6 +245,10 @@ class NET_EXPORT_PRIVATE QuicStreamFactory ...@@ -233,6 +245,10 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
int64 GetServerNetworkStatsSmoothedRttInMicroseconds( int64 GetServerNetworkStatsSmoothedRttInMicroseconds(
const QuicServerId& server_id) const; const QuicServerId& server_id) const;
// Helped methods.
bool WasAlternateProtocolRecentlyBroken(const QuicServerId& server_id) const;
bool CryptoConfigCacheIsEmpty(const QuicServerId& server_id);
// Initializes the cached state associated with |server_id| in // Initializes the cached state associated with |server_id| in
// |crypto_config_| with the information in |server_info|. // |crypto_config_| with the information in |server_info|.
void InitializeCachedStateInCryptoConfig( void InitializeCachedStateInCryptoConfig(
...@@ -274,7 +290,7 @@ class NET_EXPORT_PRIVATE QuicStreamFactory ...@@ -274,7 +290,7 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
QuicCryptoClientConfig crypto_config_; QuicCryptoClientConfig crypto_config_;
JobMap active_jobs_; JobMap active_jobs_;
JobRequestsMap job_requests_map_; ServerIDRequestsMap job_requests_map_;
RequestMap active_requests_; RequestMap active_requests_;
QuicVersionVector supported_versions_; QuicVersionVector supported_versions_;
...@@ -305,6 +321,11 @@ class NET_EXPORT_PRIVATE QuicStreamFactory ...@@ -305,6 +321,11 @@ class NET_EXPORT_PRIVATE QuicStreamFactory
// Set this for setting config's BytesForConnectionIdToSend (TCID param) to 0. // Set this for setting config's BytesForConnectionIdToSend (TCID param) to 0.
bool enable_truncated_connection_ids_; bool enable_truncated_connection_ids_;
// Set if we want to race connections - one connection that sends
// INCHOATE_HELLO and another connection that sends CHLO after loading server
// config from the disk cache.
bool enable_connection_racing_;
// Each profile will (probably) have a unique port_seed_ value. This value is // Each profile will (probably) have a unique port_seed_ value. This value is
// used to help seed a pseudo-random number generator (PortSuggester) so that // used to help seed a pseudo-random number generator (PortSuggester) so that
// we consistently (within this profile) suggest the same ephemeral port when // we consistently (within this profile) suggest the same ephemeral port when
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
using base::StringPiece; using base::StringPiece;
using std::ostream;
using std::string; using std::string;
using std::vector; using std::vector;
...@@ -43,6 +44,34 @@ namespace test { ...@@ -43,6 +44,34 @@ namespace test {
namespace { namespace {
const char kDefaultServerHostName[] = "www.google.com"; const char kDefaultServerHostName[] = "www.google.com";
const int kDefaultServerPort = 443; const int kDefaultServerPort = 443;
// Run all tests with all the combinations of versions and
// enable_connection_racing.
struct TestParams {
TestParams(const QuicVersion version, bool enable_connection_racing)
: version(version), enable_connection_racing(enable_connection_racing) {}
friend ostream& operator<<(ostream& os, const TestParams& p) {
os << "{ version: " << QuicVersionToString(p.version);
os << " enable_connection_racing: " << p.enable_connection_racing << " }";
return os;
}
QuicVersion version;
bool enable_connection_racing;
};
// Constructs various test permutations.
vector<TestParams> GetTestParams() {
vector<TestParams> params;
QuicVersionVector all_supported_versions = QuicSupportedVersions();
for (const QuicVersion version : all_supported_versions) {
params.push_back(TestParams(version, false));
params.push_back(TestParams(version, true));
}
return params;
}
} // namespace anonymous } // namespace anonymous
class QuicStreamFactoryPeer { class QuicStreamFactoryPeer {
...@@ -100,6 +129,16 @@ class QuicStreamFactoryPeer { ...@@ -100,6 +129,16 @@ class QuicStreamFactoryPeer {
size_t load_server_info_timeout) { size_t load_server_info_timeout) {
factory->load_server_info_timeout_ms_ = load_server_info_timeout; factory->load_server_info_timeout_ms_ = load_server_info_timeout;
} }
static void SetEnableConnectionRacing(QuicStreamFactory* factory,
bool enable_connection_racing) {
factory->enable_connection_racing_ = enable_connection_racing;
}
static size_t GetNumberOfActiveJobs(QuicStreamFactory* factory,
const QuicServerId& server_id) {
return (factory->active_jobs_[server_id]).size();
}
}; };
class MockQuicServerInfo : public QuicServerInfo { class MockQuicServerInfo : public QuicServerInfo {
...@@ -135,14 +174,13 @@ class MockQuicServerInfoFactory : public QuicServerInfoFactory { ...@@ -135,14 +174,13 @@ class MockQuicServerInfoFactory : public QuicServerInfoFactory {
} }
}; };
class QuicStreamFactoryTest : public ::testing::TestWithParam<TestParams> {
class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
protected: protected:
QuicStreamFactoryTest() QuicStreamFactoryTest()
: random_generator_(0), : random_generator_(0),
clock_(new MockClock()), clock_(new MockClock()),
runner_(new TestTaskRunner(clock_)), runner_(new TestTaskRunner(clock_)),
maker_(GetParam(), 0, clock_), maker_(GetParam().version, 0, clock_),
cert_verifier_(CertVerifier::CreateDefault()), cert_verifier_(CertVerifier::CreateDefault()),
channel_id_service_( channel_id_service_(
new ChannelIDService(new DefaultChannelIDStore(nullptr), new ChannelIDService(new DefaultChannelIDStore(nullptr),
...@@ -158,7 +196,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> { ...@@ -158,7 +196,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
clock_, clock_,
kDefaultMaxPacketSize, kDefaultMaxPacketSize,
std::string(), std::string(),
SupportedVersions(GetParam()), SupportedVersions(GetParam().version),
/*enable_port_selection=*/true, /*enable_port_selection=*/true,
/*always_require_handshake_confirmation=*/false, /*always_require_handshake_confirmation=*/false,
/*disable_connection_pooling=*/false, /*disable_connection_pooling=*/false,
...@@ -171,6 +209,8 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> { ...@@ -171,6 +209,8 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
privacy_mode_(PRIVACY_MODE_DISABLED) { privacy_mode_(PRIVACY_MODE_DISABLED) {
factory_.set_require_confirmation(false); factory_.set_require_confirmation(false);
clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1)); clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
QuicStreamFactoryPeer::SetEnableConnectionRacing(
&factory_, GetParam().enable_connection_racing);
} }
scoped_ptr<QuicHttpStream> CreateIfSessionExists( scoped_ptr<QuicHttpStream> CreateIfSessionExists(
...@@ -244,7 +284,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> { ...@@ -244,7 +284,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
QuicStreamId stream_id = kClientDataStreamId1; QuicStreamId stream_id = kClientDataStreamId1;
return maker_.MakeRstPacket( return maker_.MakeRstPacket(
1, true, stream_id, 1, true, stream_id,
AdjustErrorForVersion(QUIC_RST_ACKNOWLEDGEMENT, GetParam())); AdjustErrorForVersion(QUIC_RST_ACKNOWLEDGEMENT, GetParam().version));
} }
MockQuicServerInfoFactory quic_server_info_factory_; MockQuicServerInfoFactory quic_server_info_factory_;
...@@ -266,8 +306,9 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> { ...@@ -266,8 +306,9 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam<QuicVersion> {
TestCompletionCallback callback_; TestCompletionCallback callback_;
}; };
INSTANTIATE_TEST_CASE_P(Version, QuicStreamFactoryTest, INSTANTIATE_TEST_CASE_P(Version,
::testing::ValuesIn(QuicSupportedVersions())); QuicStreamFactoryTest,
::testing::ValuesIn(GetTestParams()));
TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) { TEST_P(QuicStreamFactoryTest, CreateIfSessionExists) {
EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get()); EXPECT_EQ(nullptr, CreateIfSessionExists(host_port_pair_, net_log_).get());
...@@ -1562,6 +1603,10 @@ TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) { ...@@ -1562,6 +1603,10 @@ TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) {
} }
TEST_P(QuicStreamFactoryTest, CancelWaitForDataReady) { TEST_P(QuicStreamFactoryTest, CancelWaitForDataReady) {
// Don't race quic connections when testing cancel reading of server config
// from disk cache.
if (GetParam().enable_connection_racing)
return;
factory_.set_quic_server_info_factory(&quic_server_info_factory_); factory_.set_quic_server_info_factory(&quic_server_info_factory_);
QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get()); QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get());
const size_t kLoadServerInfoTimeoutMs = 50; const size_t kLoadServerInfoTimeoutMs = 50;
...@@ -1604,5 +1649,52 @@ TEST_P(QuicStreamFactoryTest, CancelWaitForDataReady) { ...@@ -1604,5 +1649,52 @@ TEST_P(QuicStreamFactoryTest, CancelWaitForDataReady) {
EXPECT_TRUE(socket_data.at_write_eof()); EXPECT_TRUE(socket_data.at_write_eof());
} }
TEST_P(QuicStreamFactoryTest, RacingConnections) {
if (!GetParam().enable_connection_racing)
return;
factory_.set_quic_server_info_factory(&quic_server_info_factory_);
QuicStreamFactoryPeer::SetTaskRunner(&factory_, runner_.get());
const size_t kLoadServerInfoTimeoutMs = 50;
QuicStreamFactoryPeer::SetLoadServerInfoTimeout(&factory_,
kLoadServerInfoTimeoutMs);
MockRead reads[] = {
MockRead(ASYNC, OK, 0) // EOF
};
DeterministicSocketData socket_data(reads, arraysize(reads), nullptr, 0);
socket_factory_.AddSocketDataProvider(&socket_data);
socket_data.StopAfter(1);
MockRead reads2[] = {
MockRead(ASYNC, 0, 0) // EOF
};
DeterministicSocketData socket_data2(reads2, arraysize(reads2), nullptr, 0);
socket_factory_.AddSocketDataProvider(&socket_data2);
socket_data2.StopAfter(1);
crypto_client_stream_factory_.set_handshake_mode(
MockCryptoClientStream::ZERO_RTT);
host_resolver_.set_synchronous_mode(true);
host_resolver_.rules()->AddIPLiteralRule(host_port_pair_.host(),
"192.168.0.1", "");
QuicStreamRequest request(&factory_);
QuicServerId server_id(host_port_pair_, is_https_, privacy_mode_);
EXPECT_EQ(ERR_IO_PENDING,
request.Request(host_port_pair_, is_https_, privacy_mode_, "GET",
net_log_, callback_.callback()));
EXPECT_EQ(2u,
QuicStreamFactoryPeer::GetNumberOfActiveJobs(&factory_, server_id));
runner_->RunNextTask();
scoped_ptr<QuicHttpStream> stream = request.ReleaseStream();
EXPECT_TRUE(stream.get());
EXPECT_TRUE(socket_data.at_read_eof());
EXPECT_TRUE(socket_data.at_write_eof());
EXPECT_EQ(0u,
QuicStreamFactoryPeer::GetNumberOfActiveJobs(&factory_, server_id));
}
} // namespace test } // namespace test
} // namespace net } // namespace net
...@@ -221,6 +221,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { ...@@ -221,6 +221,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) {
RETURN_STRING_LITERAL(QUIC_VERSION_NEGOTIATION_MISMATCH); RETURN_STRING_LITERAL(QUIC_VERSION_NEGOTIATION_MISMATCH);
RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS); RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_SENT_PACKETS);
RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS); RETURN_STRING_LITERAL(QUIC_TOO_MANY_OUTSTANDING_RECEIVED_PACKETS);
RETURN_STRING_LITERAL(QUIC_CONNECTION_CANCELLED);
RETURN_STRING_LITERAL(QUIC_LAST_ERROR); RETURN_STRING_LITERAL(QUIC_LAST_ERROR);
// Intentionally have no default case, so we'll break the build // Intentionally have no default case, so we'll break the build
// if we add errors and don't put them here. // if we add errors and don't put them here.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment