Commit 558a66f7 authored by Helen Li's avatar Helen Li Committed by Commit Bot

Moves Jingle ProxyResolvingClientSocket to services/network/

ProxyResolvingClientSocket accesses socket pools, http auth cache,
client certs cache directly.  As a part of the network servicification
effort, all interactions with //net need to be done using mojo APIs.
Socket usage will also be through mojo APIs. This CL moves this class to
services/network so the API surface that we need to export is smaller.

This CL:
- Moves ProxyResolvingClientSocket to services/network.
- Adds a few additional unit tests.

See design doc:  https://docs.google.com/document/d/1iQl_Y2o7vykiPXpZiNbKov-WZbUb4RprmXIk311tTso/edit?usp=sharing.

Bug: 721401

Cq-Include-Trybots: master.tryserver.chromium.android:android_cronet_tester;master.tryserver.chromium.mac:ios-simulator-cronet
Change-Id: Idbc0cb2e3a9713ef111ddd8b5845f27b1445574e
Reviewed-on: https://chromium-review.googlesource.com/763651
Commit-Queue: Helen Li <xunjieli@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Reviewed-by: default avatarSergey Ulanov <sergeyu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#526510}
parent 6b6aa45d
......@@ -14,7 +14,6 @@
#include "content/common/p2p_messages.h"
#include "ipc/ipc_sender.h"
#include "jingle/glue/fake_ssl_client_socket.h"
#include "jingle/glue/proxy_resolving_client_socket.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/socket/client_socket_factory.h"
......@@ -23,7 +22,9 @@
#include "net/socket/tcp_client_socket.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/proxy_resolving_client_socket.h"
#include "third_party/webrtc/media/base/rtputils.h"
#include "url/gurl.h"
namespace {
......@@ -120,9 +121,10 @@ bool P2PSocketHostTcpBase::Init(const net::IPEndPoint& local_address,
// The default SSLConfig is good enough for us for now.
const net::SSLConfig ssl_config;
socket_.reset(new jingle_glue::ProxyResolvingClientSocket(
socket_ = std::make_unique<network::ProxyResolvingClientSocket>(
nullptr, // Default socket pool provided by the net::Proxy.
url_context_, ssl_config, dest_host_port_pair));
url_context_, ssl_config,
GURL("https://" + dest_host_port_pair.ToString()));
int status = socket_->Connect(
base::Bind(&P2PSocketHostTcpBase::OnConnected,
......
......@@ -13,8 +13,6 @@ if (enable_webrtc || !is_android) {
"glue/chrome_async_socket.h",
"glue/fake_ssl_client_socket.cc",
"glue/fake_ssl_client_socket.h",
"glue/proxy_resolving_client_socket.cc",
"glue/proxy_resolving_client_socket.h",
"glue/resolving_client_socket_factory.h",
"glue/task_pump.cc",
"glue/task_pump.h",
......@@ -32,6 +30,7 @@ if (enable_webrtc || !is_android) {
"//base",
"//base/third_party/dynamic_annotations",
"//net",
"//services/network/public/cpp",
]
configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
......@@ -39,9 +38,9 @@ if (enable_webrtc || !is_android) {
if (is_nacl) {
sources -= [
"glue/chrome_async_socket.cc",
"glue/proxy_resolving_client_socket.cc",
"glue/xmpp_client_socket_factory.cc",
]
deps -= [ "//services/network/public/cpp" ]
}
}
......@@ -140,7 +139,6 @@ if (enable_webrtc || !is_android) {
"glue/logging_unittest.cc",
"glue/mock_task.cc",
"glue/mock_task.h",
"glue/proxy_resolving_client_socket_unittest.cc",
"glue/task_pump_unittest.cc",
"glue/thread_wrapper_unittest.cc",
"notifier/base/weak_xmpp_client_unittest.cc",
......
# Needed by logging_unittest.cc since it tests the overrides.
include_rules = [
"+services/network/public/cpp",
"+third_party/libjingle_xmpp/task_runner",
"+third_party/webrtc",
'+third_party/webrtc_overrides',
"+third_party/webrtc_overrides",
]
// 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 "jingle/glue/proxy_resolving_client_socket.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/mock_host_resolver.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/socket_test_util.h"
#include "net/test/gtest_util.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
class MyTestURLRequestContext : public net::TestURLRequestContext {
public:
MyTestURLRequestContext() : TestURLRequestContext(true) {
context_storage_.set_proxy_service(
net::ProxyService::CreateFixedFromPacResult(
"PROXY bad:99; PROXY maybe:80; DIRECT"));
Init();
}
~MyTestURLRequestContext() override {}
};
} // namespace
namespace jingle_glue {
class ProxyResolvingClientSocketTest : public testing::Test {
protected:
ProxyResolvingClientSocketTest()
: url_request_context_getter_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new MyTestURLRequestContext))) {}
~ProxyResolvingClientSocketTest() override {}
void TearDown() override {
// Clear out any messages posted by ProxyResolvingClientSocket's
// destructor.
base::RunLoop().RunUntilIdle();
}
base::MessageLoop message_loop_;
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter_;
};
// TODO(sanjeevr): Fix this test on Linux.
TEST_F(ProxyResolvingClientSocketTest, DISABLED_ConnectError) {
net::HostPortPair dest("0.0.0.0", 0);
ProxyResolvingClientSocket proxy_resolving_socket(
NULL,
url_request_context_getter_,
net::SSLConfig(),
dest);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
// Connect always returns ERR_IO_PENDING because it is always asynchronous.
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
// ProxyResolvingClientSocket::Connect() will always return an error of
// ERR_ADDRESS_INVALID for a 0 IP address.
EXPECT_EQ(net::ERR_ADDRESS_INVALID, status);
}
TEST_F(ProxyResolvingClientSocketTest, ReportsBadProxies) {
net::HostPortPair dest("example.com", 443);
net::MockClientSocketFactory socket_factory;
net::StaticSocketDataProvider socket_data1;
socket_data1.set_connect_data(
net::MockConnect(net::ASYNC, net::ERR_ADDRESS_UNREACHABLE));
socket_factory.AddSocketDataProvider(&socket_data1);
net::MockRead reads[] = {
net::MockRead("HTTP/1.1 200 Success\r\n\r\n")
};
net::MockWrite writes[] = {
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::StaticSocketDataProvider socket_data2(reads, arraysize(reads),
writes, arraysize(writes));
socket_data2.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
socket_factory.AddSocketDataProvider(&socket_data2);
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory,
url_request_context_getter_,
net::SSLConfig(),
dest);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
EXPECT_EQ(net::OK, status);
net::URLRequestContext* context =
url_request_context_getter_->GetURLRequestContext();
const net::ProxyRetryInfoMap& retry_info =
context->proxy_service()->proxy_retry_info();
EXPECT_EQ(1u, retry_info.size());
net::ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99");
EXPECT_TRUE(iter != retry_info.end());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter(
new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new MyTestURLRequestContext)));
net::MockClientSocketFactory socket_factory;
net::HostPortPair dest("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.
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 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::HttpAuthCache* auth_cache =
url_request_context_getter->GetURLRequestContext()
->http_transaction_factory()
->GetSession()
->http_auth_cache();
// We are adding these credentials at an empty path so that it won't be picked
// up by the preemptive authentication step and will only be picked up via
// origin + realm + scheme lookup.
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());
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, url_request_context_getter, net::SSLConfig(), dest);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter(
new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new MyTestURLRequestContext)));
net::MockClientSocketFactory socket_factory;
net::HostPortPair dest("example.com", 443);
// Initial connect uses preemptive credentials. That is all.
net::MockWrite kConnectWrites[] = {
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 kConnectReads[] = {
net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
net::StaticSocketDataProvider kSocketData(
kConnectReads, arraysize(kConnectReads), kConnectWrites,
arraysize(kConnectWrites));
socket_factory.AddSocketDataProvider(&kSocketData);
net::HttpAuthCache* auth_cache =
url_request_context_getter->GetURLRequestContext()
->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")),
"/");
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, url_request_context_getter, net::SSLConfig(), dest);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
scoped_refptr<net::TestURLRequestContextGetter> url_request_context_getter(
new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new MyTestURLRequestContext)));
net::MockClientSocketFactory socket_factory;
net::HostPortPair dest("example.com", 443);
// Initial connect uses preemptive credentials. That is all.
net::MockWrite kConnectWrites[] = {
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 kConnectReads[] = {
net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
"Proxy-Authenticate: Basic realm=\"test_realm\"\r\n"
"\r\n")};
net::StaticSocketDataProvider kSocketData(
kConnectReads, arraysize(kConnectReads), kConnectWrites,
arraysize(kConnectWrites));
socket_factory.AddSocketDataProvider(&kSocketData);
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, url_request_context_getter, net::SSLConfig(), dest);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::ERR_PROXY_AUTH_REQUESTED);
}
// TODO(sanjeevr): Add more unit-tests.
} // namespace jingle_glue
......@@ -8,12 +8,12 @@
#include "base/logging.h"
#include "jingle/glue/fake_ssl_client_socket.h"
#include "jingle/glue/proxy_resolving_client_socket.h"
#include "net/socket/client_socket_factory.h"
#include "net/socket/client_socket_handle.h"
#include "net/socket/ssl_client_socket.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
#include "services/network/public/cpp/proxy_resolving_client_socket.h"
namespace jingle_glue {
......@@ -36,8 +36,9 @@ XmppClientSocketFactory::CreateTransportClientSocket(
const net::HostPortPair& host_and_port) {
// TODO(akalin): Use socket pools.
std::unique_ptr<net::StreamSocket> transport_socket(
new ProxyResolvingClientSocket(NULL, request_context_getter_, ssl_config_,
host_and_port));
new network::ProxyResolvingClientSocket(
nullptr, request_context_getter_, ssl_config_,
GURL("https://" + host_and_port.ToString())));
return (use_fake_ssl_client_socket_
? std::unique_ptr<net::StreamSocket>(
new FakeSSLClientSocket(std::move(transport_socket)))
......
include_rules = [
"+net",
"+jingle",
"+services/network/public/cpp",
"+third_party/webrtc/rtc_base",
"+third_party/libjingle_xmpp/xmllite",
"+third_party/libjingle_xmpp/xmpp",
......
......@@ -19,7 +19,6 @@
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "jingle/glue/proxy_resolving_client_socket.h"
#include "net/cert/cert_verifier.h"
#include "net/cert/ct_policy_enforcer.h"
#include "net/cert/multi_log_ct_verifier.h"
......@@ -34,6 +33,7 @@
#include "remoting/signaling/signaling_address.h"
#include "remoting/signaling/xmpp_login_handler.h"
#include "remoting/signaling/xmpp_stream_parser.h"
#include "services/network/public/cpp/proxy_resolving_client_socket.h"
#include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
// Use 50 seconds keep-alive interval, in case routers terminate
......@@ -178,9 +178,11 @@ void XmppSignalStrategy::Core::Connect() {
for (auto& observer : listeners_)
observer.OnSignalStrategyStateChange(CONNECTING);
socket_.reset(new jingle_glue::ProxyResolvingClientSocket(
socket_ = std::make_unique<network::ProxyResolvingClientSocket>(
socket_factory_, request_context_getter_, net::SSLConfig(),
net::HostPortPair(xmpp_server_config_.host, xmpp_server_config_.port)));
GURL("https://" +
net::HostPortPair(xmpp_server_config_.host, xmpp_server_config_.port)
.ToString()));
int result = socket_->Connect(base::Bind(
&Core::OnSocketConnected, base::Unretained(this)));
......
......@@ -16,6 +16,8 @@ static_library("cpp") {
"net_adapters.h",
"network_param_ipc_traits.cc",
"network_param_ipc_traits.h",
"proxy_resolving_client_socket.cc",
"proxy_resolving_client_socket.h",
"url_loader_completion_status.cc",
"url_loader_completion_status.h",
]
......@@ -48,12 +50,15 @@ source_set("tests") {
"mutable_network_traffic_annotation_tag_struct_traits_unittest.cc",
"mutable_partial_network_traffic_annotation_tag_struct_traits_unittest.cc",
"network_struct_traits_unittest.cc",
"proxy_resolving_client_socket_unittest.cc",
]
deps = [
":cpp",
":test_interfaces",
"//base",
"//mojo/public/cpp/bindings",
"//net",
"//net:test_support",
"//testing/gtest",
]
}
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "jingle/glue/proxy_resolving_client_socket.h"
#include "services/network/public/cpp/proxy_resolving_client_socket.h"
#include <stdint.h>
#include <string>
......@@ -26,25 +26,16 @@
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_context_getter.h"
namespace jingle_glue {
namespace network {
ProxyResolvingClientSocket::ProxyResolvingClientSocket(
net::ClientSocketFactory* socket_factory,
const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
const net::SSLConfig& ssl_config,
const net::HostPortPair& dest_host_port_pair)
: proxy_resolve_callback_(
base::Bind(&ProxyResolvingClientSocket::ProcessProxyResolveDone,
base::Unretained(this))),
connect_callback_(
base::Bind(&ProxyResolvingClientSocket::ProcessConnectDone,
base::Unretained(this))),
ssl_config_(ssl_config),
const GURL& url)
: ssl_config_(ssl_config),
pac_request_(NULL),
dest_host_port_pair_(dest_host_port_pair),
// Assume that we intend to do TLS on this socket; all
// current use cases do.
proxy_url_("https://" + dest_host_port_pair_.ToString()),
url_(url),
tried_direct_connect_fallback_(false),
net_log_(net::NetLogWithSource::Make(
request_context_getter->GetURLRequestContext()->net_log(),
......@@ -54,10 +45,12 @@ ProxyResolvingClientSocket::ProxyResolvingClientSocket(
net::URLRequestContext* request_context =
request_context_getter->GetURLRequestContext();
DCHECK(request_context);
DCHECK(!dest_host_port_pair_.host().empty());
DCHECK_GT(dest_host_port_pair_.port(), 0);
DCHECK(proxy_url_.is_valid());
// TODO(xunjieli): Handle invalid URLs more gracefully (at mojo API layer
// or when the request is created).
DCHECK(url_.is_valid());
// TODO(xunjieli): https://crbug.com/793066. Avoid creating one context per
// socket.
net::HttpNetworkSession::Context session_context;
session_context.client_socket_factory = socket_factory;
session_context.host_resolver = request_context->host_resolver();
......@@ -113,11 +106,11 @@ ProxyResolvingClientSocket::~ProxyResolvingClientSocket() {
Disconnect();
}
int ProxyResolvingClientSocket::Read(net::IOBuffer* buf, int buf_len,
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);
NOTREACHED();
return net::ERR_SOCKET_NOT_CONNECTED;
}
......@@ -129,21 +122,18 @@ int ProxyResolvingClientSocket::Write(
if (transport_.get() && transport_->socket())
return transport_->socket()->Write(buf, buf_len, callback,
traffic_annotation);
NOTREACHED();
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::SetReceiveBufferSize(int32_t size) {
if (transport_.get() && transport_->socket())
return transport_->socket()->SetReceiveBufferSize(size);
NOTREACHED();
return net::ERR_SOCKET_NOT_CONNECTED;
}
int ProxyResolvingClientSocket::SetSendBufferSize(int32_t size) {
if (transport_.get() && transport_->socket())
return transport_->socket()->SetSendBufferSize(size);
NOTREACHED();
return net::ERR_SOCKET_NOT_CONNECTED;
}
......@@ -153,104 +143,101 @@ int ProxyResolvingClientSocket::Connect(
tried_direct_connect_fallback_ = false;
// First we try and resolve the proxy.
int status = network_session_->proxy_service()->ResolveProxy(
proxy_url_,
std::string(),
&proxy_info_,
proxy_resolve_callback_,
&pac_request_,
NULL,
net_log_);
if (status != net::ERR_IO_PENDING) {
// We defer execution of ProcessProxyResolveDone instead of calling it
// 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_service()->ResolveProxy(
url_, "POST", &proxy_info_,
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxy,
base::Unretained(this)),
&pac_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::Bind(&ProxyResolvingClientSocket::ProcessProxyResolveDone,
weak_factory_.GetWeakPtr(), status));
FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
weak_factory_.GetWeakPtr(), net_error));
}
user_connect_callback_ = callback;
return net::ERR_IO_PENDING;
}
void ProxyResolvingClientSocket::RunUserConnectCallback(int status) {
DCHECK_LE(status, net::OK);
net::CompletionCallback user_connect_callback = user_connect_callback_;
user_connect_callback_.Reset();
user_connect_callback.Run(status);
}
// Always runs asynchronously.
void ProxyResolvingClientSocket::ProcessProxyResolveDone(int status) {
void ProxyResolvingClientSocket::ConnectToProxy(int net_error) {
pac_request_ = NULL;
DCHECK_NE(status, net::ERR_IO_PENDING);
if (status == net::OK) {
// Remove unsupported proxies from the list.
DCHECK_NE(net_error, net::ERR_IO_PENDING);
if (net_error == net::OK) {
// 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.
proxy_info_.RemoveProxiesWithoutScheme(
net::ProxyServer::SCHEME_DIRECT |
net::ProxyServer::SCHEME_HTTP | net::ProxyServer::SCHEME_HTTPS |
net::ProxyServer::SCHEME_SOCKS4 | net::ProxyServer::SCHEME_SOCKS5);
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;
net_error = net::ERR_NO_SUPPORTED_PROXIES;
}
}
// Since we are faking the URL, it is possible that no proxies match our URL.
// TODO(xunjieli): This results in retrying "Direct" twice, because of
// ReconsiderProxyAfterError() path. https://crbug.com/793076.
// Try falling back to a direct connection if we have not tried that before.
if (status != net::OK) {
if (net_error != net::OK) {
if (!tried_direct_connect_fallback_) {
tried_direct_connect_fallback_ = true;
proxy_info_.UseDirect();
} else {
CloseTransportSocket();
RunUserConnectCallback(status);
base::ResetAndReturn(&user_connect_callback_).Run(net_error);
return;
}
}
transport_.reset(new net::ClientSocketHandle);
// Now that we have resolved the proxy, we need to connect.
status = net::InitSocketHandleForRawConnect(
dest_host_port_pair_, network_session_.get(), proxy_info_, ssl_config_,
// Now that the proxy is resolved, issue a socket connect.
net::HostPortPair host_port_pair = net::HostPortPair::FromURL(url_);
net_error = net::InitSocketHandleForRawConnect(
host_port_pair, network_session_.get(), proxy_info_, ssl_config_,
ssl_config_, net::PRIVACY_MODE_DISABLED, net_log_, transport_.get(),
connect_callback_);
if (status != net::ERR_IO_PENDING) {
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxyDone,
base::Unretained(this)));
if (net_error != net::ERR_IO_PENDING) {
// Since this method is always called asynchronously. it is OK to call
// ProcessConnectDone synchronously.
ProcessConnectDone(status);
// ConnectToProxyDone synchronously.
ConnectToProxyDone(net_error);
}
}
void ProxyResolvingClientSocket::ProcessConnectDone(int status) {
if (status != net::OK) {
void ProxyResolvingClientSocket::ConnectToProxyDone(int net_error) {
if (net_error != net::OK) {
// If the connection fails, try another proxy.
status = ReconsiderProxyAfterError(status);
net_error = ReconsiderProxyAfterError(net_error);
// 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(status, net::OK);
if (status == net::ERR_IO_PENDING)
DCHECK_NE(net_error, net::OK);
if (net_error == net::ERR_IO_PENDING) {
// Proxy reconsideration pending. Return.
return;
}
CloseTransportSocket();
} else {
ReportSuccessfulProxyConnection();
network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL);
}
RunUserConnectCallback(status);
base::ResetAndReturn(&user_connect_callback_).Run(net_error);
}
// TODO(sanjeevr): 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
// ProcessProxyResolveDone with the result of the reconsideration.
// TODO(xunjieli): This following method is out of sync with
// HttpStreamFactoryImpl::JobController. The logic should be refactored into a
// 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
// ConnectToProxy with the result of the reconsideration.
int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
DCHECK(!pac_request_);
DCHECK_NE(error, net::OK);
......@@ -290,9 +277,11 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
net::ProxyClientSocket* proxy_socket =
static_cast<net::ProxyClientSocket*>(transport_->socket());
if (proxy_socket->GetAuthController()->HaveAuth())
return proxy_socket->RestartWithAuth(connect_callback_);
if (proxy_socket->GetAuthController()->HaveAuth()) {
return proxy_socket->RestartWithAuth(
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxyDone,
base::Unretained(this)));
}
return error;
}
default:
......@@ -305,7 +294,9 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
}
int rv = network_session_->proxy_service()->ReconsiderProxyAfterError(
proxy_url_, std::string(), error, &proxy_info_, proxy_resolve_callback_,
url_, std::string(), error, &proxy_info_,
base::BindRepeating(&ProxyResolvingClientSocket::ConnectToProxy,
base::Unretained(this)),
&pac_request_, NULL, net_log_);
if (rv == net::OK || rv == net::ERR_IO_PENDING) {
CloseTransportSocket();
......@@ -317,13 +308,12 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
}
// We either have new proxy info or there was an error in falling back.
// In both cases we want to post ProcessProxyResolveDone (in the error case
// In both cases we want to post ConnectToProxy (in the error case
// we might still want to fall back a direct connection).
if (rv != net::ERR_IO_PENDING) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::Bind(&ProxyResolvingClientSocket::ProcessProxyResolveDone,
weak_factory_.GetWeakPtr(), rv));
FROM_HERE, base::BindOnce(&ProxyResolvingClientSocket::ConnectToProxy,
weak_factory_.GetWeakPtr(), rv));
// Since we potentially have another try to go (trying the direct connect)
// set the return code code to ERR_IO_PENDING.
rv = net::ERR_IO_PENDING;
......@@ -331,10 +321,6 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) {
return rv;
}
void ProxyResolvingClientSocket::ReportSuccessfulProxyConnection() {
network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL);
}
void ProxyResolvingClientSocket::Disconnect() {
CloseTransportSocket();
if (pac_request_) {
......@@ -356,10 +342,8 @@ bool ProxyResolvingClientSocket::IsConnectedAndIdle() const {
return transport_->socket()->IsConnectedAndIdle();
}
int ProxyResolvingClientSocket::GetPeerAddress(
net::IPEndPoint* address) const {
int ProxyResolvingClientSocket::GetPeerAddress(net::IPEndPoint* address) const {
if (!transport_.get() || !transport_->socket()) {
NOTREACHED();
return net::ERR_SOCKET_NOT_CONNECTED;
}
......@@ -367,12 +351,12 @@ int ProxyResolvingClientSocket::GetPeerAddress(
return transport_->socket()->GetPeerAddress(address);
net::IPAddress ip_address;
if (!ip_address.AssignFromIPLiteral(dest_host_port_pair_.host())) {
if (!ip_address.AssignFromIPLiteral(url_.HostNoBrackets())) {
// Do not expose the proxy IP address to the caller.
return net::ERR_NAME_NOT_RESOLVED;
}
*address = net::IPEndPoint(ip_address, dest_host_port_pair_.port());
*address = net::IPEndPoint(ip_address, url_.EffectiveIntPort());
return net::OK;
}
......@@ -380,35 +364,28 @@ int ProxyResolvingClientSocket::GetLocalAddress(
net::IPEndPoint* address) const {
if (transport_.get() && transport_->socket())
return transport_->socket()->GetLocalAddress(address);
NOTREACHED();
return net::ERR_SOCKET_NOT_CONNECTED;
}
const net::NetLogWithSource& ProxyResolvingClientSocket::NetLog() const {
if (transport_.get() && transport_->socket())
return transport_->socket()->NetLog();
NOTREACHED();
return net_log_;
}
void ProxyResolvingClientSocket::SetSubresourceSpeculation() {
if (transport_.get() && transport_->socket())
transport_->socket()->SetSubresourceSpeculation();
else
NOTREACHED();
}
void ProxyResolvingClientSocket::SetOmniboxSpeculation() {
if (transport_.get() && transport_->socket())
transport_->socket()->SetOmniboxSpeculation();
else
NOTREACHED();
}
bool ProxyResolvingClientSocket::WasEverUsed() const {
if (transport_.get() && transport_->socket())
return transport_->socket()->WasEverUsed();
NOTREACHED();
return false;
}
......@@ -419,7 +396,6 @@ bool ProxyResolvingClientSocket::WasAlpnNegotiated() const {
net::NextProto ProxyResolvingClientSocket::GetNegotiatedProtocol() const {
if (transport_.get() && transport_->socket())
return transport_->socket()->GetNegotiatedProtocol();
NOTREACHED();
return net::kProtoUnknown;
}
......@@ -447,4 +423,4 @@ void ProxyResolvingClientSocket::CloseTransportSocket() {
transport_.reset();
}
} // namespace jingle_glue
} // namespace network
// 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.
//
// This StreamSocket implementation wraps a ClientSocketHandle that is created
// from the client socket pool after resolving proxies.
#ifndef JINGLE_GLUE_PROXY_RESOLVING_CLIENT_SOCKET_H_
#define JINGLE_GLUE_PROXY_RESOLVING_CLIENT_SOCKET_H_
#ifndef SERVICES_NETWORK_PROXY_RESOLVING_CLIENT_SOCKET_H_
#define SERVICES_NETWORK_PROXY_RESOLVING_CLIENT_SOCKET_H_
#include <stdint.h>
#include <memory>
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
......@@ -35,22 +32,32 @@ class HttpNetworkSession;
class URLRequestContextGetter;
} // namespace net
// TODO(sanjeevr): Move this to net/
namespace jingle_glue {
namespace network {
// This class represents a net::StreamSocket implementation that does proxy
// resolution for the provided url before establishing a connection. If there is
// a proxy configured, a connection will be established to the proxy.
//
// TODO(xunjieli): https://crbug.com/721401. This class should be private (i.e.
// moved out of services/network/public/cpp). The functionalities will be
// exposed only through a mojo interface.
class ProxyResolvingClientSocket : public net::StreamSocket {
public:
// Constructs a new ProxyResolvingClientSocket. |socket_factory| is
// the ClientSocketFactory that will be used by the underlying
// HttpNetworkSession. If |socket_factory| is NULL, the default
// socket factory (net::ClientSocketFactory::GetDefaultFactory())
// will be used. |dest_host_port_pair| is the destination for this
// socket. The hostname must be non-empty and the port must be > 0.
// Constructs a new ProxyResolvingClientSocket. |socket_factory| is the
// ClientSocketFactory that will be used by the underlying HttpNetworkSession.
// If |socket_factory| is nullptr, the default socket factory
// (net::ClientSocketFactory::GetDefaultFactory()) will be used. |url|'s host
// and port specify where a connection will be established to. The full URL
// will be only used for proxy resolution. Caller 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.
ProxyResolvingClientSocket(
net::ClientSocketFactory* socket_factory,
const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
const net::SSLConfig& ssl_config,
const net::HostPortPair& dest_host_port_pair);
const GURL& url);
~ProxyResolvingClientSocket() override;
// net::StreamSocket implementation.
......@@ -85,18 +92,14 @@ class ProxyResolvingClientSocket : public net::StreamSocket {
void ApplySocketTag(const net::SocketTag& tag) override;
private:
// Proxy resolution and connection functions.
void ProcessProxyResolveDone(int status);
void ProcessConnectDone(int status);
FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest, ConnectToProxy);
FRIEND_TEST_ALL_PREFIXES(ProxyResolvingClientSocketTest, ReadWriteErrors);
void ConnectToProxy(int net_error);
void ConnectToProxyDone(int net_error);
void CloseTransportSocket();
void RunUserConnectCallback(int status);
int ReconsiderProxyAfterError(int error);
void ReportSuccessfulProxyConnection();
// Callbacks passed to net APIs.
net::CompletionCallback proxy_resolve_callback_;
net::CompletionCallback connect_callback_;
std::unique_ptr<net::HttpNetworkSession> network_session_;
......@@ -106,8 +109,7 @@ class ProxyResolvingClientSocket : public net::StreamSocket {
const net::SSLConfig ssl_config_;
net::ProxyService::PacRequest* pac_request_;
net::ProxyInfo proxy_info_;
const net::HostPortPair dest_host_port_pair_;
const GURL proxy_url_;
const GURL url_;
bool tried_direct_connect_fallback_;
net::NetLogWithSource net_log_;
......@@ -119,6 +121,6 @@ class ProxyResolvingClientSocket : public net::StreamSocket {
DISALLOW_COPY_AND_ASSIGN(ProxyResolvingClientSocket);
};
} // namespace jingle_glue
} // namespace network
#endif // JINGLE_GLUE_PROXY_RESOLVING_CLIENT_SOCKET_H_
#endif // SERVICES_NETWORK_PROXY_RESOLVING_CLIENT_SOCKET_H_
// 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 "services/network/public/cpp/proxy_resolving_client_socket.h"
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/test_completion_callback.h"
#include "net/dns/mock_host_resolver.h"
#include "net/proxy/mock_proxy_resolver.h"
#include "net/proxy/proxy_config_service_fixed.h"
#include "net/proxy/proxy_service.h"
#include "net/socket/socket_test_util.h"
#include "net/test/gtest_util.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "net/url_request/url_request_context_getter.h"
#include "net/url_request/url_request_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace network {
namespace {
class TestURLRequestContextWithProxy : public net::TestURLRequestContext {
public:
TestURLRequestContextWithProxy(const std::string& pac_result)
: TestURLRequestContext(true) {
context_storage_.set_proxy_service(
net::ProxyService::CreateFixedFromPacResult(pac_result));
// net::MockHostResolver maps all hosts to localhost.
auto host_resolver = std::make_unique<net::MockHostResolver>();
context_storage_.set_host_resolver(std::move(host_resolver));
Init();
}
~TestURLRequestContextWithProxy() override {}
};
} // namespace
class ProxyResolvingClientSocketTest : public testing::Test {
protected:
ProxyResolvingClientSocketTest()
: context_getter_with_proxy_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy(
"PROXY bad:99; PROXY maybe:80; DIRECT")))) {}
~ProxyResolvingClientSocketTest() override {}
void TearDown() override {
// Clear out any messages posted by ProxyResolvingClientSocket's
// destructor.
base::RunLoop().RunUntilIdle();
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<net::TestURLRequestContextGetter> context_getter_with_proxy_;
};
TEST_F(ProxyResolvingClientSocketTest, ConnectError) {
const struct TestData {
// Whether the error is encountered synchronously as opposed to
// asynchronously.
bool is_error_sync;
// Whether it is using a direct connection as opposed to a proxy connection.
bool is_direct;
} kTestCases[] = {
{true, true}, {true, false}, {false, true}, {false, false},
};
const GURL kDestination("https://example.com:443");
for (auto test : kTestCases) {
scoped_refptr<net::URLRequestContextGetter> context_getter;
if (test.is_direct) {
context_getter = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy("DIRECT")));
} else {
context_getter = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy("PROXY myproxy.com:89")));
}
net::MockClientSocketFactory socket_factory;
net::StaticSocketDataProvider socket_data;
socket_data.set_connect_data(net::MockConnect(
test.is_error_sync ? net::SYNCHRONOUS : net::ASYNC, net::ERR_FAILED));
socket_factory.AddSocketDataProvider(&socket_data);
net::StaticSocketDataProvider socket_data2;
if (!test.is_direct) {
// TODO(xunjieli): https://crbug.com/793076. When net::ERR_FAILED is
// reported with proxy, this should not be retried.
socket_data2.set_connect_data(net::MockConnect(
test.is_error_sync ? net::SYNCHRONOUS : net::ASYNC, net::ERR_FAILED));
socket_factory.AddSocketDataProvider(&socket_data2);
}
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter, net::SSLConfig(), kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
EXPECT_EQ(net::ERR_FAILED, status);
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
if (!test.is_direct) {
EXPECT_TRUE(socket_data2.AllReadDataConsumed());
EXPECT_TRUE(socket_data2.AllWriteDataConsumed());
}
}
}
// Tests that the connection is established to the proxy.
TEST_F(ProxyResolvingClientSocketTest, ConnectToProxy) {
const GURL kDestination("https://example.com:443");
// Use a different port than that of |kDestination|.
const int kProxyPort = 8009;
const int kDirectPort = 443;
for (bool is_direct : {true, false}) {
net::MockClientSocketFactory socket_factory;
scoped_refptr<net::URLRequestContextGetter> context_getter;
if (is_direct) {
context_getter = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy("DIRECT")));
} else {
context_getter = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy(
base::StringPrintf("PROXY myproxy.com:%d", kProxyPort))));
}
net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
net::MockWrite writes[] = {
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::StaticSocketDataProvider socket_data(reads, arraysize(reads), writes,
arraysize(writes));
net::IPEndPoint remote_addr(net::IPAddress(127, 0, 0, 1),
is_direct ? kDirectPort : kProxyPort);
socket_data.set_connect_data(
net::MockConnect(net::ASYNC, net::OK, remote_addr));
socket_factory.AddSocketDataProvider(&socket_data);
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter, net::SSLConfig(), kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
EXPECT_EQ(net::OK, status);
net::IPEndPoint actual_remote_addr;
status = proxy_resolving_socket.GetPeerAddress(&actual_remote_addr);
if (!is_direct) {
// ProxyResolvingClientSocket::GetPeerAddress() hides the ip of the
// proxy, so call private member to make sure address is correct.
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, status);
status = proxy_resolving_socket.transport_->socket()->GetPeerAddress(
&actual_remote_addr);
}
EXPECT_EQ(net::OK, status);
EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
}
}
// Tests that connection itself is successful but an error occurred during
// Read()/Write().
TEST_F(ProxyResolvingClientSocketTest, ReadWriteErrors) {
const GURL kDestination("http://example.com:80");
const struct TestData {
// Whether there is a read error as opposed to a write error.
bool is_read_error;
// Whether the error is encountered synchronously as opposed to
// asynchronously.
bool is_error_sync;
// Whether it is using a direct connection as opposed to a proxy connection.
bool is_direct;
} kTestCases[] = {
{true, true, true}, {true, true, false}, {false, true, true},
{false, true, false}, {true, false, true}, {true, false, false},
{false, false, true}, {false, false, false},
};
// Use a different port than that of |kDestination|.
const int kProxyPort = 8009;
const int kDirectPort = 80;
for (auto test : kTestCases) {
scoped_refptr<net::URLRequestContextGetter> context_getter;
if (test.is_direct) {
context_getter = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy("DIRECT")));
} else {
context_getter = new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy(
base::StringPrintf("PROXY myproxy.com:%d", kProxyPort))));
}
std::vector<net::MockWrite> writes;
std::vector<net::MockRead> reads;
if (!test.is_direct) {
writes.push_back(
net::MockWrite("CONNECT example.com:80 HTTP/1.1\r\n"
"Host: example.com:80\r\n"
"Proxy-Connection: keep-alive\r\n\r\n"));
reads.push_back(net::MockRead("HTTP/1.1 200 Success\r\n\r\n"));
}
if (test.is_read_error) {
reads.emplace_back(test.is_error_sync ? net::SYNCHRONOUS : net::ASYNC,
net::ERR_FAILED);
} else {
writes.emplace_back(test.is_error_sync ? net::SYNCHRONOUS : net::ASYNC,
net::ERR_FAILED);
}
net::StaticSocketDataProvider socket_data(reads.data(), reads.size(),
writes.data(), writes.size());
net::IPEndPoint remote_addr(net::IPAddress(127, 0, 0, 1),
test.is_direct ? kDirectPort : kProxyPort);
socket_data.set_connect_data(
net::MockConnect(net::ASYNC, net::OK, remote_addr));
net::MockClientSocketFactory socket_factory;
socket_factory.AddSocketDataProvider(&socket_data);
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter, net::SSLConfig(), kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
EXPECT_EQ(net::OK, status);
net::IPEndPoint actual_remote_addr;
status = proxy_resolving_socket.GetPeerAddress(&actual_remote_addr);
if (!test.is_direct) {
// ProxyResolvingClientSocket::GetPeerAddress() hides the ip of the
// proxy, so call private member to make sure address is correct.
EXPECT_EQ(net::ERR_NAME_NOT_RESOLVED, status);
status = proxy_resolving_socket.transport_->socket()->GetPeerAddress(
&actual_remote_addr);
}
EXPECT_EQ(net::OK, status);
EXPECT_EQ(remote_addr.ToString(), actual_remote_addr.ToString());
net::TestCompletionCallback read_write_callback;
int read_write_result;
std::string test_data_string("test data");
scoped_refptr<net::IOBuffer> read_buffer(new net::IOBufferWithSize(10));
scoped_refptr<net::IOBuffer> write_buffer(
new net::StringIOBuffer(test_data_string));
if (test.is_read_error) {
read_write_result = proxy_resolving_socket.Read(
read_buffer.get(), 10, read_write_callback.callback());
} else {
read_write_result = proxy_resolving_socket.Write(
write_buffer.get(), test_data_string.size(),
read_write_callback.callback(), TRAFFIC_ANNOTATION_FOR_TESTS);
}
if (read_write_result == net::ERR_IO_PENDING) {
EXPECT_TRUE(!test.is_error_sync);
read_write_result = read_write_callback.WaitForResult();
}
EXPECT_EQ(net::ERR_FAILED, read_write_result);
EXPECT_TRUE(socket_data.AllReadDataConsumed());
EXPECT_TRUE(socket_data.AllWriteDataConsumed());
}
}
TEST_F(ProxyResolvingClientSocketTest, ReportsBadProxies) {
const GURL kDestination("https://example.com:443");
net::MockClientSocketFactory socket_factory;
net::StaticSocketDataProvider socket_data1;
socket_data1.set_connect_data(
net::MockConnect(net::ASYNC, net::ERR_ADDRESS_UNREACHABLE));
socket_factory.AddSocketDataProvider(&socket_data1);
net::MockRead reads[] = {net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
net::MockWrite writes[] = {
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::StaticSocketDataProvider socket_data2(reads, arraysize(reads), writes,
arraysize(writes));
socket_data2.set_connect_data(net::MockConnect(net::ASYNC, net::OK));
socket_factory.AddSocketDataProvider(&socket_data2);
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy_, net::SSLConfig(),
kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
EXPECT_EQ(net::OK, status);
net::URLRequestContext* context =
context_getter_with_proxy_->GetURLRequestContext();
const net::ProxyRetryInfoMap& retry_info =
context->proxy_service()->proxy_retry_info();
EXPECT_EQ(1u, retry_info.size());
net::ProxyRetryInfoMap::const_iterator iter = retry_info.find("bad:99");
EXPECT_TRUE(iter != retry_info.end());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Lookup) {
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.
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 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::HttpAuthCache* auth_cache =
context_getter_with_proxy_->GetURLRequestContext()
->http_transaction_factory()
->GetSession()
->http_auth_cache();
// We are adding these credentials at an empty path so that it won't be picked
// up by the preemptive authentication step and will only be picked up via
// origin + realm + scheme lookup.
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());
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy_, net::SSLConfig(),
kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_Preemptive) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
// Initial connect uses preemptive credentials. That is all.
net::MockWrite kConnectWrites[] = {
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 kConnectReads[] = {
net::MockRead("HTTP/1.1 200 Success\r\n\r\n")};
net::StaticSocketDataProvider kSocketData(
kConnectReads, arraysize(kConnectReads), kConnectWrites,
arraysize(kConnectWrites));
socket_factory.AddSocketDataProvider(&kSocketData);
net::HttpAuthCache* auth_cache =
context_getter_with_proxy_->GetURLRequestContext()
->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")),
"/");
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy_, net::SSLConfig(),
kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::test::IsOk());
}
TEST_F(ProxyResolvingClientSocketTest, ReusesHTTPAuthCache_NoCredentials) {
net::MockClientSocketFactory socket_factory;
const GURL kDestination("https://example.com:443");
// Initial connect uses preemptive credentials. That is all.
net::MockWrite kConnectWrites[] = {
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 kConnectReads[] = {
net::MockRead("HTTP/1.1 407 Proxy Authentication Required\r\n"
"Proxy-Authenticate: Basic realm=\"test_realm\"\r\n"
"\r\n")};
net::StaticSocketDataProvider kSocketData(
kConnectReads, arraysize(kConnectReads), kConnectWrites,
arraysize(kConnectWrites));
socket_factory.AddSocketDataProvider(&kSocketData);
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy_, net::SSLConfig(),
kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_THAT(callback.GetResult(status), net::ERR_PROXY_AUTH_REQUESTED);
}
// Make sure that url is sanitized before it is disclosed to the proxy.
TEST_F(ProxyResolvingClientSocketTest, URLSanitized) {
GURL url("http://username:password@www.example.com:79/?ref#hash#hash");
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::ProxyService service(
std::make_unique<net::ProxyConfigServiceFixed>(proxy_config),
std::move(proxy_resolver_factory), nullptr);
context->set_proxy_service(&service);
context->Init();
scoped_refptr<net::TestURLRequestContextGetter> context_getter_with_proxy(
new net::TestURLRequestContextGetter(base::ThreadTaskRunnerHandle::Get(),
std::move(context)));
net::MockClientSocketFactory socket_factory;
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy, net::SSLConfig(), url);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
base::RunLoop().RunUntilIdle();
ASSERT_EQ(1u, proxy_resolver_factory_raw->pending_requests().size());
EXPECT_EQ(
GURL("http://foopy/proxy.pac"),
proxy_resolver_factory_raw->pending_requests()[0]->script_data()->url());
proxy_resolver_factory_raw->pending_requests()[0]->CompleteNowWithForwarder(
net::OK, &resolver);
ASSERT_EQ(1u, resolver.pending_jobs().size());
// The URL should have been simplified, stripping the username/password/hash.
EXPECT_EQ(GURL("http://www.example.com:79/?ref"),
resolver.pending_jobs()[0]->url());
}
TEST_F(ProxyResolvingClientSocketTest, ProxyConfigChanged) {
auto context = std::make_unique<net::TestURLRequestContext>(true);
// Use direct connection.
std::unique_ptr<net::ProxyService> proxy_service =
net::ProxyService::CreateDirect();
context->set_proxy_service(proxy_service.get());
context->Init();
scoped_refptr<net::TestURLRequestContextGetter> context_getter_with_proxy(
new net::TestURLRequestContextGetter(base::ThreadTaskRunnerHandle::Get(),
std::move(context)));
net::MockClientSocketFactory socket_factory;
// There should be two connection attempts because ProxyConfig has changed
// midway through connect.
net::StaticSocketDataProvider data_1;
data_1.set_connect_data(
net::MockConnect(net::SYNCHRONOUS, net::ERR_CONNECTION_REFUSED));
socket_factory.AddSocketDataProvider(&data_1);
net::StaticSocketDataProvider data_2;
data_2.set_connect_data(net::MockConnect(net::SYNCHRONOUS, net::OK));
socket_factory.AddSocketDataProvider(&data_2);
GURL url("http://www.example.com");
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy, net::SSLConfig(), url);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
// Calling ForceReloadProxyConfig will cause the proxy configuration to
// change. It will still be the direct connection but the configuration
// version will be bumped. That is enough for the job controller to restart
// the jobs.
proxy_service->ForceReloadProxyConfig();
EXPECT_EQ(net::OK, callback.WaitForResult());
EXPECT_TRUE(data_1.AllReadDataConsumed());
EXPECT_TRUE(data_1.AllWriteDataConsumed());
EXPECT_TRUE(data_2.AllReadDataConsumed());
EXPECT_TRUE(data_2.AllWriteDataConsumed());
}
class ReconsiderProxyAfterErrorTest
: public testing::Test,
public testing::WithParamInterface<::testing::tuple<bool, int>> {
public:
ReconsiderProxyAfterErrorTest()
: context_getter_with_proxy_(new net::TestURLRequestContextGetter(
base::ThreadTaskRunnerHandle::Get(),
std::unique_ptr<net::TestURLRequestContext>(
new TestURLRequestContextWithProxy(
"HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT")))) {
}
~ReconsiderProxyAfterErrorTest() override {}
base::test::ScopedTaskEnvironment scoped_task_environment_;
scoped_refptr<net::TestURLRequestContextGetter> context_getter_with_proxy_;
};
// List of errors that are used in the proxy resolution tests.
const int kProxyTestMockErrors[] = {
net::ERR_PROXY_CONNECTION_FAILED, net::ERR_NAME_NOT_RESOLVED,
net::ERR_ADDRESS_UNREACHABLE, net::ERR_CONNECTION_CLOSED,
net::ERR_CONNECTION_RESET, net::ERR_CONNECTION_REFUSED,
net::ERR_CONNECTION_ABORTED, net::ERR_TUNNEL_CONNECTION_FAILED,
net::ERR_SOCKS_CONNECTION_FAILED, net::ERR_TIMED_OUT,
// Errors that should be retried but are currently not.
// net::ERR_CONNECTION_TIMED_OUT,
// net::ERR_PROXY_CERTIFICATE_INVALID,
// net::ERR_QUIC_PROTOCOL_ERROR,
// net::ERR_QUIC_HANDSHAKE_FAILED,
// net::ERR_SSL_PROTOCOL_ERROR,
// net::ERR_MSG_TOO_BIG,
};
INSTANTIATE_TEST_CASE_P(
/* no prefix */,
ReconsiderProxyAfterErrorTest,
testing::Combine(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());
net::URLRequestContext* context =
context_getter_with_proxy_->GetURLRequestContext();
// Before starting the test, verify that there are no proxies marked as bad.
ASSERT_TRUE(context->proxy_service()->proxy_retry_info().empty())
<< mock_error;
net::MockClientSocketFactory socket_factory;
// Connect to first broken proxy.
net::StaticSocketDataProvider data1;
data1.set_connect_data(net::MockConnect(io_mode, mock_error));
socket_factory.AddSocketDataProvider(&data1);
// Connect to second broken proxy.
net::StaticSocketDataProvider data2;
data2.set_connect_data(net::MockConnect(io_mode, mock_error));
socket_factory.AddSocketDataProvider(&data2);
// Connect using direct.
net::StaticSocketDataProvider data3;
data3.set_connect_data(net::MockConnect(io_mode, net::OK));
socket_factory.AddSocketDataProvider(&data3);
const GURL kDestination("https://example.com:443");
ProxyResolvingClientSocket proxy_resolving_socket(
&socket_factory, context_getter_with_proxy_, net::SSLConfig(),
kDestination);
net::TestCompletionCallback callback;
int status = proxy_resolving_socket.Connect(callback.callback());
EXPECT_EQ(net::ERR_IO_PENDING, status);
status = callback.WaitForResult();
EXPECT_EQ(net::OK, status);
const net::ProxyRetryInfoMap& retry_info =
context->proxy_service()->proxy_retry_info();
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"));
}
} // namespace network
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment