xref: /aosp_15_r20/external/cronet/net/socket/ssl_connect_job.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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/socket/ssl_connect_job.h"
6 
7 #include <cstdlib>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/feature_list.h"
12 #include "base/functional/bind.h"
13 #include "base/functional/callback_helpers.h"
14 #include "base/metrics/histogram_functions.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "net/base/connection_endpoint_metadata.h"
17 #include "net/base/features.h"
18 #include "net/base/host_port_pair.h"
19 #include "net/base/net_errors.h"
20 #include "net/base/trace_constants.h"
21 #include "net/base/tracing.h"
22 #include "net/base/url_util.h"
23 #include "net/cert/x509_util.h"
24 #include "net/http/http_proxy_connect_job.h"
25 #include "net/log/net_log_source_type.h"
26 #include "net/log/net_log_values.h"
27 #include "net/log/net_log_with_source.h"
28 #include "net/socket/client_socket_factory.h"
29 #include "net/socket/client_socket_handle.h"
30 #include "net/socket/socks_connect_job.h"
31 #include "net/socket/ssl_client_socket.h"
32 #include "net/socket/transport_connect_job.h"
33 #include "net/ssl/ssl_cert_request_info.h"
34 #include "net/ssl/ssl_connection_status_flags.h"
35 #include "net/ssl/ssl_info.h"
36 #include "third_party/abseil-cpp/absl/types/variant.h"
37 #include "third_party/boringssl/src/include/openssl/pool.h"
38 #include "third_party/boringssl/src/include/openssl/ssl.h"
39 
40 namespace net {
41 
42 namespace {
43 
44 // Timeout for the SSL handshake portion of the connect.
45 constexpr base::TimeDelta kSSLHandshakeTimeout(base::Seconds(30));
46 
47 }  // namespace
48 
SSLSocketParams(ConnectJobParams nested_params,const HostPortPair & host_and_port,const SSLConfig & ssl_config,NetworkAnonymizationKey network_anonymization_key)49 SSLSocketParams::SSLSocketParams(
50     ConnectJobParams nested_params,
51     const HostPortPair& host_and_port,
52     const SSLConfig& ssl_config,
53     NetworkAnonymizationKey network_anonymization_key)
54     : nested_params_(nested_params),
55       host_and_port_(host_and_port),
56       ssl_config_(ssl_config),
57       network_anonymization_key_(network_anonymization_key) {
58   CHECK(!nested_params_.is_ssl());
59 }
60 
61 SSLSocketParams::~SSLSocketParams() = default;
62 
GetConnectionType() const63 SSLSocketParams::ConnectionType SSLSocketParams::GetConnectionType() const {
64   if (nested_params_.is_socks()) {
65     return SOCKS_PROXY;
66   }
67   if (nested_params_.is_http_proxy()) {
68     return HTTP_PROXY;
69   }
70   return DIRECT;
71 }
72 
Create(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<SSLSocketParams> params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)73 std::unique_ptr<SSLConnectJob> SSLConnectJob::Factory::Create(
74     RequestPriority priority,
75     const SocketTag& socket_tag,
76     const CommonConnectJobParams* common_connect_job_params,
77     scoped_refptr<SSLSocketParams> params,
78     ConnectJob::Delegate* delegate,
79     const NetLogWithSource* net_log) {
80   return std::make_unique<SSLConnectJob>(priority, socket_tag,
81                                          common_connect_job_params,
82                                          std::move(params), delegate, net_log);
83 }
84 
SSLConnectJob(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<SSLSocketParams> params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)85 SSLConnectJob::SSLConnectJob(
86     RequestPriority priority,
87     const SocketTag& socket_tag,
88     const CommonConnectJobParams* common_connect_job_params,
89     scoped_refptr<SSLSocketParams> params,
90     ConnectJob::Delegate* delegate,
91     const NetLogWithSource* net_log)
92     : ConnectJob(
93           priority,
94           socket_tag,
95           // The SSLConnectJob's timer is only started during the SSL handshake.
96           base::TimeDelta(),
97           common_connect_job_params,
98           delegate,
99           net_log,
100           NetLogSourceType::SSL_CONNECT_JOB,
101           NetLogEventType::SSL_CONNECT_JOB_CONNECT),
102       params_(std::move(params)),
103       callback_(base::BindRepeating(&SSLConnectJob::OnIOComplete,
104                                     base::Unretained(this))) {}
105 
~SSLConnectJob()106 SSLConnectJob::~SSLConnectJob() {
107   // In the case the job was canceled, need to delete nested job first to
108   // correctly order NetLog events.
109   nested_connect_job_.reset();
110 }
111 
GetLoadState() const112 LoadState SSLConnectJob::GetLoadState() const {
113   switch (next_state_) {
114     case STATE_TRANSPORT_CONNECT:
115     case STATE_SOCKS_CONNECT:
116     case STATE_TUNNEL_CONNECT:
117       return LOAD_STATE_IDLE;
118     case STATE_TRANSPORT_CONNECT_COMPLETE:
119     case STATE_SOCKS_CONNECT_COMPLETE:
120       return nested_connect_job_->GetLoadState();
121     case STATE_TUNNEL_CONNECT_COMPLETE:
122       if (nested_socket_) {
123         return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
124       }
125       return nested_connect_job_->GetLoadState();
126     case STATE_SSL_CONNECT:
127     case STATE_SSL_CONNECT_COMPLETE:
128       return LOAD_STATE_SSL_HANDSHAKE;
129     default:
130       NOTREACHED();
131       return LOAD_STATE_IDLE;
132   }
133 }
134 
HasEstablishedConnection() const135 bool SSLConnectJob::HasEstablishedConnection() const {
136   // If waiting on a nested ConnectJob, defer to that ConnectJob's state.
137   if (nested_connect_job_) {
138     return nested_connect_job_->HasEstablishedConnection();
139   }
140   // Otherwise, return true if a socket has been created.
141   return nested_socket_ || ssl_socket_;
142 }
143 
OnConnectJobComplete(int result,ConnectJob * job)144 void SSLConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
145   DCHECK_EQ(job, nested_connect_job_.get());
146   OnIOComplete(result);
147 }
148 
OnNeedsProxyAuth(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback,ConnectJob * job)149 void SSLConnectJob::OnNeedsProxyAuth(
150     const HttpResponseInfo& response,
151     HttpAuthController* auth_controller,
152     base::OnceClosure restart_with_auth_callback,
153     ConnectJob* job) {
154   DCHECK_EQ(next_state_, STATE_TUNNEL_CONNECT_COMPLETE);
155 
156   // The timer shouldn't have started running yet, since the handshake only
157   // starts after a tunnel has been established through the proxy.
158   DCHECK(!TimerIsRunning());
159 
160   // Just pass the callback up to the consumer. This class doesn't need to do
161   // anything once credentials are provided.
162   NotifyDelegateOfProxyAuth(response, auth_controller,
163                             std::move(restart_with_auth_callback));
164 }
165 
GetConnectionAttempts() const166 ConnectionAttempts SSLConnectJob::GetConnectionAttempts() const {
167   return connection_attempts_;
168 }
169 
GetResolveErrorInfo() const170 ResolveErrorInfo SSLConnectJob::GetResolveErrorInfo() const {
171   return resolve_error_info_;
172 }
173 
IsSSLError() const174 bool SSLConnectJob::IsSSLError() const {
175   return ssl_negotiation_started_;
176 }
177 
GetCertRequestInfo()178 scoped_refptr<SSLCertRequestInfo> SSLConnectJob::GetCertRequestInfo() {
179   return ssl_cert_request_info_;
180 }
181 
HandshakeTimeoutForTesting()182 base::TimeDelta SSLConnectJob::HandshakeTimeoutForTesting() {
183   return kSSLHandshakeTimeout;
184 }
185 
OnIOComplete(int result)186 void SSLConnectJob::OnIOComplete(int result) {
187   int rv = DoLoop(result);
188   if (rv != ERR_IO_PENDING) {
189     NotifyDelegateOfCompletion(rv);  // Deletes |this|.
190   }
191 }
192 
DoLoop(int result)193 int SSLConnectJob::DoLoop(int result) {
194   TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoLoop");
195   DCHECK_NE(next_state_, STATE_NONE);
196 
197   int rv = result;
198   do {
199     State state = next_state_;
200     next_state_ = STATE_NONE;
201     switch (state) {
202       case STATE_TRANSPORT_CONNECT:
203         DCHECK_EQ(OK, rv);
204         rv = DoTransportConnect();
205         break;
206       case STATE_TRANSPORT_CONNECT_COMPLETE:
207         rv = DoTransportConnectComplete(rv);
208         break;
209       case STATE_SOCKS_CONNECT:
210         DCHECK_EQ(OK, rv);
211         rv = DoSOCKSConnect();
212         break;
213       case STATE_SOCKS_CONNECT_COMPLETE:
214         rv = DoSOCKSConnectComplete(rv);
215         break;
216       case STATE_TUNNEL_CONNECT:
217         DCHECK_EQ(OK, rv);
218         rv = DoTunnelConnect();
219         break;
220       case STATE_TUNNEL_CONNECT_COMPLETE:
221         rv = DoTunnelConnectComplete(rv);
222         break;
223       case STATE_SSL_CONNECT:
224         DCHECK_EQ(OK, rv);
225         rv = DoSSLConnect();
226         break;
227       case STATE_SSL_CONNECT_COMPLETE:
228         rv = DoSSLConnectComplete(rv);
229         break;
230       default:
231         NOTREACHED() << "bad state";
232         rv = ERR_FAILED;
233         break;
234     }
235   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
236 
237   return rv;
238 }
239 
DoTransportConnect()240 int SSLConnectJob::DoTransportConnect() {
241   DCHECK(!nested_connect_job_);
242   DCHECK(params_->GetDirectConnectionParams());
243   DCHECK(!TimerIsRunning());
244 
245   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
246   // If this is an ECH retry, connect to the same server as before.
247   std::optional<TransportConnectJob::EndpointResultOverride>
248       endpoint_result_override;
249   if (ech_retry_configs_) {
250     DCHECK(ssl_client_context()->config().ech_enabled);
251     DCHECK(endpoint_result_);
252     endpoint_result_override.emplace(*endpoint_result_, dns_aliases_);
253   }
254   nested_connect_job_ = std::make_unique<TransportConnectJob>(
255       priority(), socket_tag(), common_connect_job_params(),
256       params_->GetDirectConnectionParams(), this, &net_log(),
257       std::move(endpoint_result_override));
258   return nested_connect_job_->Connect();
259 }
260 
DoTransportConnectComplete(int result)261 int SSLConnectJob::DoTransportConnectComplete(int result) {
262   resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
263   ConnectionAttempts connection_attempts =
264       nested_connect_job_->GetConnectionAttempts();
265   connection_attempts_.insert(connection_attempts_.end(),
266                               connection_attempts.begin(),
267                               connection_attempts.end());
268   if (result == OK) {
269     next_state_ = STATE_SSL_CONNECT;
270     nested_socket_ = nested_connect_job_->PassSocket();
271     nested_socket_->GetPeerAddress(&server_address_);
272     dns_aliases_ = nested_socket_->GetDnsAliases();
273   }
274 
275   return result;
276 }
277 
DoSOCKSConnect()278 int SSLConnectJob::DoSOCKSConnect() {
279   DCHECK(!nested_connect_job_);
280   DCHECK(params_->GetSocksProxyConnectionParams());
281   DCHECK(!TimerIsRunning());
282 
283   next_state_ = STATE_SOCKS_CONNECT_COMPLETE;
284   nested_connect_job_ = std::make_unique<SOCKSConnectJob>(
285       priority(), socket_tag(), common_connect_job_params(),
286       params_->GetSocksProxyConnectionParams(), this, &net_log());
287   return nested_connect_job_->Connect();
288 }
289 
DoSOCKSConnectComplete(int result)290 int SSLConnectJob::DoSOCKSConnectComplete(int result) {
291   resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
292   if (result == OK) {
293     next_state_ = STATE_SSL_CONNECT;
294     nested_socket_ = nested_connect_job_->PassSocket();
295   }
296 
297   return result;
298 }
299 
DoTunnelConnect()300 int SSLConnectJob::DoTunnelConnect() {
301   DCHECK(!nested_connect_job_);
302   DCHECK(params_->GetHttpProxyConnectionParams());
303   DCHECK(!TimerIsRunning());
304 
305   next_state_ = STATE_TUNNEL_CONNECT_COMPLETE;
306   nested_connect_job_ = std::make_unique<HttpProxyConnectJob>(
307       priority(), socket_tag(), common_connect_job_params(),
308       params_->GetHttpProxyConnectionParams(), this, &net_log());
309   return nested_connect_job_->Connect();
310 }
311 
DoTunnelConnectComplete(int result)312 int SSLConnectJob::DoTunnelConnectComplete(int result) {
313   resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
314   nested_socket_ = nested_connect_job_->PassSocket();
315 
316   if (result < 0) {
317     // Extract the information needed to prompt for appropriate proxy
318     // authentication so that when ClientSocketPoolBaseHelper calls
319     // |GetAdditionalErrorState|, we can easily set the state.
320     if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
321       ssl_cert_request_info_ = nested_connect_job_->GetCertRequestInfo();
322     }
323     return result;
324   }
325 
326   next_state_ = STATE_SSL_CONNECT;
327   return result;
328 }
329 
DoSSLConnect()330 int SSLConnectJob::DoSSLConnect() {
331   TRACE_EVENT0(NetTracingCategory(), "SSLConnectJob::DoSSLConnect");
332   DCHECK(!TimerIsRunning());
333 
334   next_state_ = STATE_SSL_CONNECT_COMPLETE;
335 
336   // Set the timeout to just the time allowed for the SSL handshake.
337   ResetTimer(kSSLHandshakeTimeout);
338 
339   // Get the transport's connect start and DNS times.
340   const LoadTimingInfo::ConnectTiming& socket_connect_timing =
341       nested_connect_job_->connect_timing();
342 
343   // Overwriting |connect_start| serves two purposes - it adjusts timing so
344   // |connect_start| doesn't include dns times, and it adjusts the time so
345   // as not to include time spent waiting for an idle socket.
346   connect_timing_.connect_start = socket_connect_timing.connect_start;
347   connect_timing_.domain_lookup_start =
348       socket_connect_timing.domain_lookup_start;
349   connect_timing_.domain_lookup_end = socket_connect_timing.domain_lookup_end;
350 
351   ssl_negotiation_started_ = true;
352   connect_timing_.ssl_start = base::TimeTicks::Now();
353 
354   // Save the `HostResolverEndpointResult`. `nested_connect_job_` is destroyed
355   // at the end of this function.
356   endpoint_result_ = nested_connect_job_->GetHostResolverEndpointResult();
357 
358   SSLConfig ssl_config = params_->ssl_config();
359   ssl_config.ignore_certificate_errors =
360       *common_connect_job_params()->ignore_certificate_errors;
361   ssl_config.network_anonymization_key = params_->network_anonymization_key();
362 
363   if (ssl_client_context()->config().ech_enabled) {
364     if (ech_retry_configs_) {
365       ssl_config.ech_config_list = *ech_retry_configs_;
366     } else if (endpoint_result_) {
367       ssl_config.ech_config_list = endpoint_result_->metadata.ech_config_list;
368     }
369     if (!ssl_config.ech_config_list.empty()) {
370       // Overriding the DNS lookup only works for direct connections. We
371       // currently do not support ECH with other connection types.
372       DCHECK_EQ(params_->GetConnectionType(), SSLSocketParams::DIRECT);
373     }
374   }
375 
376   ssl_socket_ = client_socket_factory()->CreateSSLClientSocket(
377       ssl_client_context(), std::move(nested_socket_), params_->host_and_port(),
378       ssl_config);
379   nested_connect_job_.reset();
380   return ssl_socket_->Connect(callback_);
381 }
382 
DoSSLConnectComplete(int result)383 int SSLConnectJob::DoSSLConnectComplete(int result) {
384   connect_timing_.ssl_end = base::TimeTicks::Now();
385 
386   if (result != OK && !server_address_.address().empty()) {
387     connection_attempts_.push_back(ConnectionAttempt(server_address_, result));
388     server_address_ = IPEndPoint();
389   }
390 
391   // Historically, many servers which negotiated SHA-1 server signatures in
392   // TLS 1.2 actually support SHA-2 but preferentially sign SHA-1 if available.
393   // In order to get accurate metrics while deprecating SHA-1, we initially
394   // connected with SHA-1 disabled and then retried with enabled.
395   //
396   // SHA-1 is now always disabled, but we retained the fallback to separate the
397   // effect of disabling SHA-1 from the effect of having a single automatic
398   // retry on a potentially unreliably network connection.
399   //
400   // TODO(https://crbug.com/658905): Remove this now redundant retry.
401   if (disable_legacy_crypto_with_fallback_ &&
402       (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET ||
403        result == ERR_SSL_PROTOCOL_ERROR ||
404        result == ERR_SSL_VERSION_OR_CIPHER_MISMATCH)) {
405     ResetStateForRestart();
406     disable_legacy_crypto_with_fallback_ = false;
407     next_state_ = GetInitialState(params_->GetConnectionType());
408     return OK;
409   }
410 
411   // We record metrics based on whether the server advertised ECH support in
412   // DNS. This allows the metrics to measure the same set of servers in both
413   // control and experiment group.
414   const bool is_ech_capable =
415       endpoint_result_ && !endpoint_result_->metadata.ech_config_list.empty();
416   const bool ech_enabled = ssl_client_context()->config().ech_enabled;
417 
418   if (!ech_retry_configs_ && result == ERR_ECH_NOT_NEGOTIATED && ech_enabled) {
419     // We used ECH, and the server could not decrypt the ClientHello. However,
420     // it was able to handshake with the public name and send authenticated
421     // retry configs. If this is not the first time around, retry the connection
422     // with the new ECHConfigList, or with ECH disabled (empty retry configs),
423     // as directed.
424     //
425     // See
426     // https://www.ietf.org/archive/id/draft-ietf-tls-esni-13.html#section-6.1.6
427     DCHECK(is_ech_capable);
428     ech_retry_configs_ = ssl_socket_->GetECHRetryConfigs();
429     net_log().AddEvent(
430         NetLogEventType::SSL_CONNECT_JOB_RESTART_WITH_ECH_CONFIG_LIST, [&] {
431           return base::Value::Dict().Set(
432               "bytes", NetLogBinaryValue(*ech_retry_configs_));
433         });
434 
435     ResetStateForRestart();
436     next_state_ = GetInitialState(params_->GetConnectionType());
437     return OK;
438   }
439 
440   if (is_ech_capable && ech_enabled) {
441     // These values are persisted to logs. Entries should not be renumbered
442     // and numeric values should never be reused.
443     enum class ECHResult {
444       // The connection succeeded on the initial connection.
445       kSuccessInitial = 0,
446       // The connection failed on the initial connection, without providing
447       // retry configs.
448       kErrorInitial = 1,
449       // The connection succeeded after getting retry configs.
450       kSuccessRetry = 2,
451       // The connection failed after getting retry configs.
452       kErrorRetry = 3,
453       // The connection succeeded after getting a rollback signal.
454       kSuccessRollback = 4,
455       // The connection failed after getting a rollback signal.
456       kErrorRollback = 5,
457       kMaxValue = kErrorRollback,
458     };
459     const bool is_ok = result == OK;
460     ECHResult ech_result;
461     if (!ech_retry_configs_.has_value()) {
462       ech_result =
463           is_ok ? ECHResult::kSuccessInitial : ECHResult::kErrorInitial;
464     } else if (ech_retry_configs_->empty()) {
465       ech_result =
466           is_ok ? ECHResult::kSuccessRollback : ECHResult::kErrorRollback;
467     } else {
468       ech_result = is_ok ? ECHResult::kSuccessRetry : ECHResult::kErrorRetry;
469     }
470     base::UmaHistogramEnumeration("Net.SSL.ECHResult", ech_result);
471   }
472 
473   if (result == OK) {
474     DCHECK(!connect_timing_.ssl_start.is_null());
475     base::TimeDelta connect_duration =
476         connect_timing_.ssl_end - connect_timing_.ssl_start;
477     UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency_2", connect_duration,
478                                base::Milliseconds(1), base::Minutes(1), 100);
479     if (is_ech_capable) {
480       UMA_HISTOGRAM_CUSTOM_TIMES("Net.SSL_Connection_Latency_ECH",
481                                  connect_duration, base::Milliseconds(1),
482                                  base::Minutes(1), 100);
483     }
484 
485     SSLInfo ssl_info;
486     bool has_ssl_info = ssl_socket_->GetSSLInfo(&ssl_info);
487     DCHECK(has_ssl_info);
488 
489     SSLVersion version =
490         SSLConnectionStatusToVersion(ssl_info.connection_status);
491     UMA_HISTOGRAM_ENUMERATION("Net.SSLVersion", version,
492                               SSL_CONNECTION_VERSION_MAX);
493 
494     uint16_t cipher_suite =
495         SSLConnectionStatusToCipherSuite(ssl_info.connection_status);
496     base::UmaHistogramSparse("Net.SSL_CipherSuite", cipher_suite);
497 
498     if (ssl_info.key_exchange_group != 0) {
499       base::UmaHistogramSparse("Net.SSL_KeyExchange.ECDHE",
500                                ssl_info.key_exchange_group);
501     }
502   }
503 
504   base::UmaHistogramSparse("Net.SSL_Connection_Error", std::abs(result));
505   if (is_ech_capable) {
506     base::UmaHistogramSparse("Net.SSL_Connection_Error_ECH", std::abs(result));
507   }
508 
509   if (result == OK || IsCertificateError(result)) {
510     SetSocket(std::move(ssl_socket_), std::move(dns_aliases_));
511   } else if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
512     ssl_cert_request_info_ = base::MakeRefCounted<SSLCertRequestInfo>();
513     ssl_socket_->GetSSLCertRequestInfo(ssl_cert_request_info_.get());
514   }
515 
516   return result;
517 }
518 
GetInitialState(SSLSocketParams::ConnectionType connection_type)519 SSLConnectJob::State SSLConnectJob::GetInitialState(
520     SSLSocketParams::ConnectionType connection_type) {
521   switch (connection_type) {
522     case SSLSocketParams::DIRECT:
523       return STATE_TRANSPORT_CONNECT;
524     case SSLSocketParams::HTTP_PROXY:
525       return STATE_TUNNEL_CONNECT;
526     case SSLSocketParams::SOCKS_PROXY:
527       return STATE_SOCKS_CONNECT;
528   }
529   NOTREACHED();
530   return STATE_NONE;
531 }
532 
ConnectInternal()533 int SSLConnectJob::ConnectInternal() {
534   next_state_ = GetInitialState(params_->GetConnectionType());
535   return DoLoop(OK);
536 }
537 
ResetStateForRestart()538 void SSLConnectJob::ResetStateForRestart() {
539   ResetTimer(base::TimeDelta());
540   nested_connect_job_ = nullptr;
541   nested_socket_ = nullptr;
542   ssl_socket_ = nullptr;
543   ssl_cert_request_info_ = nullptr;
544   ssl_negotiation_started_ = false;
545   resolve_error_info_ = ResolveErrorInfo();
546   server_address_ = IPEndPoint();
547 }
548 
ChangePriorityInternal(RequestPriority priority)549 void SSLConnectJob::ChangePriorityInternal(RequestPriority priority) {
550   if (nested_connect_job_) {
551     nested_connect_job_->ChangePriority(priority);
552   }
553 }
554 
555 }  // namespace net
556