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.
5 #include "net/quic/quic_stream_factory.h"
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/crypto/quic_server_info.h"
26 #include "net/quic/port_suggester.h"
27 #include "net/quic/quic_client_session.h"
28 #include "net/quic/quic_clock.h"
29 #include "net/quic/quic_connection.h"
30 #include "net/quic/quic_connection_helper.h"
31 #include "net/quic/quic_crypto_client_stream_factory.h"
32 #include "net/quic/quic_default_packet_writer.h"
33 #include "net/quic/quic_http_stream.h"
34 #include "net/quic/quic_protocol.h"
35 #include "net/socket/client_socket_factory.h"
42 // Responsible for creating a new QUIC session to the specified server, and
43 // for notifying any associated requests when complete.
44 class QuicStreamFactory::Job
{
46 Job(QuicStreamFactory
* factory
,
47 HostResolver
* host_resolver
,
48 const HostPortProxyPair
& host_port_proxy_pair
,
50 base::StringPiece method
,
51 CertVerifier
* cert_verifier
,
52 const BoundNetLog
& net_log
);
56 int Run(const CompletionCallback
& callback
);
60 int DoResolveHostComplete(int rv
);
62 int DoConnectComplete(int rv
);
64 void OnIOComplete(int rv
);
66 CompletionCallback
callback() {
70 const HostPortProxyPair
& host_port_proxy_pair() const {
71 return host_port_proxy_pair_
;
78 STATE_RESOLVE_HOST_COMPLETE
,
80 STATE_CONNECT_COMPLETE
,
84 QuicStreamFactory
* factory_
;
85 SingleRequestHostResolver host_resolver_
;
86 const HostPortProxyPair host_port_proxy_pair_
;
89 CertVerifier
* cert_verifier_
;
90 const BoundNetLog net_log_
;
91 QuicClientSession
* session_
;
92 CompletionCallback callback_
;
93 AddressList address_list_
;
94 DISALLOW_COPY_AND_ASSIGN(Job
);
97 QuicStreamFactory::Job::Job(QuicStreamFactory
* factory
,
98 HostResolver
* host_resolver
,
99 const HostPortProxyPair
& host_port_proxy_pair
,
101 base::StringPiece method
,
102 CertVerifier
* cert_verifier
,
103 const BoundNetLog
& net_log
)
105 host_resolver_(host_resolver
),
106 host_port_proxy_pair_(host_port_proxy_pair
),
108 is_post_(method
== "POST"),
109 cert_verifier_(cert_verifier
),
113 QuicStreamFactory::Job::~Job() {
116 int QuicStreamFactory::Job::Run(const CompletionCallback
& callback
) {
117 io_state_
= STATE_RESOLVE_HOST
;
119 if (rv
== ERR_IO_PENDING
)
120 callback_
= callback
;
122 return rv
> 0 ? OK
: rv
;
125 int QuicStreamFactory::Job::DoLoop(int rv
) {
127 IoState state
= io_state_
;
128 io_state_
= STATE_NONE
;
130 case STATE_RESOLVE_HOST
:
132 rv
= DoResolveHost();
134 case STATE_RESOLVE_HOST_COMPLETE
:
135 rv
= DoResolveHostComplete(rv
);
141 case STATE_CONNECT_COMPLETE
:
142 rv
= DoConnectComplete(rv
);
145 NOTREACHED() << "io_state_: " << io_state_
;
148 } while (io_state_
!= STATE_NONE
&& rv
!= ERR_IO_PENDING
);
152 void QuicStreamFactory::Job::OnIOComplete(int rv
) {
155 if (rv
!= ERR_IO_PENDING
&& !callback_
.is_null()) {
160 int QuicStreamFactory::Job::DoResolveHost() {
161 io_state_
= STATE_RESOLVE_HOST_COMPLETE
;
162 return host_resolver_
.Resolve(
163 HostResolver::RequestInfo(host_port_proxy_pair_
.first
),
166 base::Bind(&QuicStreamFactory::Job::OnIOComplete
, base::Unretained(this)),
170 int QuicStreamFactory::Job::DoResolveHostComplete(int rv
) {
174 DCHECK(!factory_
->HasActiveSession(host_port_proxy_pair_
));
176 // Inform the factory of this resolution, which will set up
177 // a session alias, if possible.
178 if (factory_
->OnResolution(host_port_proxy_pair_
, address_list_
)) {
182 io_state_
= STATE_CONNECT
;
186 QuicStreamRequest::QuicStreamRequest(QuicStreamFactory
* factory
)
187 : factory_(factory
) {}
189 QuicStreamRequest::~QuicStreamRequest() {
190 if (factory_
&& !callback_
.is_null())
191 factory_
->CancelRequest(this);
194 int QuicStreamRequest::Request(const HostPortProxyPair
& host_port_proxy_pair
,
196 base::StringPiece method
,
197 CertVerifier
* cert_verifier
,
198 const BoundNetLog
& net_log
,
199 const CompletionCallback
& callback
) {
201 DCHECK(callback_
.is_null());
203 int rv
= factory_
->Create(
204 host_port_proxy_pair
, is_https
, method
, cert_verifier
, net_log
, this);
205 if (rv
== ERR_IO_PENDING
) {
206 host_port_proxy_pair_
= host_port_proxy_pair
;
207 is_https_
= is_https
;
208 cert_verifier_
= cert_verifier
;
210 callback_
= callback
;
219 void QuicStreamRequest::set_stream(scoped_ptr
<QuicHttpStream
> stream
) {
221 stream_
= stream
.Pass();
224 void QuicStreamRequest::OnRequestComplete(int rv
) {
229 scoped_ptr
<QuicHttpStream
> QuicStreamRequest::ReleaseStream() {
231 return stream_
.Pass();
234 int QuicStreamFactory::Job::DoConnect() {
235 io_state_
= STATE_CONNECT_COMPLETE
;
237 int rv
= factory_
->CreateSession(host_port_proxy_pair_
, is_https_
,
238 cert_verifier_
, address_list_
, net_log_
, &session_
);
240 DCHECK(rv
!= ERR_IO_PENDING
);
245 session_
->StartReading();
246 if (!session_
->connection()->connected()) {
247 return ERR_QUIC_PROTOCOL_ERROR
;
249 rv
= session_
->CryptoConnect(
250 factory_
->require_confirmation() || is_https_
,
251 base::Bind(&QuicStreamFactory::Job::OnIOComplete
,
252 base::Unretained(this)));
256 int QuicStreamFactory::Job::DoConnectComplete(int rv
) {
260 DCHECK(!factory_
->HasActiveSession(host_port_proxy_pair_
));
261 // There may well now be an active session for this IP. If so, use the
262 // existing session instead.
263 AddressList
address(session_
->connection()->peer_address());
264 if (factory_
->OnResolution(host_port_proxy_pair_
, address
)) {
265 session_
->connection()->SendConnectionClose(QUIC_NO_ERROR
);
270 factory_
->ActivateSession(host_port_proxy_pair_
, session_
);
275 QuicStreamFactory::QuicStreamFactory(
276 HostResolver
* host_resolver
,
277 ClientSocketFactory
* client_socket_factory
,
278 base::WeakPtr
<HttpServerProperties
> http_server_properties
,
279 QuicServerInfoFactory
* quic_server_info_factory
,
280 QuicCryptoClientStreamFactory
* quic_crypto_client_stream_factory
,
281 QuicRandom
* random_generator
,
283 size_t max_packet_length
,
284 const QuicVersionVector
& supported_versions
,
285 bool enable_port_selection
,
287 : require_confirmation_(true),
288 host_resolver_(host_resolver
),
289 client_socket_factory_(client_socket_factory
),
290 http_server_properties_(http_server_properties
),
291 quic_server_info_factory_(quic_server_info_factory
),
292 quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory
),
293 random_generator_(random_generator
),
295 max_packet_length_(max_packet_length
),
296 supported_versions_(supported_versions
),
297 enable_port_selection_(enable_port_selection
),
298 enable_pacing_(enable_pacing
),
299 port_seed_(random_generator_
->RandUint64()),
300 weak_factory_(this) {
301 config_
.SetDefaults();
302 config_
.EnablePacing(enable_pacing_
);
303 config_
.set_idle_connection_state_lifetime(
304 QuicTime::Delta::FromSeconds(30),
305 QuicTime::Delta::FromSeconds(30));
307 canoncial_suffixes_
.push_back(string(".c.youtube.com"));
308 canoncial_suffixes_
.push_back(string(".googlevideo.com"));
311 QuicStreamFactory::~QuicStreamFactory() {
312 CloseAllSessions(ERR_ABORTED
);
313 STLDeleteElements(&all_sessions_
);
314 STLDeleteValues(&active_jobs_
);
315 STLDeleteValues(&all_crypto_configs_
);
318 int QuicStreamFactory::Create(const HostPortProxyPair
& host_port_proxy_pair
,
320 base::StringPiece method
,
321 CertVerifier
* cert_verifier
,
322 const BoundNetLog
& net_log
,
323 QuicStreamRequest
* request
) {
324 if (HasActiveSession(host_port_proxy_pair
)) {
325 request
->set_stream(CreateIfSessionExists(host_port_proxy_pair
, net_log
));
329 if (HasActiveJob(host_port_proxy_pair
)) {
330 Job
* job
= active_jobs_
[host_port_proxy_pair
];
331 active_requests_
[request
] = job
;
332 job_requests_map_
[job
].insert(request
);
333 return ERR_IO_PENDING
;
336 // Create crypto config and start the process of loading QUIC server
337 // information from disk cache.
338 QuicCryptoClientConfig
* crypto_config
=
339 GetOrCreateCryptoConfig(host_port_proxy_pair
);
340 DCHECK(crypto_config
);
342 scoped_ptr
<Job
> job(new Job(this, host_resolver_
, host_port_proxy_pair
,
343 is_https
, method
, cert_verifier
, net_log
));
344 int rv
= job
->Run(base::Bind(&QuicStreamFactory::OnJobComplete
,
345 base::Unretained(this), job
.get()));
347 if (rv
== ERR_IO_PENDING
) {
348 active_requests_
[request
] = job
.get();
349 job_requests_map_
[job
.get()].insert(request
);
350 active_jobs_
[host_port_proxy_pair
] = job
.release();
353 DCHECK(HasActiveSession(host_port_proxy_pair
));
354 request
->set_stream(CreateIfSessionExists(host_port_proxy_pair
, net_log
));
359 bool QuicStreamFactory::OnResolution(
360 const HostPortProxyPair
& host_port_proxy_pair
,
361 const AddressList
& address_list
) {
362 DCHECK(!HasActiveSession(host_port_proxy_pair
));
363 for (size_t i
= 0; i
< address_list
.size(); ++i
) {
364 const IPEndPoint
& address
= address_list
[i
];
365 if (!ContainsKey(ip_aliases_
, address
))
368 const SessionSet
& sessions
= ip_aliases_
[address
];
369 for (SessionSet::const_iterator i
= sessions
.begin();
370 i
!= sessions
.end(); ++i
) {
371 QuicClientSession
* session
= *i
;
372 if (!session
->CanPool(host_port_proxy_pair
.first
.host()))
374 active_sessions_
[host_port_proxy_pair
] = session
;
375 session_aliases_
[session
].insert(host_port_proxy_pair
);
382 void QuicStreamFactory::OnJobComplete(Job
* job
, int rv
) {
384 require_confirmation_
= false;
386 // Create all the streams, but do not notify them yet.
387 for (RequestSet::iterator it
= job_requests_map_
[job
].begin();
388 it
!= job_requests_map_
[job
].end() ; ++it
) {
389 DCHECK(HasActiveSession(job
->host_port_proxy_pair()));
390 (*it
)->set_stream(CreateIfSessionExists(job
->host_port_proxy_pair(),
394 while (!job_requests_map_
[job
].empty()) {
395 RequestSet::iterator it
= job_requests_map_
[job
].begin();
396 QuicStreamRequest
* request
= *it
;
397 job_requests_map_
[job
].erase(it
);
398 active_requests_
.erase(request
);
399 // Even though we're invoking callbacks here, we don't need to worry
400 // about |this| being deleted, because the factory is owned by the
401 // profile which can not be deleted via callbacks.
402 request
->OnRequestComplete(rv
);
404 active_jobs_
.erase(job
->host_port_proxy_pair());
405 job_requests_map_
.erase(job
);
410 // Returns a newly created QuicHttpStream owned by the caller, if a
411 // matching session already exists. Returns NULL otherwise.
412 scoped_ptr
<QuicHttpStream
> QuicStreamFactory::CreateIfSessionExists(
413 const HostPortProxyPair
& host_port_proxy_pair
,
414 const BoundNetLog
& net_log
) {
415 if (!HasActiveSession(host_port_proxy_pair
)) {
416 DVLOG(1) << "No active session";
417 return scoped_ptr
<QuicHttpStream
>();
420 QuicClientSession
* session
= active_sessions_
[host_port_proxy_pair
];
422 return scoped_ptr
<QuicHttpStream
>(
423 new QuicHttpStream(session
->GetWeakPtr()));
426 void QuicStreamFactory::OnIdleSession(QuicClientSession
* session
) {
429 void QuicStreamFactory::OnSessionGoingAway(QuicClientSession
* session
) {
430 const AliasSet
& aliases
= session_aliases_
[session
];
431 for (AliasSet::const_iterator it
= aliases
.begin(); it
!= aliases
.end();
433 DCHECK(active_sessions_
.count(*it
));
434 DCHECK_EQ(session
, active_sessions_
[*it
]);
435 active_sessions_
.erase(*it
);
436 if (!http_server_properties_
)
439 if (!session
->IsCryptoHandshakeConfirmed()) {
440 // TODO(rch): In the special case where the session has received no
441 // packets from the peer, we should consider blacklisting this
442 // differently so that we still race TCP but we don't consider the
443 // session connected until the handshake has been confirmed.
444 http_server_properties_
->SetBrokenAlternateProtocol(it
->first
);
446 QuicConnectionStats stats
= session
->connection()->GetStats();
447 HttpServerProperties::NetworkStats network_stats
;
448 network_stats
.rtt
= base::TimeDelta::FromMicroseconds(stats
.rtt
);
449 network_stats
.bandwidth_estimate
= stats
.estimated_bandwidth
;
450 http_server_properties_
->SetServerNetworkStats(
451 it
->first
, network_stats
);
454 IPEndPoint peer_address
= session
->connection()->peer_address();
455 ip_aliases_
[peer_address
].erase(session
);
456 if (ip_aliases_
[peer_address
].empty()) {
457 ip_aliases_
.erase(peer_address
);
459 session_aliases_
.erase(session
);
462 void QuicStreamFactory::OnSessionClosed(QuicClientSession
* session
) {
463 DCHECK_EQ(0u, session
->GetNumOpenStreams());
464 OnSessionGoingAway(session
);
465 all_sessions_
.erase(session
);
469 void QuicStreamFactory::CancelRequest(QuicStreamRequest
* request
) {
470 DCHECK(ContainsKey(active_requests_
, request
));
471 Job
* job
= active_requests_
[request
];
472 job_requests_map_
[job
].erase(request
);
473 active_requests_
.erase(request
);
476 void QuicStreamFactory::CloseAllSessions(int error
) {
477 while (!active_sessions_
.empty()) {
478 size_t initial_size
= active_sessions_
.size();
479 active_sessions_
.begin()->second
->CloseSessionOnError(error
);
480 DCHECK_NE(initial_size
, active_sessions_
.size());
482 while (!all_sessions_
.empty()) {
483 size_t initial_size
= all_sessions_
.size();
484 (*all_sessions_
.begin())->CloseSessionOnError(error
);
485 DCHECK_NE(initial_size
, all_sessions_
.size());
487 DCHECK(all_sessions_
.empty());
490 base::Value
* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
491 base::ListValue
* list
= new base::ListValue();
493 for (SessionMap::const_iterator it
= active_sessions_
.begin();
494 it
!= active_sessions_
.end(); ++it
) {
495 const HostPortProxyPair
& pair
= it
->first
;
496 QuicClientSession
* session
= it
->second
;
497 const AliasSet
& aliases
= session_aliases_
.find(session
)->second
;
498 if (pair
.first
.Equals(aliases
.begin()->first
) &&
499 pair
.second
== aliases
.begin()->second
) {
500 list
->Append(session
->GetInfoAsValue(aliases
));
506 void QuicStreamFactory::OnIPAddressChanged() {
507 CloseAllSessions(ERR_NETWORK_CHANGED
);
508 require_confirmation_
= true;
511 void QuicStreamFactory::OnCertAdded(const X509Certificate
* cert
) {
512 CloseAllSessions(ERR_CERT_DATABASE_CHANGED
);
515 void QuicStreamFactory::OnCACertChanged(const X509Certificate
* cert
) {
516 // We should flush the sessions if we removed trust from a
517 // cert, because a previously trusted server may have become
520 // We should not flush the sessions if we added trust to a cert.
522 // Since the OnCACertChanged method doesn't tell us what
523 // kind of change it is, we have to flush the socket
525 CloseAllSessions(ERR_CERT_DATABASE_CHANGED
);
528 bool QuicStreamFactory::HasActiveSession(
529 const HostPortProxyPair
& host_port_proxy_pair
) {
530 return ContainsKey(active_sessions_
, host_port_proxy_pair
);
533 int QuicStreamFactory::CreateSession(
534 const HostPortProxyPair
& host_port_proxy_pair
,
536 CertVerifier
* cert_verifier
,
537 const AddressList
& address_list
,
538 const BoundNetLog
& net_log
,
539 QuicClientSession
** session
) {
540 QuicConnectionId connection_id
= random_generator_
->RandUint64();
541 IPEndPoint addr
= *address_list
.begin();
542 scoped_refptr
<PortSuggester
> port_suggester
=
543 new PortSuggester(host_port_proxy_pair
.first
, port_seed_
);
544 DatagramSocket::BindType bind_type
= enable_port_selection_
?
545 DatagramSocket::RANDOM_BIND
: // Use our callback.
546 DatagramSocket::DEFAULT_BIND
; // Use OS to randomize.
547 scoped_ptr
<DatagramClientSocket
> socket(
548 client_socket_factory_
->CreateDatagramClientSocket(
550 base::Bind(&PortSuggester::SuggestPort
, port_suggester
),
551 net_log
.net_log(), net_log
.source()));
552 int rv
= socket
->Connect(addr
);
555 UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
556 port_suggester
->call_count());
557 if (enable_port_selection_
) {
558 DCHECK_LE(1u, port_suggester
->call_count());
560 DCHECK_EQ(0u, port_suggester
->call_count());
563 // We should adaptively set this buffer size, but for now, we'll use a size
564 // that is more than large enough for a full receive window, and yet
565 // does not consume "too much" memory. If we see bursty packet loss, we may
566 // revisit this setting and test for its impact.
567 const int32
kSocketBufferSize(TcpReceiver::kReceiveWindowTCP
);
568 socket
->SetReceiveBufferSize(kSocketBufferSize
);
569 // Set a buffer large enough to contain the initial CWND's worth of packet
570 // to work around the problem with CHLO packets being sent out with the
571 // wrong encryption level, when the send buffer is full.
572 socket
->SetSendBufferSize(kMaxPacketSize
* 20); // Support 20 packets.
574 scoped_ptr
<QuicDefaultPacketWriter
> writer(
575 new QuicDefaultPacketWriter(socket
.get()));
577 if (!helper_
.get()) {
578 helper_
.reset(new QuicConnectionHelper(
579 base::MessageLoop::current()->message_loop_proxy().get(),
580 clock_
.get(), random_generator_
));
583 QuicConnection
* connection
= new QuicConnection(connection_id
, addr
,
586 supported_versions_
);
587 writer
->SetConnection(connection
);
588 connection
->options()->max_packet_length
= max_packet_length_
;
590 QuicCryptoClientConfig
* crypto_config
=
591 GetOrCreateCryptoConfig(host_port_proxy_pair
);
592 DCHECK(crypto_config
);
594 QuicConfig config
= config_
;
595 if (http_server_properties_
) {
596 const HttpServerProperties::NetworkStats
* stats
=
597 http_server_properties_
->GetServerNetworkStats(
598 host_port_proxy_pair
.first
);
600 config
.set_initial_round_trip_time_us(stats
->rtt
.InMicroseconds(),
601 stats
->rtt
.InMicroseconds());
605 *session
= new QuicClientSession(
606 connection
, socket
.Pass(), writer
.Pass(), this,
607 quic_crypto_client_stream_factory_
, host_port_proxy_pair
.first
.host(),
608 config
, crypto_config
, net_log
.net_log());
609 all_sessions_
.insert(*session
); // owning pointer
611 crypto_config
->SetProofVerifier(
612 new ProofVerifierChromium(cert_verifier
, net_log
));
617 bool QuicStreamFactory::HasActiveJob(
618 const HostPortProxyPair
& host_port_proxy_pair
) {
619 return ContainsKey(active_jobs_
, host_port_proxy_pair
);
622 void QuicStreamFactory::ActivateSession(
623 const HostPortProxyPair
& host_port_proxy_pair
,
624 QuicClientSession
* session
) {
625 DCHECK(!HasActiveSession(host_port_proxy_pair
));
626 active_sessions_
[host_port_proxy_pair
] = session
;
627 session_aliases_
[session
].insert(host_port_proxy_pair
);
628 DCHECK(!ContainsKey(ip_aliases_
[session
->connection()->peer_address()],
630 ip_aliases_
[session
->connection()->peer_address()].insert(session
);
633 QuicCryptoClientConfig
* QuicStreamFactory::GetOrCreateCryptoConfig(
634 const HostPortProxyPair
& host_port_proxy_pair
) {
635 QuicCryptoClientConfig
* crypto_config
;
637 if (ContainsKey(all_crypto_configs_
, host_port_proxy_pair
)) {
638 crypto_config
= all_crypto_configs_
[host_port_proxy_pair
];
639 DCHECK(crypto_config
);
641 // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
642 // share the same crypto_config, will it cause issues?
643 crypto_config
= new QuicCryptoClientConfig();
644 if (quic_server_info_factory_
) {
645 QuicCryptoClientConfig::CachedState
* cached
=
646 crypto_config
->Create(host_port_proxy_pair
.first
.host(),
647 quic_server_info_factory_
);
650 crypto_config
->SetDefaults();
651 all_crypto_configs_
[host_port_proxy_pair
] = crypto_config
;
652 PopulateFromCanonicalConfig(host_port_proxy_pair
, crypto_config
);
654 return crypto_config
;
657 void QuicStreamFactory::PopulateFromCanonicalConfig(
658 const HostPortProxyPair
& host_port_proxy_pair
,
659 QuicCryptoClientConfig
* crypto_config
) {
660 const string server_hostname
= host_port_proxy_pair
.first
.host();
663 for (; i
< canoncial_suffixes_
.size(); ++i
) {
664 if (EndsWith(server_hostname
, canoncial_suffixes_
[i
], false)) {
668 if (i
== canoncial_suffixes_
.size())
671 HostPortPair
canonical_host_port(canoncial_suffixes_
[i
],
672 host_port_proxy_pair
.first
.port());
673 if (!ContainsKey(canonical_hostname_to_origin_map_
, canonical_host_port
)) {
674 // This is the first host we've seen which matches the suffix, so make it
676 canonical_hostname_to_origin_map_
[canonical_host_port
] =
677 host_port_proxy_pair
;
681 const HostPortProxyPair
& canonical_host_port_proxy_pair
=
682 canonical_hostname_to_origin_map_
[canonical_host_port
];
683 QuicCryptoClientConfig
* canonical_crypto_config
=
684 all_crypto_configs_
[canonical_host_port_proxy_pair
];
685 DCHECK(canonical_crypto_config
);
687 // Copy the CachedState for the canonical server from canonical_crypto_config
688 // as the initial CachedState for the server_hostname in crypto_config.
689 crypto_config
->InitializeFrom(server_hostname
,
690 canonical_host_port_proxy_pair
.first
.host(),
691 canonical_crypto_config
);
692 // Update canonical version to point at the "most recent" crypto_config.
693 canonical_hostname_to_origin_map_
[canonical_host_port
] = host_port_proxy_pair
;