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
)
286 : require_confirmation_(true),
287 host_resolver_(host_resolver
),
288 client_socket_factory_(client_socket_factory
),
289 http_server_properties_(http_server_properties
),
290 quic_server_info_factory_(quic_server_info_factory
),
291 quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory
),
292 random_generator_(random_generator
),
294 max_packet_length_(max_packet_length
),
295 supported_versions_(supported_versions
),
296 enable_port_selection_(enable_port_selection
),
297 port_seed_(random_generator_
->RandUint64()),
298 weak_factory_(this) {
299 config_
.SetDefaults();
300 config_
.set_idle_connection_state_lifetime(
301 QuicTime::Delta::FromSeconds(30),
302 QuicTime::Delta::FromSeconds(30));
304 canoncial_suffixes_
.push_back(string(".c.youtube.com"));
305 canoncial_suffixes_
.push_back(string(".googlevideo.com"));
308 QuicStreamFactory::~QuicStreamFactory() {
309 CloseAllSessions(ERR_ABORTED
);
310 STLDeleteElements(&all_sessions_
);
311 STLDeleteValues(&active_jobs_
);
312 STLDeleteValues(&all_crypto_configs_
);
315 int QuicStreamFactory::Create(const HostPortProxyPair
& host_port_proxy_pair
,
317 base::StringPiece method
,
318 CertVerifier
* cert_verifier
,
319 const BoundNetLog
& net_log
,
320 QuicStreamRequest
* request
) {
321 if (HasActiveSession(host_port_proxy_pair
)) {
322 request
->set_stream(CreateIfSessionExists(host_port_proxy_pair
, net_log
));
326 if (HasActiveJob(host_port_proxy_pair
)) {
327 Job
* job
= active_jobs_
[host_port_proxy_pair
];
328 active_requests_
[request
] = job
;
329 job_requests_map_
[job
].insert(request
);
330 return ERR_IO_PENDING
;
333 // Create crypto config and start the process of loading QUIC server
334 // information from disk cache.
335 QuicCryptoClientConfig
* crypto_config
=
336 GetOrCreateCryptoConfig(host_port_proxy_pair
);
337 DCHECK(crypto_config
);
339 scoped_ptr
<Job
> job(new Job(this, host_resolver_
, host_port_proxy_pair
,
340 is_https
, method
, cert_verifier
, net_log
));
341 int rv
= job
->Run(base::Bind(&QuicStreamFactory::OnJobComplete
,
342 base::Unretained(this), job
.get()));
344 if (rv
== ERR_IO_PENDING
) {
345 active_requests_
[request
] = job
.get();
346 job_requests_map_
[job
.get()].insert(request
);
347 active_jobs_
[host_port_proxy_pair
] = job
.release();
350 DCHECK(HasActiveSession(host_port_proxy_pair
));
351 request
->set_stream(CreateIfSessionExists(host_port_proxy_pair
, net_log
));
356 bool QuicStreamFactory::OnResolution(
357 const HostPortProxyPair
& host_port_proxy_pair
,
358 const AddressList
& address_list
) {
359 DCHECK(!HasActiveSession(host_port_proxy_pair
));
360 for (size_t i
= 0; i
< address_list
.size(); ++i
) {
361 const IPEndPoint
& address
= address_list
[i
];
362 if (!ContainsKey(ip_aliases_
, address
))
365 const SessionSet
& sessions
= ip_aliases_
[address
];
366 for (SessionSet::const_iterator i
= sessions
.begin();
367 i
!= sessions
.end(); ++i
) {
368 QuicClientSession
* session
= *i
;
369 if (!session
->CanPool(host_port_proxy_pair
.first
.host()))
371 active_sessions_
[host_port_proxy_pair
] = session
;
372 session_aliases_
[session
].insert(host_port_proxy_pair
);
379 void QuicStreamFactory::OnJobComplete(Job
* job
, int rv
) {
381 require_confirmation_
= false;
383 // Create all the streams, but do not notify them yet.
384 for (RequestSet::iterator it
= job_requests_map_
[job
].begin();
385 it
!= job_requests_map_
[job
].end() ; ++it
) {
386 DCHECK(HasActiveSession(job
->host_port_proxy_pair()));
387 (*it
)->set_stream(CreateIfSessionExists(job
->host_port_proxy_pair(),
391 while (!job_requests_map_
[job
].empty()) {
392 RequestSet::iterator it
= job_requests_map_
[job
].begin();
393 QuicStreamRequest
* request
= *it
;
394 job_requests_map_
[job
].erase(it
);
395 active_requests_
.erase(request
);
396 // Even though we're invoking callbacks here, we don't need to worry
397 // about |this| being deleted, because the factory is owned by the
398 // profile which can not be deleted via callbacks.
399 request
->OnRequestComplete(rv
);
401 active_jobs_
.erase(job
->host_port_proxy_pair());
402 job_requests_map_
.erase(job
);
407 // Returns a newly created QuicHttpStream owned by the caller, if a
408 // matching session already exists. Returns NULL otherwise.
409 scoped_ptr
<QuicHttpStream
> QuicStreamFactory::CreateIfSessionExists(
410 const HostPortProxyPair
& host_port_proxy_pair
,
411 const BoundNetLog
& net_log
) {
412 if (!HasActiveSession(host_port_proxy_pair
)) {
413 DVLOG(1) << "No active session";
414 return scoped_ptr
<QuicHttpStream
>();
417 QuicClientSession
* session
= active_sessions_
[host_port_proxy_pair
];
419 return scoped_ptr
<QuicHttpStream
>(
420 new QuicHttpStream(session
->GetWeakPtr()));
423 void QuicStreamFactory::OnIdleSession(QuicClientSession
* session
) {
426 void QuicStreamFactory::OnSessionGoingAway(QuicClientSession
* session
) {
427 const AliasSet
& aliases
= session_aliases_
[session
];
428 for (AliasSet::const_iterator it
= aliases
.begin(); it
!= aliases
.end();
430 DCHECK(active_sessions_
.count(*it
));
431 DCHECK_EQ(session
, active_sessions_
[*it
]);
432 active_sessions_
.erase(*it
);
433 if (!http_server_properties_
)
436 if (!session
->IsCryptoHandshakeConfirmed()) {
437 // TODO(rch): In the special case where the session has received no
438 // packets from the peer, we should consider blacklisting this
439 // differently so that we still race TCP but we don't consider the
440 // session connected until the handshake has been confirmed.
441 http_server_properties_
->SetBrokenAlternateProtocol(it
->first
);
443 QuicConnectionStats stats
= session
->connection()->GetStats();
444 HttpServerProperties::NetworkStats network_stats
;
445 network_stats
.rtt
= base::TimeDelta::FromMicroseconds(stats
.rtt
);
446 network_stats
.bandwidth_estimate
= stats
.estimated_bandwidth
;
447 http_server_properties_
->SetServerNetworkStats(
448 it
->first
, network_stats
);
451 IPEndPoint peer_address
= session
->connection()->peer_address();
452 ip_aliases_
[peer_address
].erase(session
);
453 if (ip_aliases_
[peer_address
].empty()) {
454 ip_aliases_
.erase(peer_address
);
456 session_aliases_
.erase(session
);
459 void QuicStreamFactory::OnSessionClosed(QuicClientSession
* session
) {
460 DCHECK_EQ(0u, session
->GetNumOpenStreams());
461 OnSessionGoingAway(session
);
462 all_sessions_
.erase(session
);
466 void QuicStreamFactory::CancelRequest(QuicStreamRequest
* request
) {
467 DCHECK(ContainsKey(active_requests_
, request
));
468 Job
* job
= active_requests_
[request
];
469 job_requests_map_
[job
].erase(request
);
470 active_requests_
.erase(request
);
473 void QuicStreamFactory::CloseAllSessions(int error
) {
474 while (!active_sessions_
.empty()) {
475 size_t initial_size
= active_sessions_
.size();
476 active_sessions_
.begin()->second
->CloseSessionOnError(error
);
477 DCHECK_NE(initial_size
, active_sessions_
.size());
479 while (!all_sessions_
.empty()) {
480 size_t initial_size
= all_sessions_
.size();
481 (*all_sessions_
.begin())->CloseSessionOnError(error
);
482 DCHECK_NE(initial_size
, all_sessions_
.size());
484 DCHECK(all_sessions_
.empty());
487 base::Value
* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
488 base::ListValue
* list
= new base::ListValue();
490 for (SessionMap::const_iterator it
= active_sessions_
.begin();
491 it
!= active_sessions_
.end(); ++it
) {
492 const HostPortProxyPair
& pair
= it
->first
;
493 QuicClientSession
* session
= it
->second
;
494 const AliasSet
& aliases
= session_aliases_
.find(session
)->second
;
495 if (pair
.first
.Equals(aliases
.begin()->first
) &&
496 pair
.second
== aliases
.begin()->second
) {
497 list
->Append(session
->GetInfoAsValue(aliases
));
503 void QuicStreamFactory::OnIPAddressChanged() {
504 CloseAllSessions(ERR_NETWORK_CHANGED
);
505 require_confirmation_
= true;
508 void QuicStreamFactory::OnCertAdded(const X509Certificate
* cert
) {
509 CloseAllSessions(ERR_CERT_DATABASE_CHANGED
);
512 void QuicStreamFactory::OnCACertChanged(const X509Certificate
* cert
) {
513 // We should flush the sessions if we removed trust from a
514 // cert, because a previously trusted server may have become
517 // We should not flush the sessions if we added trust to a cert.
519 // Since the OnCACertChanged method doesn't tell us what
520 // kind of change it is, we have to flush the socket
522 CloseAllSessions(ERR_CERT_DATABASE_CHANGED
);
525 bool QuicStreamFactory::HasActiveSession(
526 const HostPortProxyPair
& host_port_proxy_pair
) {
527 return ContainsKey(active_sessions_
, host_port_proxy_pair
);
530 int QuicStreamFactory::CreateSession(
531 const HostPortProxyPair
& host_port_proxy_pair
,
533 CertVerifier
* cert_verifier
,
534 const AddressList
& address_list
,
535 const BoundNetLog
& net_log
,
536 QuicClientSession
** session
) {
537 QuicGuid guid
= random_generator_
->RandUint64();
538 IPEndPoint addr
= *address_list
.begin();
539 scoped_refptr
<PortSuggester
> port_suggester
=
540 new PortSuggester(host_port_proxy_pair
.first
, port_seed_
);
541 DatagramSocket::BindType bind_type
= enable_port_selection_
?
542 DatagramSocket::RANDOM_BIND
: // Use our callback.
543 DatagramSocket::DEFAULT_BIND
; // Use OS to randomize.
544 scoped_ptr
<DatagramClientSocket
> socket(
545 client_socket_factory_
->CreateDatagramClientSocket(
547 base::Bind(&PortSuggester::SuggestPort
, port_suggester
),
548 net_log
.net_log(), net_log
.source()));
549 int rv
= socket
->Connect(addr
);
552 UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
553 port_suggester
->call_count());
554 if (enable_port_selection_
) {
555 DCHECK_LE(1u, port_suggester
->call_count());
557 DCHECK_EQ(0u, port_suggester
->call_count());
560 // We should adaptively set this buffer size, but for now, we'll use a size
561 // that is more than large enough for a full receive window, and yet
562 // does not consume "too much" memory. If we see bursty packet loss, we may
563 // revisit this setting and test for its impact.
564 const int32
kSocketBufferSize(TcpReceiver::kReceiveWindowTCP
);
565 socket
->SetReceiveBufferSize(kSocketBufferSize
);
566 // Set a buffer large enough to contain the initial CWND's worth of packet
567 // to work around the problem with CHLO packets being sent out with the
568 // wrong encryption level, when the send buffer is full.
569 socket
->SetSendBufferSize(kMaxPacketSize
* 20); // Support 20 packets.
571 scoped_ptr
<QuicDefaultPacketWriter
> writer(
572 new QuicDefaultPacketWriter(socket
.get()));
574 if (!helper_
.get()) {
575 helper_
.reset(new QuicConnectionHelper(
576 base::MessageLoop::current()->message_loop_proxy().get(),
577 clock_
.get(), random_generator_
));
580 QuicConnection
* connection
= new QuicConnection(guid
, addr
, helper_
.get(),
582 supported_versions_
);
583 writer
->SetConnection(connection
);
584 connection
->options()->max_packet_length
= max_packet_length_
;
586 QuicCryptoClientConfig
* crypto_config
=
587 GetOrCreateCryptoConfig(host_port_proxy_pair
);
588 DCHECK(crypto_config
);
590 QuicConfig config
= config_
;
591 if (http_server_properties_
) {
592 const HttpServerProperties::NetworkStats
* stats
=
593 http_server_properties_
->GetServerNetworkStats(
594 host_port_proxy_pair
.first
);
596 config
.set_initial_round_trip_time_us(stats
->rtt
.InMicroseconds(),
597 stats
->rtt
.InMicroseconds());
601 *session
= new QuicClientSession(
602 connection
, socket
.Pass(), writer
.Pass(), this,
603 quic_crypto_client_stream_factory_
, host_port_proxy_pair
.first
.host(),
604 config
, crypto_config
, net_log
.net_log());
605 all_sessions_
.insert(*session
); // owning pointer
607 crypto_config
->SetProofVerifier(
608 new ProofVerifierChromium(cert_verifier
, net_log
));
613 bool QuicStreamFactory::HasActiveJob(
614 const HostPortProxyPair
& host_port_proxy_pair
) {
615 return ContainsKey(active_jobs_
, host_port_proxy_pair
);
618 void QuicStreamFactory::ActivateSession(
619 const HostPortProxyPair
& host_port_proxy_pair
,
620 QuicClientSession
* session
) {
621 DCHECK(!HasActiveSession(host_port_proxy_pair
));
622 active_sessions_
[host_port_proxy_pair
] = session
;
623 session_aliases_
[session
].insert(host_port_proxy_pair
);
624 DCHECK(!ContainsKey(ip_aliases_
[session
->connection()->peer_address()],
626 ip_aliases_
[session
->connection()->peer_address()].insert(session
);
629 QuicCryptoClientConfig
* QuicStreamFactory::GetOrCreateCryptoConfig(
630 const HostPortProxyPair
& host_port_proxy_pair
) {
631 QuicCryptoClientConfig
* crypto_config
;
633 if (ContainsKey(all_crypto_configs_
, host_port_proxy_pair
)) {
634 crypto_config
= all_crypto_configs_
[host_port_proxy_pair
];
635 DCHECK(crypto_config
);
637 // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
638 // share the same crypto_config, will it cause issues?
639 crypto_config
= new QuicCryptoClientConfig();
640 if (quic_server_info_factory_
) {
641 QuicCryptoClientConfig::CachedState
* cached
=
642 crypto_config
->Create(host_port_proxy_pair
.first
.host(),
643 quic_server_info_factory_
);
646 crypto_config
->SetDefaults();
647 all_crypto_configs_
[host_port_proxy_pair
] = crypto_config
;
648 PopulateFromCanonicalConfig(host_port_proxy_pair
, crypto_config
);
650 return crypto_config
;
653 void QuicStreamFactory::PopulateFromCanonicalConfig(
654 const HostPortProxyPair
& host_port_proxy_pair
,
655 QuicCryptoClientConfig
* crypto_config
) {
656 const string server_hostname
= host_port_proxy_pair
.first
.host();
659 for (; i
< canoncial_suffixes_
.size(); ++i
) {
660 if (EndsWith(server_hostname
, canoncial_suffixes_
[i
], false)) {
664 if (i
== canoncial_suffixes_
.size())
667 HostPortPair
canonical_host_port(canoncial_suffixes_
[i
],
668 host_port_proxy_pair
.first
.port());
669 if (!ContainsKey(canonical_hostname_to_origin_map_
, canonical_host_port
)) {
670 // This is the first host we've seen which matches the suffix, so make it
672 canonical_hostname_to_origin_map_
[canonical_host_port
] =
673 host_port_proxy_pair
;
677 const HostPortProxyPair
& canonical_host_port_proxy_pair
=
678 canonical_hostname_to_origin_map_
[canonical_host_port
];
679 QuicCryptoClientConfig
* canonical_crypto_config
=
680 all_crypto_configs_
[canonical_host_port_proxy_pair
];
681 DCHECK(canonical_crypto_config
);
683 // Copy the CachedState for the canonical server from canonical_crypto_config
684 // as the initial CachedState for the server_hostname in crypto_config.
685 crypto_config
->InitializeFrom(server_hostname
,
686 canonical_host_port_proxy_pair
.first
.host(),
687 canonical_crypto_config
);
688 // Update canonical version to point at the "most recent" crypto_config.
689 canonical_hostname_to_origin_map_
[canonical_host_port
] = host_port_proxy_pair
;