summaryrefslogtreecommitdiff
path: root/chromium/net/http/http_proxy_connect_job.cc
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 11:40:17 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 12:42:11 +0000
commit5d87695f37678f96492b258bbab36486c59866b4 (patch)
treebe9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/net/http/http_proxy_connect_job.cc
parent6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff)
downloadqtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/net/http/http_proxy_connect_job.cc')
-rw-r--r--chromium/net/http/http_proxy_connect_job.cc740
1 files changed, 626 insertions, 114 deletions
diff --git a/chromium/net/http/http_proxy_connect_job.cc b/chromium/net/http/http_proxy_connect_job.cc
index de7c2086b3c..42103498d4f 100644
--- a/chromium/net/http/http_proxy_connect_job.cc
+++ b/chromium/net/http/http_proxy_connect_job.cc
@@ -10,18 +10,23 @@
#include "base/callback.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
+#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/numerics/ranges.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
+#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "build/build_config.h"
+#include "net/base/http_user_agent_settings.h"
#include "net/base/net_errors.h"
-#include "net/http/http_proxy_client_socket_wrapper.h"
#include "net/log/net_log_source_type.h"
#include "net/log/net_log_with_source.h"
#include "net/nqe/network_quality_estimator.h"
+#include "net/quic/quic_http_utils.h"
+#include "net/quic/quic_proxy_client_socket.h"
+#include "net/quic/quic_stream_factory.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_client_socket.h"
@@ -125,186 +130,693 @@ HttpProxyTimeoutExperiments* GetProxyTimeoutExperiments() {
} // namespace
HttpProxySocketParams::HttpProxySocketParams(
- const scoped_refptr<TransportSocketParams>& transport_params,
- const scoped_refptr<SSLSocketParams>& ssl_params,
- quic::QuicTransportVersion quic_version,
- const std::string& user_agent,
+ scoped_refptr<TransportSocketParams> transport_params,
+ scoped_refptr<SSLSocketParams> ssl_params,
+ bool is_quic,
const HostPortPair& endpoint,
- HttpAuthCache* http_auth_cache,
- HttpAuthHandlerFactory* http_auth_handler_factory,
- SpdySessionPool* spdy_session_pool,
- QuicStreamFactory* quic_stream_factory,
bool is_trusted_proxy,
bool tunnel,
const NetworkTrafficAnnotationTag traffic_annotation)
- : transport_params_(transport_params),
- ssl_params_(ssl_params),
- quic_version_(quic_version),
- spdy_session_pool_(spdy_session_pool),
- quic_stream_factory_(quic_stream_factory),
- user_agent_(user_agent),
+ : transport_params_(std::move(transport_params)),
+ ssl_params_(std::move(ssl_params)),
+ is_quic_(is_quic),
endpoint_(endpoint),
- http_auth_cache_(tunnel ? http_auth_cache : NULL),
- http_auth_handler_factory_(tunnel ? http_auth_handler_factory : NULL),
is_trusted_proxy_(is_trusted_proxy),
tunnel_(tunnel),
traffic_annotation_(traffic_annotation) {
- // If doing a QUIC proxy, |quic_version| must not be
- // quic::QUIC_VERSION_UNSUPPORTED, and |ssl_params| must be valid while
- // |transport_params| is null. Otherwise, |quic_version| must be
- // quic::QUIC_VERSION_UNSUPPORTED, and exactly one of |transport_params| or
- // |ssl_params| must be set.
- DCHECK(quic_version_ == quic::QUIC_VERSION_UNSUPPORTED
- ? (bool)transport_params != (bool)ssl_params
- : !transport_params && ssl_params);
- // Exactly one of |transport_params_| and |ssl_params_| must be non-null.
+ // This is either a connection to an HTTP proxy or an SSL/QUIC proxy.
DCHECK(transport_params_ || ssl_params_);
DCHECK(!transport_params_ || !ssl_params_);
+
+ // If connecting to a QUIC proxy, and |ssl_params_| must be valid. This also
+ // implies |transport_params_| is null, per the above DCHECKs.
+ if (is_quic_)
+ DCHECK(ssl_params_);
}
HttpProxySocketParams::~HttpProxySocketParams() = default;
HttpProxyConnectJob::HttpProxyConnectJob(
RequestPriority priority,
- const CommonConnectJobParams& common_connect_job_params,
- const scoped_refptr<HttpProxySocketParams>& params,
- Delegate* delegate,
+ const SocketTag& socket_tag,
+ const CommonConnectJobParams* common_connect_job_params,
+ scoped_refptr<HttpProxySocketParams> params,
+ ConnectJob::Delegate* delegate,
const NetLogWithSource* net_log)
: ConnectJob(priority,
+ socket_tag,
base::TimeDelta() /* The socket takes care of timeouts */,
common_connect_job_params,
delegate,
net_log,
NetLogSourceType::HTTP_PROXY_CONNECT_JOB,
NetLogEventType::HTTP_PROXY_CONNECT_JOB_CONNECT),
- client_socket_(std::make_unique<HttpProxyClientSocketWrapper>(
- base::BindRepeating(&HttpProxyConnectJob::OnNeedsProxyAuth,
- base::Unretained(this)),
- priority,
- ConnectionTimeout(
- *params,
- common_connect_job_params.network_quality_estimator),
- kHttpProxyConnectJobTunnelTimeout,
- common_connect_job_params,
- params->transport_params(),
- params->ssl_params(),
- params->quic_version(),
- params->user_agent(),
- params->endpoint(),
- params->http_auth_cache(),
- params->http_auth_handler_factory(),
- params->spdy_session_pool(),
- params->quic_stream_factory(),
- params->is_trusted_proxy(),
- params->tunnel(),
- params->traffic_annotation(),
- this->net_log())),
- params_(std::move(params)) {}
-
-HttpProxyConnectJob::~HttpProxyConnectJob() = default;
+ params_(std::move(params)),
+ next_state_(STATE_NONE),
+ has_restarted_(false),
+ using_spdy_(false),
+ negotiated_protocol_(kProtoUnknown),
+ has_established_connection_(false),
+ http_auth_controller_(
+ params_->tunnel()
+ ? new HttpAuthController(
+ HttpAuth::AUTH_PROXY,
+ GURL((params_->ssl_params() ? "https://" : "http://") +
+ GetDestination().ToString()),
+ common_connect_job_params->http_auth_cache,
+ common_connect_job_params->http_auth_handler_factory,
+ host_resolver())
+ : nullptr),
+ weak_ptr_factory_(this) {}
+
+HttpProxyConnectJob::~HttpProxyConnectJob() {}
+
+const RequestPriority HttpProxyConnectJob::kH2QuicTunnelPriority =
+ DEFAULT_PRIORITY;
LoadState HttpProxyConnectJob::GetLoadState() const {
- return client_socket_->GetConnectLoadState();
+ switch (next_state_) {
+ case STATE_TCP_CONNECT_COMPLETE:
+ case STATE_SSL_CONNECT_COMPLETE:
+ return nested_connect_job_->GetLoadState();
+ case STATE_HTTP_PROXY_CONNECT:
+ case STATE_HTTP_PROXY_CONNECT_COMPLETE:
+ case STATE_SPDY_PROXY_CREATE_STREAM:
+ case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
+ case STATE_QUIC_PROXY_CREATE_SESSION:
+ case STATE_QUIC_PROXY_CREATE_STREAM:
+ case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE:
+ case STATE_RESTART_WITH_AUTH:
+ case STATE_RESTART_WITH_AUTH_COMPLETE:
+ return LOAD_STATE_ESTABLISHING_PROXY_TUNNEL;
+ // These states shouldn't be possible to be called in.
+ case STATE_TCP_CONNECT:
+ case STATE_SSL_CONNECT:
+
+ case STATE_BEGIN_CONNECT:
+ case STATE_NONE:
+ // May be possible for this method to be called after an error, shouldn't
+ // be called after a successful connect.
+ break;
+ }
+ return LOAD_STATE_IDLE;
}
bool HttpProxyConnectJob::HasEstablishedConnection() const {
- return client_socket_->HasEstablishedConnection();
+ if (has_established_connection_)
+ return true;
+
+ // It's possible the nested connect job has established a connection, but
+ // hasn't completed yet (For example, an SSLConnectJob may be negotiating
+ // SSL).
+ if (nested_connect_job_)
+ return nested_connect_job_->HasEstablishedConnection();
+ return false;
+}
+
+bool HttpProxyConnectJob::IsSSLError() const {
+ return ssl_cert_request_info_ != nullptr;
+}
+
+scoped_refptr<SSLCertRequestInfo> HttpProxyConnectJob::GetCertRequestInfo() {
+ return ssl_cert_request_info_;
+}
+
+void HttpProxyConnectJob::OnConnectJobComplete(int result, ConnectJob* job) {
+ DCHECK_EQ(nested_connect_job_.get(), job);
+ DCHECK(next_state_ == STATE_TCP_CONNECT_COMPLETE ||
+ next_state_ == STATE_SSL_CONNECT_COMPLETE);
+ OnIOComplete(result);
}
void HttpProxyConnectJob::OnNeedsProxyAuth(
const HttpResponseInfo& response,
HttpAuthController* auth_controller,
- base::OnceClosure restart_with_auth_callback) {
- NotifyDelegateOfProxyAuth(response, auth_controller,
- std::move(restart_with_auth_callback));
+ base::OnceClosure restart_with_auth_callback,
+ ConnectJob* job) {
+ // None of the nested ConnectJob used by this class can encounter auth
+ // challenges. Instead, the challenges are returned by the ProxyClientSocket
+ // implementations after nested_connect_job_ has already established a
+ // connection.
+ NOTREACHED();
}
-void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle* handle) {
- if (error_response_info_) {
- handle->set_ssl_error_response_info(*error_response_info_);
- handle->set_is_ssl_error(true);
- }
-}
-
-base::TimeDelta HttpProxyConnectJob::ConnectionTimeout(
+base::TimeDelta HttpProxyConnectJob::AlternateNestedConnectionTimeout(
const HttpProxySocketParams& params,
const NetworkQualityEstimator* network_quality_estimator) {
+ base::TimeDelta default_alternate_timeout;
+
+ // On Android and iOS, a default proxy connection timeout is used instead of
+ // the actual TCP/SSL timeouts of nested jobs.
+#if defined(OS_ANDROID) || defined(OS_IOS)
+ default_alternate_timeout = kHttpProxyConnectJobTunnelTimeout;
+#endif // !defined(OS_ANDROID) && !defined(OS_IOS)
+
bool is_https = params.ssl_params() != nullptr;
// HTTP proxy connections can't be on top of proxy connections.
DCHECK(!is_https ||
params.ssl_params()->GetConnectionType() == SSLSocketParams::DIRECT);
- if (network_quality_estimator) {
- base::Optional<base::TimeDelta> http_rtt_estimate =
- network_quality_estimator->GetHttpRTT();
- if (http_rtt_estimate) {
- int32_t multiplier =
- is_https
- ? GetProxyTimeoutExperiments()->ssl_http_rtt_multiplier()
- : GetProxyTimeoutExperiments()->non_ssl_http_rtt_multiplier();
- base::TimeDelta timeout = base::TimeDelta::FromMicroseconds(
- multiplier * http_rtt_estimate.value().InMicroseconds());
- // Ensure that connection timeout is between
- // |min_proxy_connection_timeout_| and |max_proxy_connection_timeout_|.
- return base::ClampToRange(
- timeout, GetProxyTimeoutExperiments()->min_proxy_connection_timeout(),
- GetProxyTimeoutExperiments()->max_proxy_connection_timeout());
+ if (!network_quality_estimator)
+ return default_alternate_timeout;
+
+ base::Optional<base::TimeDelta> http_rtt_estimate =
+ network_quality_estimator->GetHttpRTT();
+ if (!http_rtt_estimate)
+ return default_alternate_timeout;
+
+ int32_t multiplier =
+ is_https ? GetProxyTimeoutExperiments()->ssl_http_rtt_multiplier()
+ : GetProxyTimeoutExperiments()->non_ssl_http_rtt_multiplier();
+ base::TimeDelta timeout = multiplier * http_rtt_estimate.value();
+ // Ensure that connection timeout is between
+ // |min_proxy_connection_timeout_| and |max_proxy_connection_timeout_|.
+ return base::ClampToRange(
+ timeout, GetProxyTimeoutExperiments()->min_proxy_connection_timeout(),
+ GetProxyTimeoutExperiments()->max_proxy_connection_timeout());
+}
+
+base::TimeDelta HttpProxyConnectJob::TunnelTimeoutForTesting() {
+ return kHttpProxyConnectJobTunnelTimeout;
+}
+
+void HttpProxyConnectJob::UpdateFieldTrialParametersForTesting() {
+ GetProxyTimeoutExperiments()->Init();
+}
+
+int HttpProxyConnectJob::ConnectInternal() {
+ DCHECK_EQ(next_state_, STATE_NONE);
+ next_state_ = STATE_BEGIN_CONNECT;
+ int result = DoLoop(OK);
+ if (result != ERR_IO_PENDING)
+ HandleConnectResult(result);
+ return result;
+}
+
+ProxyServer::Scheme HttpProxyConnectJob::GetProxyServerScheme() const {
+ if (params_->is_quic())
+ return ProxyServer::SCHEME_QUIC;
+
+ if (params_->transport_params())
+ return ProxyServer::SCHEME_HTTP;
+
+ return ProxyServer::SCHEME_HTTPS;
+}
+
+void HttpProxyConnectJob::OnIOComplete(int result) {
+ int rv = DoLoop(result);
+ if (rv != ERR_IO_PENDING) {
+ HandleConnectResult(rv);
+
+ // May delete |this|.
+ NotifyDelegateOfCompletion(rv);
+ }
+}
+
+void HttpProxyConnectJob::RestartWithAuthCredentials() {
+ DCHECK(transport_socket_);
+ DCHECK_EQ(STATE_NONE, next_state_);
+
+ // Always do this asynchronously, to avoid re-entrancy.
+ next_state_ = STATE_RESTART_WITH_AUTH;
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
+ weak_ptr_factory_.GetWeakPtr(), net::OK));
+}
+
+int HttpProxyConnectJob::DoLoop(int result) {
+ DCHECK_NE(next_state_, STATE_NONE);
+
+ int rv = result;
+ do {
+ State state = next_state_;
+ next_state_ = STATE_NONE;
+ switch (state) {
+ case STATE_BEGIN_CONNECT:
+ DCHECK_EQ(OK, rv);
+ rv = DoBeginConnect();
+ break;
+ case STATE_TCP_CONNECT:
+ DCHECK_EQ(OK, rv);
+ rv = DoTransportConnect();
+ break;
+ case STATE_TCP_CONNECT_COMPLETE:
+ rv = DoTransportConnectComplete(rv);
+ break;
+ case STATE_SSL_CONNECT:
+ DCHECK_EQ(OK, rv);
+ rv = DoSSLConnect();
+ break;
+ case STATE_SSL_CONNECT_COMPLETE:
+ rv = DoSSLConnectComplete(rv);
+ break;
+ case STATE_HTTP_PROXY_CONNECT:
+ DCHECK_EQ(OK, rv);
+ rv = DoHttpProxyConnect();
+ break;
+ case STATE_HTTP_PROXY_CONNECT_COMPLETE:
+ rv = DoHttpProxyConnectComplete(rv);
+ break;
+ case STATE_SPDY_PROXY_CREATE_STREAM:
+ DCHECK_EQ(OK, rv);
+ rv = DoSpdyProxyCreateStream();
+ break;
+ case STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE:
+ rv = DoSpdyProxyCreateStreamComplete(rv);
+ break;
+ case STATE_QUIC_PROXY_CREATE_SESSION:
+ DCHECK_EQ(OK, rv);
+ rv = DoQuicProxyCreateSession();
+ break;
+ case STATE_QUIC_PROXY_CREATE_STREAM:
+ rv = DoQuicProxyCreateStream(rv);
+ break;
+ case STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE:
+ rv = DoQuicProxyCreateStreamComplete(rv);
+ break;
+ case STATE_RESTART_WITH_AUTH:
+ DCHECK_EQ(OK, rv);
+ rv = DoRestartWithAuth();
+ break;
+ case STATE_RESTART_WITH_AUTH_COMPLETE:
+ rv = DoRestartWithAuthComplete(rv);
+ break;
+ default:
+ NOTREACHED() << "bad state";
+ rv = ERR_FAILED;
+ break;
}
+ } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
+
+ return rv;
+}
+
+int HttpProxyConnectJob::DoBeginConnect() {
+ connect_start_time_ = base::TimeTicks::Now();
+ ResetTimer(
+ AlternateNestedConnectionTimeout(*params_, network_quality_estimator()));
+ switch (GetProxyServerScheme()) {
+ case ProxyServer::SCHEME_QUIC:
+ next_state_ = STATE_QUIC_PROXY_CREATE_SESSION;
+ // QUIC connections are always considered to have been established.
+ // |has_established_connection_| is only used to start retries if a
+ // connection hasn't been established yet, and QUIC has its own connection
+ // establishment logic.
+ has_established_connection_ = true;
+ break;
+ case ProxyServer::SCHEME_HTTP:
+ next_state_ = STATE_TCP_CONNECT;
+ break;
+ case ProxyServer::SCHEME_HTTPS:
+ next_state_ = STATE_SSL_CONNECT;
+ break;
+ default:
+ NOTREACHED();
+ }
+ return OK;
+}
+
+int HttpProxyConnectJob::DoTransportConnect() {
+ next_state_ = STATE_TCP_CONNECT_COMPLETE;
+ nested_connect_job_ = TransportConnectJob::CreateTransportConnectJob(
+ params_->transport_params(), priority(), socket_tag(),
+ common_connect_job_params(), this, &net_log());
+ return nested_connect_job_->Connect();
+}
+
+int HttpProxyConnectJob::DoTransportConnectComplete(int result) {
+ if (result != OK) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Error",
+ base::TimeTicks::Now() - connect_start_time_);
+ // This is a special error code meaning to reuse an existing SPDY session
+ // rather than use a fresh socket. Overriding it with a proxy error message
+ // would cause the request to fail, instead of switching to using the SPDY
+ // session.
+ if (result == ERR_SPDY_SESSION_ALREADY_EXISTS)
+ return result;
+ return ERR_PROXY_CONNECTION_FAILED;
+ }
+
+ has_established_connection_ = true;
+
+ next_state_ = STATE_HTTP_PROXY_CONNECT;
+ return result;
+}
+
+int HttpProxyConnectJob::DoSSLConnect() {
+ DCHECK(params_->ssl_params());
+ if (params_->tunnel()) {
+ SpdySessionKey key(
+ params_->ssl_params()->GetDirectConnectionParams()->destination(),
+ ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
+ SpdySessionKey::IsProxySession::kTrue, socket_tag());
+ if (common_connect_job_params()->spdy_session_pool->FindAvailableSession(
+ key, /* enable_ip_based_pooling = */ true,
+ /* is_websocket = */ false, net_log())) {
+ using_spdy_ = true;
+ next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
+ return OK;
+ }
+ }
+ next_state_ = STATE_SSL_CONNECT_COMPLETE;
+ nested_connect_job_ = std::make_unique<SSLConnectJob>(
+ priority(), socket_tag(), common_connect_job_params(),
+ params_->ssl_params(), this, &net_log());
+ return nested_connect_job_->Connect();
+}
+
+int HttpProxyConnectJob::DoSSLConnectComplete(int result) {
+ if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error",
+ base::TimeTicks::Now() - connect_start_time_);
+
+ ssl_cert_request_info_ = nested_connect_job_->GetCertRequestInfo();
+ DCHECK(ssl_cert_request_info_);
+ ssl_cert_request_info_->is_proxy = true;
+ return result;
+ }
+
+ if (IsCertificateError(result)) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error",
+ base::TimeTicks::Now() - connect_start_time_);
+ // TODO(rch): allow the user to deal with proxy cert errors in the
+ // same way as server cert errors.
+ return ERR_PROXY_CERTIFICATE_INVALID;
+ }
+ // A SPDY session to the proxy completed prior to resolving the proxy
+ // hostname. Surface this error, and allow the delegate to retry.
+ // See crbug.com/334413.
+ if (result == ERR_SPDY_SESSION_ALREADY_EXISTS) {
+ DCHECK(!nested_connect_job_->socket());
+ return ERR_SPDY_SESSION_ALREADY_EXISTS;
+ }
+ if (result < 0) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Error",
+ base::TimeTicks::Now() - connect_start_time_);
+ return ERR_PROXY_CONNECTION_FAILED;
}
- // Return the default proxy connection timeout.
- base::TimeDelta nested_job_timeout;
-#if !defined(OS_ANDROID) && !defined(OS_IOS)
- if (is_https) {
- nested_job_timeout = SSLConnectJob::ConnectionTimeout(
- *params.ssl_params(), network_quality_estimator);
+ has_established_connection_ = true;
+
+ negotiated_protocol_ = nested_connect_job_->socket()->GetNegotiatedProtocol();
+ using_spdy_ = negotiated_protocol_ == kProtoHTTP2;
+
+ // Reset the timer to just the length of time allowed for HttpProxy handshake
+ // so that a fast SSL connection plus a slow HttpProxy failure doesn't take
+ // longer to timeout than it should.
+ ResetTimer(kHttpProxyConnectJobTunnelTimeout);
+
+ // TODO(rch): If we ever decide to implement a "trusted" SPDY proxy
+ // (one that we speak SPDY over SSL to, but to which we send HTTPS
+ // request directly instead of through CONNECT tunnels, then we
+ // need to add a predicate to this if statement so we fall through
+ // to the else case. (HttpProxyClientSocket currently acts as
+ // a "trusted" SPDY proxy).
+ if (using_spdy_ && params_->tunnel()) {
+ next_state_ = STATE_SPDY_PROXY_CREATE_STREAM;
} else {
- nested_job_timeout = TransportConnectJob::ConnectionTimeout();
+ next_state_ = STATE_HTTP_PROXY_CONNECT;
}
-#endif // !defined(OS_ANDROID) && !defined(OS_IOS)
+ return result;
+}
+
+int HttpProxyConnectJob::DoHttpProxyConnect() {
+ next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+
+ // Reset the timer to just the length of time allowed for HttpProxy handshake
+ // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
+ // longer to timeout than it should.
+ ResetTimer(kHttpProxyConnectJobTunnelTimeout);
- return nested_job_timeout + kHttpProxyConnectJobTunnelTimeout;
+ if (params_->transport_params()) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.Success",
+ base::TimeTicks::Now() - connect_start_time_);
+ } else {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.Success",
+ base::TimeTicks::Now() - connect_start_time_);
+ }
+
+ // Add a HttpProxy connection on top of the tcp socket.
+ transport_socket_ = client_socket_factory()->CreateProxyClientSocket(
+ nested_connect_job_->PassSocket(), GetUserAgent(), params_->endpoint(),
+ ProxyServer(GetProxyServerScheme(), GetDestination()),
+ http_auth_controller_.get(), params_->tunnel(), using_spdy_,
+ negotiated_protocol_, common_connect_job_params()->proxy_delegate,
+ params_->ssl_params() != nullptr, params_->traffic_annotation());
+ nested_connect_job_.reset();
+ return transport_socket_->Connect(base::BindOnce(
+ &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
}
-base::TimeDelta HttpProxyConnectJob::TunnelTimeoutForTesting() {
- return kHttpProxyConnectJobTunnelTimeout;
+int HttpProxyConnectJob::DoHttpProxyConnectComplete(int result) {
+ // Always inform caller of auth requests asynchronously.
+ if (result == ERR_PROXY_AUTH_REQUESTED) {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(
+ FROM_HERE, base::BindOnce(&HttpProxyConnectJob::OnAuthChallenge,
+ weak_ptr_factory_.GetWeakPtr()));
+ return ERR_IO_PENDING;
+ }
+
+ if (result == ERR_HTTP_1_1_REQUIRED)
+ return ERR_PROXY_HTTP_1_1_REQUIRED;
+
+ return result;
}
-void HttpProxyConnectJob::UpdateFieldTrialParametersForTesting() {
- GetProxyTimeoutExperiments()->Init();
+int HttpProxyConnectJob::DoSpdyProxyCreateStream() {
+ DCHECK(using_spdy_);
+ DCHECK(params_->tunnel());
+ DCHECK(params_->ssl_params());
+
+ // Reset the timer to just the length of time allowed for HttpProxy handshake
+ // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
+ // longer to timeout than it should.
+ ResetTimer(kHttpProxyConnectJobTunnelTimeout);
+
+ SpdySessionKey key(
+ params_->ssl_params()->GetDirectConnectionParams()->destination(),
+ ProxyServer::Direct(), PRIVACY_MODE_DISABLED,
+ SpdySessionKey::IsProxySession::kTrue, socket_tag());
+ base::WeakPtr<SpdySession> spdy_session =
+ common_connect_job_params()->spdy_session_pool->FindAvailableSession(
+ key, /* enable_ip_based_pooling = */ true,
+ /* is_websocket = */ false, net_log());
+ // It's possible that a session to the proxy has recently been created
+ if (spdy_session) {
+ nested_connect_job_.reset();
+ } else {
+ // Create a session direct to the proxy itself
+ spdy_session = common_connect_job_params()
+ ->spdy_session_pool->CreateAvailableSessionFromSocket(
+ key, params_->is_trusted_proxy(),
+ nested_connect_job_->PassSocket(),
+ nested_connect_job_->connect_timing(), net_log());
+ DCHECK(spdy_session);
+ nested_connect_job_.reset();
+ }
+
+ next_state_ = STATE_SPDY_PROXY_CREATE_STREAM_COMPLETE;
+ spdy_stream_request_ = std::make_unique<SpdyStreamRequest>();
+ return spdy_stream_request_->StartRequest(
+ SPDY_BIDIRECTIONAL_STREAM, spdy_session,
+ GURL("https://" + params_->endpoint().ToString()),
+ false /* no early data */, kH2QuicTunnelPriority, socket_tag(),
+ spdy_session->net_log(),
+ base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
+ base::Unretained(this)),
+ params_->traffic_annotation());
}
-int HttpProxyConnectJob::ConnectInternal() {
- int result = client_socket_->Connect(base::BindOnce(
- &HttpProxyConnectJob::OnConnectComplete, base::Unretained(this)));
- return HandleConnectResult(result);
+int HttpProxyConnectJob::DoSpdyProxyCreateStreamComplete(int result) {
+ if (result < 0) {
+ spdy_stream_request_.reset();
+ return result;
+ }
+
+ next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+ base::WeakPtr<SpdyStream> stream = spdy_stream_request_->ReleaseStream();
+ spdy_stream_request_.reset();
+ DCHECK(stream.get());
+ // |transport_socket_| will set itself as |stream|'s delegate.
+ transport_socket_ = std::make_unique<SpdyProxyClientSocket>(
+ stream, GetUserAgent(), params_->endpoint(), net_log(),
+ http_auth_controller_.get());
+ return transport_socket_->Connect(base::BindOnce(
+ &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
}
-void HttpProxyConnectJob::ChangePriorityInternal(RequestPriority priority) {
- if (client_socket_)
- client_socket_->SetPriority(priority);
+int HttpProxyConnectJob::DoQuicProxyCreateSession() {
+ SSLSocketParams* ssl_params = params_->ssl_params().get();
+ DCHECK(ssl_params);
+ DCHECK(params_->tunnel());
+ DCHECK(!common_connect_job_params()->quic_supported_versions->empty());
+
+ // Reset the timer to just the length of time allowed for HttpProxy handshake
+ // so that a fast TCP connection plus a slow HttpProxy failure doesn't take
+ // longer to timeout than it should.
+ ResetTimer(kHttpProxyConnectJobTunnelTimeout);
+
+ next_state_ = STATE_QUIC_PROXY_CREATE_STREAM;
+ const HostPortPair& proxy_server =
+ ssl_params->GetDirectConnectionParams()->destination();
+ quic_stream_request_ = std::make_unique<QuicStreamRequest>(
+ common_connect_job_params()->quic_stream_factory);
+
+ // Use default QUIC version, which is the version listed supported version.
+ quic::QuicTransportVersion quic_version =
+ common_connect_job_params()->quic_supported_versions->front();
+ return quic_stream_request_->Request(
+ proxy_server, quic_version, ssl_params->privacy_mode(),
+ kH2QuicTunnelPriority, socket_tag(),
+ ssl_params->ssl_config().GetCertVerifyFlags(),
+ GURL("https://" + proxy_server.ToString()), net_log(),
+ &quic_net_error_details_,
+ /*failed_on_default_network_callback=*/CompletionOnceCallback(),
+ base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
+ base::Unretained(this)));
+}
+
+int HttpProxyConnectJob::DoQuicProxyCreateStream(int result) {
+ if (result < 0) {
+ quic_stream_request_.reset();
+ return result;
+ }
+
+ next_state_ = STATE_QUIC_PROXY_CREATE_STREAM_COMPLETE;
+ quic_session_ = quic_stream_request_->ReleaseSessionHandle();
+ quic_stream_request_.reset();
+
+ return quic_session_->RequestStream(
+ false,
+ base::BindOnce(&HttpProxyConnectJob::OnIOComplete,
+ base::Unretained(this)),
+ params_->traffic_annotation());
}
-void HttpProxyConnectJob::OnConnectComplete(int result) {
+int HttpProxyConnectJob::DoQuicProxyCreateStreamComplete(int result) {
+ if (result < 0)
+ return result;
+
+ next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+ std::unique_ptr<QuicChromiumClientStream::Handle> quic_stream =
+ quic_session_->ReleaseStream();
+
+ spdy::SpdyPriority spdy_priority =
+ ConvertRequestPriorityToQuicPriority(kH2QuicTunnelPriority);
+ quic_stream->SetPriority(spdy_priority);
+
+ transport_socket_ = std::make_unique<QuicProxyClientSocket>(
+ std::move(quic_stream), std::move(quic_session_), GetUserAgent(),
+ params_->endpoint(), net_log(), http_auth_controller_.get());
+ return transport_socket_->Connect(base::BindOnce(
+ &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
+}
+
+int HttpProxyConnectJob::DoRestartWithAuth() {
+ DCHECK(transport_socket_);
+
+ // Start the timeout timer again.
+ ResetTimer(kHttpProxyConnectJobTunnelTimeout);
+
+ next_state_ = STATE_RESTART_WITH_AUTH_COMPLETE;
+ return transport_socket_->RestartWithAuth(base::BindOnce(
+ &HttpProxyConnectJob::OnIOComplete, base::Unretained(this)));
+}
+
+int HttpProxyConnectJob::DoRestartWithAuthComplete(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
- result = HandleConnectResult(result);
- if (result != ERR_IO_PENDING) {
- NotifyDelegateOfCompletion(result);
- // |this| will have been deleted at this point.
+
+ if (result == OK && !transport_socket_->IsConnected())
+ result = ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
+
+ // If the connection could not be reused to attempt to send proxy auth
+ // credentials, try reconnecting. Do not reset the HttpAuthController in this
+ // case; the server may, for instance, send "Proxy-Connection: close" and
+ // expect that each leg of the authentication progress on separate
+ // connections.
+ bool reconnect = result == ERR_UNABLE_TO_REUSE_CONNECTION_FOR_PROXY_AUTH;
+
+ // If auth credentials were sent but the connection was closed, the server may
+ // have timed out while the user was selecting credentials. Retry once.
+ if (!has_restarted_ &&
+ (result == ERR_CONNECTION_CLOSED || result == ERR_CONNECTION_RESET ||
+ result == ERR_CONNECTION_ABORTED ||
+ result == ERR_SOCKET_NOT_CONNECTED)) {
+ reconnect = true;
+ has_restarted_ = true;
+
+ // Release any auth state bound to the connection. The new connection will
+ // start the current scheme and identity from scratch.
+ if (http_auth_controller_)
+ http_auth_controller_->OnConnectionClosed();
+ }
+
+ if (reconnect) {
+ // Attempt to create a new one.
+ transport_socket_.reset();
+ using_spdy_ = false;
+ negotiated_protocol_ = NextProto();
+ next_state_ = STATE_BEGIN_CONNECT;
+ return OK;
}
+
+ // If not reconnecting, treat the result as the result of establishing a
+ // tunnel through the proxy. This is important in the case another auth
+ // challenge is seen.
+ next_state_ = STATE_HTTP_PROXY_CONNECT_COMPLETE;
+ return result;
}
-int HttpProxyConnectJob::HandleConnectResult(int result) {
- // Stop the timer. Only needed for the ERR_PROXY_AUTH_REQUESTED case, but
- // shouldn't be returning a result more than once, anyways.
- ResetTimer(base::TimeDelta());
+void HttpProxyConnectJob::ChangePriorityInternal(RequestPriority priority) {
+ // Do not set the priority on |spdy_stream_request_| or
+ // |quic_stream_request_|, since those should always use
+ // kH2QuicTunnelPriority.
+ if (nested_connect_job_)
+ nested_connect_job_->ChangePriority(priority);
+
+ if (transport_socket_)
+ transport_socket_->SetStreamPriority(priority);
+}
- if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
- error_response_info_ = client_socket_->GetAdditionalErrorState();
+void HttpProxyConnectJob::OnTimedOutInternal() {
+ if (next_state_ == STATE_TCP_CONNECT_COMPLETE) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Insecure.TimedOut",
+ base::TimeTicks::Now() - connect_start_time_);
+ } else if (next_state_ == STATE_SSL_CONNECT_COMPLETE) {
+ UMA_HISTOGRAM_MEDIUM_TIMES("Net.HttpProxy.ConnectLatency.Secure.TimedOut",
+ base::TimeTicks::Now() - connect_start_time_);
+ }
+}
+int HttpProxyConnectJob::HandleConnectResult(int result) {
if (result == OK || result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT)
- SetSocket(std::move(client_socket_));
+ SetSocket(std::move(transport_socket_));
return result;
}
+void HttpProxyConnectJob::OnAuthChallenge() {
+ // Stop timer while potentially waiting for user input.
+ ResetTimer(base::TimeDelta());
+
+ NotifyDelegateOfProxyAuth(
+ *transport_socket_->GetConnectResponseInfo(),
+ transport_socket_->GetAuthController().get(),
+ base::BindOnce(&HttpProxyConnectJob::RestartWithAuthCredentials,
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+const HostPortPair& HttpProxyConnectJob::GetDestination() {
+ if (params_->transport_params()) {
+ return params_->transport_params()->destination();
+ } else {
+ return params_->ssl_params()->GetDirectConnectionParams()->destination();
+ }
+}
+
+std::string HttpProxyConnectJob::GetUserAgent() const {
+ if (!http_user_agent_settings())
+ return std::string();
+ return http_user_agent_settings()->GetUserAgent();
+}
+
} // namespace net