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/message_loop/message_loop.h"
10 #include "base/message_loop/message_loop_proxy.h"
11 #include "base/metrics/histogram.h"
12 #include "base/rand_util.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_util.h"
15 #include "base/values.h"
16 #include "net/base/net_errors.h"
17 #include "net/cert/cert_verifier.h"
18 #include "net/dns/host_resolver.h"
19 #include "net/dns/single_request_host_resolver.h"
20 #include "net/http/http_server_properties.h"
21 #include "net/quic/congestion_control/tcp_receiver.h"
22 #include "net/quic/crypto/proof_verifier_chromium.h"
23 #include "net/quic/crypto/quic_random.h"
24 #include "net/quic/crypto/quic_server_info.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"
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
{
45 Job(QuicStreamFactory
* factory
,
46 HostResolver
* host_resolver
,
47 const HostPortProxyPair
& host_port_proxy_pair
,
49 base::StringPiece method
,
50 CertVerifier
* cert_verifier
,
51 const BoundNetLog
& net_log
);
55 int Run(const CompletionCallback
& callback
);
59 int DoResolveHostComplete(int rv
);
61 int DoConnectComplete(int rv
);
63 void OnIOComplete(int rv
);
65 CompletionCallback
callback() {
69 const HostPortProxyPair
& host_port_proxy_pair() const {
70 return host_port_proxy_pair_
;
77 STATE_RESOLVE_HOST_COMPLETE
,
79 STATE_CONNECT_COMPLETE
,
83 QuicStreamFactory
* factory_
;
84 SingleRequestHostResolver host_resolver_
;
85 const HostPortProxyPair host_port_proxy_pair_
;
88 CertVerifier
* cert_verifier_
;
89 const BoundNetLog net_log_
;
90 QuicClientSession
* session_
;
91 CompletionCallback callback_
;
92 AddressList address_list_
;
93 DISALLOW_COPY_AND_ASSIGN(Job
);
96 QuicStreamFactory::Job::Job(QuicStreamFactory
* factory
,
97 HostResolver
* host_resolver
,
98 const HostPortProxyPair
& host_port_proxy_pair
,
100 base::StringPiece method
,
101 CertVerifier
* cert_verifier
,
102 const BoundNetLog
& net_log
)
104 host_resolver_(host_resolver
),
105 host_port_proxy_pair_(host_port_proxy_pair
),
107 is_post_(method
== "POST"),
108 cert_verifier_(cert_verifier
),
112 QuicStreamFactory::Job::~Job() {
115 int QuicStreamFactory::Job::Run(const CompletionCallback
& callback
) {
116 io_state_
= STATE_RESOLVE_HOST
;
118 if (rv
== ERR_IO_PENDING
)
119 callback_
= callback
;
121 return rv
> 0 ? OK
: rv
;
124 int QuicStreamFactory::Job::DoLoop(int rv
) {
126 IoState state
= io_state_
;
127 io_state_
= STATE_NONE
;
129 case STATE_RESOLVE_HOST
:
131 rv
= DoResolveHost();
133 case STATE_RESOLVE_HOST_COMPLETE
:
134 rv
= DoResolveHostComplete(rv
);
140 case STATE_CONNECT_COMPLETE
:
141 rv
= DoConnectComplete(rv
);
144 NOTREACHED() << "io_state_: " << io_state_
;
147 } while (io_state_
!= STATE_NONE
&& rv
!= ERR_IO_PENDING
);
151 void QuicStreamFactory::Job::OnIOComplete(int rv
) {
154 if (rv
!= ERR_IO_PENDING
&& !callback_
.is_null()) {
159 int QuicStreamFactory::Job::DoResolveHost() {
160 io_state_
= STATE_RESOLVE_HOST_COMPLETE
;
161 return host_resolver_
.Resolve(
162 HostResolver::RequestInfo(host_port_proxy_pair_
.first
),
165 base::Bind(&QuicStreamFactory::Job::OnIOComplete
, base::Unretained(this)),
169 int QuicStreamFactory::Job::DoResolveHostComplete(int rv
) {
173 DCHECK(!factory_
->HasActiveSession(host_port_proxy_pair_
));
175 // Inform the factory of this resolution, which will set up
176 // a session alias, if possible.
177 if (factory_
->OnResolution(host_port_proxy_pair_
, address_list_
)) {
181 io_state_
= STATE_CONNECT
;
185 QuicStreamRequest::QuicStreamRequest(QuicStreamFactory
* factory
)
186 : factory_(factory
) {}
188 QuicStreamRequest::~QuicStreamRequest() {
189 if (factory_
&& !callback_
.is_null())
190 factory_
->CancelRequest(this);
193 int QuicStreamRequest::Request(const HostPortProxyPair
& host_port_proxy_pair
,
195 base::StringPiece method
,
196 CertVerifier
* cert_verifier
,
197 const BoundNetLog
& net_log
,
198 const CompletionCallback
& callback
) {
200 DCHECK(callback_
.is_null());
202 int rv
= factory_
->Create(
203 host_port_proxy_pair
, is_https
, method
, cert_verifier
, net_log
, this);
204 if (rv
== ERR_IO_PENDING
) {
205 host_port_proxy_pair_
= host_port_proxy_pair
;
206 is_https_
= is_https
;
207 cert_verifier_
= cert_verifier
;
209 callback_
= callback
;
218 void QuicStreamRequest::set_stream(scoped_ptr
<QuicHttpStream
> stream
) {
220 stream_
= stream
.Pass();
223 void QuicStreamRequest::OnRequestComplete(int rv
) {
228 scoped_ptr
<QuicHttpStream
> QuicStreamRequest::ReleaseStream() {
230 return stream_
.Pass();
233 int QuicStreamFactory::Job::DoConnect() {
234 io_state_
= STATE_CONNECT_COMPLETE
;
236 int rv
= factory_
->CreateSession(host_port_proxy_pair_
, is_https_
,
237 cert_verifier_
, address_list_
, net_log_
, &session_
);
239 DCHECK(rv
!= ERR_IO_PENDING
);
244 session_
->StartReading();
245 if (!session_
->connection()->connected()) {
246 return ERR_QUIC_PROTOCOL_ERROR
;
248 rv
= session_
->CryptoConnect(
249 factory_
->require_confirmation() || is_https_
,
250 base::Bind(&QuicStreamFactory::Job::OnIOComplete
,
251 base::Unretained(this)));
255 int QuicStreamFactory::Job::DoConnectComplete(int rv
) {
259 DCHECK(!factory_
->HasActiveSession(host_port_proxy_pair_
));
260 // There may well now be an active session for this IP. If so, use the
261 // existing session instead.
262 AddressList
address(session_
->connection()->peer_address());
263 if (factory_
->OnResolution(host_port_proxy_pair_
, address
)) {
264 session_
->connection()->SendConnectionClose(QUIC_NO_ERROR
);
269 factory_
->ActivateSession(host_port_proxy_pair_
, session_
);
274 QuicStreamFactory::QuicStreamFactory(
275 HostResolver
* host_resolver
,
276 ClientSocketFactory
* client_socket_factory
,
277 base::WeakPtr
<HttpServerProperties
> http_server_properties
,
278 QuicCryptoClientStreamFactory
* quic_crypto_client_stream_factory
,
279 QuicRandom
* random_generator
,
281 size_t max_packet_length
,
282 const QuicVersionVector
& supported_versions
,
283 bool enable_port_selection
,
285 : require_confirmation_(true),
286 host_resolver_(host_resolver
),
287 client_socket_factory_(client_socket_factory
),
288 http_server_properties_(http_server_properties
),
289 quic_server_info_factory_(NULL
),
290 quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory
),
291 random_generator_(random_generator
),
293 max_packet_length_(max_packet_length
),
294 supported_versions_(supported_versions
),
295 enable_port_selection_(enable_port_selection
),
296 enable_pacing_(enable_pacing
),
297 port_seed_(random_generator_
->RandUint64()),
298 weak_factory_(this) {
299 config_
.SetDefaults();
300 config_
.EnablePacing(enable_pacing_
);
301 config_
.set_idle_connection_state_lifetime(
302 QuicTime::Delta::FromSeconds(30),
303 QuicTime::Delta::FromSeconds(30));
305 canoncial_suffixes_
.push_back(string(".c.youtube.com"));
306 canoncial_suffixes_
.push_back(string(".googlevideo.com"));
309 QuicStreamFactory::~QuicStreamFactory() {
310 CloseAllSessions(ERR_ABORTED
);
311 STLDeleteElements(&all_sessions_
);
312 STLDeleteValues(&active_jobs_
);
313 STLDeleteValues(&all_crypto_configs_
);
316 int QuicStreamFactory::Create(const HostPortProxyPair
& host_port_proxy_pair
,
318 base::StringPiece method
,
319 CertVerifier
* cert_verifier
,
320 const BoundNetLog
& net_log
,
321 QuicStreamRequest
* request
) {
322 if (HasActiveSession(host_port_proxy_pair
)) {
323 request
->set_stream(CreateIfSessionExists(host_port_proxy_pair
, net_log
));
327 if (HasActiveJob(host_port_proxy_pair
)) {
328 Job
* job
= active_jobs_
[host_port_proxy_pair
];
329 active_requests_
[request
] = job
;
330 job_requests_map_
[job
].insert(request
);
331 return ERR_IO_PENDING
;
334 // Create crypto config and start the process of loading QUIC server
335 // information from disk cache.
336 QuicCryptoClientConfig
* crypto_config
=
337 GetOrCreateCryptoConfig(host_port_proxy_pair
);
338 DCHECK(crypto_config
);
340 scoped_ptr
<Job
> job(new Job(this, host_resolver_
, host_port_proxy_pair
,
341 is_https
, method
, cert_verifier
, net_log
));
342 int rv
= job
->Run(base::Bind(&QuicStreamFactory::OnJobComplete
,
343 base::Unretained(this), job
.get()));
345 if (rv
== ERR_IO_PENDING
) {
346 active_requests_
[request
] = job
.get();
347 job_requests_map_
[job
.get()].insert(request
);
348 active_jobs_
[host_port_proxy_pair
] = job
.release();
351 DCHECK(HasActiveSession(host_port_proxy_pair
));
352 request
->set_stream(CreateIfSessionExists(host_port_proxy_pair
, net_log
));
357 bool QuicStreamFactory::OnResolution(
358 const HostPortProxyPair
& host_port_proxy_pair
,
359 const AddressList
& address_list
) {
360 DCHECK(!HasActiveSession(host_port_proxy_pair
));
361 for (size_t i
= 0; i
< address_list
.size(); ++i
) {
362 const IPEndPoint
& address
= address_list
[i
];
363 if (!ContainsKey(ip_aliases_
, address
))
366 const SessionSet
& sessions
= ip_aliases_
[address
];
367 for (SessionSet::const_iterator i
= sessions
.begin();
368 i
!= sessions
.end(); ++i
) {
369 QuicClientSession
* session
= *i
;
370 if (!session
->CanPool(host_port_proxy_pair
.first
.host()))
372 active_sessions_
[host_port_proxy_pair
] = session
;
373 session_aliases_
[session
].insert(host_port_proxy_pair
);
380 void QuicStreamFactory::OnJobComplete(Job
* job
, int rv
) {
382 require_confirmation_
= false;
384 // Create all the streams, but do not notify them yet.
385 for (RequestSet::iterator it
= job_requests_map_
[job
].begin();
386 it
!= job_requests_map_
[job
].end() ; ++it
) {
387 DCHECK(HasActiveSession(job
->host_port_proxy_pair()));
388 (*it
)->set_stream(CreateIfSessionExists(job
->host_port_proxy_pair(),
392 while (!job_requests_map_
[job
].empty()) {
393 RequestSet::iterator it
= job_requests_map_
[job
].begin();
394 QuicStreamRequest
* request
= *it
;
395 job_requests_map_
[job
].erase(it
);
396 active_requests_
.erase(request
);
397 // Even though we're invoking callbacks here, we don't need to worry
398 // about |this| being deleted, because the factory is owned by the
399 // profile which can not be deleted via callbacks.
400 request
->OnRequestComplete(rv
);
402 active_jobs_
.erase(job
->host_port_proxy_pair());
403 job_requests_map_
.erase(job
);
408 // Returns a newly created QuicHttpStream owned by the caller, if a
409 // matching session already exists. Returns NULL otherwise.
410 scoped_ptr
<QuicHttpStream
> QuicStreamFactory::CreateIfSessionExists(
411 const HostPortProxyPair
& host_port_proxy_pair
,
412 const BoundNetLog
& net_log
) {
413 if (!HasActiveSession(host_port_proxy_pair
)) {
414 DVLOG(1) << "No active session";
415 return scoped_ptr
<QuicHttpStream
>();
418 QuicClientSession
* session
= active_sessions_
[host_port_proxy_pair
];
420 return scoped_ptr
<QuicHttpStream
>(
421 new QuicHttpStream(session
->GetWeakPtr()));
424 void QuicStreamFactory::OnIdleSession(QuicClientSession
* session
) {
427 void QuicStreamFactory::OnSessionGoingAway(QuicClientSession
* session
) {
428 const AliasSet
& aliases
= session_aliases_
[session
];
429 for (AliasSet::const_iterator it
= aliases
.begin(); it
!= aliases
.end();
431 DCHECK(active_sessions_
.count(*it
));
432 DCHECK_EQ(session
, active_sessions_
[*it
]);
433 active_sessions_
.erase(*it
);
434 if (!http_server_properties_
)
437 if (!session
->IsCryptoHandshakeConfirmed()) {
438 // TODO(rch): In the special case where the session has received no
439 // packets from the peer, we should consider blacklisting this
440 // differently so that we still race TCP but we don't consider the
441 // session connected until the handshake has been confirmed.
442 http_server_properties_
->SetBrokenAlternateProtocol(it
->first
);
444 QuicConnectionStats stats
= session
->connection()->GetStats();
445 HttpServerProperties::NetworkStats network_stats
;
446 network_stats
.rtt
= base::TimeDelta::FromMicroseconds(stats
.rtt
);
447 network_stats
.bandwidth_estimate
= stats
.estimated_bandwidth
;
448 http_server_properties_
->SetServerNetworkStats(
449 it
->first
, network_stats
);
452 IPEndPoint peer_address
= session
->connection()->peer_address();
453 ip_aliases_
[peer_address
].erase(session
);
454 if (ip_aliases_
[peer_address
].empty()) {
455 ip_aliases_
.erase(peer_address
);
457 session_aliases_
.erase(session
);
460 void QuicStreamFactory::OnSessionClosed(QuicClientSession
* session
) {
461 DCHECK_EQ(0u, session
->GetNumOpenStreams());
462 OnSessionGoingAway(session
);
463 all_sessions_
.erase(session
);
467 void QuicStreamFactory::CancelRequest(QuicStreamRequest
* request
) {
468 DCHECK(ContainsKey(active_requests_
, request
));
469 Job
* job
= active_requests_
[request
];
470 job_requests_map_
[job
].erase(request
);
471 active_requests_
.erase(request
);
474 void QuicStreamFactory::CloseAllSessions(int error
) {
475 while (!active_sessions_
.empty()) {
476 size_t initial_size
= active_sessions_
.size();
477 active_sessions_
.begin()->second
->CloseSessionOnError(error
);
478 DCHECK_NE(initial_size
, active_sessions_
.size());
480 while (!all_sessions_
.empty()) {
481 size_t initial_size
= all_sessions_
.size();
482 (*all_sessions_
.begin())->CloseSessionOnError(error
);
483 DCHECK_NE(initial_size
, all_sessions_
.size());
485 DCHECK(all_sessions_
.empty());
488 base::Value
* QuicStreamFactory::QuicStreamFactoryInfoToValue() const {
489 base::ListValue
* list
= new base::ListValue();
491 for (SessionMap::const_iterator it
= active_sessions_
.begin();
492 it
!= active_sessions_
.end(); ++it
) {
493 const HostPortProxyPair
& pair
= it
->first
;
494 QuicClientSession
* session
= it
->second
;
495 const AliasSet
& aliases
= session_aliases_
.find(session
)->second
;
496 if (pair
.first
.Equals(aliases
.begin()->first
) &&
497 pair
.second
== aliases
.begin()->second
) {
498 list
->Append(session
->GetInfoAsValue(aliases
));
504 void QuicStreamFactory::OnIPAddressChanged() {
505 CloseAllSessions(ERR_NETWORK_CHANGED
);
506 require_confirmation_
= true;
509 void QuicStreamFactory::OnCertAdded(const X509Certificate
* cert
) {
510 CloseAllSessions(ERR_CERT_DATABASE_CHANGED
);
513 void QuicStreamFactory::OnCACertChanged(const X509Certificate
* cert
) {
514 // We should flush the sessions if we removed trust from a
515 // cert, because a previously trusted server may have become
518 // We should not flush the sessions if we added trust to a cert.
520 // Since the OnCACertChanged method doesn't tell us what
521 // kind of change it is, we have to flush the socket
523 CloseAllSessions(ERR_CERT_DATABASE_CHANGED
);
526 bool QuicStreamFactory::HasActiveSession(
527 const HostPortProxyPair
& host_port_proxy_pair
) {
528 return ContainsKey(active_sessions_
, host_port_proxy_pair
);
531 int QuicStreamFactory::CreateSession(
532 const HostPortProxyPair
& host_port_proxy_pair
,
534 CertVerifier
* cert_verifier
,
535 const AddressList
& address_list
,
536 const BoundNetLog
& net_log
,
537 QuicClientSession
** session
) {
538 QuicGuid guid
= random_generator_
->RandUint64();
539 IPEndPoint addr
= *address_list
.begin();
540 scoped_refptr
<PortSuggester
> port_suggester
=
541 new PortSuggester(host_port_proxy_pair
.first
, port_seed_
);
542 DatagramSocket::BindType bind_type
= enable_port_selection_
?
543 DatagramSocket::RANDOM_BIND
: // Use our callback.
544 DatagramSocket::DEFAULT_BIND
; // Use OS to randomize.
545 scoped_ptr
<DatagramClientSocket
> socket(
546 client_socket_factory_
->CreateDatagramClientSocket(
548 base::Bind(&PortSuggester::SuggestPort
, port_suggester
),
549 net_log
.net_log(), net_log
.source()));
550 int rv
= socket
->Connect(addr
);
553 UMA_HISTOGRAM_COUNTS("Net.QuicEphemeralPortsSuggested",
554 port_suggester
->call_count());
555 if (enable_port_selection_
) {
556 DCHECK_LE(1u, port_suggester
->call_count());
558 DCHECK_EQ(0u, port_suggester
->call_count());
561 // We should adaptively set this buffer size, but for now, we'll use a size
562 // that is more than large enough for a full receive window, and yet
563 // does not consume "too much" memory. If we see bursty packet loss, we may
564 // revisit this setting and test for its impact.
565 const int32
kSocketBufferSize(TcpReceiver::kReceiveWindowTCP
);
566 socket
->SetReceiveBufferSize(kSocketBufferSize
);
567 // Set a buffer large enough to contain the initial CWND's worth of packet
568 // to work around the problem with CHLO packets being sent out with the
569 // wrong encryption level, when the send buffer is full.
570 socket
->SetSendBufferSize(kMaxPacketSize
* 20); // Support 20 packets.
572 scoped_ptr
<QuicDefaultPacketWriter
> writer(
573 new QuicDefaultPacketWriter(socket
.get()));
575 if (!helper_
.get()) {
576 helper_
.reset(new QuicConnectionHelper(
577 base::MessageLoop::current()->message_loop_proxy().get(),
578 clock_
.get(), random_generator_
));
581 QuicConnection
* connection
= new QuicConnection(guid
, addr
, helper_
.get(),
583 supported_versions_
);
584 writer
->SetConnection(connection
);
585 connection
->options()->max_packet_length
= max_packet_length_
;
587 QuicCryptoClientConfig
* crypto_config
=
588 GetOrCreateCryptoConfig(host_port_proxy_pair
);
589 DCHECK(crypto_config
);
591 QuicConfig config
= config_
;
592 if (http_server_properties_
) {
593 const HttpServerProperties::NetworkStats
* stats
=
594 http_server_properties_
->GetServerNetworkStats(
595 host_port_proxy_pair
.first
);
597 config
.set_initial_round_trip_time_us(stats
->rtt
.InMicroseconds(),
598 stats
->rtt
.InMicroseconds());
602 *session
= new QuicClientSession(
603 connection
, socket
.Pass(), writer
.Pass(), this,
604 quic_crypto_client_stream_factory_
, host_port_proxy_pair
.first
.host(),
605 config
, crypto_config
, net_log
.net_log());
606 all_sessions_
.insert(*session
); // owning pointer
608 crypto_config
->SetProofVerifier(
609 new ProofVerifierChromium(cert_verifier
, net_log
));
614 bool QuicStreamFactory::HasActiveJob(
615 const HostPortProxyPair
& host_port_proxy_pair
) {
616 return ContainsKey(active_jobs_
, host_port_proxy_pair
);
619 void QuicStreamFactory::ActivateSession(
620 const HostPortProxyPair
& host_port_proxy_pair
,
621 QuicClientSession
* session
) {
622 DCHECK(!HasActiveSession(host_port_proxy_pair
));
623 active_sessions_
[host_port_proxy_pair
] = session
;
624 session_aliases_
[session
].insert(host_port_proxy_pair
);
625 DCHECK(!ContainsKey(ip_aliases_
[session
->connection()->peer_address()],
627 ip_aliases_
[session
->connection()->peer_address()].insert(session
);
630 QuicCryptoClientConfig
* QuicStreamFactory::GetOrCreateCryptoConfig(
631 const HostPortProxyPair
& host_port_proxy_pair
) {
632 QuicCryptoClientConfig
* crypto_config
;
634 if (ContainsKey(all_crypto_configs_
, host_port_proxy_pair
)) {
635 crypto_config
= all_crypto_configs_
[host_port_proxy_pair
];
636 DCHECK(crypto_config
);
638 // TODO(rtenneti): if two quic_sessions for the same host_port_proxy_pair
639 // share the same crypto_config, will it cause issues?
640 crypto_config
= new QuicCryptoClientConfig();
641 if (quic_server_info_factory_
) {
642 QuicCryptoClientConfig::CachedState
* cached
=
643 crypto_config
->Create(host_port_proxy_pair
.first
.host(),
644 quic_server_info_factory_
);
647 crypto_config
->SetDefaults();
648 all_crypto_configs_
[host_port_proxy_pair
] = crypto_config
;
649 PopulateFromCanonicalConfig(host_port_proxy_pair
, crypto_config
);
651 return crypto_config
;
654 void QuicStreamFactory::PopulateFromCanonicalConfig(
655 const HostPortProxyPair
& host_port_proxy_pair
,
656 QuicCryptoClientConfig
* crypto_config
) {
657 const string server_hostname
= host_port_proxy_pair
.first
.host();
660 for (; i
< canoncial_suffixes_
.size(); ++i
) {
661 if (EndsWith(server_hostname
, canoncial_suffixes_
[i
], false)) {
665 if (i
== canoncial_suffixes_
.size())
668 HostPortPair
canonical_host_port(canoncial_suffixes_
[i
],
669 host_port_proxy_pair
.first
.port());
670 if (!ContainsKey(canonical_hostname_to_origin_map_
, canonical_host_port
)) {
671 // This is the first host we've seen which matches the suffix, so make it
673 canonical_hostname_to_origin_map_
[canonical_host_port
] =
674 host_port_proxy_pair
;
678 const HostPortProxyPair
& canonical_host_port_proxy_pair
=
679 canonical_hostname_to_origin_map_
[canonical_host_port
];
680 QuicCryptoClientConfig
* canonical_crypto_config
=
681 all_crypto_configs_
[canonical_host_port_proxy_pair
];
682 DCHECK(canonical_crypto_config
);
684 // Copy the CachedState for the canonical server from canonical_crypto_config
685 // as the initial CachedState for the server_hostname in crypto_config.
686 crypto_config
->InitializeFrom(server_hostname
,
687 canonical_host_port_proxy_pair
.first
.host(),
688 canonical_crypto_config
);
689 // Update canonical version to point at the "most recent" crypto_config.
690 canonical_hostname_to_origin_map_
[canonical_host_port
] = host_port_proxy_pair
;