• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 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/http/http_proxy_connect_job.h"
6 
7 #include <algorithm>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/functional/bind.h"
12 #include "base/functional/callback.h"
13 #include "base/metrics/field_trial.h"
14 #include "base/metrics/field_trial_params.h"
15 #include "base/metrics/histogram_functions.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/values.h"
20 #include "build/build_config.h"
21 #include "http_proxy_client_socket.h"
22 #include "net/base/features.h"
23 #include "net/base/host_port_pair.h"
24 #include "net/base/http_user_agent_settings.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/proxy_chain.h"
27 #include "net/dns/public/secure_dns_policy.h"
28 #include "net/log/net_log_source_type.h"
29 #include "net/log/net_log_with_source.h"
30 #include "net/nqe/network_quality_estimator.h"
31 #include "net/quic/quic_http_utils.h"
32 #include "net/quic/quic_proxy_client_socket.h"
33 #include "net/quic/quic_stream_factory.h"
34 #include "net/socket/client_socket_handle.h"
35 #include "net/socket/next_proto.h"
36 #include "net/socket/ssl_client_socket.h"
37 #include "net/socket/ssl_connect_job.h"
38 #include "net/socket/transport_client_socket_pool.h"
39 #include "net/socket/transport_connect_job.h"
40 #include "net/spdy/spdy_proxy_client_socket.h"
41 #include "net/spdy/spdy_session.h"
42 #include "net/spdy/spdy_session_pool.h"
43 #include "net/spdy/spdy_stream.h"
44 #include "net/ssl/ssl_cert_request_info.h"
45 #include "third_party/abseil-cpp/absl/types/optional.h"
46 #include "third_party/abseil-cpp/absl/types/variant.h"
47 #include "url/gurl.h"
48 #include "url/scheme_host_port.h"
49 
50 namespace net {
51 
52 namespace {
53 
54 // HttpProxyConnectJobs will time out after this many seconds.  Note this is in
55 // addition to the timeout for the transport socket.
56 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
57 constexpr base::TimeDelta kHttpProxyConnectJobTunnelTimeout = base::Seconds(10);
58 #else
59 constexpr base::TimeDelta kHttpProxyConnectJobTunnelTimeout = base::Seconds(30);
60 #endif
61 
62 class HttpProxyTimeoutExperiments {
63  public:
HttpProxyTimeoutExperiments()64   HttpProxyTimeoutExperiments() { Init(); }
65 
66   ~HttpProxyTimeoutExperiments() = default;
67 
Init()68   void Init() {
69     min_proxy_connection_timeout_ =
70         base::Seconds(GetInt32Param("min_proxy_connection_timeout_seconds", 8));
71     max_proxy_connection_timeout_ = base::Seconds(
72         GetInt32Param("max_proxy_connection_timeout_seconds", 30));
73     ssl_http_rtt_multiplier_ = GetInt32Param("ssl_http_rtt_multiplier", 10);
74     non_ssl_http_rtt_multiplier_ =
75         GetInt32Param("non_ssl_http_rtt_multiplier", 5);
76 
77     DCHECK_LT(0, ssl_http_rtt_multiplier_);
78     DCHECK_LT(0, non_ssl_http_rtt_multiplier_);
79     DCHECK_LE(base::TimeDelta(), min_proxy_connection_timeout_);
80     DCHECK_LE(base::TimeDelta(), max_proxy_connection_timeout_);
81     DCHECK_LE(min_proxy_connection_timeout_, max_proxy_connection_timeout_);
82   }
83 
min_proxy_connection_timeout() const84   base::TimeDelta min_proxy_connection_timeout() const {
85     return min_proxy_connection_timeout_;
86   }
max_proxy_connection_timeout() const87   base::TimeDelta max_proxy_connection_timeout() const {
88     return max_proxy_connection_timeout_;
89   }
ssl_http_rtt_multiplier() const90   int32_t ssl_http_rtt_multiplier() const { return ssl_http_rtt_multiplier_; }
non_ssl_http_rtt_multiplier() const91   int32_t non_ssl_http_rtt_multiplier() const {
92     return non_ssl_http_rtt_multiplier_;
93   }
94 
95  private:
96   // Returns the value of the parameter |param_name| for the field trial
97   // "NetAdaptiveProxyConnectionTimeout". If the value of the parameter is
98   // unavailable, then |default_value| is available.
GetInt32Param(const std::string & param_name,int32_t default_value)99   static int32_t GetInt32Param(const std::string& param_name,
100                                int32_t default_value) {
101     int32_t param;
102     if (!base::StringToInt(base::GetFieldTrialParamValue(
103                                "NetAdaptiveProxyConnectionTimeout", param_name),
104                            &param)) {
105       return default_value;
106     }
107     return param;
108   }
109 
110   // For secure proxies, the connection timeout is set to
111   // |ssl_http_rtt_multiplier_| times the HTTP RTT estimate. For insecure
112   // proxies, the connection timeout is set to |non_ssl_http_rtt_multiplier_|
113   // times the HTTP RTT estimate. In either case, the connection timeout
114   // is clamped to be between |min_proxy_connection_timeout_| and
115   // |max_proxy_connection_timeout_|.
116   base::TimeDelta min_proxy_connection_timeout_;
117   base::TimeDelta max_proxy_connection_timeout_;
118   int32_t ssl_http_rtt_multiplier_;
119   int32_t non_ssl_http_rtt_multiplier_;
120 };
121 
GetProxyTimeoutExperiments()122 HttpProxyTimeoutExperiments* GetProxyTimeoutExperiments() {
123   static HttpProxyTimeoutExperiments proxy_timeout_experiments;
124   return &proxy_timeout_experiments;
125 }
126 
127 }  // namespace
128 
HttpProxySocketParams(scoped_refptr<TransportSocketParams> transport_params,scoped_refptr<SSLSocketParams> ssl_params,const HostPortPair & endpoint,const ProxyChain & proxy_chain,size_t proxy_chain_index,bool tunnel,const NetworkTrafficAnnotationTag traffic_annotation,const NetworkAnonymizationKey & network_anonymization_key,SecureDnsPolicy secure_dns_policy)129 HttpProxySocketParams::HttpProxySocketParams(
130     scoped_refptr<TransportSocketParams> transport_params,
131     scoped_refptr<SSLSocketParams> ssl_params,
132     const HostPortPair& endpoint,
133     const ProxyChain& proxy_chain,
134     size_t proxy_chain_index,
135     bool tunnel,
136     const NetworkTrafficAnnotationTag traffic_annotation,
137     const NetworkAnonymizationKey& network_anonymization_key,
138     SecureDnsPolicy secure_dns_policy)
139     : transport_params_(std::move(transport_params)),
140       ssl_params_(std::move(ssl_params)),
141       endpoint_(endpoint),
142       proxy_chain_(proxy_chain),
143       proxy_chain_index_(proxy_chain_index),
144       tunnel_(tunnel),
145       network_anonymization_key_(network_anonymization_key),
146       traffic_annotation_(traffic_annotation),
147       secure_dns_policy_(secure_dns_policy) {
148   // This is either a connection to an HTTP proxy or an SSL/QUIC proxy.
149   DCHECK(transport_params_ || ssl_params_);
150   DCHECK(!transport_params_ || !ssl_params_);
151   DCHECK(!proxy_chain_.is_direct());
152   DCHECK(proxy_chain_.IsValid());
153 
154   CHECK(proxy_chain_index_ < proxy_chain_.length());
155 
156   // If connecting to a QUIC proxy, and |ssl_params_| must be valid. This also
157   // implies |transport_params_| is null, per the above DCHECKs.
158   if (proxy_server().is_quic()) {
159     DCHECK(ssl_params_);
160   }
161 
162   // Only supports proxy endpoints without scheme for now.
163   // TODO(crbug.com/1206799): Handle scheme.
164   if (transport_params_) {
165     DCHECK(absl::holds_alternative<HostPortPair>(
166         transport_params_->destination()));
167   } else if (ssl_params_->GetConnectionType() ==
168              SSLSocketParams::ConnectionType::DIRECT) {
169     DCHECK(absl::holds_alternative<HostPortPair>(
170         ssl_params_->GetDirectConnectionParams()->destination()));
171   }
172 }
173 
174 HttpProxySocketParams::~HttpProxySocketParams() = default;
175 
Create(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<HttpProxySocketParams> params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)176 std::unique_ptr<HttpProxyConnectJob> HttpProxyConnectJob::Factory::Create(
177     RequestPriority priority,
178     const SocketTag& socket_tag,
179     const CommonConnectJobParams* common_connect_job_params,
180     scoped_refptr<HttpProxySocketParams> params,
181     ConnectJob::Delegate* delegate,
182     const NetLogWithSource* net_log) {
183   return std::make_unique<HttpProxyConnectJob>(
184       priority, socket_tag, common_connect_job_params, std::move(params),
185       delegate, net_log);
186 }
187 
HttpProxyConnectJob(RequestPriority priority,const SocketTag & socket_tag,const CommonConnectJobParams * common_connect_job_params,scoped_refptr<HttpProxySocketParams> params,ConnectJob::Delegate * delegate,const NetLogWithSource * net_log)188 HttpProxyConnectJob::HttpProxyConnectJob(
189     RequestPriority priority,
190     const SocketTag& socket_tag,
191     const CommonConnectJobParams* common_connect_job_params,
192     scoped_refptr<HttpProxySocketParams> params,
193     ConnectJob::Delegate* delegate,
194     const NetLogWithSource* net_log)
195     : ConnectJob(priority,
196                  socket_tag,
197                  base::TimeDelta() /* The socket takes care of timeouts */,
198                  common_connect_job_params,
199                  delegate,
200                  net_log,
201                  NetLogSourceType::HTTP_PROXY_CONNECT_JOB,
202                  NetLogEventType::HTTP_PROXY_CONNECT_JOB_CONNECT),
203       params_(std::move(params)),
204       http_auth_controller_(
205           params_->tunnel()
206               ? base::MakeRefCounted<HttpAuthController>(
207                     HttpAuth::AUTH_PROXY,
208                     GURL((params_->ssl_params() ? "https://" : "http://") +
209                          params_->proxy_server().host_port_pair().ToString()),
210                     params_->network_anonymization_key(),
211                     common_connect_job_params->http_auth_cache,
212                     common_connect_job_params->http_auth_handler_factory,
213                     host_resolver())
214               : nullptr) {}
215 
216 HttpProxyConnectJob::~HttpProxyConnectJob() = default;
217 
218 const RequestPriority HttpProxyConnectJob::kH2QuicTunnelPriority =
219     DEFAULT_PRIORITY;
220 
GetLoadState() const221 LoadState HttpProxyConnectJob::GetLoadState() const {
222   switch (next_state_) {
223     case STATE_TRANSPORT_CONNECT_COMPLETE:
224       return nested_connect_job_->GetLoadState();
225     case STATE_HTTP_PROXY_CONNECT:
226     case STATE_HTTP_PROXY_CONNECT_COMPLETE:
227     case STATE_SPDY_PROXY_CREATE_STREAM:
228     case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
229     case STATE_QUIC_PROXY_CREATE_SESSION:
230     case STATE_QUIC_PROXY_CREATE_STREAM:
231     case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE:
232     case STATE_RESTART_WITH_AUTH:
233     case STATE_RESTART_WITH_AUTH_COMPLETE:
234       return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
235     // This state shouldn't be possible to be called in.
236     case STATE_TRANSPORT_CONNECT:
237       NOTREACHED();
238       [[fallthrough]];
239     case STATE_BEGIN_CONNECT:
240     case STATE_NONE:
241       // May be possible for this method to be called after an error, shouldn't
242       // be called after a successful connect.
243       break;
244   }
245   return LOAD_STATE_IDLE;
246 }
247 
HasEstablishedConnection() const248 bool HttpProxyConnectJob::HasEstablishedConnection() const {
249   if (has_established_connection_)
250     return true;
251 
252   // It's possible the nested connect job has established a connection, but
253   // hasn't completed yet (For example, an SSLConnectJob may be negotiating
254   // SSL).
255   if (nested_connect_job_)
256     return nested_connect_job_->HasEstablishedConnection();
257   return false;
258 }
259 
GetResolveErrorInfo() const260 ResolveErrorInfo HttpProxyConnectJob::GetResolveErrorInfo() const {
261   return resolve_error_info_;
262 }
263 
IsSSLError() const264 bool HttpProxyConnectJob::IsSSLError() const {
265   return ssl_cert_request_info_ != nullptr;
266 }
267 
GetCertRequestInfo()268 scoped_refptr<SSLCertRequestInfo> HttpProxyConnectJob::GetCertRequestInfo() {
269   return ssl_cert_request_info_;
270 }
271 
OnConnectJobComplete(int result,ConnectJob * job)272 void HttpProxyConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
273   DCHECK_EQ(nested_connect_job_.get(), job);
274   DCHECK_EQ(next_state_, STATE_TRANSPORT_CONNECT_COMPLETE);
275   OnIOComplete(result);
276 }
277 
OnNeedsProxyAuth(const HttpResponseInfo & response,HttpAuthController * auth_controller,base::OnceClosure restart_with_auth_callback,ConnectJob * job)278 void HttpProxyConnectJob::OnNeedsProxyAuth(
279     const HttpResponseInfo& response,
280     HttpAuthController* auth_controller,
281     base::OnceClosure restart_with_auth_callback,
282     ConnectJob* job) {
283   // None of the nested ConnectJob used by this class can encounter auth
284   // challenges. Instead, the challenges are returned by the ProxyClientSocket
285   // implementations after nested_connect_job_ has already established a
286   // connection.
287   NOTREACHED();
288 }
289 
AlternateNestedConnectionTimeout(const HttpProxySocketParams & params,const NetworkQualityEstimator * network_quality_estimator)290 base::TimeDelta HttpProxyConnectJob::AlternateNestedConnectionTimeout(
291     const HttpProxySocketParams& params,
292     const NetworkQualityEstimator* network_quality_estimator) {
293   base::TimeDelta default_alternate_timeout;
294 
295   // On Android and iOS, a default proxy connection timeout is used instead of
296   // the actual TCP/SSL timeouts of nested jobs.
297 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
298   default_alternate_timeout = kHttpProxyConnectJobTunnelTimeout;
299 #endif  // !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS)
300 
301   bool is_https = params.proxy_server().is_https();
302 
303   if (!network_quality_estimator)
304     return default_alternate_timeout;
305 
306   absl::optional<base::TimeDelta> http_rtt_estimate =
307       network_quality_estimator->GetHttpRTT();
308   if (!http_rtt_estimate)
309     return default_alternate_timeout;
310 
311   int32_t multiplier =
312       is_https ? GetProxyTimeoutExperiments()->ssl_http_rtt_multiplier()
313                : GetProxyTimeoutExperiments()->non_ssl_http_rtt_multiplier();
314   base::TimeDelta timeout = multiplier * http_rtt_estimate.value();
315   // Ensure that connection timeout is between
316   // |min_proxy_connection_timeout_| and |max_proxy_connection_timeout_|.
317   return std::clamp(
318       timeout, GetProxyTimeoutExperiments()->min_proxy_connection_timeout(),
319       GetProxyTimeoutExperiments()->max_proxy_connection_timeout());
320 }
321 
TunnelTimeoutForTesting()322 base::TimeDelta HttpProxyConnectJob::TunnelTimeoutForTesting() {
323   return kHttpProxyConnectJobTunnelTimeout;
324 }
325 
UpdateFieldTrialParametersForTesting()326 void HttpProxyConnectJob::UpdateFieldTrialParametersForTesting() {
327   GetProxyTimeoutExperiments()->Init();
328 }
329 
ConnectInternal()330 int HttpProxyConnectJob::ConnectInternal() {
331   DCHECK_EQ(next_state_, STATE_NONE);
332   next_state_ = STATE_BEGIN_CONNECT;
333   return DoLoop(OK);
334 }
335 
GetProxyServerScheme() const336 ProxyServer::Scheme HttpProxyConnectJob::GetProxyServerScheme() const {
337   return params_->proxy_server().scheme();
338 }
339 
OnIOComplete(int result)340 void HttpProxyConnectJob::OnIOComplete(int result) {
341   int rv = DoLoop(result);
342   if (rv != ERR_IO_PENDING) {
343     // May delete |this|.
344     NotifyDelegateOfCompletion(rv);
345   }
346 }
347 
RestartWithAuthCredentials()348 void HttpProxyConnectJob::RestartWithAuthCredentials() {
349   DCHECK(transport_socket_);
350   DCHECK_EQ(STATE_NONE, next_state_);
351 
352   // Always do this asynchronously, to avoid re-entrancy.
353   next_state_ = STATE_RESTART_WITH_AUTH;
354   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
355       FROM_HERE, base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
356                                 weak_ptr_factory_.GetWeakPtr(), net::OK));
357 }
358 
DoLoop(int result)359 int HttpProxyConnectJob::DoLoop(int result) {
360   DCHECK_NE(next_state_, STATE_NONE);
361 
362   int rv = result;
363   do {
364     State state = next_state_;
365     next_state_ = STATE_NONE;
366     switch (state) {
367       case STATE_BEGIN_CONNECT:
368         DCHECK_EQ(OK, rv);
369         rv = DoBeginConnect();
370         break;
371       case STATE_TRANSPORT_CONNECT:
372         DCHECK_EQ(OK, rv);
373         rv = DoTransportConnect();
374         break;
375       case STATE_TRANSPORT_CONNECT_COMPLETE:
376         rv = DoTransportConnectComplete(rv);
377         break;
378       case STATE_HTTP_PROXY_CONNECT:
379         DCHECK_EQ(OK, rv);
380         rv = DoHttpProxyConnect();
381         break;
382       case STATE_HTTP_PROXY_CONNECT_COMPLETE:
383         rv = DoHttpProxyConnectComplete(rv);
384         break;
385       case STATE_SPDY_PROXY_CREATE_STREAM:
386         DCHECK_EQ(OK, rv);
387         rv = DoSpdyProxyCreateStream();
388         break;
389       case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
390         rv = DoSpdyProxyCreateStreamComplete(rv);
391         break;
392       case STATE_QUIC_PROXY_CREATE_SESSION:
393         DCHECK_EQ(OK, rv);
394         rv = DoQuicProxyCreateSession();
395         break;
396       case STATE_QUIC_PROXY_CREATE_STREAM:
397         rv = DoQuicProxyCreateStream(rv);
398         break;
399       case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE:
400         rv = DoQuicProxyCreateStreamComplete(rv);
401         break;
402       case STATE_RESTART_WITH_AUTH:
403         DCHECK_EQ(OK, rv);
404         rv = DoRestartWithAuth();
405         break;
406       case STATE_RESTART_WITH_AUTH_COMPLETE:
407         rv = DoRestartWithAuthComplete(rv);
408         break;
409       default:
410         NOTREACHED() << "bad state";
411         rv = ERR_FAILED;
412         break;
413     }
414   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
415 
416   return rv;
417 }
418 
DoBeginConnect()419 int HttpProxyConnectJob::DoBeginConnect() {
420   connect_start_time_ = base::TimeTicks::Now();
421   ResetTimer(
422       AlternateNestedConnectionTimeout(*params_, network_quality_estimator()));
423   switch (GetProxyServerScheme()) {
424     case ProxyServer::SCHEME_QUIC:
425       next_state_ = STATE_QUIC_PROXY_CREATE_SESSION;
426       // QUIC connections are always considered to have been established.
427       // |has_established_connection_| is only used to start retries if a
428       // connection hasn't been established yet, and QUIC has its own connection
429       // establishment logic.
430       has_established_connection_ = true;
431       break;
432     case ProxyServer::SCHEME_HTTP:
433     case ProxyServer::SCHEME_HTTPS:
434       next_state_ = STATE_TRANSPORT_CONNECT;
435       break;
436     default:
437       NOTREACHED();
438   }
439   return OK;
440 }
441 
DoTransportConnect()442 int HttpProxyConnectJob::DoTransportConnect() {
443   ProxyServer::Scheme scheme = GetProxyServerScheme();
444   if (scheme == ProxyServer::SCHEME_HTTP) {
445     nested_connect_job_ = std::make_unique<TransportConnectJob>(
446         priority(), socket_tag(), common_connect_job_params(),
447         params_->transport_params(), this, &net_log());
448   } else {
449     DCHECK_EQ(scheme, ProxyServer::SCHEME_HTTPS);
450     DCHECK(params_->ssl_params());
451     // Skip making a new connection if we have an existing HTTP/2 session.
452     if (params_->tunnel() &&
453         common_connect_job_params()->spdy_session_pool->FindAvailableSession(
454             CreateSpdySessionKey(), /*enable_ip_based_pooling=*/false,
455             /*is_websocket=*/false, net_log())) {
456       next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
457       return OK;
458     }
459 
460     nested_connect_job_ = std::make_unique<SSLConnectJob>(
461         priority(), socket_tag(), common_connect_job_params(),
462         params_->ssl_params(), this, &net_log());
463   }
464 
465   next_state_ = STATE_TRANSPORT_CONNECT_COMPLETE;
466   return nested_connect_job_->Connect();
467 }
468 
DoTransportConnectComplete(int result)469 int HttpProxyConnectJob::DoTransportConnectComplete(int result) {
470   resolve_error_info_ = nested_connect_job_->GetResolveErrorInfo();
471   ProxyServer::Scheme scheme = GetProxyServerScheme();
472   if (result != OK) {
473     base::UmaHistogramMediumTimes(
474         scheme == ProxyServer::SCHEME_HTTP
475             ? "Net.HttpProxy.ConnectLatency.Insecure.Error"
476             : "Net.HttpProxy.ConnectLatency.Secure.Error",
477         base::TimeTicks::Now() - connect_start_time_);
478 
479     if (IsCertificateError(result)) {
480       DCHECK_EQ(ProxyServer::SCHEME_HTTPS, scheme);
481       // TODO(rch): allow the user to deal with proxy cert errors in the
482       // same way as server cert errors.
483       return ERR_PROXY_CERTIFICATE_INVALID;
484     }
485 
486     if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
487       DCHECK_EQ(ProxyServer::SCHEME_HTTPS, scheme);
488       ssl_cert_request_info_ = nested_connect_job_->GetCertRequestInfo();
489       if (params_->proxy_chain().is_multi_proxy() && !ssl_cert_request_info_) {
490         // When multi-proxy chains are in use, it's possible that a client auth
491         // cert is requested by the first proxy after the transport connection
492         // to it has been established. When this occurs,
493         // ERR_SSL_CLIENT_AUTH_CERT_NEEDED will get passed back to the parent
494         // SSLConnectJob and then to the parent HttpProxyConnectJob, but the SSL
495         // cert request info won't have been set up for the parent
496         // HttpProxyConnectJob to use it in this method. Fail gracefully when
497         // this case is encountered.
498         // TODO(https://crbug.com/1491092): Investigate whether changes are
499         // needed to support making the SSL cert request info available here in
500         // the case described above. Just returning `result` here makes the
501         // behavior for multi-proxy chains match that of single-proxy chains
502         // (where the proxied request fails with ERR_SSL_CLIENT_AUTH_CERT_NEEDED
503         // and no `SSLCertRequestInfo` is available from the corresponding
504         // `ResponseInfo`), though, so it could be that no further action is
505         // needed here.
506         return result;
507       }
508       DCHECK(ssl_cert_request_info_);
509       ssl_cert_request_info_->is_proxy = true;
510       return result;
511     }
512 
513     // If this transport connection was attempting to be made through other
514     // proxies, prefer to propagate errors from attempting to establish the
515     // previous proxy connection(s) instead of returning
516     // `ERR_PROXY_CONNECTION_FAILED`. For instance, if the attempt to connect to
517     // the first proxy resulted in `ERR_PROXY_HTTP_1_1_REQUIRED`, return that so
518     // that the whole job will be restarted using HTTP/1.1.
519     if (params_->proxy_chain_index() != 0) {
520       return result;
521     }
522 
523     return ERR_PROXY_CONNECTION_FAILED;
524   }
525 
526   base::UmaHistogramMediumTimes(
527       scheme == ProxyServer::SCHEME_HTTP
528           ? "Net.HttpProxy.ConnectLatency.Insecure.Success"
529           : "Net.HttpProxy.ConnectLatency.Secure.Success",
530       base::TimeTicks::Now() - connect_start_time_);
531 
532   has_established_connection_ = true;
533 
534   if (!params_->tunnel()) {
535     // If not tunneling, this is an HTTP URL being fetched directly over the
536     // proxy. Return the underlying socket directly. The caller will handle the
537     // ALPN protocol, etc., from here. Clear the DNS aliases to match the other
538     // proxy codepaths.
539     SetSocket(nested_connect_job_->PassSocket(),
540               /*dns_aliases=*/std::set<std::string>());
541     return result;
542   }
543 
544   // Establish a tunnel over the proxy by making a CONNECT request. HTTP/1.1 and
545   // HTTP/2 handle CONNECT differently.
546   if (nested_connect_job_->socket()->GetNegotiatedProtocol() == kProtoHTTP2) {
547     DCHECK_EQ(ProxyServer::SCHEME_HTTPS, scheme);
548     next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
549   } else {
550     next_state_ = STATE_HTTP_PROXY_CONNECT;
551   }
552   return result;
553 }
554 
DoHttpProxyConnect()555 int HttpProxyConnectJob::DoHttpProxyConnect() {
556   DCHECK(params_->tunnel());
557   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
558 
559   // Reset the timer to just the length of time allowed for HttpProxy handshake
560   // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
561   // longer to timeout than it should.
562   ResetTimer(kHttpProxyConnectJobTunnelTimeout);
563 
564   // Add a HttpProxy connection on top of the tcp socket.
565   transport_socket_ = std::make_unique<HttpProxyClientSocket>(
566       nested_connect_job_->PassSocket(), GetUserAgent(), params_->endpoint(),
567       params_->proxy_chain(), params_->proxy_chain_index(),
568       http_auth_controller_, common_connect_job_params()->proxy_delegate,
569       params_->traffic_annotation());
570   nested_connect_job_.reset();
571   return transport_socket_->Connect(base::BindOnce(
572       &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
573 }
574 
DoHttpProxyConnectComplete(int result)575 int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
576   // Always inform caller of auth requests asynchronously.
577   if (result == ERR_PROXY_AUTH_REQUESTED) {
578     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
579         FROM_HERE, base::BindOnce(&HttpProxyConnectJob::OnAuthChallenge,
580                                   weak_ptr_factory_.GetWeakPtr()));
581     return ERR_IO_PENDING;
582   }
583 
584   if (result == ERR_HTTP_1_1_REQUIRED)
585     return ERR_PROXY_HTTP_1_1_REQUIRED;
586 
587   // In TLS 1.2 with False Start or TLS 1.3, alerts from the server rejecting
588   // our client certificate are received at the first Read(), not Connect(), so
589   // the error mapping in DoTransportConnectComplete does not apply. Repeat the
590   // mapping here.
591   if (result == ERR_BAD_SSL_CLIENT_AUTH_CERT)
592     return ERR_PROXY_CONNECTION_FAILED;
593 
594   if (result == OK) {
595     SetSocket(std::move(transport_socket_), /*dns_aliases=*/absl::nullopt);
596   }
597 
598   return result;
599 }
600 
DoSpdyProxyCreateStream()601 int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
602   DCHECK(params_->tunnel());
603   DCHECK(params_->ssl_params());
604 
605   // Reset the timer to just the length of time allowed for HttpProxy handshake
606   // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
607   // longer to timeout than it should.
608   ResetTimer(kHttpProxyConnectJobTunnelTimeout);
609 
610   SpdySessionKey key = CreateSpdySessionKey();
611   base::WeakPtr<SpdySession> spdy_session =
612       common_connect_job_params()->spdy_session_pool->FindAvailableSession(
613           key, /* enable_ip_based_pooling = */ false,
614           /* is_websocket = */ false, net_log());
615   // It's possible that a session to the proxy has recently been created
616   if (spdy_session) {
617     nested_connect_job_.reset();
618   } else {
619     // Create a session direct to the proxy itself
620     spdy_session = common_connect_job_params()
621                        ->spdy_session_pool->CreateAvailableSessionFromSocket(
622                            key, nested_connect_job_->PassSocket(),
623                            nested_connect_job_->connect_timing(), net_log());
624     DCHECK(spdy_session);
625     nested_connect_job_.reset();
626   }
627 
628   next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
629   spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
630   return spdy_stream_request_->StartRequest(
631       SPDY_BIDIRECTIONAL_STREAM, spdy_session,
632       GURL("https://" + params_->endpoint().ToString()),
633       false /* no early data */, kH2QuicTunnelPriority, socket_tag(),
634       spdy_session->net_log(),
635       base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
636                      base::Unretained(this)),
637       params_->traffic_annotation());
638 }
639 
DoSpdyProxyCreateStreamComplete(int result)640 int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) {
641   if (result < 0) {
642     // See the comment in DoHttpProxyConnectComplete(). HTTP/2 proxies will
643     // typically also fail here, as a result of SpdyProxyClientSocket::Connect()
644     // below, but the error may surface out of SpdyStreamRequest if there were
645     // enough requests in parallel that stream creation became asynchronous.
646     if (result == ERR_BAD_SSL_CLIENT_AUTH_CERT)
647       result = ERR_PROXY_CONNECTION_FAILED;
648 
649     spdy_stream_request_.reset();
650     return result;
651   }
652 
653   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
654   base::WeakPtr<SpdyStream> stream = spdy_stream_request_->ReleaseStream();
655   spdy_stream_request_.reset();
656   DCHECK(stream.get());
657   // |transport_socket_| will set itself as |stream|'s delegate.
658   transport_socket_ = std::make_unique<SpdyProxyClientSocket>(
659       stream, params_->proxy_chain(), params_->proxy_chain_index(),
660       GetUserAgent(), params_->endpoint(), net_log(), http_auth_controller_,
661       common_connect_job_params()->proxy_delegate);
662   return transport_socket_->Connect(base::BindOnce(
663       &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
664 }
665 
DoQuicProxyCreateSession()666 int HttpProxyConnectJob::DoQuicProxyCreateSession() {
667   SSLSocketParams* ssl_params = params_->ssl_params().get();
668   DCHECK(ssl_params);
669   DCHECK(params_->tunnel());
670   DCHECK(!common_connect_job_params()->quic_supported_versions->empty());
671 
672   // Reset the timer to just the length of time allowed for HttpProxy handshake
673   // so that a fast QUIC connection plus a slow tunnel setup doesn't take longer
674   // to timeout than it should.
675   ResetTimer(kHttpProxyConnectJobTunnelTimeout);
676 
677   next_state_ = STATE_QUIC_PROXY_CREATE_STREAM;
678   const HostPortPair& proxy_server = params_->proxy_server().host_port_pair();
679   quic_stream_request_ = std::make_unique<QuicStreamRequest>(
680       common_connect_job_params()->quic_stream_factory);
681 
682   // Use default QUIC version, which is the version listed supported version.
683   quic::ParsedQuicVersion quic_version =
684       common_connect_job_params()->quic_supported_versions->front();
685   return quic_stream_request_->Request(
686       // TODO(crbug.com/1206799) Pass the destination directly once it's
687       // converted to contain scheme.
688       url::SchemeHostPort(url::kHttpsScheme, proxy_server.host(),
689                           proxy_server.port()),
690       quic_version, ssl_params->privacy_mode(), kH2QuicTunnelPriority,
691       socket_tag(), params_->network_anonymization_key(),
692       params_->secure_dns_policy(),
693       /*use_dns_aliases=*/false, /*require_dns_https_alpn=*/false,
694       ssl_params->ssl_config().GetCertVerifyFlags(),
695       GURL("https://" + proxy_server.ToString()), net_log(),
696       &quic_net_error_details_,
697       /*failed_on_default_network_callback=*/CompletionOnceCallback(),
698       base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
699                      base::Unretained(this)));
700 }
701 
DoQuicProxyCreateStream(int result)702 int HttpProxyConnectJob::DoQuicProxyCreateStream(int result) {
703   if (result < 0) {
704     quic_stream_request_.reset();
705     return result;
706   }
707 
708   next_state_ = STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE;
709   quic_session_ = quic_stream_request_->ReleaseSessionHandle();
710   quic_stream_request_.reset();
711 
712   return quic_session_->RequestStream(
713       false,
714       base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
715                      base::Unretained(this)),
716       params_->traffic_annotation());
717 }
718 
DoQuicProxyCreateStreamComplete(int result)719 int HttpProxyConnectJob::DoQuicProxyCreateStreamComplete(int result) {
720   if (result < 0)
721     return result;
722 
723   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
724   std::unique_ptr<QuicChromiumClientStream::Handle> quic_stream =
725       quic_session_->ReleaseStream();
726 
727   uint8_t urgency = ConvertRequestPriorityToQuicPriority(kH2QuicTunnelPriority);
728   quic_stream->SetPriority(quic::QuicStreamPriority(
729       quic::HttpStreamPriority{urgency, kDefaultPriorityIncremental}));
730 
731   transport_socket_ = std::make_unique<QuicProxyClientSocket>(
732       std::move(quic_stream), std::move(quic_session_), params_->proxy_chain(),
733       params_->proxy_chain_index(), GetUserAgent(), params_->endpoint(),
734       net_log(), http_auth_controller_,
735       common_connect_job_params()->proxy_delegate);
736   return transport_socket_->Connect(base::BindOnce(
737       &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
738 }
739 
DoRestartWithAuth()740 int HttpProxyConnectJob::DoRestartWithAuth() {
741   DCHECK(transport_socket_);
742 
743   // Start the timeout timer again.
744   ResetTimer(kHttpProxyConnectJobTunnelTimeout);
745 
746   next_state_ = STATE_RESTART_WITH_AUTH_COMPLETE;
747   return transport_socket_->RestartWithAuth(base::BindOnce(
748       &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
749 }
750 
DoRestartWithAuthComplete(int result)751 int HttpProxyConnectJob::DoRestartWithAuthComplete(int result) {
752   DCHECK_NE(ERR_IO_PENDING, result);
753 
754   if (result == OK && !transport_socket_->IsConnected())
755     result = ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
756 
757   // If the connection could not be reused to attempt to send proxy auth
758   // credentials, try reconnecting. Do not reset the HttpAuthController in this
759   // case; the server may, for instance, send "Proxy-Connection: close" and
760   // expect that each leg of the authentication progress on separate
761   // connections.
762   bool reconnect = result == ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
763 
764   // If auth credentials were sent but the connection was closed, the server may
765   // have timed out while the user was selecting credentials. Retry once.
766   if (!has_restarted_ &&
767       (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET ||
768        result == ERR_CONNECTION_ABORTED ||
769        result == ERR_SOCKET_NOT_CONNECTED)) {
770     reconnect = true;
771     has_restarted_ = true;
772 
773     // Release any auth state bound to the connection. The new connection will
774     // start the current scheme and identity from scratch.
775     if (http_auth_controller_)
776       http_auth_controller_->OnConnectionClosed();
777   }
778 
779   if (reconnect) {
780     // Attempt to create a new one.
781     transport_socket_.reset();
782     next_state_ = STATE_BEGIN_CONNECT;
783     return OK;
784   }
785 
786   // If not reconnecting, treat the result as the result of establishing a
787   // tunnel through the proxy. This is important in the case another auth
788   // challenge is seen.
789   next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
790   return result;
791 }
792 
ChangePriorityInternal(RequestPriority priority)793 void HttpProxyConnectJob::ChangePriorityInternal(RequestPriority priority) {
794   // Do not set the priority on |spdy_stream_request_| or
795   // |quic_stream_request_|, since those should always use
796   // kH2QuicTunnelPriority.
797   if (nested_connect_job_)
798     nested_connect_job_->ChangePriority(priority);
799 
800   if (transport_socket_)
801     transport_socket_->SetStreamPriority(priority);
802 }
803 
OnTimedOutInternal()804 void HttpProxyConnectJob::OnTimedOutInternal() {
805   if (next_state_ == STATE_TRANSPORT_CONNECT_COMPLETE) {
806     base::UmaHistogramMediumTimes(
807         GetProxyServerScheme() == ProxyServer::SCHEME_HTTP
808             ? "Net.HttpProxy.ConnectLatency.Insecure.TimedOut"
809             : "Net.HttpProxy.ConnectLatency.Secure.TimedOut",
810         base::TimeTicks::Now() - connect_start_time_);
811   }
812 }
813 
OnAuthChallenge()814 void HttpProxyConnectJob::OnAuthChallenge() {
815   // Stop timer while potentially waiting for user input.
816   ResetTimer(base::TimeDelta());
817 
818   NotifyDelegateOfProxyAuth(
819       *transport_socket_->GetConnectResponseInfo(),
820       transport_socket_->GetAuthController().get(),
821       base::BindOnce(&HttpProxyConnectJob::RestartWithAuthCredentials,
822                      weak_ptr_factory_.GetWeakPtr()));
823 }
824 
GetUserAgent() const825 std::string HttpProxyConnectJob::GetUserAgent() const {
826   if (!http_user_agent_settings())
827     return std::string();
828   return http_user_agent_settings()->GetUserAgent();
829 }
830 
CreateSpdySessionKey() const831 SpdySessionKey HttpProxyConnectJob::CreateSpdySessionKey() const {
832   // Construct the SpdySessionKey using a ProxyChain that corresponds to what we
833   // are sending the CONNECT to. For the first proxy server use
834   // `ProxyChain::Direct()`, and for the others use a proxy chain containing all
835   // proxy servers that we have already connected through.
836   std::vector<ProxyServer> intermediate_proxy_servers;
837   for (size_t proxy_index = 0; proxy_index < params_->proxy_chain_index();
838        ++proxy_index) {
839     intermediate_proxy_servers.push_back(
840         params_->proxy_chain().GetProxyServer(proxy_index));
841   }
842   ProxyChain session_key_proxy_chain(std::move(intermediate_proxy_servers));
843   if (params_->proxy_chain_index() == 0) {
844     DCHECK(session_key_proxy_chain.is_direct());
845   }
846   return SpdySessionKey(params_->proxy_server().host_port_pair(),
847                         session_key_proxy_chain, PRIVACY_MODE_DISABLED,
848                         SpdySessionKey::IsProxySession::kTrue, socket_tag(),
849                         params_->network_anonymization_key(),
850                         params_->secure_dns_policy());
851 }
852 
853 }  // namespace net
854