Home | History | Annotate | Download | only in quic
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/quic/quic_stream_factory.h"
      6 
      7 #include <set>
      8 
      9 #include "base/logging.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/message_loop/message_loop_proxy.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/rand_util.h"
     14 #include "base/stl_util.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/values.h"
     17 #include "net/base/net_errors.h"
     18 #include "net/cert/cert_verifier.h"
     19 #include "net/dns/host_resolver.h"
     20 #include "net/dns/single_request_host_resolver.h"
     21 #include "net/http/http_server_properties.h"
     22 #include "net/quic/congestion_control/tcp_receiver.h"
     23 #include "net/quic/crypto/proof_verifier_chromium.h"
     24 #include "net/quic/crypto/quic_random.h"
     25 #include "net/quic/port_suggester.h"
     26 #include "net/quic/quic_client_session.h"
     27 #include "net/quic/quic_clock.h"
     28 #include "net/quic/quic_connection.h"
     29 #include "net/quic/quic_connection_helper.h"
     30 #include "net/quic/quic_crypto_client_stream_factory.h"
     31 #include "net/quic/quic_default_packet_writer.h"
     32 #include "net/quic/quic_http_stream.h"
     33 #include "net/quic/quic_protocol.h"
     34 #include "net/socket/client_socket_factory.h"
     35 
     36 using std::string;
     37 using std::vector;
     38 
     39 namespace net {
     40 
     41 // Responsible for creating a new QUIC session to the specified server, and
     42 // for notifying any associated requests when complete.
     43 class QuicStreamFactory::Job {
     44  public:
     45   Job(QuicStreamFactory* factory,
     46       HostResolver* host_resolver,
     47       const HostPortProxyPair& host_port_proxy_pair,
     48       bool is_https,
     49       CertVerifier* cert_verifier,
     50       const BoundNetLog& net_log);
     51 
     52   ~Job();
     53 
     54   int Run(const CompletionCallback& callback);
     55 
     56   int DoLoop(int rv);
     57   int DoResolveHost();
     58   int DoResolveHostComplete(int rv);
     59   int DoConnect();
     60   int DoConnectComplete(int rv);
     61 
     62   void OnIOComplete(int rv);
     63 
     64   CompletionCallback callback() {
     65     return callback_;
     66   }
     67 
     68   const HostPortProxyPair& host_port_proxy_pair() const {
     69     return host_port_proxy_pair_;
     70   }
     71 
     72  private:
     73   enum IoState {
     74     STATE_NONE,
     75     STATE_RESOLVE_HOST,
     76     STATE_RESOLVE_HOST_COMPLETE,
     77     STATE_CONNECT,
     78     STATE_CONNECT_COMPLETE,
     79   };
     80   IoState io_state_;
     81 
     82   QuicStreamFactory* factory_;
     83   SingleRequestHostResolver host_resolver_;
     84   const HostPortProxyPair host_port_proxy_pair_;
     85   bool is_https_;
     86   CertVerifier* cert_verifier_;
     87   const BoundNetLog net_log_;
     88   QuicClientSession* session_;
     89   CompletionCallback callback_;
     90   AddressList address_list_;
     91   DISALLOW_COPY_AND_ASSIGN(Job);
     92 };
     93 
     94 QuicStreamFactory::Job::Job(
     95     QuicStreamFactory* factory,
     96     HostResolver* host_resolver,
     97     const HostPortProxyPair& host_port_proxy_pair,
     98     bool is_https,
     99     CertVerifier* cert_verifier,
    100     const BoundNetLog& net_log)
    101     : factory_(factory),
    102       host_resolver_(host_resolver),
    103       host_port_proxy_pair_(host_port_proxy_pair),
    104       is_https_(is_https),
    105       cert_verifier_(cert_verifier),
    106       net_log_(net_log),
    107       session_(NULL) {
    108 }
    109 
    110 QuicStreamFactory::Job::~Job() {
    111 }
    112 
    113 int QuicStreamFactory::Job::Run(const CompletionCallback& callback) {
    114   io_state_ = STATE_RESOLVE_HOST;
    115   int rv = DoLoop(OK);
    116   if (rv == ERR_IO_PENDING)
    117     callback_ = callback;
    118 
    119   return rv > 0 ? OK : rv;
    120 }
    121 
    122 int QuicStreamFactory::Job::DoLoop(int rv) {
    123   do {
    124     IoState state = io_state_;
    125     io_state_ = STATE_NONE;
    126     switch (state) {
    127       case STATE_RESOLVE_HOST:
    128         CHECK_EQ(OK, rv);
    129         rv = DoResolveHost();
    130         break;
    131       case STATE_RESOLVE_HOST_COMPLETE:
    132         rv = DoResolveHostComplete(rv);
    133         break;
    134       case STATE_CONNECT:
    135         CHECK_EQ(OK, rv);
    136         rv = DoConnect();
    137         break;
    138       case STATE_CONNECT_COMPLETE:
    139         rv = DoConnectComplete(rv);
    140         break;
    141       default:
    142         NOTREACHED() << "io_state_: " << io_state_;
    143         break;
    144     }
    145   } while (io_state_ != STATE_NONE && rv != ERR_IO_PENDING);
    146   return rv;
    147 }
    148 
    149 void QuicStreamFactory::Job::OnIOComplete(int rv) {
    150   rv = DoLoop(rv);
    151 
    152   if (rv != ERR_IO_PENDING && !callback_.is_null()) {
    153     callback_.Run(rv);
    154   }
    155 }
    156 
    157 int QuicStreamFactory::Job::DoResolveHost() {
    158   io_state_ = STATE_RESOLVE_HOST_COMPLETE;
    159   return host_resolver_.Resolve(
    160       HostResolver::RequestInfo(host_port_proxy_pair_.first),
    161       DEFAULT_PRIORITY,
    162       &address_list_,
    163       base::Bind(&QuicStreamFactory::Job::OnIOComplete, base::Unretained(this)),
    164       net_log_);
    165 }
    166 
    167 int QuicStreamFactory::Job::DoResolveHostComplete(int rv) {
    168   if (rv != OK)
    169     return rv;
    170 
    171   DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
    172   io_state_ = STATE_CONNECT;
    173   return OK;
    174 }
    175 
    176 QuicStreamRequest::QuicStreamRequest(QuicStreamFactory* factory)
    177     : factory_(factory) {}
    178 
    179 QuicStreamRequest::~QuicStreamRequest() {
    180   if (factory_ && !callback_.is_null())
    181     factory_->CancelRequest(this);
    182 }
    183 
    184 int QuicStreamRequest::Request(
    185     const HostPortProxyPair& host_port_proxy_pair,
    186     bool is_https,
    187     CertVerifier* cert_verifier,
    188     const BoundNetLog& net_log,
    189     const CompletionCallback& callback) {
    190   DCHECK(!stream_);
    191   DCHECK(callback_.is_null());
    192   int rv = factory_->Create(host_port_proxy_pair, is_https, cert_verifier,
    193                             net_log, this);
    194   if (rv == ERR_IO_PENDING) {
    195     host_port_proxy_pair_ = host_port_proxy_pair;
    196     is_https_ = is_https;
    197     cert_verifier_ = cert_verifier;
    198     net_log_ = net_log;
    199     callback_ = callback;
    200   } else {
    201     factory_ = NULL;
    202   }
    203   if (rv == OK)
    204     DCHECK(stream_);
    205   return rv;
    206 }
    207 
    208 void QuicStreamRequest::set_stream(scoped_ptr<QuicHttpStream> stream) {
    209   DCHECK(stream);
    210   stream_ = stream.Pass();
    211 }
    212 
    213 void QuicStreamRequest::OnRequestComplete(int rv) {
    214   factory_ = NULL;
    215   callback_.Run(rv);
    216 }
    217 
    218 scoped_ptr<QuicHttpStream> QuicStreamRequest::ReleaseStream() {
    219   DCHECK(stream_);
    220   return stream_.Pass();
    221 }
    222 
    223 int QuicStreamFactory::Job::DoConnect() {
    224   io_state_ = STATE_CONNECT_COMPLETE;
    225 
    226   int rv = factory_->CreateSession(host_port_proxy_pair_, is_https_,
    227       cert_verifier_, address_list_, net_log_, &session_);
    228   if (rv != OK) {
    229     DCHECK(rv != ERR_IO_PENDING);
    230     DCHECK(!session_);
    231     return rv;
    232   }
    233 
    234   session_->StartReading();
    235   rv = session_->CryptoConnect(
    236       factory_->require_confirmation() || is_https_,
    237       base::Bind(&QuicStreamFactory::Job::OnIOComplete,
    238                  base::Unretained(this)));
    239   return rv;
    240 }
    241 
    242 int QuicStreamFactory::Job::DoConnectComplete(int rv) {
    243   if (rv != OK)
    244     return rv;
    245 
    246   DCHECK(!factory_->HasActiveSession(host_port_proxy_pair_));
    247   factory_->ActivateSession(host_port_proxy_pair_, session_);
    248 
    249   return OK;
    250 }
    251 
    252 QuicStreamFactory::QuicStreamFactory(
    253     HostResolver* host_resolver,
    254     ClientSocketFactory* client_socket_factory,
    255     base::WeakPtr<HttpServerProperties> http_server_properties,
    256     QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory,
    257     QuicRandom* random_generator,
    258     QuicClock* clock,
    259     size_t max_packet_length)
    260     : require_confirmation_(true),
    261       host_resolver_(host_resolver),
    262       client_socket_factory_(client_socket_factory),
    263       http_server_properties_(http_server_properties),
    264       quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory),
    265       random_generator_(random_generator),
    266       clock_(clock),
    267       max_packet_length_(max_packet_length),
    268       weak_factory_(this),
    269       port_seed_(random_generator_->RandUint64()) {
    270   config_.SetDefaults();
    271   config_.set_idle_connection_state_lifetime(
    272       QuicTime::Delta::FromSeconds(30),
    273       QuicTime::Delta::FromSeconds(30));
    274 
    275   cannoncial_suffixes_.push_back(string(".c.youtube.com"));
    276   cannoncial_suffixes_.push_back(string(".googlevideo.com"));
    277 }
    278 
    279 QuicStreamFactory::~QuicStreamFactory() {
    280   CloseAllSessions(ERR_ABORTED);
    281   STLDeleteElements(&all_sessions_);
    282   STLDeleteValues(&active_jobs_);
    283   STLDeleteValues(&all_crypto_configs_);
    284 }
    285 
    286 int QuicStreamFactory::Create(const HostPortProxyPair& host_port_proxy_pair,
    287                               bool is_https,
    288                               CertVerifier* cert_verifier,
    289                               const BoundNetLog& net_log,
    290                               QuicStreamRequest* request) {
    291   if (HasActiveSession(host_port_proxy_pair)) {
    292     request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
    293     return OK;
    294   }
    295 
    296   if (HasActiveJob(host_port_proxy_pair)) {
    297     Job* job = active_jobs_[host_port_proxy_pair];
    298     active_requests_[request] = job;
    299     job_requests_map_[job].insert(request);
    300     return ERR_IO_PENDING;
    301   }
    302 
    303   scoped_ptr<Job> job(new Job(this, host_resolver_, host_port_proxy_pair,
    304                               is_https, cert_verifier, net_log));
    305   int rv = job->Run(base::Bind(&QuicStreamFactory::OnJobComplete,
    306                                base::Unretained(this), job.get()));
    307 
    308   if (rv == ERR_IO_PENDING) {
    309     active_requests_[request] = job.get();
    310     job_requests_map_[job.get()].insert(request);
    311     active_jobs_[host_port_proxy_pair] = job.release();
    312   }
    313   if (rv == OK) {
    314     DCHECK(HasActiveSession(host_port_proxy_pair));
    315     request->set_stream(CreateIfSessionExists(host_port_proxy_pair, net_log));
    316   }
    317   return rv;
    318 }
    319 
    320 void QuicStreamFactory::OnJobComplete(Job* job, int rv) {
    321   if (rv == OK) {
    322     require_confirmation_ = false;
    323 
    324     // Create all the streams, but do not notify them yet.
    325     for (RequestSet::iterator it = job_requests_map_[job].begin();
    326          it != job_requests_map_[job].end() ; ++it) {
    327       DCHECK(HasActiveSession(job->host_port_proxy_pair()));
    328       (*it)->set_stream(CreateIfSessionExists(job->host_port_proxy_pair(),
    329                                               (*it)->net_log()));
    330     }
    331   }
    332   while (!job_requests_map_[job].empty()) {
    333     RequestSet::iterator it = job_requests_map_[job].begin();
    334     QuicStreamRequest* request = *it;
    335     job_requests_map_[job].erase(it);
    336     active_requests_.erase(request);
    337     // Even though we're invoking callbacks here, we don't need to worry
    338     // about |this| being deleted, because the factory is owned by the
    339     // profile which can not be deleted via callbacks.
    340     request->OnRequestComplete(rv);
    341   }
    342   active_jobs_.erase(job->host_port_proxy_pair());
    343   job_requests_map_.erase(job);
    344   delete job;
    345   return;
    346 }
    347 
    348 // Returns a newly created QuicHttpStream owned by the caller, if a
    349 // matching session already exists.  Returns NULL otherwise.
    350 scoped_ptr<QuicHttpStream> QuicStreamFactory::CreateIfSessionExists(
    351     const HostPortProxyPair& host_port_proxy_pair,
    352     const BoundNetLog& net_log) {
    353   if (!HasActiveSession(host_port_proxy_pair)) {
    354     DVLOG(1) << "No active session";
    355     return scoped_ptr<QuicHttpStream>();
    356   }
    357 
    358   QuicClientSession* session = active_sessions_[host_port_proxy_pair];
    359   DCHECK(session);
    360   return scoped_ptr<QuicHttpStream>(
    361       new QuicHttpStream(session->GetWeakPtr()));
    362 }
    363 
    364 void QuicStreamFactory::OnIdleSession(QuicClientSession* session) {
    365 }
    366 
    367 void QuicStreamFactory::OnSessionGoingAway(QuicClientSession* session) {
    368   const AliasSet& aliases = session_aliases_[session];
    369   for (AliasSet::const_iterator it = aliases.begin(); it != aliases.end();
    370        ++it) {
    371     DCHECK(active_sessions_.count(*it));
    372     DCHECK_EQ(session, active_sessions_[*it]);
    373     active_sessions_.erase(*it);
    374     if (!session->IsCryptoHandshakeConfirmed() && http_server_properties_) {
    375       // TODO(rch):  In the special case where the session has received no
    376       // packets from the peer, we should consider blacklisting this
    377       // differently so that we still race TCP but we don't consider the
    378       // session connected until the handshake has been confirmed.
    379       http_server_properties_->SetBrokenAlternateProtocol(it->first);
    380     }
    381   }
    382   session_aliases_.erase(session);
    383 }
    384 
    385 void QuicStreamFactory::OnSessionClosed(QuicClientSession* session) {
    386   DCHECK_EQ(0u, session->GetNumOpenStreams());
    387   OnSessionGoingAway(session);
    388   all_sessions_.erase(session);
    389   delete session;
    390 }
    391 
    392 void QuicStreamFactory::CancelRequest(QuicStreamRequest* request) {
    393   DCHECK(ContainsKey(active_requests_, request));
    394   Job* job = active_requests_[request];
    395   job_requests_map_[job].erase(request);
    396   active_requests_.erase(request);
    397 }
    398 
    399 void QuicStreamFactory::CloseAllSessions(int error) {
    400   while (!active_sessions_.empty()) {
    401     size_t initial_size = active_sessions_.size();
    402     active_sessions_.begin()->second->CloseSessionOnError(error);
    403     DCHECK_NE(initial_size, active_sessions_.size());
    404   }
    405   while (!all_sessions_.empty()) {
    406     size_t initial_size = all_sessions_.size();
    407     (*all_sessions_.begin())->CloseSessionOnError(error);
    408     DCHECK_NE(initial_size, all_sessions_.size());
    409   }
    410   DCHECK(all_sessions_.empty());
    411 }
    412 
    413 base::Value* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
    414   base::ListValue* list = new base::ListValue();
    415 
    416   for (SessionMap::const_iterator it = active_sessions_.begin();
    417        it != active_sessions_.end(); ++it) {
    418     const HostPortProxyPair& pair = it->first;
    419     const QuicClientSession* session = it->second;
    420 
    421     list->Append(session->GetInfoAsValue(pair.first));
    422   }
    423   return list;
    424 }
    425 
    426 void QuicStreamFactory::OnIPAddressChanged() {
    427   CloseAllSessions(ERR_NETWORK_CHANGED);
    428   require_confirmation_ = true;
    429 }
    430 
    431 void QuicStreamFactory::OnCertAdded(const X509Certificate* cert) {
    432   CloseAllSessions(ERR_CERT_DATABASE_CHANGED);
    433 }
    434 
    435 void QuicStreamFactory::OnCACertChanged(const X509Certificate* cert) {
    436   // We should flush the sessions if we removed trust from a
    437   // cert, because a previously trusted server may have become
    438   // untrusted.
    439   //
    440   // We should not flush the sessions if we added trust to a cert.
    441   //
    442   // Since the OnCACertChanged method doesn't tell us what
    443   // kind of change it is, we have to flush the socket
    444   // pools to be safe.
    445   CloseAllSessions(ERR_CERT_DATABASE_CHANGED);
    446 }
    447 
    448 bool QuicStreamFactory::HasActiveSession(
    449     const HostPortProxyPair& host_port_proxy_pair) {
    450   return ContainsKey(active_sessions_, host_port_proxy_pair);
    451 }
    452 
    453 int QuicStreamFactory::CreateSession(
    454     const HostPortProxyPair& host_port_proxy_pair,
    455     bool is_https,
    456     CertVerifier* cert_verifier,
    457     const AddressList& address_list,
    458     const BoundNetLog& net_log,
    459     QuicClientSession** session) {
    460   QuicGuid guid = random_generator_->RandUint64();
    461   IPEndPoint addr = *address_list.begin();
    462   scoped_refptr<PortSuggester> port_suggester =
    463       new PortSuggester(host_port_proxy_pair.first, port_seed_);
    464   DatagramSocket::BindType bind_type = DatagramSocket::RANDOM_BIND;
    465 #if defined(OS_WIN)
    466   // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
    467   bind_type = DatagramSocket::DEFAULT_BIND;
    468 #endif
    469   scoped_ptr<DatagramClientSocket> socket(
    470       client_socket_factory_->CreateDatagramClientSocket(
    471           bind_type,
    472           base::Bind(&PortSuggester::SuggestPort, port_suggester),
    473           net_log.net_log(), net_log.source()));
    474   int rv = socket->Connect(addr);
    475   if (rv != OK)
    476     return rv;
    477   UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
    478                        port_suggester->call_count());
    479 
    480 #if defined(OS_WIN)
    481   // TODO(jar)bug=329255 Provide better implementation to avoid pop-up warning.
    482   DCHECK_EQ(0u, port_suggester->call_count());
    483 #else
    484   DCHECK_LE(1u, port_suggester->call_count());
    485 #endif
    486 
    487   // We should adaptively set this buffer size, but for now, we'll use a size
    488   // that is more than large enough for a full receive window, and yet
    489   // does not consume "too much" memory.  If we see bursty packet loss, we may
    490   // revisit this setting and test for its impact.
    491   const int32 kSocketBufferSize(TcpReceiver::kReceiveWindowTCP);
    492   socket->SetReceiveBufferSize(kSocketBufferSize);
    493   // Set a buffer large enough to contain the initial CWND's worth of packet
    494   // to work around the problem with CHLO packets being sent out with the
    495   // wrong encryption level, when the send buffer is full.
    496   socket->SetSendBufferSize(kMaxPacketSize * 20); // Support 20 packets.
    497 
    498   scoped_ptr<QuicDefaultPacketWriter> writer(
    499       new QuicDefaultPacketWriter(socket.get()));
    500 
    501   if (!helper_.get()) {
    502     helper_.reset(new QuicConnectionHelper(
    503         base::MessageLoop::current()->message_loop_proxy().get(),
    504         clock_.get(), random_generator_));
    505   }
    506 
    507   QuicConnection* connection = new QuicConnection(guid, addr, helper_.get(),
    508                                                   writer.get(), false,
    509                                                   QuicSupportedVersions());
    510   writer->SetConnection(connection);
    511   connection->options()->max_packet_length = max_packet_length_;
    512 
    513   QuicCryptoClientConfig* crypto_config =
    514       GetOrCreateCryptoConfig(host_port_proxy_pair);
    515   DCHECK(crypto_config);
    516 
    517   *session = new QuicClientSession(
    518       connection, socket.Pass(), writer.Pass(), this,
    519       quic_crypto_client_stream_factory_, host_port_proxy_pair.first.host(),
    520       config_, crypto_config, net_log.net_log());
    521   all_sessions_.insert(*session);  // owning pointer
    522   if (is_https) {
    523     crypto_config->SetProofVerifier(
    524         new ProofVerifierChromium(cert_verifier, net_log));
    525   }
    526   return OK;
    527 }
    528 
    529 bool QuicStreamFactory::HasActiveJob(
    530     const HostPortProxyPair& host_port_proxy_pair) {
    531   return ContainsKey(active_jobs_, host_port_proxy_pair);
    532 }
    533 
    534 void QuicStreamFactory::ActivateSession(
    535     const HostPortProxyPair& host_port_proxy_pair,
    536     QuicClientSession* session) {
    537   DCHECK(!HasActiveSession(host_port_proxy_pair));
    538   active_sessions_[host_port_proxy_pair] = session;
    539   session_aliases_[session].insert(host_port_proxy_pair);
    540 }
    541 
    542 QuicCryptoClientConfig* QuicStreamFactory::GetOrCreateCryptoConfig(
    543     const HostPortProxyPair& host_port_proxy_pair) {
    544   QuicCryptoClientConfig* crypto_config;
    545 
    546   if (ContainsKey(all_crypto_configs_, host_port_proxy_pair)) {
    547     crypto_config = all_crypto_configs_[host_port_proxy_pair];
    548     DCHECK(crypto_config);
    549   } else {
    550     // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
    551     // share the same crypto_config, will it cause issues?
    552     crypto_config = new QuicCryptoClientConfig();
    553     crypto_config->SetDefaults();
    554     all_crypto_configs_[host_port_proxy_pair] = crypto_config;
    555     PopulateFromCanonicalConfig(host_port_proxy_pair, crypto_config);
    556   }
    557   return crypto_config;
    558 }
    559 
    560 void QuicStreamFactory::PopulateFromCanonicalConfig(
    561     const HostPortProxyPair& host_port_proxy_pair,
    562     QuicCryptoClientConfig* crypto_config) {
    563   const string server_hostname = host_port_proxy_pair.first.host();
    564 
    565   unsigned i = 0;
    566   for (; i < cannoncial_suffixes_.size(); ++i) {
    567     if (EndsWith(server_hostname, cannoncial_suffixes_[i], false)) {
    568       break;
    569     }
    570   }
    571   if (i == cannoncial_suffixes_.size())
    572     return;
    573 
    574   HostPortPair canonical_host_port(cannoncial_suffixes_[i],
    575                                    host_port_proxy_pair.first.port());
    576   if (!ContainsKey(canonical_hostname_to_origin_map_, canonical_host_port)) {
    577     // This is the first host we've seen which matches the suffix, so make it
    578     // canonical.
    579     canonical_hostname_to_origin_map_[canonical_host_port] =
    580         host_port_proxy_pair;
    581     return;
    582   }
    583 
    584   const HostPortProxyPair& canonical_host_port_proxy_pair =
    585       canonical_hostname_to_origin_map_[canonical_host_port];
    586   QuicCryptoClientConfig* canonical_crypto_config =
    587       all_crypto_configs_[canonical_host_port_proxy_pair];
    588   DCHECK(canonical_crypto_config);
    589 
    590   // Copy the CachedState for the canonical server from canonical_crypto_config
    591   // as the initial CachedState for the server_hostname in crypto_config.
    592   crypto_config->InitializeFrom(server_hostname,
    593                                 canonical_host_port_proxy_pair.first.host(),
    594                                 canonical_crypto_config);
    595   // Update canonical version to point at the "most recent" crypto_config.
    596   canonical_hostname_to_origin_map_[canonical_host_port] = host_port_proxy_pair;
    597 }
    598 
    599 }  // namespace net
    600