summaryrefslogtreecommitdiffstats
path: root/chromium/net/quic/quic_stream_factory.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/quic/quic_stream_factory.cc')
-rw-r--r--chromium/net/quic/quic_stream_factory.cc744
1 files changed, 550 insertions, 194 deletions
diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc
index 036a91d38aa..2fc1b9e334c 100644
--- a/chromium/net/quic/quic_stream_factory.cc
+++ b/chromium/net/quic/quic_stream_factory.cc
@@ -6,7 +6,7 @@
#include <set>
-#include "base/logging.h"
+#include "base/cpu.h"
#include "base/message_loop/message_loop.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/metrics/histogram.h"
@@ -22,6 +22,7 @@
#include "net/quic/congestion_control/tcp_receiver.h"
#include "net/quic/crypto/proof_verifier_chromium.h"
#include "net/quic/crypto/quic_random.h"
+#include "net/quic/crypto/quic_server_info.h"
#include "net/quic/port_suggester.h"
#include "net/quic/quic_client_session.h"
#include "net/quic/quic_clock.h"
@@ -31,24 +32,117 @@
#include "net/quic/quic_default_packet_writer.h"
#include "net/quic/quic_http_stream.h"
#include "net/quic/quic_protocol.h"
+#include "net/quic/quic_server_id.h"
#include "net/socket/client_socket_factory.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
+
using std::string;
using std::vector;
namespace net {
+namespace {
+
+enum CreateSessionFailure {
+ CREATION_ERROR_CONNECTING_SOCKET,
+ CREATION_ERROR_SETTING_RECEIVE_BUFFER,
+ CREATION_ERROR_SETTING_SEND_BUFFER,
+ CREATION_ERROR_MAX
+};
+
+// When a connection is idle for 30 seconds it will be closed.
+const int kIdleConnectionTimeoutSeconds = 30;
+
+// The initial receive window size for both streams and sessions.
+const int32 kInitialReceiveWindowSize = 10 * 1024 * 1024; // 10MB
+
+// The suggested initial congestion windows for a server to use.
+// TODO: This should be tested and optimized, and even better, suggest a window
+// that corresponds to historical bandwidth and min-RTT.
+// Larger initial congestion windows can, if we don't overshoot, reduce latency
+// by avoiding the RTT needed for slow start to double (and re-double) from a
+// default of 10.
+// We match SPDY's use of 32 when secure (since we'd compete with SPDY).
+const int32 kServerSecureInitialCongestionWindow = 32;
+// Be conservative, and just use double a typical TCP ICWND for HTTP.
+const int32 kServerInecureInitialCongestionWindow = 20;
+
+void HistogramCreateSessionFailure(enum CreateSessionFailure error) {
+ UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.CreationError", error,
+ CREATION_ERROR_MAX);
+}
+
+bool IsEcdsaSupported() {
+#if defined(OS_WIN)
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
+ return false;
+#endif
+
+ return true;
+}
+
+QuicConfig InitializeQuicConfig(bool enable_pacing,
+ bool enable_time_based_loss_detection) {
+ QuicConfig config;
+ config.SetDefaults();
+ config.EnablePacing(enable_pacing);
+ if (enable_time_based_loss_detection)
+ config.SetLossDetectionToSend(kTIME);
+ config.set_idle_connection_state_lifetime(
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds),
+ QuicTime::Delta::FromSeconds(kIdleConnectionTimeoutSeconds));
+ return config;
+}
+
+} // namespace
+
+QuicStreamFactory::IpAliasKey::IpAliasKey() {}
+
+QuicStreamFactory::IpAliasKey::IpAliasKey(IPEndPoint ip_endpoint,
+ bool is_https)
+ : ip_endpoint(ip_endpoint),
+ is_https(is_https) {}
+
+QuicStreamFactory::IpAliasKey::~IpAliasKey() {}
+
+bool QuicStreamFactory::IpAliasKey::operator<(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ if (!(ip_endpoint == other.ip_endpoint)) {
+ return ip_endpoint < other.ip_endpoint;
+ }
+ return is_https < other.is_https;
+}
+
+bool QuicStreamFactory::IpAliasKey::operator==(
+ const QuicStreamFactory::IpAliasKey& other) const {
+ return is_https == other.is_https &&
+ ip_endpoint == other.ip_endpoint;
+};
+
// Responsible for creating a new QUIC session to the specified server, and
// for notifying any associated requests when complete.
class QuicStreamFactory::Job {
public:
Job(QuicStreamFactory* factory,
HostResolver* host_resolver,
- const HostPortProxyPair& host_port_proxy_pair,
+ const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
const BoundNetLog& net_log);
+ // Creates a new job to handle the resumption of for connecting an
+ // existing session.
+ Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id);
+
~Job();
int Run(const CompletionCallback& callback);
@@ -56,7 +150,10 @@ class QuicStreamFactory::Job {
int DoLoop(int rv);
int DoResolveHost();
int DoResolveHostComplete(int rv);
+ int DoLoadServerInfo();
+ int DoLoadServerInfoComplete(int rv);
int DoConnect();
+ int DoResumeConnect();
int DoConnectComplete(int rv);
void OnIOComplete(int rv);
@@ -65,8 +162,8 @@ class QuicStreamFactory::Job {
return callback_;
}
- const HostPortProxyPair& host_port_proxy_pair() const {
- return host_port_proxy_pair_;
+ const QuicServerId server_id() const {
+ return server_id_;
}
private:
@@ -74,44 +171,68 @@ class QuicStreamFactory::Job {
STATE_NONE,
STATE_RESOLVE_HOST,
STATE_RESOLVE_HOST_COMPLETE,
+ STATE_LOAD_SERVER_INFO,
+ STATE_LOAD_SERVER_INFO_COMPLETE,
STATE_CONNECT,
+ STATE_RESUME_CONNECT,
STATE_CONNECT_COMPLETE,
};
IoState io_state_;
QuicStreamFactory* factory_;
SingleRequestHostResolver host_resolver_;
- const HostPortProxyPair host_port_proxy_pair_;
- bool is_https_;
- CertVerifier* cert_verifier_;
+ QuicServerId server_id_;
+ bool is_post_;
+ bool was_alternate_protocol_recently_broken_;
+ scoped_ptr<QuicServerInfo> server_info_;
const BoundNetLog net_log_;
QuicClientSession* session_;
CompletionCallback callback_;
AddressList address_list_;
+ base::TimeTicks disk_cache_load_start_time_;
+ base::WeakPtrFactory<Job> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(Job);
};
-QuicStreamFactory::Job::Job(
- QuicStreamFactory* factory,
- HostResolver* host_resolver,
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
- const BoundNetLog& net_log)
- : factory_(factory),
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ const HostPortPair& host_port_pair,
+ bool is_https,
+ bool was_alternate_protocol_recently_broken,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ QuicServerInfo* server_info,
+ const BoundNetLog& net_log)
+ : io_state_(STATE_RESOLVE_HOST),
+ factory_(factory),
host_resolver_(host_resolver),
- host_port_proxy_pair_(host_port_proxy_pair),
- is_https_(is_https),
- cert_verifier_(cert_verifier),
+ server_id_(host_port_pair, is_https, privacy_mode),
+ is_post_(method == "POST"),
+ was_alternate_protocol_recently_broken_(
+ was_alternate_protocol_recently_broken),
+ server_info_(server_info),
net_log_(net_log),
- session_(NULL) {
-}
+ session_(NULL),
+ weak_factory_(this) {}
+
+QuicStreamFactory::Job::Job(QuicStreamFactory* factory,
+ HostResolver* host_resolver,
+ QuicClientSession* session,
+ QuicServerId server_id)
+ : io_state_(STATE_RESUME_CONNECT),
+ factory_(factory),
+ host_resolver_(host_resolver), // unused
+ server_id_(server_id),
+ is_post_(false), // unused
+ was_alternate_protocol_recently_broken_(false), // unused
+ net_log_(session->net_log()), // unused
+ session_(session),
+ weak_factory_(this) {}
QuicStreamFactory::Job::~Job() {
}
int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
- io_state_ = STATE_RESOLVE_HOST;
int rv = DoLoop(OK);
if (rv == ERR_IO_PENDING)
callback_ = callback;
@@ -131,10 +252,21 @@ int QuicStreamFactory::Job::DoLoop(int rv) {
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();
break;
+ case STATE_RESUME_CONNECT:
+ CHECK_EQ(OK, rv);
+ rv = DoResumeConnect();
+ break;
case STATE_CONNECT_COMPLETE:
rv = DoConnectComplete(rv);
break;
@@ -155,12 +287,19 @@ void QuicStreamFactory::Job::OnIOComplete(int rv) {
}
int QuicStreamFactory::Job::DoResolveHost() {
+ // Start loading the data now, and wait for it after we resolve the host.
+ if (server_info_) {
+ disk_cache_load_start_time_ = base::TimeTicks::Now();
+ server_info_->Start();
+ }
+
io_state_ = STATE_RESOLVE_HOST_COMPLETE;
return host_resolver_.Resolve(
- HostResolver::RequestInfo(host_port_proxy_pair_.first),
+ HostResolver::RequestInfo(server_id_.host_port_pair()),
DEFAULT_PRIORITY,
&address_list_,
- base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Unretained(this)),
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()),
net_log_);
}
@@ -168,11 +307,97 @@ int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
if (rv != OK)
return rv;
- DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
+ DCHECK(!factory_->HasActiveSession(server_id_));
+
+ // Inform the factory of this resolution, which will set up
+ // a session alias, if possible.
+ if (factory_->OnResolution(server_id_, address_list_)) {
+ return OK;
+ }
+
+ io_state_ = STATE_LOAD_SERVER_INFO;
+ return OK;
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfo() {
+ io_state_ = STATE_LOAD_SERVER_INFO_COMPLETE;
+
+ if (!server_info_)
+ return OK;
+
+ return server_info_->WaitForDataReady(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ weak_factory_.GetWeakPtr()));
+}
+
+int QuicStreamFactory::Job::DoLoadServerInfoComplete(int rv) {
+ if (server_info_) {
+ UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheReadTime",
+ base::TimeTicks::Now() - disk_cache_load_start_time_);
+ }
+
+ if (rv != OK) {
+ server_info_.reset();
+ }
+
io_state_ = STATE_CONNECT;
return OK;
}
+int QuicStreamFactory::Job::DoConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = factory_->CreateSession(server_id_, server_info_.Pass(),
+ address_list_, net_log_, &session_);
+ if (rv != OK) {
+ DCHECK(rv != ERR_IO_PENDING);
+ DCHECK(!session_);
+ return rv;
+ }
+
+ session_->StartReading();
+ if (!session_->connection()->connected()) {
+ return ERR_QUIC_PROTOCOL_ERROR;
+ }
+ bool require_confirmation =
+ factory_->require_confirmation() || is_post_ ||
+ was_alternate_protocol_recently_broken_;
+ rv = session_->CryptoConnect(
+ require_confirmation,
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoResumeConnect() {
+ io_state_ = STATE_CONNECT_COMPLETE;
+
+ int rv = session_->ResumeCryptoConnect(
+ base::Bind(&QuicStreamFactory::Job::OnIOComplete,
+ base::Unretained(this)));
+
+ return rv;
+}
+
+int QuicStreamFactory::Job::DoConnectComplete(int rv) {
+ if (rv != OK)
+ return rv;
+
+ DCHECK(!factory_->HasActiveSession(server_id_));
+ // There may well now be an active session for this IP. If so, use the
+ // existing session instead.
+ AddressList address(session_->connection()->peer_address());
+ if (factory_->OnResolution(server_id_, address)) {
+ session_->connection()->SendConnectionClose(QUIC_CONNECTION_IP_POOLED);
+ session_ = NULL;
+ return OK;
+ }
+
+ factory_->ActivateSession(server_id_, session_);
+
+ return OK;
+}
+
QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory)
: factory_(factory) {}
@@ -181,20 +406,20 @@ QuicStreamRequest::~QuicStreamRequest() {
factory_->CancelRequest(this);
}
-int QuicStreamRequest::Request(
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
- const BoundNetLog& net_log,
- const CompletionCallback& callback) {
+int QuicStreamRequest::Request(const HostPortPair& host_port_pair,
+ bool is_https,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
+ const BoundNetLog& net_log,
+ const CompletionCallback& callback) {
DCHECK(!stream_);
DCHECK(callback_.is_null());
- int rv = factory_->Create(host_port_proxy_pair, is_https, cert_verifier,
+ DCHECK(factory_);
+ int rv = factory_->Create(host_port_pair, is_https, privacy_mode, method,
net_log, this);
if (rv == ERR_IO_PENDING) {
- host_port_proxy_pair_ = host_port_proxy_pair;
+ host_port_pair_ = host_port_pair;
is_https_ = is_https;
- cert_verifier_ = cert_verifier;
net_log_ = net_log;
callback_ = callback;
} else {
@@ -220,103 +445,131 @@ scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() {
return stream_.Pass();
}
-int QuicStreamFactory::Job::DoConnect() {
- io_state_ = STATE_CONNECT_COMPLETE;
-
- int rv = factory_->CreateSession(host_port_proxy_pair_, is_https_,
- cert_verifier_, address_list_, net_log_, &session_);
- if (rv != OK) {
- DCHECK(rv != ERR_IO_PENDING);
- DCHECK(!session_);
- return rv;
- }
-
- session_->StartReading();
- rv = session_->CryptoConnect(
- factory_->require_confirmation() || is_https_,
- base::Bind(&QuicStreamFactory::Job::OnIOComplete,
- base::Unretained(this)));
- return rv;
-}
-
-int QuicStreamFactory::Job::DoConnectComplete(int rv) {
- if (rv != OK)
- return rv;
-
- DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
- factory_->ActivateSession(host_port_proxy_pair_, session_);
-
- return OK;
-}
-
QuicStreamFactory::QuicStreamFactory(
HostResolver* host_resolver,
ClientSocketFactory* client_socket_factory,
base::WeakPtr<HttpServerProperties> http_server_properties,
+ CertVerifier* cert_verifier,
QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
QuicRandom* random_generator,
QuicClock* clock,
- size_t max_packet_length)
+ size_t max_packet_length,
+ const std::string& user_agent_id,
+ const QuicVersionVector& supported_versions,
+ bool enable_port_selection,
+ bool enable_pacing,
+ bool enable_time_based_loss_detection)
: require_confirmation_(true),
host_resolver_(host_resolver),
client_socket_factory_(client_socket_factory),
http_server_properties_(http_server_properties),
+ cert_verifier_(cert_verifier),
+ quic_server_info_factory_(NULL),
quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory),
random_generator_(random_generator),
clock_(clock),
max_packet_length_(max_packet_length),
- weak_factory_(this),
- port_seed_(random_generator_->RandUint64()) {
- config_.SetDefaults();
- config_.set_idle_connection_state_lifetime(
- QuicTime::Delta::FromSeconds(30),
- QuicTime::Delta::FromSeconds(30));
-
- cannoncial_suffixes_.push_back(string(".c.youtube.com"));
- cannoncial_suffixes_.push_back(string(".googlevideo.com"));
+ config_(InitializeQuicConfig(enable_pacing,
+ enable_time_based_loss_detection)),
+ supported_versions_(supported_versions),
+ enable_port_selection_(enable_port_selection),
+ port_seed_(random_generator_->RandUint64()),
+ weak_factory_(this) {
+ crypto_config_.SetDefaults();
+ crypto_config_.set_user_agent_id(user_agent_id);
+ crypto_config_.AddCanonicalSuffix(".c.youtube.com");
+ crypto_config_.AddCanonicalSuffix(".googlevideo.com");
+ crypto_config_.SetProofVerifier(new ProofVerifierChromium(cert_verifier));
+ base::CPU cpu;
+ if (cpu.has_aesni() && cpu.has_avx())
+ crypto_config_.PreferAesGcm();
+ if (!IsEcdsaSupported())
+ crypto_config_.DisableEcdsa();
}
QuicStreamFactory::~QuicStreamFactory() {
CloseAllSessions(ERR_ABORTED);
- STLDeleteElements(&all_sessions_);
+ while (!all_sessions_.empty()) {
+ delete all_sessions_.begin()->first;
+ all_sessions_.erase(all_sessions_.begin());
+ }
STLDeleteValues(&active_jobs_);
- STLDeleteValues(&all_crypto_configs_);
}
-int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair,
+int QuicStreamFactory::Create(const HostPortPair& host_port_pair,
bool is_https,
- CertVerifier* cert_verifier,
+ PrivacyMode privacy_mode,
+ base::StringPiece method,
const BoundNetLog& net_log,
QuicStreamRequest* request) {
- if (HasActiveSession(host_port_proxy_pair)) {
- request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
+ QuicServerId server_id(host_port_pair, is_https, privacy_mode);
+ if (HasActiveSession(server_id)) {
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
return OK;
}
- if (HasActiveJob(host_port_proxy_pair)) {
- Job* job = active_jobs_[host_port_proxy_pair];
+ if (HasActiveJob(server_id)) {
+ Job* job = active_jobs_[server_id];
active_requests_[request] = job;
job_requests_map_[job].insert(request);
return ERR_IO_PENDING;
}
- scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_proxy_pair,
- is_https, cert_verifier, net_log));
+ QuicServerInfo* quic_server_info = NULL;
+ if (quic_server_info_factory_) {
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ DCHECK(cached);
+ if (cached->IsEmpty()) {
+ quic_server_info = quic_server_info_factory_->GetForServer(server_id);
+ }
+ }
+ 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,
+ was_alternate_protocol_recently_broken,
+ privacy_mode, method, quic_server_info, net_log));
int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
base::Unretained(this), job.get()));
if (rv == ERR_IO_PENDING) {
active_requests_[request] = job.get();
job_requests_map_[job.get()].insert(request);
- active_jobs_[host_port_proxy_pair] = job.release();
+ active_jobs_[server_id] = job.release();
}
if (rv == OK) {
- DCHECK(HasActiveSession(host_port_proxy_pair));
- request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
+ DCHECK(HasActiveSession(server_id));
+ request->set_stream(CreateIfSessionExists(server_id, net_log));
}
return rv;
}
+bool QuicStreamFactory::OnResolution(
+ const QuicServerId& server_id,
+ const AddressList& address_list) {
+ DCHECK(!HasActiveSession(server_id));
+ for (size_t i = 0; i < address_list.size(); ++i) {
+ const IPEndPoint& address = address_list[i];
+ const IpAliasKey ip_alias_key(address, server_id.is_https());
+ if (!ContainsKey(ip_aliases_, ip_alias_key))
+ continue;
+
+ const SessionSet& sessions = ip_aliases_[ip_alias_key];
+ for (SessionSet::const_iterator i = sessions.begin();
+ i != sessions.end(); ++i) {
+ QuicClientSession* session = *i;
+ if (!session->CanPool(server_id.host()))
+ continue;
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ return true;
+ }
+ }
+ return false;
+}
+
void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
if (rv == OK) {
require_confirmation_ = false;
@@ -324,8 +577,8 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// Create all the streams, but do not notify them yet.
for (RequestSet::iterator it = job_requests_map_[job].begin();
it != job_requests_map_[job].end() ; ++it) {
- DCHECK(HasActiveSession(job->host_port_proxy_pair()));
- (*it)->set_stream(CreateIfSessionExists(job->host_port_proxy_pair(),
+ DCHECK(HasActiveSession(job->server_id()));
+ (*it)->set_stream(CreateIfSessionExists(job->server_id(),
(*it)->net_log()));
}
}
@@ -339,7 +592,7 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// profile which can not be deleted via callbacks.
request->OnRequestComplete(rv);
}
- active_jobs_.erase(job->host_port_proxy_pair());
+ active_jobs_.erase(job->server_id());
job_requests_map_.erase(job);
delete job;
return;
@@ -348,14 +601,14 @@ void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
// Returns a newly created QuicHttpStream owned by the caller, if a
// matching session already exists. Returns NULL otherwise.
scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists(
- const HostPortProxyPair& host_port_proxy_pair,
+ const QuicServerId& server_id,
const BoundNetLog& net_log) {
- if (!HasActiveSession(host_port_proxy_pair)) {
+ if (!HasActiveSession(server_id)) {
DVLOG(1) << "No active session";
return scoped_ptr<QuicHttpStream>();
}
- QuicClientSession* session = active_sessions_[host_port_proxy_pair];
+ QuicClientSession* session = active_sessions_[server_id];
DCHECK(session);
return scoped_ptr<QuicHttpStream>(
new QuicHttpStream(session->GetWeakPtr()));
@@ -370,13 +623,22 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
++it) {
DCHECK(active_sessions_.count(*it));
DCHECK_EQ(session, active_sessions_[*it]);
+ // Track sessions which have recently gone away so that we can disable
+ // port suggestions.
+ if (session->goaway_received()) {
+ gone_away_aliases_.insert(*it);
+ }
+
active_sessions_.erase(*it);
- if (!session->IsCryptoHandshakeConfirmed() && http_server_properties_) {
- // TODO(rch): In the special case where the session has received no
- // packets from the peer, we should consider blacklisting this
- // differently so that we still race TCP but we don't consider the
- // session connected until the handshake has been confirmed.
- http_server_properties_->SetBrokenAlternateProtocol(it->first);
+ ProcessGoingAwaySession(session, *it, true);
+ }
+ ProcessGoingAwaySession(session, all_sessions_[session], false);
+ if (!aliases.empty()) {
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
}
}
session_aliases_.erase(session);
@@ -385,8 +647,37 @@ void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) {
DCHECK_EQ(0u, session->GetNumOpenStreams());
OnSessionGoingAway(session);
- all_sessions_.erase(session);
delete session;
+ all_sessions_.erase(session);
+}
+
+void QuicStreamFactory::OnSessionConnectTimeout(
+ QuicClientSession* session) {
+ const AliasSet& aliases = session_aliases_[session];
+ for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
+ ++it) {
+ DCHECK(active_sessions_.count(*it));
+ DCHECK_EQ(session, active_sessions_[*it]);
+ active_sessions_.erase(*it);
+ }
+
+ if (aliases.empty()) {
+ return;
+ }
+
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ aliases.begin()->is_https());
+ ip_aliases_[ip_alias_key].erase(session);
+ if (ip_aliases_[ip_alias_key].empty()) {
+ ip_aliases_.erase(ip_alias_key);
+ }
+ QuicServerId server_id = *aliases.begin();
+ session_aliases_.erase(session);
+ Job* job = new Job(this, host_resolver_, session, server_id);
+ active_jobs_[server_id] = job;
+ int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
+ base::Unretained(this), job));
+ DCHECK_EQ(ERR_IO_PENDING, rv);
}
void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
@@ -404,7 +695,7 @@ void QuicStreamFactory::CloseAllSessions(int error) {
}
while (!all_sessions_.empty()) {
size_t initial_size = all_sessions_.size();
- (*all_sessions_.begin())->CloseSessionOnError(error);
+ all_sessions_.begin()->first->CloseSessionOnError(error);
DCHECK_NE(initial_size, all_sessions_.size());
}
DCHECK(all_sessions_.empty());
@@ -415,14 +706,26 @@ base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
for (SessionMap::const_iterator it = active_sessions_.begin();
it != active_sessions_.end(); ++it) {
- const HostPortProxyPair& pair = it->first;
- const QuicClientSession* session = it->second;
-
- list->Append(session->GetInfoAsValue(pair.first));
+ const QuicServerId& server_id = it->first;
+ QuicClientSession* session = it->second;
+ const AliasSet& aliases = session_aliases_.find(session)->second;
+ // Only add a session to the list once.
+ if (server_id == *aliases.begin()) {
+ std::set<HostPortPair> hosts;
+ for (AliasSet::const_iterator alias_it = aliases.begin();
+ alias_it != aliases.end(); ++alias_it) {
+ hosts.insert(alias_it->host_port_pair());
+ }
+ list->Append(session->GetInfoAsValue(hosts));
+ }
}
return list;
}
+void QuicStreamFactory::ClearCachedStatesInCryptoConfig() {
+ crypto_config_.ClearCachedStates();
+}
+
void QuicStreamFactory::OnIPAddressChanged() {
CloseAllSessions(ERR_NETWORK_CHANGED);
require_confirmation_ = true;
@@ -446,54 +749,69 @@ void QuicStreamFactory::OnCACertChanged(const X509Certificate* cert) {
}
bool QuicStreamFactory::HasActiveSession(
- const HostPortProxyPair& host_port_proxy_pair) {
- return ContainsKey(active_sessions_, host_port_proxy_pair);
+ const QuicServerId& server_id) const {
+ return ContainsKey(active_sessions_, server_id);
}
int QuicStreamFactory::CreateSession(
- const HostPortProxyPair& host_port_proxy_pair,
- bool is_https,
- CertVerifier* cert_verifier,
+ const QuicServerId& server_id,
+ scoped_ptr<QuicServerInfo> server_info,
const AddressList& address_list,
const BoundNetLog& net_log,
QuicClientSession** session) {
- QuicGuid guid = random_generator_->RandUint64();
+ bool enable_port_selection = enable_port_selection_;
+ if (enable_port_selection &&
+ ContainsKey(gone_away_aliases_, server_id)) {
+ // Disable port selection when the server is going away.
+ // There is no point in trying to return to the same server, if
+ // that server is no longer handling requests.
+ enable_port_selection = false;
+ gone_away_aliases_.erase(server_id);
+ }
+
+ QuicConnectionId connection_id = random_generator_->RandUint64();
IPEndPoint addr = *address_list.begin();
scoped_refptr<PortSuggester> port_suggester =
- new PortSuggester(host_port_proxy_pair.first, port_seed_);
- DatagramSocket::BindType bind_type = DatagramSocket::RANDOM_BIND;
-#if defined(OS_WIN)
- // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
- bind_type = DatagramSocket::DEFAULT_BIND;
-#endif
+ new PortSuggester(server_id.host_port_pair(), port_seed_);
+ DatagramSocket::BindType bind_type = enable_port_selection ?
+ DatagramSocket::RANDOM_BIND : // Use our callback.
+ DatagramSocket::DEFAULT_BIND; // Use OS to randomize.
scoped_ptr<DatagramClientSocket> socket(
client_socket_factory_->CreateDatagramClientSocket(
bind_type,
base::Bind(&PortSuggester::SuggestPort, port_suggester),
net_log.net_log(), net_log.source()));
int rv = socket->Connect(addr);
- if (rv != OK)
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_CONNECTING_SOCKET);
return rv;
+ }
UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
port_suggester->call_count());
-
-#if defined(OS_WIN)
- // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
- DCHECK_EQ(0u, port_suggester->call_count());
-#else
- DCHECK_LE(1u, port_suggester->call_count());
-#endif
+ if (enable_port_selection) {
+ DCHECK_LE(1u, port_suggester->call_count());
+ } else {
+ DCHECK_EQ(0u, port_suggester->call_count());
+ }
// We should adaptively set this buffer size, but for now, we'll use a size
// that is more than large enough for a full receive window, and yet
// does not consume "too much" memory. If we see bursty packet loss, we may
// revisit this setting and test for its impact.
const int32 kSocketBufferSize(TcpReceiver::kReceiveWindowTCP);
- socket->SetReceiveBufferSize(kSocketBufferSize);
+ rv = socket->SetReceiveBufferSize(kSocketBufferSize);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_RECEIVE_BUFFER);
+ return rv;
+ }
// Set a buffer large enough to contain the initial CWND's worth of packet
// to work around the problem with CHLO packets being sent out with the
// wrong encryption level, when the send buffer is full.
- socket->SetSendBufferSize(kMaxPacketSize * 20); // Support 20 packets.
+ rv = socket->SetSendBufferSize(kMaxPacketSize * 20);
+ if (rv != OK) {
+ HistogramCreateSessionFailure(CREATION_ERROR_SETTING_SEND_BUFFER);
+ return rv;
+ }
scoped_ptr<QuicDefaultPacketWriter> writer(
new QuicDefaultPacketWriter(socket.get()));
@@ -504,96 +822,134 @@ int QuicStreamFactory::CreateSession(
clock_.get(), random_generator_));
}
- QuicConnection* connection = new QuicConnection(guid, addr, helper_.get(),
- writer.get(), false,
- QuicSupportedVersions());
+ QuicConnection* connection =
+ new QuicConnection(connection_id, addr, helper_.get(), writer.get(),
+ false, supported_versions_);
writer->SetConnection(connection);
- connection->options()->max_packet_length = max_packet_length_;
-
- QuicCryptoClientConfig* crypto_config =
- GetOrCreateCryptoConfig(host_port_proxy_pair);
- DCHECK(crypto_config);
+ connection->set_max_packet_length(max_packet_length_);
+
+ InitializeCachedStateInCryptoConfig(server_id, server_info);
+
+ QuicConfig config = config_;
+ config.SetInitialCongestionWindowToSend(
+ server_id.is_https() ? kServerSecureInitialCongestionWindow
+ : kServerInecureInitialCongestionWindow);
+ config.SetInitialFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialStreamFlowControlWindowToSend(kInitialReceiveWindowSize);
+ config.SetInitialSessionFlowControlWindowToSend(kInitialReceiveWindowSize);
+ if (http_server_properties_) {
+ const HttpServerProperties::NetworkStats* stats =
+ http_server_properties_->GetServerNetworkStats(
+ server_id.host_port_pair());
+ if (stats != NULL) {
+ config.SetInitialRoundTripTimeUsToSend(stats->srtt.InMicroseconds());
+ }
+ }
*session = new QuicClientSession(
connection, socket.Pass(), writer.Pass(), this,
- quic_crypto_client_stream_factory_, host_port_proxy_pair.first.host(),
- config_, crypto_config, net_log.net_log());
- all_sessions_.insert(*session); // owning pointer
- if (is_https) {
- crypto_config->SetProofVerifier(
- new ProofVerifierChromium(cert_verifier, net_log));
- }
+ quic_crypto_client_stream_factory_, server_info.Pass(), server_id,
+ config, &crypto_config_,
+ base::MessageLoop::current()->message_loop_proxy().get(),
+ net_log.net_log());
+ all_sessions_[*session] = server_id; // owning pointer
return OK;
}
-bool QuicStreamFactory::HasActiveJob(
- const HostPortProxyPair& host_port_proxy_pair) {
- return ContainsKey(active_jobs_, host_port_proxy_pair);
+bool QuicStreamFactory::HasActiveJob(const QuicServerId& key) const {
+ return ContainsKey(active_jobs_, key);
}
void QuicStreamFactory::ActivateSession(
- const HostPortProxyPair& host_port_proxy_pair,
+ const QuicServerId& server_id,
QuicClientSession* session) {
- DCHECK(!HasActiveSession(host_port_proxy_pair));
- active_sessions_[host_port_proxy_pair] = session;
- session_aliases_[session].insert(host_port_proxy_pair);
-}
+ DCHECK(!HasActiveSession(server_id));
+ UMA_HISTOGRAM_COUNTS("Net.QuicActiveSessions", active_sessions_.size());
+ active_sessions_[server_id] = session;
+ session_aliases_[session].insert(server_id);
+ const IpAliasKey ip_alias_key(session->connection()->peer_address(),
+ server_id.is_https());
+ DCHECK(!ContainsKey(ip_aliases_[ip_alias_key], session));
+ ip_aliases_[ip_alias_key].insert(session);
+}
+
+void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
+ const QuicServerId& server_id,
+ const scoped_ptr<QuicServerInfo>& server_info) {
+ if (!server_info)
+ return;
-QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig(
- const HostPortProxyPair& host_port_proxy_pair) {
- QuicCryptoClientConfig* crypto_config;
+ QuicCryptoClientConfig::CachedState* cached =
+ crypto_config_.LookupOrCreate(server_id);
+ if (!cached->IsEmpty())
+ return;
- if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) {
- crypto_config = all_crypto_configs_[host_port_proxy_pair];
- DCHECK(crypto_config);
- } else {
- // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
- // share the same crypto_config, will it cause issues?
- crypto_config = new QuicCryptoClientConfig();
- crypto_config->SetDefaults();
- all_crypto_configs_[host_port_proxy_pair] = crypto_config;
- PopulateFromCanonicalConfig(host_port_proxy_pair, crypto_config);
+ if (!cached->Initialize(server_info->state().server_config,
+ server_info->state().source_address_token,
+ server_info->state().certs,
+ server_info->state().server_config_sig,
+ clock_->WallNow()))
+ return;
+
+ if (!server_id.is_https()) {
+ // Don't check the certificates for insecure QUIC.
+ cached->SetProofValid();
}
- return crypto_config;
}
-void QuicStreamFactory::PopulateFromCanonicalConfig(
- const HostPortProxyPair& host_port_proxy_pair,
- QuicCryptoClientConfig* crypto_config) {
- const string server_hostname = host_port_proxy_pair.first.host();
-
- unsigned i = 0;
- for (; i < cannoncial_suffixes_.size(); ++i) {
- if (EndsWith(server_hostname, cannoncial_suffixes_[i], false)) {
- break;
- }
- }
- if (i == cannoncial_suffixes_.size())
+void QuicStreamFactory::ProcessGoingAwaySession(
+ QuicClientSession* session,
+ const QuicServerId& server_id,
+ bool session_was_active) {
+ if (!http_server_properties_)
return;
- HostPortPair canonical_host_port(cannoncial_suffixes_[i],
- host_port_proxy_pair.first.port());
- if (!ContainsKey(canonical_hostname_to_origin_map_, canonical_host_port)) {
- // This is the first host we've seen which matches the suffix, so make it
- // canonical.
- canonical_hostname_to_origin_map_[canonical_host_port] =
- host_port_proxy_pair;
+ const QuicConnectionStats& stats = session->connection()->GetStats();
+ if (session->IsCryptoHandshakeConfirmed()) {
+ HttpServerProperties::NetworkStats network_stats;
+ network_stats.srtt = base::TimeDelta::FromMicroseconds(stats.srtt_us);
+ network_stats.bandwidth_estimate = stats.estimated_bandwidth;
+ http_server_properties_->SetServerNetworkStats(server_id.host_port_pair(),
+ network_stats);
return;
}
- const HostPortProxyPair& canonical_host_port_proxy_pair =
- canonical_hostname_to_origin_map_[canonical_host_port];
- QuicCryptoClientConfig* canonical_crypto_config =
- all_crypto_configs_[canonical_host_port_proxy_pair];
- DCHECK(canonical_crypto_config);
+ UMA_HISTOGRAM_COUNTS("Net.QuicHandshakeNotConfirmedNumPacketsReceived",
+ stats.packets_received);
+
+ if (!session_was_active)
+ return;
+
+ const HostPortPair& server = server_id.host_port_pair();
+ // Don't try to change the alternate-protocol state, if the
+ // alternate-protocol state is unknown.
+ if (!http_server_properties_->HasAlternateProtocol(server))
+ return;
- // Copy the CachedState for the canonical server from canonical_crypto_config
- // as the initial CachedState for the server_hostname in crypto_config.
- crypto_config->InitializeFrom(server_hostname,
- canonical_host_port_proxy_pair.first.host(),
- canonical_crypto_config);
- // Update canonical version to point at the "most recent" crypto_config.
- canonical_hostname_to_origin_map_[canonical_host_port] = host_port_proxy_pair;
+ // TODO(rch): In the special case where the session has received no
+ // packets from the peer, we should consider blacklisting this
+ // differently so that we still race TCP but we don't consider the
+ // session connected until the handshake has been confirmed.
+ HistogramBrokenAlternateProtocolLocation(
+ BROKEN_ALTERNATE_PROTOCOL_LOCATION_QUIC_STREAM_FACTORY);
+ PortAlternateProtocolPair alternate =
+ http_server_properties_->GetAlternateProtocol(server);
+ DCHECK_EQ(QUIC, alternate.protocol);
+
+ // Since the session was active, there's no longer an
+ // HttpStreamFactoryImpl::Job running which can mark it broken, unless the
+ // TCP job also fails. So to avoid not using QUIC when we otherwise could,
+ // we mark it as broken, and then immediately re-enable it. This leaves
+ // QUIC as "recently broken" which means that 0-RTT will be disabled but
+ // we'll still race.
+ http_server_properties_->SetBrokenAlternateProtocol(server);
+ http_server_properties_->ClearAlternateProtocol(server);
+ http_server_properties_->SetAlternateProtocol(
+ server, alternate.port, alternate.protocol);
+ DCHECK_EQ(QUIC,
+ http_server_properties_->GetAlternateProtocol(server).protocol);
+ DCHECK(http_server_properties_->WasAlternateProtocolRecentlyBroken(
+ server));
}
} // namespace net