network_service_proxy_delegate_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "services/network/network_service_proxy_delegate.h"
  5. #include <string>
  6. #include "base/memory/raw_ptr.h"
  7. #include "base/memory/scoped_refptr.h"
  8. #include "base/test/task_environment.h"
  9. #include "mojo/public/cpp/bindings/remote.h"
  10. #include "mojo/public/cpp/bindings/self_owned_receiver.h"
  11. #include "net/base/proxy_server.h"
  12. #include "net/base/proxy_string_util.h"
  13. #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
  14. #include "net/url_request/url_request_context.h"
  15. #include "net/url_request/url_request_context_builder.h"
  16. #include "net/url_request/url_request_test_util.h"
  17. #include "testing/gmock/include/gmock/gmock.h"
  18. #include "testing/gtest/include/gtest/gtest.h"
  19. #include "third_party/abseil-cpp/absl/types/optional.h"
  20. namespace network {
  21. namespace {
  22. constexpr char kHttpUrl[] = "http://example.com";
  23. constexpr char kLocalhost[] = "http://localhost";
  24. constexpr char kHttpsUrl[] = "https://example.com";
  25. constexpr char kWebsocketUrl[] = "ws://example.com";
  26. } // namespace
  27. MATCHER_P2(Contain,
  28. expected_name,
  29. expected_value,
  30. std::string("headers ") + (negation ? "don't " : "") + "contain '" +
  31. expected_name + ": " + expected_value + "'") {
  32. std::string value;
  33. return arg.GetHeader(expected_name, &value) && value == expected_value;
  34. }
  35. class TestCustomProxyConnectionObserver
  36. : public mojom::CustomProxyConnectionObserver {
  37. public:
  38. TestCustomProxyConnectionObserver() = default;
  39. ~TestCustomProxyConnectionObserver() override = default;
  40. const absl::optional<std::pair<net::ProxyServer, int>>& FallbackArgs() const {
  41. return fallback_;
  42. }
  43. const absl::optional<
  44. std::pair<net::ProxyServer, scoped_refptr<net::HttpResponseHeaders>>>&
  45. HeadersReceivedArgs() const {
  46. return headers_received_;
  47. }
  48. // mojom::CustomProxyConnectionObserver:
  49. void OnFallback(const net::ProxyServer& bad_proxy, int net_error) override {
  50. fallback_ = std::make_pair(bad_proxy, net_error);
  51. }
  52. void OnTunnelHeadersReceived(const net::ProxyServer& proxy_server,
  53. const scoped_refptr<net::HttpResponseHeaders>&
  54. response_headers) override {
  55. headers_received_ = std::make_pair(proxy_server, response_headers);
  56. }
  57. private:
  58. absl::optional<std::pair<net::ProxyServer, int>> fallback_;
  59. absl::optional<
  60. std::pair<net::ProxyServer, scoped_refptr<net::HttpResponseHeaders>>>
  61. headers_received_;
  62. };
  63. class NetworkServiceProxyDelegateTest : public testing::Test {
  64. public:
  65. NetworkServiceProxyDelegateTest() {}
  66. void SetUp() override {
  67. context_ = net::CreateTestURLRequestContextBuilder()->Build();
  68. }
  69. protected:
  70. std::unique_ptr<NetworkServiceProxyDelegate> CreateDelegate(
  71. mojom::CustomProxyConfigPtr config) {
  72. std::unique_ptr<TestCustomProxyConnectionObserver> observer =
  73. std::make_unique<TestCustomProxyConnectionObserver>();
  74. observer_ = observer.get();
  75. mojo::PendingRemote<mojom::CustomProxyConnectionObserver> observer_remote;
  76. mojo::MakeSelfOwnedReceiver(
  77. std::move(observer), observer_remote.InitWithNewPipeAndPassReceiver());
  78. auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
  79. network::mojom::CustomProxyConfig::New(),
  80. client_.BindNewPipeAndPassReceiver(), std::move(observer_remote));
  81. SetConfig(std::move(config));
  82. return delegate;
  83. }
  84. std::unique_ptr<net::URLRequest> CreateRequest(const GURL& url) {
  85. return context_->CreateRequest(url, net::DEFAULT_PRIORITY, nullptr,
  86. TRAFFIC_ANNOTATION_FOR_TESTS);
  87. }
  88. void SetConfig(mojom::CustomProxyConfigPtr config) {
  89. base::RunLoop loop;
  90. client_->OnCustomProxyConfigUpdated(std::move(config), loop.QuitClosure());
  91. loop.Run();
  92. }
  93. void RunUntilIdle() { task_environment_.RunUntilIdle(); }
  94. TestCustomProxyConnectionObserver* TestObserver() const { return observer_; }
  95. private:
  96. mojo::Remote<mojom::CustomProxyConfigClient> client_;
  97. // Owned by the proxy delegate returned by |CreateDelegate|.
  98. raw_ptr<TestCustomProxyConnectionObserver> observer_ = nullptr;
  99. std::unique_ptr<net::URLRequestContext> context_;
  100. base::test::TaskEnvironment task_environment_;
  101. };
  102. TEST_F(NetworkServiceProxyDelegateTest, NullConfigDoesNotCrash) {
  103. mojo::Remote<mojom::CustomProxyConfigClient> client;
  104. auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
  105. nullptr, client.BindNewPipeAndPassReceiver(), mojo::NullRemote());
  106. net::HttpRequestHeaders headers;
  107. auto request = CreateRequest(GURL(kHttpUrl));
  108. }
  109. TEST_F(NetworkServiceProxyDelegateTest, AddsHeadersToTunnelRequest) {
  110. auto config = mojom::CustomProxyConfig::New();
  111. config->rules.ParseFromString("https://proxy");
  112. config->connect_tunnel_headers.SetHeader("connect", "baz");
  113. auto delegate = CreateDelegate(std::move(config));
  114. net::HttpRequestHeaders headers;
  115. auto proxy_server = net::PacResultElementToProxyServer("HTTPS proxy");
  116. delegate->OnBeforeTunnelRequest(proxy_server, &headers);
  117. EXPECT_THAT(headers, Contain("connect", "baz"));
  118. }
  119. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessHttpProxy) {
  120. auto config = mojom::CustomProxyConfig::New();
  121. config->rules.ParseFromString("http=foo");
  122. auto delegate = CreateDelegate(std::move(config));
  123. net::ProxyInfo result;
  124. result.UseDirect();
  125. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
  126. &result);
  127. net::ProxyList expected_proxy_list;
  128. expected_proxy_list.AddProxyServer(
  129. net::PacResultElementToProxyServer("PROXY foo"));
  130. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  131. }
  132. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessHttpsUrl) {
  133. auto config = mojom::CustomProxyConfig::New();
  134. config->rules.ParseFromString("https://foo");
  135. auto delegate = CreateDelegate(std::move(config));
  136. net::ProxyInfo result;
  137. result.UseDirect();
  138. delegate->OnResolveProxy(GURL(kHttpsUrl), "GET", net::ProxyRetryInfoMap(),
  139. &result);
  140. net::ProxyList expected_proxy_list;
  141. expected_proxy_list.AddProxyServer(
  142. net::PacResultElementToProxyServer("HTTPS foo"));
  143. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  144. }
  145. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxySuccessWebSocketUrl) {
  146. auto config = mojom::CustomProxyConfig::New();
  147. config->rules.ParseFromString("https://foo");
  148. auto delegate = CreateDelegate(std::move(config));
  149. net::ProxyInfo result;
  150. result.UseDirect();
  151. delegate->OnResolveProxy(GURL(kWebsocketUrl), "GET", net::ProxyRetryInfoMap(),
  152. &result);
  153. net::ProxyList expected_proxy_list;
  154. expected_proxy_list.AddProxyServer(
  155. net::PacResultElementToProxyServer("HTTPS foo"));
  156. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  157. }
  158. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNoRuleForHttpsUrl) {
  159. auto config = mojom::CustomProxyConfig::New();
  160. config->rules.ParseFromString("http=foo");
  161. auto delegate = CreateDelegate(std::move(config));
  162. net::ProxyInfo result;
  163. result.UseDirect();
  164. delegate->OnResolveProxy(GURL(kHttpsUrl), "GET", net::ProxyRetryInfoMap(),
  165. &result);
  166. EXPECT_TRUE(result.is_direct());
  167. }
  168. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyLocalhost) {
  169. auto config = mojom::CustomProxyConfig::New();
  170. config->rules.ParseFromString("http=foo");
  171. auto delegate = CreateDelegate(std::move(config));
  172. net::ProxyInfo result;
  173. result.UseDirect();
  174. delegate->OnResolveProxy(GURL(kLocalhost), "GET", net::ProxyRetryInfoMap(),
  175. &result);
  176. EXPECT_TRUE(result.is_direct());
  177. }
  178. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyEmptyConfig) {
  179. auto delegate = CreateDelegate(mojom::CustomProxyConfig::New());
  180. net::ProxyInfo result;
  181. result.UseDirect();
  182. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
  183. &result);
  184. EXPECT_TRUE(result.is_direct());
  185. }
  186. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyNonIdempotentMethod) {
  187. auto config = mojom::CustomProxyConfig::New();
  188. config->rules.ParseFromString("http=foo");
  189. auto delegate = CreateDelegate(std::move(config));
  190. net::ProxyInfo result;
  191. result.UseDirect();
  192. delegate->OnResolveProxy(GURL(kHttpUrl), "POST", net::ProxyRetryInfoMap(),
  193. &result);
  194. EXPECT_TRUE(result.is_direct());
  195. }
  196. TEST_F(NetworkServiceProxyDelegateTest,
  197. OnResolveProxyNonIdempotentMethodAllowed) {
  198. auto config = mojom::CustomProxyConfig::New();
  199. config->rules.ParseFromString("http=foo");
  200. config->allow_non_idempotent_methods = true;
  201. auto delegate = CreateDelegate(std::move(config));
  202. net::ProxyInfo result;
  203. result.UseDirect();
  204. delegate->OnResolveProxy(GURL(kHttpUrl), "POST", net::ProxyRetryInfoMap(),
  205. &result);
  206. net::ProxyList expected_proxy_list;
  207. expected_proxy_list.AddProxyServer(
  208. net::PacResultElementToProxyServer("PROXY foo"));
  209. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  210. }
  211. TEST_F(NetworkServiceProxyDelegateTest,
  212. OnResolveProxyBypassForWebSocketScheme) {
  213. auto config = mojom::CustomProxyConfig::New();
  214. config->rules.ParseFromString("http=foo");
  215. config->rules.bypass_rules.AddRuleFromString(GURL(kWebsocketUrl).scheme() +
  216. "://*");
  217. auto delegate = CreateDelegate(std::move(config));
  218. net::ProxyInfo result;
  219. result.UseDirect();
  220. delegate->OnResolveProxy(GURL(kWebsocketUrl), "GET", net::ProxyRetryInfoMap(),
  221. &result);
  222. EXPECT_TRUE(result.is_direct());
  223. }
  224. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDoesNotOverrideExisting) {
  225. auto config = mojom::CustomProxyConfig::New();
  226. config->rules.ParseFromString("http=foo");
  227. config->should_override_existing_config = false;
  228. auto delegate = CreateDelegate(std::move(config));
  229. net::ProxyInfo result;
  230. result.UsePacString("PROXY bar");
  231. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
  232. &result);
  233. net::ProxyList expected_proxy_list;
  234. expected_proxy_list.AddProxyServer(
  235. net::PacResultElementToProxyServer("PROXY bar"));
  236. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  237. }
  238. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyOverridesExisting) {
  239. auto config = mojom::CustomProxyConfig::New();
  240. config->rules.ParseFromString("http=foo");
  241. config->should_override_existing_config = true;
  242. auto delegate = CreateDelegate(std::move(config));
  243. net::ProxyInfo result;
  244. result.UsePacString("PROXY bar");
  245. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
  246. &result);
  247. net::ProxyList expected_proxy_list;
  248. expected_proxy_list.AddProxyServer(
  249. net::PacResultElementToProxyServer("PROXY foo"));
  250. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  251. }
  252. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyDeprioritizesBadProxies) {
  253. auto config = mojom::CustomProxyConfig::New();
  254. config->rules.ParseFromString("http=foo,bar");
  255. auto delegate = CreateDelegate(std::move(config));
  256. net::ProxyInfo result;
  257. result.UseDirect();
  258. net::ProxyRetryInfoMap retry_map;
  259. net::ProxyRetryInfo& info = retry_map["foo:80"];
  260. info.try_while_bad = false;
  261. info.bad_until = base::TimeTicks::Now() + base::Days(2);
  262. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", retry_map, &result);
  263. net::ProxyList expected_proxy_list;
  264. expected_proxy_list.AddProxyServer(
  265. net::PacResultElementToProxyServer("PROXY bar"));
  266. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  267. }
  268. TEST_F(NetworkServiceProxyDelegateTest, OnResolveProxyAllProxiesBad) {
  269. auto config = mojom::CustomProxyConfig::New();
  270. config->rules.ParseFromString("http=foo");
  271. auto delegate = CreateDelegate(std::move(config));
  272. net::ProxyInfo result;
  273. result.UseDirect();
  274. net::ProxyRetryInfoMap retry_map;
  275. net::ProxyRetryInfo& info = retry_map["foo:80"];
  276. info.try_while_bad = false;
  277. info.bad_until = base::TimeTicks::Now() + base::Days(2);
  278. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", retry_map, &result);
  279. EXPECT_TRUE(result.is_direct());
  280. }
  281. TEST_F(NetworkServiceProxyDelegateTest, InitialConfigUsedForProxy) {
  282. auto config = mojom::CustomProxyConfig::New();
  283. config->rules.ParseFromString("http=foo");
  284. mojo::Remote<mojom::CustomProxyConfigClient> client;
  285. auto delegate = std::make_unique<NetworkServiceProxyDelegate>(
  286. std::move(config), client.BindNewPipeAndPassReceiver(),
  287. mojo::NullRemote());
  288. net::ProxyInfo result;
  289. result.UseDirect();
  290. delegate->OnResolveProxy(GURL(kHttpUrl), "GET", net::ProxyRetryInfoMap(),
  291. &result);
  292. net::ProxyList expected_proxy_list;
  293. expected_proxy_list.AddProxyServer(
  294. net::PacResultElementToProxyServer("PROXY foo"));
  295. EXPECT_TRUE(result.proxy_list().Equals(expected_proxy_list));
  296. }
  297. TEST_F(NetworkServiceProxyDelegateTest, OnFallbackObserved) {
  298. net::ProxyServer proxy(net::ProxyServer::SCHEME_HTTP,
  299. net::HostPortPair("proxy.com", 80));
  300. auto config = mojom::CustomProxyConfig::New();
  301. config->rules.ParseFromString("http=foo");
  302. auto delegate = CreateDelegate(std::move(config));
  303. EXPECT_FALSE(TestObserver()->FallbackArgs());
  304. delegate->OnFallback(proxy, net::ERR_FAILED);
  305. RunUntilIdle();
  306. ASSERT_TRUE(TestObserver()->FallbackArgs());
  307. EXPECT_EQ(TestObserver()->FallbackArgs()->first, proxy);
  308. EXPECT_EQ(TestObserver()->FallbackArgs()->second, net::ERR_FAILED);
  309. }
  310. TEST_F(NetworkServiceProxyDelegateTest, OnTunnelHeadersReceivedObserved) {
  311. net::ProxyServer proxy(net::ProxyServer::SCHEME_HTTP,
  312. net::HostPortPair("proxy.com", 80));
  313. scoped_refptr<net::HttpResponseHeaders> headers =
  314. base::MakeRefCounted<net::HttpResponseHeaders>(
  315. "HTTP/1.1 200\nHello: World\n\n");
  316. auto config = mojom::CustomProxyConfig::New();
  317. config->rules.ParseFromString("http=foo");
  318. auto delegate = CreateDelegate(std::move(config));
  319. EXPECT_FALSE(TestObserver()->HeadersReceivedArgs());
  320. EXPECT_EQ(net::OK, delegate->OnTunnelHeadersReceived(proxy, *headers));
  321. RunUntilIdle();
  322. ASSERT_TRUE(TestObserver()->HeadersReceivedArgs());
  323. EXPECT_EQ(TestObserver()->HeadersReceivedArgs()->first, proxy);
  324. // Compare raw header strings since the headers pointer is copied.
  325. EXPECT_EQ(TestObserver()->HeadersReceivedArgs()->second->raw_headers(),
  326. headers->raw_headers());
  327. }
  328. } // namespace network