0

Switch gcm connection_factory_impl.cc to using services/network/proxy_resolving_client_socket.h

This CL migrates gcm to using proxy_resolving_client_socket.h/cc.

As a part of network servicification, we are reducing the //net's API surface.
connection_factory_impl.cc calls into static
net::InitSocketHandleForTlsConnect(), which will not be exposed by network
service. This CL migrates gcm off that static function.

Bug: 817094
Cq-Include-Trybots: master.tryserver.chromium.linux:linux_mojo
Change-Id: If5a6df107603c676412b971a3d74284a1d01cfc0
Reviewed-on: https://chromium-review.googlesource.com/952333
Reviewed-by: Asanka Herath <asanka@chromium.org>
Reviewed-by: Sergey Ulanov <sergeyu@chromium.org>
Reviewed-by: Ilya Sherman <isherman@chromium.org>
Reviewed-by: Steven Valdez <svaldez@chromium.org>
Reviewed-by: Nicolas Zea (slow) <zea@chromium.org>
Commit-Queue: Helen Li <xunjieli@chromium.org>
Cr-Commit-Position: refs/heads/master@{#544537}
This commit is contained in:
Helen Li
2018-03-20 21:47:01 +00:00
committed by Commit Bot
parent 4b749d0e31
commit 345f95ae2c
17 changed files with 523 additions and 296 deletions

@ -126,7 +126,8 @@ bool P2PSocketHostTcpBase::Init(const net::IPEndPoint& local_address,
// The default SSLConfig is good enough for us for now.
const net::SSLConfig ssl_config;
socket_ = proxy_resolving_socket_factory_->CreateSocket(
ssl_config, GURL("https://" + dest_host_port_pair.ToString()));
ssl_config, GURL("https://" + dest_host_port_pair.ToString()),
false /*use_tls*/);
int status = socket_->Connect(
base::Bind(&P2PSocketHostTcpBase::OnConnected,

@ -67,6 +67,7 @@ component("gcm") {
"//base",
"//base/third_party/dynamic_annotations",
"//net",
"//services/network:network_service",
"//third_party/leveldatabase",
"//url",
]

@ -10,4 +10,5 @@ include_rules = [
"+google", # For third_party/protobuf/src.
"+net",
"+third_party/leveldatabase",
"+services/network",
]

@ -24,6 +24,7 @@
#include "net/socket/client_socket_handle.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/ssl/ssl_config_service.h"
#include "services/network/proxy_resolving_client_socket.h"
namespace gcm {
@ -61,9 +62,6 @@ ConnectionFactoryImpl::ConnectionFactoryImpl(
backoff_policy_(backoff_policy),
gcm_network_session_(gcm_network_session),
http_network_session_(http_network_session),
net_log_(
net::NetLogWithSource::Make(net_log, net::NetLogSourceType::SOCKET)),
proxy_resolve_request_(NULL),
connecting_(false),
waiting_for_backoff_(false),
waiting_for_network_online_(false),
@ -79,11 +77,6 @@ ConnectionFactoryImpl::ConnectionFactoryImpl(
ConnectionFactoryImpl::~ConnectionFactoryImpl() {
CloseSocket();
net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this);
if (proxy_resolve_request_) {
gcm_network_session_->proxy_resolution_service()->CancelRequest(
proxy_resolve_request_);
proxy_resolve_request_ = NULL;
}
}
void ConnectionFactoryImpl::Initialize(
@ -299,11 +292,11 @@ GURL ConnectionFactoryImpl::GetCurrentEndpoint() const {
}
net::IPEndPoint ConnectionFactoryImpl::GetPeerIP() {
if (!socket_handle_.socket())
if (!socket_)
return net::IPEndPoint();
net::IPEndPoint ip_endpoint;
int result = socket_handle_.socket()->GetPeerAddress(&ip_endpoint);
int result = socket_->GetPeerAddress(&ip_endpoint);
if (result != net::OK)
return net::IPEndPoint();
@ -318,7 +311,7 @@ void ConnectionFactoryImpl::ConnectImpl() {
void ConnectionFactoryImpl::StartConnection() {
DCHECK(!IsEndpointReachable());
// TODO(zea): Make this a dcheck again. crbug.com/462319
CHECK(!socket_handle_.socket());
CHECK(!socket_);
// TODO(zea): if the network is offline, don't attempt to connect.
// See crbug.com/396687
@ -327,13 +320,14 @@ void ConnectionFactoryImpl::StartConnection() {
GURL current_endpoint = GetCurrentEndpoint();
recorder_->RecordConnectionInitiated(current_endpoint.host());
UpdateFromHttpNetworkSession();
int status = gcm_network_session_->proxy_resolution_service()->ResolveProxy(
current_endpoint, std::string(), &proxy_info_,
base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
weak_ptr_factory_.GetWeakPtr()),
&proxy_resolve_request_, NULL, net_log_);
net::SSLConfig ssl_config;
gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
socket_ = std::make_unique<network::ProxyResolvingClientSocket>(
gcm_network_session_, ssl_config, current_endpoint, true /*use_tls*/);
int status = socket_->Connect(base::BindRepeating(
&ConnectionFactoryImpl::OnConnectDone, weak_ptr_factory_.GetWeakPtr()));
if (status != net::ERR_IO_PENDING)
OnProxyResolveDone(status);
OnConnectDone(status);
}
void ConnectionFactoryImpl::InitHandler() {
@ -381,8 +375,7 @@ void ConnectionFactoryImpl::InitHandler() {
"but does not have any effect on other Google Cloud messages."
)");
connection_handler_->Init(login_request, traffic_annotation,
socket_handle_.socket());
connection_handler_->Init(login_request, traffic_annotation, socket_.get());
}
std::unique_ptr<net::BackoffEntry> ConnectionFactoryImpl::CreateBackoffEntry(
@ -405,15 +398,8 @@ base::TimeTicks ConnectionFactoryImpl::NowTicks() {
}
void ConnectionFactoryImpl::OnConnectDone(int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
if (result != net::OK) {
// If the connection fails, try another proxy.
result = ReconsiderProxyAfterError(result);
// ReconsiderProxyAfterError either returns an error (in which case it is
// not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
// another proxy.
DCHECK_NE(result, net::OK);
if (result == net::ERR_IO_PENDING)
return; // Proxy reconsideration pending. Return.
LOG(ERROR) << "Failed to connect to MCS endpoint with error " << result;
UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", false);
recorder_->RecordConnectionFailure(result);
@ -436,9 +422,6 @@ void ConnectionFactoryImpl::OnConnectDone(int result) {
UMA_HISTOGRAM_BOOLEAN("GCM.ConnectionSuccessRate", true);
UMA_HISTOGRAM_COUNTS("GCM.ConnectionEndpoint", next_endpoint_);
UMA_HISTOGRAM_BOOLEAN("GCM.ConnectedViaProxy",
!(proxy_info_.is_empty() || proxy_info_.is_direct()));
ReportSuccessfulProxyConnection();
recorder_->RecordConnectionSuccess();
// Reset the endpoint back to the default.
@ -478,106 +461,15 @@ void ConnectionFactoryImpl::ConnectionHandlerCallback(int result) {
listener_->OnConnected(GetCurrentEndpoint(), GetPeerIP());
}
// This has largely been copied from
// HttpStreamFactoryImpl::Job::DoResolveProxyComplete. This should be
// refactored into some common place.
void ConnectionFactoryImpl::OnProxyResolveDone(int status) {
proxy_resolve_request_ = NULL;
DVLOG(1) << "Proxy resolution status: " << status;
DCHECK_NE(status, net::ERR_IO_PENDING);
if (status == net::OK) {
// Remove unsupported proxies from the list.
proxy_info_.RemoveProxiesWithoutScheme(
net::ProxyServer::SCHEME_DIRECT |
net::ProxyServer::SCHEME_HTTP | net::ProxyServer::SCHEME_HTTPS |
net::ProxyServer::SCHEME_SOCKS4 | net::ProxyServer::SCHEME_SOCKS5);
if (proxy_info_.is_empty()) {
// No proxies/direct to choose from. This happens when we don't support
// any of the proxies in the returned list.
status = net::ERR_NO_SUPPORTED_PROXIES;
}
}
if (status != net::OK) {
// Failed to resolve proxy. Retry later.
OnConnectDone(status);
return;
}
DVLOG(1) << "Resolved proxy with PAC:" << proxy_info_.ToPacString();
net::SSLConfig ssl_config;
gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
status = net::InitSocketHandleForTlsConnect(
net::HostPortPair::FromURL(GetCurrentEndpoint()),
gcm_network_session_,
proxy_info_,
ssl_config,
ssl_config,
net::PRIVACY_MODE_DISABLED,
net_log_,
&socket_handle_,
base::Bind(&ConnectionFactoryImpl::OnConnectDone,
weak_ptr_factory_.GetWeakPtr()));
if (status != net::ERR_IO_PENDING)
OnConnectDone(status);
}
// This has largely been copied from
// HttpStreamFactoryImpl::Job::ReconsiderProxyAfterError. This should be
// refactored into some common place.
// This method reconsiders the proxy on certain errors. If it does reconsider
// a proxy it always returns ERR_IO_PENDING and posts a call to
// OnProxyResolveDone with the result of the reconsideration.
int ConnectionFactoryImpl::ReconsiderProxyAfterError(int error) {
DCHECK(!proxy_resolve_request_);
DCHECK_NE(error, net::OK);
DCHECK_NE(error, net::ERR_IO_PENDING);
// Check if the error was a proxy failure.
if (!net::CanFalloverToNextProxy(&error))
return error;
net::SSLConfig ssl_config;
gcm_network_session_->ssl_config_service()->GetSSLConfig(&ssl_config);
if (proxy_info_.is_https() && ssl_config.send_client_cert) {
gcm_network_session_->ssl_client_auth_cache()->Remove(
proxy_info_.proxy_server().host_port_pair());
}
if (!proxy_info_.Fallback(error, net_log_)) {
// There was nothing left to fall-back to, so fail the transaction
// with the last connection error we got.
return error;
}
CloseSocket();
// If there is new proxy info, post OnProxyResolveDone to retry it.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&ConnectionFactoryImpl::OnProxyResolveDone,
weak_ptr_factory_.GetWeakPtr(), net::OK));
return net::ERR_IO_PENDING;
}
void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() {
if (gcm_network_session_ && gcm_network_session_->proxy_resolution_service())
gcm_network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_,
NULL);
}
void ConnectionFactoryImpl::CloseSocket() {
// The connection handler needs to be reset, else it'll attempt to keep using
// the destroyed socket.
if (connection_handler_)
connection_handler_->Reset();
if (socket_handle_.socket() && socket_handle_.socket()->IsConnected())
socket_handle_.socket()->Disconnect();
socket_handle_.Reset();
if (socket_)
socket_->Disconnect();
socket_ = nullptr;
}
void ConnectionFactoryImpl::UpdateFromHttpNetworkSession() {

@ -18,11 +18,12 @@
#include "net/base/backoff_entry.h"
#include "net/base/network_change_notifier.h"
#include "net/log/net_log_with_source.h"
#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/socket/client_socket_handle.h"
#include "url/gurl.h"
namespace network {
class ProxyResolvingClientSocket;
}
namespace net {
class HttpNetworkSession;
class NetLog;
@ -123,12 +124,6 @@ class GCM_EXPORT ConnectionFactoryImpl :
// handshake. On connection/handshake failure, goes into backoff.
void ConnectImpl();
// Proxy resolution and connection functions.
void OnProxyResolveDone(int status);
void OnProxyConnectDone(int status);
int ReconsiderProxyAfterError(int error);
void ReportSuccessfulProxyConnection();
// Closes the local socket if one is present, and resets connection handler.
void CloseSocket();
@ -157,15 +152,8 @@ class GCM_EXPORT ConnectionFactoryImpl :
// HTTP Network session. If set, is used for extracting proxy auth
// credentials. If nullptr, is ignored.
net::HttpNetworkSession* http_network_session_;
// Net log to use in connection attempts.
net::NetLogWithSource net_log_;
// The current proxy resolution request, if one exists. Owned by the proxy
// service.
net::ProxyResolutionService::Request* proxy_resolve_request_;
// The current proxy info.
net::ProxyInfo proxy_info_;
// The handle to the socket for the current connection, if one exists.
net::ClientSocketHandle socket_handle_;
std::unique_ptr<network::ProxyResolvingClientSocket> socket_;
// Current backoff entry.
std::unique_ptr<net::BackoffEntry> backoff_entry_;
// Backoff entry from previous connection attempt. Updated on each login

@ -39,7 +39,8 @@ XmppClientSocketFactory::CreateTransportClientSocket(
const net::HostPortPair& host_and_port) {
// TODO(akalin): Use socket pools.
auto transport_socket = proxy_resolving_socket_factory_.CreateSocket(
ssl_config_, GURL("https://" + host_and_port.ToString()));
ssl_config_, GURL("https://" + host_and_port.ToString()),
false /*use_tls*/);
return (use_fake_ssl_client_socket_
? std::unique_ptr<net::StreamSocket>(
new FakeSSLClientSocket(std::move(transport_socket)))

@ -427,6 +427,8 @@ int InitSocketHandleForRawConnect(const HostPortPair& host_port_pair,
int InitSocketHandleForTlsConnect(const HostPortPair& endpoint,
HttpNetworkSession* session,
int request_load_flags,
RequestPriority request_priority,
const ProxyInfo& proxy_info,
const SSLConfig& ssl_config_for_origin,
const SSLConfig& ssl_config_for_proxy,
@ -436,8 +438,6 @@ int InitSocketHandleForTlsConnect(const HostPortPair& endpoint,
const CompletionCallback& callback) {
DCHECK(socket_handle);
HttpRequestHeaders request_extra_headers;
int request_load_flags = 0;
RequestPriority request_priority = MEDIUM;
return InitSocketPoolHelper(
ClientSocketPoolManager::SSL_GROUP, endpoint, request_extra_headers,
request_load_flags, request_priority, session, proxy_info,

@ -146,6 +146,7 @@ int InitSocketHandleForWebSocketRequest(
const OnHostResolutionCallback& resolution_callback,
const CompletionCallback& callback);
// Deprecated: Please do not use this outside of //net and //services/network.
// A helper method that uses the passed in proxy information to initialize a
// ClientSocketHandle with the relevant socket pool. Use this method for
// a raw socket connection to a host-port pair (that needs to tunnel through
@ -163,6 +164,7 @@ NET_EXPORT int InitSocketHandleForRawConnect(
ClientSocketHandle* socket_handle,
const CompletionCallback& callback);
// Deprecated: Please do not use this outside of //net and //services/network.
// A helper method that uses the passed in proxy information to initialize a
// ClientSocketHandle with the relevant socket pool. Use this method for
// a raw socket connection with TLS negotiation to a host-port pair (that needs
@ -170,6 +172,8 @@ NET_EXPORT int InitSocketHandleForRawConnect(
NET_EXPORT int InitSocketHandleForTlsConnect(
const HostPortPair& host_port_pair,
HttpNetworkSession* session,
int request_load_flags,
RequestPriority request_priority,
const ProxyInfo& proxy_info,
const SSLConfig& ssl_config_for_origin,
const SSLConfig& ssl_config_for_proxy,

@ -1212,6 +1212,7 @@ int MockSSLClientSocket::Write(
int MockSSLClientSocket::Connect(const CompletionCallback& callback) {
DCHECK(transport_->socket()->IsConnected());
data_->is_connect_data_consumed = true;
if (data_->connect.result == OK)
connected_ = true;
if (data_->connect.mode == ASYNC) {

@ -360,6 +360,9 @@ struct SSLSocketDataProvider {
SSLSocketDataProvider(const SSLSocketDataProvider& other);
~SSLSocketDataProvider();
// Returns whether MockConnect data has been consumed.
bool ConnectDataConsumed() const { return is_connect_data_consumed; }
// Result for Connect().
MockConnect connect;
@ -374,6 +377,8 @@ struct SSLSocketDataProvider {
ChannelIDService* channel_id_service;
base::Optional<NextProtoVector> next_protos_expected_in_ssl_config;
bool is_connect_data_consumed = false;
};
// Uses the sequence_number field in the mock reads and writes to

@ -190,7 +190,8 @@ void XmppSignalStrategy::Core::Connect() {
net::SSLConfig(),
GURL("https://" +
net::HostPortPair(xmpp_server_config_.host, xmpp_server_config_.port)
.ToString()));
.ToString()),
false /*use_tls*/);
int result = socket_->Connect(base::Bind(
&Core::OnSocketConnected, base::Unretained(this)));

@ -31,13 +31,17 @@ namespace network {
ProxyResolvingClientSocket::ProxyResolvingClientSocket(
net::HttpNetworkSession* network_session,
const net::SSLConfig& ssl_config,
const GURL& url)
const GURL& url,
bool use_tls)
: network_session_(network_session),
socket_handle_(std::make_unique<net::ClientSocketHandle>()),
ssl_config_(ssl_config),
proxy_resolve_request_(nullptr),
url_(url),
use_tls_(use_tls),
net_log_(net::NetLogWithSource::Make(network_session_->net_log(),
net::NetLogSourceType::SOCKET)),
next_state_(STATE_NONE),
weak_factory_(this) {
// TODO(xunjieli): Handle invalid URLs more gracefully (at mojo API layer
// or when the request is created).
@ -51,8 +55,8 @@ ProxyResolvingClientSocket::~ProxyResolvingClientSocket() {
int ProxyResolvingClientSocket::Read(net::IOBuffer* buf,
int buf_len,
const net::CompletionCallback& callback) {
if (transport_.get() && transport_->socket())
return transport_->socket()->Read(buf, buf_len, callback);
if (socket_handle_->socket())
return socket_handle_->socket()->Read(buf, buf_len, callback);
return net::ERR_SOCKET_NOT_CONNECTED;
}
@ -61,21 +65,21 @@ int ProxyResolvingClientSocket::Write(
int buf_len,
const net::CompletionCallback& callback,
const net::NetworkTrafficAnnotationTag& traffic_annotation) {
if (transport_.get() && transport_->socket())
return transport_->socket()->Write(buf, buf_len, callback,
traffic_annotation);
if (socket_handle_->socket())
return socket_handle_->socket()->Write(buf, buf_len, callback,
traffic_annotation);
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::SetReceiveBufferSize(int32_t size) {
if (transport_.get() && transport_->socket())
return transport_->socket()->SetReceiveBufferSize(size);
if (socket_handle_->socket())
return socket_handle_->socket()->SetReceiveBufferSize(size);
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::SetSendBufferSize(int32_t size) {
if (transport_.get() && transport_->socket())
return transport_->socket()->SetSendBufferSize(size);
if (socket_handle_->socket())
return socket_handle_->socket()->SetSendBufferSize(size);
return net::ERR_SOCKET_NOT_CONNECTED;
}
@ -83,29 +87,16 @@ int ProxyResolvingClientSocket::Connect(
const net::CompletionCallback& callback) {
DCHECK(user_connect_callback_.is_null());
// First try to resolve the proxy.
// TODO(xunjieli): Having a null ProxyDelegate is bad. Figure out how to
// interact with the new interface for proxy delegate.
// https://crbug.com/793071.
int net_error = network_session_->proxy_resolution_service()->ResolveProxy(
url_, "POST", &proxy_info_,
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxy,
base::Unretained(this)),
&proxy_resolve_request_, nullptr /*proxy_delegate*/, net_log_);
if (net_error != net::ERR_IO_PENDING) {
// Defer execution of ConnectToProxy instead of calling it
// directly here for simplicity. From the caller's point of view,
// the connect always happens asynchronously.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
weak_factory_.GetWeakPtr(), net_error));
next_state_ = STATE_PROXY_RESOLVE;
int result = DoLoop(net::OK);
if (result == net::ERR_IO_PENDING) {
user_connect_callback_ = callback;
}
user_connect_callback_ = callback;
return net::ERR_IO_PENDING;
return result;
}
void ProxyResolvingClientSocket::Disconnect() {
CloseTransportSocket();
CloseSocket(true /*close_connection*/);
if (proxy_resolve_request_) {
network_session_->proxy_resolution_service()->CancelRequest(
proxy_resolve_request_);
@ -115,24 +106,24 @@ void ProxyResolvingClientSocket::Disconnect() {
}
bool ProxyResolvingClientSocket::IsConnected() const {
if (!transport_.get() || !transport_->socket())
if (!socket_handle_->socket())
return false;
return transport_->socket()->IsConnected();
return socket_handle_->socket()->IsConnected();
}
bool ProxyResolvingClientSocket::IsConnectedAndIdle() const {
if (!transport_.get() || !transport_->socket())
if (!socket_handle_->socket())
return false;
return transport_->socket()->IsConnectedAndIdle();
return socket_handle_->socket()->IsConnectedAndIdle();
}
int ProxyResolvingClientSocket::GetPeerAddress(net::IPEndPoint* address) const {
if (!transport_.get() || !transport_->socket()) {
if (!socket_handle_->socket()) {
return net::ERR_SOCKET_NOT_CONNECTED;
}
if (proxy_info_.is_direct())
return transport_->socket()->GetPeerAddress(address);
return socket_handle_->socket()->GetPeerAddress(address);
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(url_.HostNoBrackets())) {
@ -146,44 +137,48 @@ int ProxyResolvingClientSocket::GetPeerAddress(net::IPEndPoint* address) const {
int ProxyResolvingClientSocket::GetLocalAddress(
net::IPEndPoint* address) const {
if (transport_.get() && transport_->socket())
return transport_->socket()->GetLocalAddress(address);
if (socket_handle_->socket())
return socket_handle_->socket()->GetLocalAddress(address);
return net::ERR_SOCKET_NOT_CONNECTED;
}
const net::NetLogWithSource& ProxyResolvingClientSocket::NetLog() const {
if (transport_.get() && transport_->socket())
return transport_->socket()->NetLog();
if (socket_handle_->socket())
return socket_handle_->socket()->NetLog();
return net_log_;
}
void ProxyResolvingClientSocket::SetSubresourceSpeculation() {
if (transport_.get() && transport_->socket())
transport_->socket()->SetSubresourceSpeculation();
if (socket_handle_->socket())
socket_handle_->socket()->SetSubresourceSpeculation();
}
void ProxyResolvingClientSocket::SetOmniboxSpeculation() {
if (transport_.get() && transport_->socket())
transport_->socket()->SetOmniboxSpeculation();
if (socket_handle_->socket())
socket_handle_->socket()->SetOmniboxSpeculation();
}
bool ProxyResolvingClientSocket::WasEverUsed() const {
if (transport_.get() && transport_->socket())
return transport_->socket()->WasEverUsed();
if (socket_handle_->socket())
return socket_handle_->socket()->WasEverUsed();
return false;
}
bool ProxyResolvingClientSocket::WasAlpnNegotiated() const {
if (socket_handle_->socket())
return socket_handle_->socket()->WasAlpnNegotiated();
return false;
}
net::NextProto ProxyResolvingClientSocket::GetNegotiatedProtocol() const {
if (transport_.get() && transport_->socket())
return transport_->socket()->GetNegotiatedProtocol();
if (socket_handle_->socket())
return socket_handle_->socket()->GetNegotiatedProtocol();
return net::kProtoUnknown;
}
bool ProxyResolvingClientSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
if (socket_handle_->socket())
return socket_handle_->socket()->GetSSLInfo(ssl_info);
return false;
}
@ -201,11 +196,67 @@ void ProxyResolvingClientSocket::ApplySocketTag(const net::SocketTag& tag) {
NOTIMPLEMENTED();
}
void ProxyResolvingClientSocket::ConnectToProxy(int net_error) {
proxy_resolve_request_ = nullptr;
void ProxyResolvingClientSocket::OnIOComplete(int result) {
DCHECK_NE(net::ERR_IO_PENDING, result);
int net_error = DoLoop(result);
if (net_error != net::ERR_IO_PENDING)
base::ResetAndReturn(&user_connect_callback_).Run(net_error);
}
DCHECK_NE(net_error, net::ERR_IO_PENDING);
if (net_error == net::OK) {
int ProxyResolvingClientSocket::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_PROXY_RESOLVE:
DCHECK_EQ(net::OK, rv);
rv = DoProxyResolve();
break;
case STATE_PROXY_RESOLVE_COMPLETE:
rv = DoProxyResolveComplete(rv);
break;
case STATE_INIT_CONNECTION:
DCHECK_EQ(net::OK, rv);
rv = DoInitConnection();
break;
case STATE_INIT_CONNECTION_COMPLETE:
rv = DoInitConnectionComplete(rv);
break;
case STATE_RESTART_TUNNEL_AUTH:
rv = DoRestartTunnelAuth(rv);
break;
case STATE_RESTART_TUNNEL_AUTH_COMPLETE:
rv = DoRestartTunnelAuthComplete(rv);
break;
default:
NOTREACHED() << "bad state";
rv = net::ERR_FAILED;
break;
}
} while (rv != net::ERR_IO_PENDING && next_state_ != STATE_NONE);
return rv;
}
int ProxyResolvingClientSocket::DoProxyResolve() {
next_state_ = STATE_PROXY_RESOLVE_COMPLETE;
// TODO(xunjieli): Having a null ProxyDelegate is bad. Figure out how to
// interact with the new interface for proxy delegate.
// https://crbug.com/793071.
// base::Unretained(this) is safe because resolution request is canceled when
// |proxy_resolve_request_| is destroyed.
return network_session_->proxy_resolution_service()->ResolveProxy(
url_, "POST", &proxy_info_,
base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
base::Unretained(this)),
&proxy_resolve_request_, nullptr /*proxy_delegate*/, net_log_);
}
int ProxyResolvingClientSocket::DoProxyResolveComplete(int result) {
proxy_resolve_request_ = nullptr;
if (result == net::OK) {
next_state_ = STATE_INIT_CONNECTION;
// Removes unsupported proxies from the list. Currently, this removes
// just the SCHEME_QUIC proxy, which doesn't yet support tunneling.
// TODO(xunjieli): Allow QUIC proxy once it supports tunneling.
@ -217,82 +268,119 @@ void ProxyResolvingClientSocket::ConnectToProxy(int net_error) {
if (proxy_info_.is_empty()) {
// No proxies/direct to choose from. This happens when we don't support
// any of the proxies in the returned list.
net_error = net::ERR_NO_SUPPORTED_PROXIES;
result = net::ERR_NO_SUPPORTED_PROXIES;
}
}
return result;
}
if (net_error != net::OK) {
CloseTransportSocket();
base::ResetAndReturn(&user_connect_callback_).Run(net_error);
return;
}
int ProxyResolvingClientSocket::DoInitConnection() {
DCHECK(!socket_handle_->socket());
next_state_ = STATE_INIT_CONNECTION_COMPLETE;
transport_.reset(new net::ClientSocketHandle);
// Now that the proxy is resolved, issue a socket connect.
net::HostPortPair host_port_pair = net::HostPortPair::FromURL(url_);
// Ignore socket limit set by socket pool for this type of socket.
int request_load_flags = net::LOAD_IGNORE_LIMITS;
net::RequestPriority request_priority = net::MAXIMUM_PRIORITY;
net_error = net::InitSocketHandleForRawConnect(
// base::Unretained(this) is safe because request is canceled when
// |socket_handle_| is destroyed.
if (use_tls_) {
return net::InitSocketHandleForTlsConnect(
host_port_pair, network_session_, request_load_flags, request_priority,
proxy_info_, ssl_config_, ssl_config_, net::PRIVACY_MODE_DISABLED,
net_log_, socket_handle_.get(),
base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
base::Unretained(this)));
}
return net::InitSocketHandleForRawConnect(
host_port_pair, network_session_, request_load_flags, request_priority,
proxy_info_, ssl_config_, ssl_config_, net::PRIVACY_MODE_DISABLED,
net_log_, transport_.get(),
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxyDone,
net_log_, socket_handle_.get(),
base::BindRepeating(&ProxyResolvingClientSocket::OnIOComplete,
base::Unretained(this)));
if (net_error != net::ERR_IO_PENDING) {
// Since this method is always called asynchronously. it is OK to call
// ConnectToProxyDone synchronously.
ConnectToProxyDone(net_error);
}
}
void ProxyResolvingClientSocket::ConnectToProxyDone(int net_error) {
if (net_error != net::OK) {
// If the connection fails, try another proxy.
net_error = ReconsiderProxyAfterError(net_error);
int ProxyResolvingClientSocket::DoInitConnectionComplete(int result) {
if (result == net::ERR_PROXY_AUTH_REQUESTED) {
if (use_tls_) {
// Put the in-progress HttpProxyClientSocket into |socket_handle_| to
// do tunnel auth. After auth completes, it's important to reset
// |socket_handle_|, so it doesn't have a HttpProxyClientSocket when the
// code expects an SSLClientSocket. The tunnel restart code is careful to
// put it back to the socket pool before returning control to the rest of
// this class.
socket_handle_.reset(
socket_handle_->release_pending_http_proxy_connection());
}
next_state_ = STATE_RESTART_TUNNEL_AUTH;
return result;
}
if (result != net::OK) {
// ReconsiderProxyAfterError either returns an error (in which case it is
// not reconsidering a proxy) or returns ERR_IO_PENDING if it is considering
// another proxy.
DCHECK_NE(net_error, net::OK);
if (net_error == net::ERR_IO_PENDING) {
// Proxy reconsideration pending. Return.
return;
}
CloseTransportSocket();
} else {
network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_,
nullptr);
return ReconsiderProxyAfterError(result);
}
base::ResetAndReturn(&user_connect_callback_).Run(net_error);
network_session_->proxy_resolution_service()->ReportSuccess(proxy_info_,
nullptr);
return net::OK;
}
void ProxyResolvingClientSocket::CloseTransportSocket() {
if (transport_.get() && transport_->socket())
transport_->socket()->Disconnect();
transport_.reset();
int ProxyResolvingClientSocket::DoRestartTunnelAuth(int result) {
DCHECK_EQ(net::ERR_PROXY_AUTH_REQUESTED, result);
net::ProxyClientSocket* proxy_socket =
static_cast<net::ProxyClientSocket*>(socket_handle_->socket());
if (proxy_socket->GetAuthController() &&
proxy_socket->GetAuthController()->HaveAuth()) {
next_state_ = STATE_RESTART_TUNNEL_AUTH_COMPLETE;
// base::Unretained(this) is safe because |proxy_socket| is owned by this.
return proxy_socket->RestartWithAuth(base::BindRepeating(
&ProxyResolvingClientSocket::OnIOComplete, base::Unretained(this)));
}
// This socket is unusable if the underlying authentication handler doesn't
// already have credentials. It is possible to overcome this hurdle and
// finish the handshake if this class exposes an interface for an embedder to
// supply credentials.
CloseSocket(true /*close_connection*/);
return result;
}
int ProxyResolvingClientSocket::DoRestartTunnelAuthComplete(int result) {
if (result == net::ERR_PROXY_AUTH_REQUESTED) {
// Handle multi-round auth challenge.
next_state_ = STATE_RESTART_TUNNEL_AUTH;
return result;
}
if (result == net::OK) {
CloseSocket(false /*close_connection*/);
// Now that the HttpProxyClientSocket is connected, release it as an idle
// socket into the pool and start the connection process from the beginning.
next_state_ = STATE_INIT_CONNECTION;
return net::OK;
}
CloseSocket(true /*close_connection*/);
return ReconsiderProxyAfterError(result);
}
void ProxyResolvingClientSocket::CloseSocket(bool close_connection) {
if (close_connection && socket_handle_->socket())
socket_handle_->socket()->Disconnect();
socket_handle_->Reset();
}
// This method reconsiders the proxy on certain errors. If it does
// reconsider a proxy it always returns ERR_IO_PENDING and posts a call to
// ConnectToProxy with the result of the reconsideration.
int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
DCHECK(!socket_handle_->socket());
DCHECK(!proxy_resolve_request_);
DCHECK_NE(error, net::OK);
DCHECK_NE(error, net::ERR_IO_PENDING);
if (error == net::ERR_PROXY_AUTH_REQUESTED) {
net::ProxyClientSocket* proxy_socket =
static_cast<net::ProxyClientSocket*>(transport_->socket());
if (proxy_socket->GetAuthController()->HaveAuth()) {
return proxy_socket->RestartWithAuth(
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxyDone,
base::Unretained(this)));
}
return error;
}
// Check if the error was a proxy failure.
if (!net::CanFalloverToNextProxy(&error))
return error;
@ -307,14 +395,8 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
if (!proxy_info_.Fallback(error, net_log_))
return error;
CloseTransportSocket();
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
weak_factory_.GetWeakPtr(), net::OK));
// Since we potentially have another try to go, set the return code code to
// ERR_IO_PENDING.
return net::ERR_IO_PENDING;
next_state_ = STATE_INIT_CONNECTION;
return net::OK;
}
} // namespace network

@ -50,10 +50,12 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingClientSocket
// any sensitive data (like embedded usernames and passwords), and local data
// (i.e. reference fragment) will be sanitized by
// net::ProxyResolutionService::ResolveProxyHelper() before the url is
// disclosed to the proxy. |network_session| must outlive |this|.
// disclosed to the proxy. If |use_tls|, this will try to do a tls connect
// instead of a regular tcp connect. |network_session| must outlive |this|.
ProxyResolvingClientSocket(net::HttpNetworkSession* network_session,
const net::SSLConfig& ssl_config,
const GURL& url);
const GURL& url,
bool use_tls);
~ProxyResolvingClientSocket() override;
// net::StreamSocket implementation.
@ -88,29 +90,53 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingClientSocket
void ApplySocketTag(const net::SocketTag& tag) override;
private:
enum State {
STATE_PROXY_RESOLVE,
STATE_PROXY_RESOLVE_COMPLETE,
STATE_INIT_CONNECTION,
STATE_INIT_CONNECTION_COMPLETE,
STATE_RESTART_TUNNEL_AUTH,
STATE_RESTART_TUNNEL_AUTH_COMPLETE,
STATE_DONE,
STATE_NONE,
};
FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest, ConnectToProxy);
FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest, ReadWriteErrors);
FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest,
ResetSocketAfterTunnelAuth);
void ConnectToProxy(int net_error);
void ConnectToProxyDone(int net_error);
void OnIOComplete(int result);
int DoLoop(int result);
int DoProxyResolve();
int DoProxyResolveComplete(int result);
int DoInitConnection();
int DoInitConnectionComplete(int result);
int DoRestartTunnelAuth(int result);
int DoRestartTunnelAuthComplete(int result);
void CloseSocket(bool close_connection);
void CloseTransportSocket();
int ReconsiderProxyAfterError(int error);
net::HttpNetworkSession* network_session_;
// The transport socket.
std::unique_ptr<net::ClientSocketHandle> transport_;
std::unique_ptr<net::ClientSocketHandle> socket_handle_;
const net::SSLConfig ssl_config_;
net::ProxyResolutionService::Request* proxy_resolve_request_;
net::ProxyInfo proxy_info_;
const GURL url_;
const bool use_tls_;
net::NetLogWithSource net_log_;
// The callback passed to Connect().
net::CompletionCallback user_connect_callback_;
State next_state_;
base::WeakPtrFactory<ProxyResolvingClientSocket> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ProxyResolvingClientSocket);

@ -70,7 +70,8 @@ ProxyResolvingClientSocketFactory::~ProxyResolvingClientSocketFactory() {}
std::unique_ptr<ProxyResolvingClientSocket>
ProxyResolvingClientSocketFactory::CreateSocket(
const net::SSLConfig& ssl_config,
const GURL& url) {
const GURL& url,
bool use_tls) {
// |request_context|'s HttpAuthCache might have updates. For example, a user
// might have since entered proxy credentials. Clear the http auth of
// |network_session_| and copy over the data from |request_context|'s auth
@ -83,7 +84,7 @@ ProxyResolvingClientSocketFactory::CreateSocket(
->http_auth_cache();
network_session_->http_auth_cache()->UpdateAllFrom(*other_auth_cache);
return std::make_unique<ProxyResolvingClientSocket>(network_session_.get(),
ssl_config, url);
ssl_config, url, use_tls);
}
} // namespace network

@ -39,10 +39,10 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) ProxyResolvingClientSocketFactory {
// doesn't need to explicitly sanitize the url, any sensitive data (like
// embedded usernames and passwords), and local data (i.e. reference fragment)
// will be sanitized by net::ProxyService::ResolveProxyHelper() before the url
// is disclosed to the proxy.
std::unique_ptr<ProxyResolvingClientSocket> CreateSocket(
const net::SSLConfig& ssl_config,
const GURL& url);
// is disclosed to the proxy. If |use_tls|, tls connect will be used in
// addition to tcp connect.
std::unique_ptr<ProxyResolvingClientSocket>
CreateSocket(const net::SSLConfig& ssl_config, const GURL& url, bool use_tls);
private:
std::unique_ptr<net::HttpNetworkSession> network_session_;

@ -20,6 +20,7 @@
#include "net/dns/mock_host_resolver.h"
#include "net/proxy_resolution/mock_proxy_resolver.h"
#include "net/proxy_resolution/proxy_config_service_fixed.h"
#include "net/proxy_resolution/proxy_config_with_annotation.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/socket/client_socket_pool_manager.h"
#include "net/socket/socket_test_util.h"
@ -50,10 +51,13 @@ class TestURLRequestContextWithProxy : public net::TestURLRequestContext {
} // namespace
class ProxyResolvingClientSocketTest : public testing::Test {
class ProxyResolvingClientSocketTest
: public testing::Test,
public testing::WithParamInterface<bool> {
protected:
ProxyResolvingClientSocketTest()
: context_with_proxy_("PROXY bad:99; PROXY maybe:80; DIRECT") {}
: context_with_proxy_("PROXY bad:99; PROXY maybe:80; DIRECT"),
use_tls_(GetParam()) {}
~ProxyResolvingClientSocketTest() override {}
@ -65,12 +69,17 @@ class ProxyResolvingClientSocketTest : public testing::Test {
base::test::ScopedTaskEnvironment scoped_task_environment_;
TestURLRequestContextWithProxy context_with_proxy_;
const bool use_tls_;
};
INSTANTIATE_TEST_CASE_P(/* no prefix */,
ProxyResolvingClientSocketTest,
::testing::Bool());
// Tests that the global socket pool limit
// (ClientSocketPoolManager::max_sockets_per_group) doesn't apply to this
// type of sockets.
TEST_F(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
TEST_P(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
const int kNumSockets = net::ClientSocketPoolManager::max_sockets_per_group(
net::HttpNetworkSession::NORMAL_SOCKET_POOL) +
10;
@ -82,11 +91,15 @@ TEST_F(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
"Proxy-Connection: keep-alive\r\n\r\n")};
net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
std::vector<std::unique_ptr<net::StaticSocketDataProvider>> socket_data;
std::vector<std::unique_ptr<net::SSLSocketDataProvider>> ssl_data;
for (int i = 0; i < kNumSockets; ++i) {
socket_data.push_back(std::make_unique<net::StaticSocketDataProvider>(
reads, arraysize(reads), writes, arraysize(writes)));
socket_data[i]->set_connect_data(net::MockConnect(net::ASYNC, net::OK));
socket_factory.AddSocketDataProvider(socket_data[i].get());
ssl_data.push_back(
std::make_unique<net::SSLSocketDataProvider>(net::ASYNC, net::OK));
socket_factory.AddSSLSocketDataProvider(ssl_data[i].get());
}
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
@ -95,7 +108,7 @@ TEST_F(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
for (int i = 0; i < kNumSockets; ++i) {
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
@ -104,10 +117,11 @@ TEST_F(ProxyResolvingClientSocketTest, SocketLimitNotApply) {
for (int i = 0; i < kNumSockets; ++i) {
EXPECT_TRUE(socket_data[i]->AllReadDataConsumed());
EXPECT_TRUE(socket_data[i]->AllWriteDataConsumed());
EXPECT_EQ(use_tls_, ssl_data[i]->ConnectDataConsumed());
}
}
TEST_F(ProxyResolvingClientSocketTest, ConnectError) {
TEST_P(ProxyResolvingClientSocketTest, ConnectError) {
const struct TestData {
// Whether the error is encountered synchronously as opposed to
// asynchronously.
@ -136,7 +150,7 @@ TEST_F(ProxyResolvingClientSocketTest, ConnectError) {
&socket_factory, context.get());
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
@ -152,7 +166,7 @@ TEST_F(ProxyResolvingClientSocketTest, ConnectError) {
}
// Tests that the connection is established to the proxy.
TEST_F(ProxyResolvingClientSocketTest, ConnectToProxy) {
TEST_P(ProxyResolvingClientSocketTest, ConnectToProxy) {
const GURL kDestination("https://example.com:443");
// Use a different port than that of |kDestination|.
const int kProxyPort = 8009;
@ -171,6 +185,9 @@ TEST_F(ProxyResolvingClientSocketTest, ConnectToProxy) {
net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
"Host: example.com:443\r\n"
"Proxy-Connection: keep-alive\r\n\r\n")};
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
net::StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
arraysize(writes));
net::IPEndPoint remote_addr(net::IPAddress(127, 0, 0, 1),
@ -183,7 +200,7 @@ TEST_F(ProxyResolvingClientSocketTest, ConnectToProxy) {
&socket_factory, context.get());
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
@ -196,16 +213,42 @@ TEST_F(ProxyResolvingClientSocketTest, ConnectToProxy) {
// proxy, so call private member to make sure address is correct.
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, status);
status =
socket->transport_->socket()->GetPeerAddress(&actual_remote_addr);
socket->socket_handle_->socket()->GetPeerAddress(&actual_remote_addr);
}
EXPECT_EQ(net::OK, status);
EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
}
TEST_P(ProxyResolvingClientSocketTest, SocketDestroyedBeforeConnectComplete) {
const GURL kDestination("https://example.com:443");
net::StaticSocketDataProvider socket_data;
socket_data.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
net::MockClientSocketFactory socket_factory;
socket_factory.AddSocketDataProvider(&socket_data);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
auto context = std::make_unique<TestURLRequestContextWithProxy>("DIRECT");
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, context.get());
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
socket.reset();
// Makes sure there is no UAF and socket request is canceled properly.
base::RunLoop().RunUntilIdle();
}
// Tests that connection itself is successful but an error occurred during
// Read()/Write().
TEST_F(ProxyResolvingClientSocketTest, ReadWriteErrors) {
TEST_P(ProxyResolvingClientSocketTest, ReadWriteErrors) {
const GURL kDestination("http://example.com:80");
const struct TestData {
// Whether there is a read error as opposed to a write error.
@ -254,12 +297,14 @@ TEST_F(ProxyResolvingClientSocketTest, ReadWriteErrors) {
socket_data.set_connect_data(
net::MockConnect(net::ASYNC, net::OK, remote_addr));
net::MockClientSocketFactory socket_factory;
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
socket_factory.AddSocketDataProvider(&socket_data);
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, context.get());
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
@ -272,7 +317,7 @@ TEST_F(ProxyResolvingClientSocketTest, ReadWriteErrors) {
// proxy, so call private member to make sure address is correct.
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, status);
status =
socket->transport_->socket()->GetPeerAddress(&actual_remote_addr);
socket->socket_handle_->socket()->GetPeerAddress(&actual_remote_addr);
}
EXPECT_EQ(net::OK, status);
EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
@ -298,10 +343,11 @@ TEST_F(ProxyResolvingClientSocketTest, ReadWriteErrors) {
EXPECT_EQ(net::ERR_FAILED, read_write_result);
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
}
TEST_F(ProxyResolvingClientSocketTest, ReportsBadProxies) {
TEST_P(ProxyResolvingClientSocketTest, ReportsBadProxies) {
const GURL kDestination("https://example.com:443");
net::MockClientSocketFactory socket_factory;
@ -319,12 +365,14 @@ TEST_F(ProxyResolvingClientSocketTest, ReportsBadProxies) {
arraysize(writes));
socket_data2.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
socket_factory.AddSocketDataProvider(&socket_data2);
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
@ -337,9 +385,127 @@ TEST_F(ProxyResolvingClientSocketTest, ReportsBadProxies) {
EXPECT_EQ(1u, retry_info.size());
net::ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99");
EXPECT_TRUE(iter != retry_info.end());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
TEST_P(ProxyResolvingClientSocketTest, ResetSocketAfterTunnelAuth) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
// Initial connect without credentials. The server responds with a 407.
net::MockWrite kConnectWrites1[] = {
net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
"Host: example.com:443\r\n"
"Proxy-Connection: keep-alive\r\n"
"\r\n")};
net::MockRead kConnectReads1[] = {
net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
"Proxy-Authenticate: Basic realm=\"test_realm\"\r\n"
"\r\n")};
net::StaticSocketDataProvider kSocketData1(
kConnectReads1, arraysize(kConnectReads1), kConnectWrites1,
arraysize(kConnectWrites1));
socket_factory.AddSocketDataProvider(&kSocketData1);
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status),
net::test::IsError(net::ERR_PROXY_AUTH_REQUESTED));
// Make sure |socket_handle_| is closed appropriately.
EXPECT_FALSE(socket->socket_handle_->socket());
}
TEST_P(ProxyResolvingClientSocketTest, MultiroundAuth) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
// Initial connect without credentials. The server responds with a 407.
net::MockWrite kConnectWrites1[] = {
net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
"Host: example.com:443\r\n"
"Proxy-Connection: keep-alive\r\n"
"\r\n")};
net::MockRead kConnectReads1[] = {
net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
"Proxy-Authenticate: Basic realm=\"test_realm\"\r\n"
"\r\n")};
// Second connect attempt includes credentials for test_realm.
net::MockWrite kConnectWrites2[] = {
net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
"Host: example.com:443\r\n"
"Proxy-Connection: keep-alive\r\n"
"Proxy-Authorization: Basic dXNlcjpwYXNzd29yZA==\r\n"
"\r\n")};
net::MockRead kConnectReads2[] = {
net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
"Proxy-Authenticate: Basic realm=\"test_realm2\"\r\n"
"\r\n")};
// Third connect attempt include credentials for test_realm2.
net::MockWrite kConnectWrites3[] = {
net::MockWrite("CONNECT example.com:443 HTTP/1.1\r\n"
"Host: example.com:443\r\n"
"Proxy-Connection: keep-alive\r\n"
"Proxy-Authorization: Basic dXNlcjI6cGFzc3dvcmQy\r\n"
"\r\n")};
net::MockRead kConnectReads3[] = {
net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
net::StaticSocketDataProvider kSocketData1(
kConnectReads1, arraysize(kConnectReads1), kConnectWrites1,
arraysize(kConnectWrites1));
socket_factory.AddSocketDataProvider(&kSocketData1);
net::StaticSocketDataProvider kSocketData2(
kConnectReads2, arraysize(kConnectReads2), kConnectWrites2,
arraysize(kConnectWrites2));
socket_factory.AddSocketDataProvider(&kSocketData2);
net::StaticSocketDataProvider kSocketData3(
kConnectReads3, arraysize(kConnectReads3), kConnectWrites3,
arraysize(kConnectWrites3));
socket_factory.AddSocketDataProvider(&kSocketData3);
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
net::HttpAuthCache* auth_cache =
context_with_proxy_.http_transaction_factory()
->GetSession()
->http_auth_cache();
auth_cache->Add(GURL("http://bad:99"), "test_realm",
net::HttpAuth::AUTH_SCHEME_BASIC,
"Basic realm=\"test_realm\"",
net::AuthCredentials(base::ASCIIToUTF16("user"),
base::ASCIIToUTF16("password")),
std::string());
auth_cache->Add(GURL("http://bad:99"), "test_realm2",
net::HttpAuth::AUTH_SCHEME_BASIC,
"Basic realm=\"test_realm2\"",
net::AuthCredentials(base::ASCIIToUTF16("user2"),
base::ASCIIToUTF16("password2")),
std::string());
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
@ -373,6 +539,8 @@ TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
kConnectReads2, arraysize(kConnectReads2), kConnectWrites2,
arraysize(kConnectWrites2));
socket_factory.AddSocketDataProvider(&kSocketData2);
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
net::HttpAuthCache* auth_cache =
context_with_proxy_.http_transaction_factory()
@ -393,16 +561,17 @@ TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
// Make sure that if HttpAuthCache is updated e.g through normal URLRequests,
// ProxyResolvingClientSocketFactory uses the latest cache for creating new
// sockets.
TEST_F(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) {
TEST_P(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) {
net::MockClientSocketFactory socket_factory;
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, &context_with_proxy_);
@ -449,16 +618,19 @@ TEST_F(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) {
kConnectReads, arraysize(kConnectReads), kConnectWrites,
arraysize(kConnectWrites));
socket_factory.AddSocketDataProvider(&kSocketData);
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
@ -476,6 +648,8 @@ TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
kConnectReads, arraysize(kConnectReads), kConnectWrites,
arraysize(kConnectWrites));
socket_factory.AddSocketDataProvider(&kSocketData);
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
net::HttpAuthCache* auth_cache =
context_with_proxy_.http_transaction_factory()
@ -493,14 +667,15 @@ TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
TEST_P(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
@ -524,7 +699,7 @@ TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
@ -532,7 +707,7 @@ TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
}
// Make sure that url is sanitized before it is disclosed to the proxy.
TEST_F(ProxyResolvingClientSocketTest, URLSanitized) {
TEST_P(ProxyResolvingClientSocketTest, URLSanitized) {
GURL url("http://username:password@www.example.com:79/?ref#hash#hash");
auto context = std::make_unique<net::TestURLRequestContext>(true);
@ -555,7 +730,8 @@ TEST_F(ProxyResolvingClientSocketTest, URLSanitized) {
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
nullptr, context.get());
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url);
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url,
use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
@ -572,18 +748,57 @@ TEST_F(ProxyResolvingClientSocketTest, URLSanitized) {
resolver.pending_jobs()[0]->url());
}
// Tests that socket is destroyed before proxy resolution can complete
// asynchronously.
TEST_P(ProxyResolvingClientSocketTest,
SocketDestroyedBeforeProxyResolutionCompletes) {
GURL url("http://www.example.com:79");
auto context = std::make_unique<net::TestURLRequestContext>(true);
net::ProxyConfig proxy_config;
proxy_config.set_pac_url(GURL("http://foopy/proxy.pac"));
proxy_config.set_pac_mandatory(true);
net::MockAsyncProxyResolver resolver;
auto proxy_resolver_factory =
std::make_unique<net::MockAsyncProxyResolverFactory>(false);
net::MockAsyncProxyResolverFactory* proxy_resolver_factory_raw =
proxy_resolver_factory.get();
net::ProxyResolutionService service(
std::make_unique<net::ProxyConfigServiceFixed>(
net::ProxyConfigWithAnnotation(proxy_config,
TRAFFIC_ANNOTATION_FOR_TESTS)),
std::move(proxy_resolver_factory), nullptr);
context->set_proxy_resolution_service(&service);
context->Init();
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
nullptr, context.get());
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(), url,
use_tls_);
net::TestCompletionCallback callback;
EXPECT_EQ(net::ERR_IO_PENDING, socket->Connect(callback.callback()));
socket.reset();
ASSERT_EQ(1u, proxy_resolver_factory_raw->pending_requests().size());
proxy_resolver_factory_raw->pending_requests()[0]->CompleteNowWithForwarder(
net::OK, &resolver);
base::RunLoop().RunUntilIdle();
}
class ReconsiderProxyAfterErrorTest
: public testing::Test,
public testing::WithParamInterface<::testing::tuple<bool, int>> {
public testing::WithParamInterface<::testing::tuple<bool, bool, int>> {
public:
ReconsiderProxyAfterErrorTest()
: context_with_proxy_(
"HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT") {}
"HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT"),
use_tls_(::testing::get<0>(GetParam())) {}
~ReconsiderProxyAfterErrorTest() override {}
base::test::ScopedTaskEnvironment scoped_task_environment_;
TestURLRequestContextWithProxy context_with_proxy_;
const bool use_tls_;
};
// List of errors that are used in the proxy resolution tests.
@ -600,12 +815,14 @@ const int kProxyTestMockErrors[] = {
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
ReconsiderProxyAfterErrorTest,
testing::Combine(testing::Bool(), testing::ValuesIn(kProxyTestMockErrors)));
testing::Combine(testing::Bool(),
testing::Bool(),
testing::ValuesIn(kProxyTestMockErrors)));
TEST_P(ReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) {
net::IoMode io_mode =
::testing::get<0>(GetParam()) ? net::SYNCHRONOUS : net::ASYNC;
const int mock_error = ::testing::get<1>(GetParam());
::testing::get<1>(GetParam()) ? net::SYNCHRONOUS : net::ASYNC;
const int mock_error = ::testing::get<2>(GetParam());
// Before starting the test, verify that there are no proxies marked as bad.
ASSERT_TRUE(context_with_proxy_.proxy_resolution_service()
@ -628,13 +845,15 @@ TEST_P(ReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) {
net::StaticSocketDataProvider data3;
data3.set_connect_data(net::MockConnect(io_mode, net::OK));
socket_factory.AddSocketDataProvider(&data3);
net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK);
socket_factory.AddSSLSocketDataProvider(&ssl_socket);
const GURL kDestination("https://example.com:443");
ProxyResolvingClientSocketFactory proxy_resolving_socket_factory(
&socket_factory, &context_with_proxy_);
std::unique_ptr<ProxyResolvingClientSocket> socket =
proxy_resolving_socket_factory.CreateSocket(net::SSLConfig(),
kDestination);
kDestination, use_tls_);
net::TestCompletionCallback callback;
int status = socket->Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
@ -646,6 +865,7 @@ TEST_P(ReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) {
EXPECT_EQ(2u, retry_info.size()) << mock_error;
EXPECT_NE(retry_info.end(), retry_info.find("https://badproxy:99"));
EXPECT_NE(retry_info.end(), retry_info.find("https://badfallbackproxy:98"));
EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed());
}
} // namespace network

@ -27250,6 +27250,9 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries.
</histogram>
<histogram name="GCM.ConnectedViaProxy" enum="Boolean">
<obsolete>
Deprecated as of 03/2018 (M67).
</obsolete>
<owner>zea@chromium.org</owner>
<summary>Whether the GCM connection was made via a proxy or not.</summary>
</histogram>