blob: 8e41e1a94e2e333390f2ff938241d10e15875c36 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/http/http_proxy_client_socket_pool.h"
#include <algorithm>
#include <map>
#include <string>
#include <utility>
#include "base/compiler_specific.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/field_trial_params.h"
#include "base/optional.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "build/build_config.h"
#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
#include "net/http/http_network_session.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_provider.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_base.h"
#include "net/socket/ssl_client_socket.h"
#include "net/socket/ssl_client_socket_pool.h"
#include "net/socket/transport_client_socket_pool.h"
#include "net/spdy/spdy_proxy_client_socket.h"
#include "net/spdy/spdy_session.h"
#include "net/spdy/spdy_session_pool.h"
#include "net/spdy/spdy_stream.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "url/gurl.h"
namespace net {
namespace {
// HttpProxyConnectJobs will time out after this many seconds. Note this is on
// top of the timeout for the transport socket.
#if defined(OS_ANDROID) || defined(OS_IOS)
static const int kHttpProxyConnectJobTimeoutInSeconds = 10;
#else
static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
#endif
// Return the value of the parameter |param_name| for the field trial
// "NetAdaptiveProxyConnectionTimeout". If the value of the parameter is
// unavailable, then |default_value| is available.
int32_t GetInt32Param(const std::string& param_name, int32_t default_value) {
#if defined(STARBOARD)
return default_value;
#else
int32_t param;
if (!base::StringToInt(base::GetFieldTrialParamValue(
"NetAdaptiveProxyConnectionTimeout", param_name),
&param)) {
return default_value;
}
return param;
#endif
}
} // namespace
HttpProxySocketParams::HttpProxySocketParams(
const scoped_refptr<TransportSocketParams>& transport_params,
const scoped_refptr<SSLSocketParams>& ssl_params,
quic::QuicTransportVersion quic_version,
const std::string& user_agent,
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),
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.
DCHECK(transport_params_ || ssl_params_);
DCHECK(!transport_params_ || !ssl_params_);
}
const HostResolver::RequestInfo& HttpProxySocketParams::destination() const {
if (transport_params_.get() == NULL) {
return ssl_params_->GetDirectConnectionParams()->destination();
} else {
return transport_params_->destination();
}
}
HttpProxySocketParams::~HttpProxySocketParams() = default;
HttpProxyConnectJob::HttpProxyConnectJob(
const std::string& group_name,
RequestPriority priority,
const SocketTag& socket_tag,
ClientSocketPool::RespectLimits respect_limits,
const scoped_refptr<HttpProxySocketParams>& params,
const base::TimeDelta& timeout_duration,
TransportClientSocketPool* transport_pool,
SSLClientSocketPool* ssl_pool,
Delegate* delegate,
NetLog* net_log)
: ConnectJob(
group_name,
base::TimeDelta() /* The socket takes care of timeouts */,
priority,
socket_tag,
respect_limits,
delegate,
NetLogWithSource::Make(net_log,
NetLogSourceType::HTTP_PROXY_CONNECT_JOB)),
client_socket_(new HttpProxyClientSocketWrapper(
group_name,
priority,
socket_tag,
respect_limits,
timeout_duration,
base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds),
transport_pool,
ssl_pool,
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(),
#ifndef QUIC_DISABLED_FOR_STARBOARD
params->quic_stream_factory(),
#endif
params->is_trusted_proxy(),
params->tunnel(),
params->traffic_annotation(),
this->net_log())) {
}
HttpProxyConnectJob::~HttpProxyConnectJob() = default;
LoadState HttpProxyConnectJob::GetLoadState() const {
return client_socket_->GetConnectLoadState();
}
void HttpProxyConnectJob::GetAdditionalErrorState(ClientSocketHandle * handle) {
if (error_response_info_) {
handle->set_ssl_error_response_info(*error_response_info_);
handle->set_is_ssl_error(true);
}
}
int HttpProxyConnectJob::ConnectInternal() {
int result = client_socket_->Connect(base::Bind(
&HttpProxyConnectJob::OnConnectComplete, base::Unretained(this)));
return HandleConnectResult(result);
}
void HttpProxyConnectJob::OnConnectComplete(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
result = HandleConnectResult(result);
NotifyDelegateOfCompletion(result);
// |this| will have been deleted at this point.
}
int HttpProxyConnectJob::HandleConnectResult(int result) {
if (result == ERR_SSL_CLIENT_AUTH_CERT_NEEDED)
error_response_info_ = client_socket_->GetAdditionalErrorState();
if (result == OK || result == ERR_PROXY_AUTH_REQUESTED ||
result == ERR_HTTPS_PROXY_TUNNEL_RESPONSE_REDIRECT) {
SetSocket(std::move(client_socket_));
}
return result;
}
HttpProxyClientSocketPool::HttpProxyConnectJobFactory::
HttpProxyConnectJobFactory(TransportClientSocketPool* transport_pool,
SSLClientSocketPool* ssl_pool,
NetworkQualityProvider* network_quality_provider,
NetLog* net_log)
: transport_pool_(transport_pool),
ssl_pool_(ssl_pool),
network_quality_provider_(network_quality_provider),
ssl_http_rtt_multiplier_(GetInt32Param("ssl_http_rtt_multiplier", 10)),
non_ssl_http_rtt_multiplier_(
GetInt32Param("non_ssl_http_rtt_multiplier", 5)),
#if defined(OS_ANDROID) || defined(OS_IOS)
min_proxy_connection_timeout_(base::TimeDelta::FromSeconds(
GetInt32Param("min_proxy_connection_timeout_seconds", 8))),
max_proxy_connection_timeout_(base::TimeDelta::FromSeconds(
GetInt32Param("max_proxy_connection_timeout_seconds", 30))),
#else
min_proxy_connection_timeout_(base::TimeDelta::FromSeconds(
GetInt32Param("min_proxy_connection_timeout_seconds", 30))),
max_proxy_connection_timeout_(base::TimeDelta::FromSeconds(
GetInt32Param("max_proxy_connection_timeout_seconds", 60))),
#endif
net_log_(net_log) {
DCHECK_LT(0, ssl_http_rtt_multiplier_);
DCHECK_LT(0, non_ssl_http_rtt_multiplier_);
DCHECK_LE(base::TimeDelta(), min_proxy_connection_timeout_);
DCHECK_LE(base::TimeDelta(), max_proxy_connection_timeout_);
DCHECK_LE(min_proxy_connection_timeout_, max_proxy_connection_timeout_);
}
std::unique_ptr<ConnectJob>
HttpProxyClientSocketPool::HttpProxyConnectJobFactory::NewConnectJob(
const std::string& group_name,
const PoolBase::Request& request,
ConnectJob::Delegate* delegate) const {
bool is_secure_connection = (request.params()->ssl_params() != nullptr);
return std::unique_ptr<ConnectJob>(new HttpProxyConnectJob(
group_name, request.priority(), request.socket_tag(),
request.respect_limits(), request.params(),
ConnectionTimeoutWithConnectionProperty(is_secure_connection),
transport_pool_, ssl_pool_, delegate, net_log_));
}
base::TimeDelta
HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout()
const {
// Take a conservative approach: Return the timeout for the secure proxies
// which is higher than the connection timeout for the insecure proxies.
return ConnectionTimeoutWithConnectionProperty(
true /* is_secure_connection */);
}
base::TimeDelta HttpProxyClientSocketPool::HttpProxyConnectJobFactory::
ConnectionTimeoutWithConnectionProperty(bool is_secure_connection) const {
if (network_quality_provider_) {
base::Optional<base::TimeDelta> http_rtt_estimate =
network_quality_provider_->GetHttpRTT();
if (http_rtt_estimate) {
int32_t multiplier = is_secure_connection ? ssl_http_rtt_multiplier_
: 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_|.
if (timeout < min_proxy_connection_timeout_)
return min_proxy_connection_timeout_;
if (timeout > max_proxy_connection_timeout_)
return max_proxy_connection_timeout_;
return timeout;
}
}
// Return the default proxy connection timeout.
base::TimeDelta max_pool_timeout = base::TimeDelta();
#if (!defined(OS_ANDROID) && !defined(OS_IOS))
if (transport_pool_)
max_pool_timeout = transport_pool_->ConnectionTimeout();
if (ssl_pool_) {
max_pool_timeout =
std::max(max_pool_timeout, ssl_pool_->ConnectionTimeout());
}
#endif // !defined(OS_ANDROID) && !defined(OS_IOS)
return max_pool_timeout +
base::TimeDelta::FromSeconds(kHttpProxyConnectJobTimeoutInSeconds);
}
HttpProxyClientSocketPool::HttpProxyClientSocketPool(
int max_sockets,
int max_sockets_per_group,
TransportClientSocketPool* transport_pool,
SSLClientSocketPool* ssl_pool,
NetworkQualityProvider* network_quality_provider,
NetLog* net_log)
: transport_pool_(transport_pool),
ssl_pool_(ssl_pool),
base_(this,
max_sockets,
max_sockets_per_group,
ClientSocketPool::unused_idle_socket_timeout(),
ClientSocketPool::used_idle_socket_timeout(),
new HttpProxyConnectJobFactory(transport_pool,
ssl_pool,
network_quality_provider,
net_log)) {
// We should always have a |transport_pool_| except in unit tests.
if (transport_pool_)
base_.AddLowerLayeredPool(transport_pool_);
if (ssl_pool_)
base_.AddLowerLayeredPool(ssl_pool_);
}
HttpProxyClientSocketPool::~HttpProxyClientSocketPool() = default;
int HttpProxyClientSocketPool::RequestSocket(const std::string& group_name,
const void* socket_params,
RequestPriority priority,
const SocketTag& socket_tag,
RespectLimits respect_limits,
ClientSocketHandle* handle,
CompletionOnceCallback callback,
const NetLogWithSource& net_log) {
const scoped_refptr<HttpProxySocketParams>* casted_socket_params =
static_cast<const scoped_refptr<HttpProxySocketParams>*>(socket_params);
return base_.RequestSocket(group_name, *casted_socket_params, priority,
socket_tag, respect_limits, handle,
std::move(callback), net_log);
}
void HttpProxyClientSocketPool::RequestSockets(
const std::string& group_name,
const void* params,
int num_sockets,
const NetLogWithSource& net_log) {
const scoped_refptr<HttpProxySocketParams>* casted_params =
static_cast<const scoped_refptr<HttpProxySocketParams>*>(params);
base_.RequestSockets(group_name, *casted_params, num_sockets, net_log);
}
void HttpProxyClientSocketPool::CancelRequest(
const std::string& group_name,
ClientSocketHandle* handle) {
base_.CancelRequest(group_name, handle);
}
void HttpProxyClientSocketPool::SetPriority(const std::string& group_name,
ClientSocketHandle* handle,
RequestPriority priority) {
base_.SetPriority(group_name, handle, priority);
}
void HttpProxyClientSocketPool::ReleaseSocket(
const std::string& group_name,
std::unique_ptr<StreamSocket> socket,
int id) {
base_.ReleaseSocket(group_name, std::move(socket), id);
}
void HttpProxyClientSocketPool::FlushWithError(int error) {
base_.FlushWithError(error);
}
void HttpProxyClientSocketPool::CloseIdleSockets() {
base_.CloseIdleSockets();
}
void HttpProxyClientSocketPool::CloseIdleSocketsInGroup(
const std::string& group_name) {
base_.CloseIdleSocketsInGroup(group_name);
}
int HttpProxyClientSocketPool::IdleSocketCount() const {
return base_.idle_socket_count();
}
int HttpProxyClientSocketPool::IdleSocketCountInGroup(
const std::string& group_name) const {
return base_.IdleSocketCountInGroup(group_name);
}
LoadState HttpProxyClientSocketPool::GetLoadState(
const std::string& group_name, const ClientSocketHandle* handle) const {
return base_.GetLoadState(group_name, handle);
}
std::unique_ptr<base::DictionaryValue>
HttpProxyClientSocketPool::GetInfoAsValue(const std::string& name,
const std::string& type,
bool include_nested_pools) const {
std::unique_ptr<base::DictionaryValue> dict(base_.GetInfoAsValue(name, type));
if (include_nested_pools) {
auto list = std::make_unique<base::ListValue>();
if (transport_pool_) {
list->Append(transport_pool_->GetInfoAsValue("transport_socket_pool",
"transport_socket_pool",
true));
}
if (ssl_pool_) {
list->Append(ssl_pool_->GetInfoAsValue("ssl_socket_pool",
"ssl_socket_pool",
true));
}
dict->Set("nested_pools", std::move(list));
}
return dict;
}
base::TimeDelta HttpProxyClientSocketPool::ConnectionTimeout() const {
return base_.ConnectionTimeout();
}
bool HttpProxyClientSocketPool::IsStalled() const {
return base_.IsStalled();
}
void HttpProxyClientSocketPool::AddHigherLayeredPool(
HigherLayeredPool* higher_pool) {
base_.AddHigherLayeredPool(higher_pool);
}
void HttpProxyClientSocketPool::RemoveHigherLayeredPool(
HigherLayeredPool* higher_pool) {
base_.RemoveHigherLayeredPool(higher_pool);
}
bool HttpProxyClientSocketPool::CloseOneIdleConnection() {
if (base_.CloseOneIdleSocket())
return true;
return base_.CloseOneIdleConnectionInHigherLayeredPool();
}
} // namespace net