mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-11-24 14:26:09 +03:00
410 lines
15 KiB
C++
410 lines
15 KiB
C++
|
// 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 "net/base/load_flags.h"
|
||
|
#include "net/base/net_errors.h"
|
||
|
#include "net/base/proxy_delegate.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/chromium/spdy_proxy_client_socket.h"
|
||
|
#include "net/spdy/chromium/spdy_session.h"
|
||
|
#include "net/spdy/chromium/spdy_session_pool.h"
|
||
|
#include "net/spdy/chromium/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.
|
||
|
// TODO(kundaji): Proxy connect timeout should be independent of platform and be
|
||
|
// based on proxy. Bug http://crbug.com/407446.
|
||
|
#if defined(OS_ANDROID) || defined(OS_IOS)
|
||
|
static const int kHttpProxyConnectJobTimeoutInSeconds = 10;
|
||
|
#else
|
||
|
static const int kHttpProxyConnectJobTimeoutInSeconds = 30;
|
||
|
#endif
|
||
|
|
||
|
static const char kNetAdaptiveProxyConnectionTimeout[] =
|
||
|
"NetAdaptiveProxyConnectionTimeout";
|
||
|
|
||
|
bool IsInNetAdaptiveProxyConnectionTimeoutFieldTrial() {
|
||
|
// Field trial is enabled if the group name starts with "Enabled".
|
||
|
return base::FieldTrialList::FindFullName(kNetAdaptiveProxyConnectionTimeout)
|
||
|
.find("Enabled") == 0;
|
||
|
}
|
||
|
|
||
|
// Return the value of the parameter |param_name| for the field trial
|
||
|
// |kNetAdaptiveProxyConnectionTimeout|. 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) {
|
||
|
int32_t param;
|
||
|
if (!base::StringToInt(base::GetFieldTrialParamValue(
|
||
|
kNetAdaptiveProxyConnectionTimeout, param_name),
|
||
|
¶m)) {
|
||
|
return default_value;
|
||
|
}
|
||
|
return param;
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
HttpProxySocketParams::HttpProxySocketParams(
|
||
|
const scoped_refptr<TransportSocketParams>& transport_params,
|
||
|
const scoped_refptr<SSLSocketParams>& ssl_params,
|
||
|
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 tunnel,
|
||
|
ProxyDelegate* proxy_delegate)
|
||
|
: 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),
|
||
|
tunnel_(tunnel),
|
||
|
proxy_delegate_(proxy_delegate) {
|
||
|
// If doing a QUIC proxy, |quic_version| must not be QUIC_VERSION_UNSUPPORTED,
|
||
|
// and |ssl_params| must be valid while |transport_params| is null.
|
||
|
// Otherwise, |quic_version| must be QUIC_VERSION_UNSUPPORTED, and exactly
|
||
|
// one of |transport_params| or |ssl_params| must be set.
|
||
|
DCHECK(quic_version_ == QUIC_VERSION_UNSUPPORTED
|
||
|
? (bool)transport_params != (bool)ssl_params
|
||
|
: !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,
|
||
|
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,
|
||
|
respect_limits,
|
||
|
delegate,
|
||
|
NetLogWithSource::Make(net_log,
|
||
|
NetLogSourceType::HTTP_PROXY_CONNECT_JOB)),
|
||
|
client_socket_(new HttpProxyClientSocketWrapper(
|
||
|
group_name,
|
||
|
priority,
|
||
|
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(),
|
||
|
params->quic_stream_factory(),
|
||
|
params->tunnel(),
|
||
|
params->proxy_delegate(),
|
||
|
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) {
|
||
|
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),
|
||
|
transport_rtt_multiplier_(GetInt32Param("transport_rtt_multiplier", 5)),
|
||
|
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", 60))),
|
||
|
net_log_(net_log) {
|
||
|
DCHECK_LT(0, transport_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 {
|
||
|
return std::unique_ptr<ConnectJob>(new HttpProxyConnectJob(
|
||
|
group_name, request.priority(), request.respect_limits(),
|
||
|
request.params(), ConnectionTimeout(), transport_pool_, ssl_pool_,
|
||
|
delegate, net_log_));
|
||
|
}
|
||
|
|
||
|
base::TimeDelta
|
||
|
HttpProxyClientSocketPool::HttpProxyConnectJobFactory::ConnectionTimeout()
|
||
|
const {
|
||
|
if (IsInNetAdaptiveProxyConnectionTimeoutFieldTrial() &&
|
||
|
network_quality_provider_) {
|
||
|
base::Optional<base::TimeDelta> transport_rtt_estimate =
|
||
|
network_quality_provider_->GetTransportRTT();
|
||
|
if (transport_rtt_estimate) {
|
||
|
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
|
||
|
transport_rtt_multiplier_ *
|
||
|
transport_rtt_estimate.value().InMilliseconds());
|
||
|
// 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,
|
||
|
RespectLimits respect_limits,
|
||
|
ClientSocketHandle* handle,
|
||
|
const CompletionCallback& 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,
|
||
|
respect_limits, handle, callback, net_log);
|
||
|
}
|
||
|
|
||
|
void HttpProxyClientSocketPool::RequestSockets(
|
||
|
const std::string& group_name,
|
||
|
const void* params,
|
||
|
int num_sockets,
|
||
|
const NetLogWithSource& net_log,
|
||
|
HttpRequestInfo::RequestMotivation motivation) {
|
||
|
const scoped_refptr<HttpProxySocketParams>* casted_params =
|
||
|
static_cast<const scoped_refptr<HttpProxySocketParams>*>(params);
|
||
|
|
||
|
base_.RequestSockets(group_name, *casted_params, num_sockets, net_log,
|
||
|
motivation);
|
||
|
}
|
||
|
|
||
|
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
|