| // 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/proxy_resolving_client_socket.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/compiler_specific.h" |
| #include "base/macros.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_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" |
| #include "net/test/gtest_util.h" |
| #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" |
| #include "net/url_request/url_request_test_util.h" |
| #include "services/network/proxy_resolving_client_socket_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace network { |
| |
| namespace { |
| |
| class TestURLRequestContextWithProxy : public net::TestURLRequestContext { |
| public: |
| explicit TestURLRequestContextWithProxy( |
| const std::string& pac_result, |
| net::ClientSocketFactory* client_socket_factory) |
| : TestURLRequestContext(true) { |
| context_storage_.set_proxy_resolution_service( |
| net::ProxyResolutionService::CreateFixedFromPacResult( |
| pac_result, TRAFFIC_ANNOTATION_FOR_TESTS)); |
| // net::MockHostResolver maps all hosts to localhost. |
| auto host_resolver = std::make_unique<net::MockHostResolver>(); |
| context_storage_.set_host_resolver(std::move(host_resolver)); |
| set_client_socket_factory(client_socket_factory); |
| Init(); |
| } |
| ~TestURLRequestContextWithProxy() override {} |
| }; |
| |
| } // namespace |
| |
| class ProxyResolvingClientSocketTest |
| : public testing::Test, |
| public testing::WithParamInterface<bool> { |
| protected: |
| ProxyResolvingClientSocketTest() |
| : context_with_proxy_("PROXY bad:99; PROXY maybe:80; DIRECT", |
| &mock_client_socket_factory_), |
| use_tls_(GetParam()) {} |
| |
| ~ProxyResolvingClientSocketTest() override {} |
| |
| void TearDown() override { |
| // Clear out any messages posted by ProxyResolvingClientSocket's |
| // destructor. |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| TestURLRequestContextWithProxy context_with_proxy_; |
| net::MockClientSocketFactory mock_client_socket_factory_; |
| 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_P(ProxyResolvingClientSocketTest, SocketLimitNotApply) { |
| const int kNumSockets = net::ClientSocketPoolManager::max_sockets_per_group( |
| net::HttpNetworkSession::NORMAL_SOCKET_POOL) + |
| 10; |
| const GURL kDestination("https://example.com:443"); |
| net::MockClientSocketFactory socket_factory; |
| 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::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, writes)); |
| socket_data[i]->set_connect_data(net::MockConnect(net::ASYNC, net::OK)); |
| mock_client_socket_factory_.AddSocketDataProvider(socket_data[i].get()); |
| ssl_data.push_back( |
| std::make_unique<net::SSLSocketDataProvider>(net::ASYNC, net::OK)); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(ssl_data[i].get()); |
| } |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::vector<std::unique_ptr<ProxyResolvingClientSocket>> sockets; |
| for (int i = 0; i < kNumSockets; ++i) { |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = socket->Connect(callback.callback()); |
| EXPECT_THAT(callback.GetResult(status), net::test::IsOk()); |
| sockets.push_back(std::move(socket)); |
| } |
| 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_P(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) { |
| std::unique_ptr<net::URLRequestContext> context; |
| if (test.is_direct) { |
| context = std::make_unique<TestURLRequestContextWithProxy>( |
| "DIRECT", &mock_client_socket_factory_); |
| } else { |
| context = std::make_unique<TestURLRequestContextWithProxy>( |
| "PROXY myproxy.com:89", &mock_client_socket_factory_); |
| } |
| net::StaticSocketDataProvider socket_data; |
| socket_data.set_connect_data(net::MockConnect( |
| test.is_error_sync ? net::SYNCHRONOUS : net::ASYNC, net::ERR_FAILED)); |
| mock_client_socket_factory_.AddSocketDataProvider(&socket_data); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = socket->Connect(callback.callback()); |
| EXPECT_EQ(net::ERR_IO_PENDING, status); |
| status = callback.WaitForResult(); |
| if (test.is_direct) { |
| EXPECT_EQ(net::ERR_FAILED, status); |
| } else { |
| EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED, status); |
| } |
| EXPECT_TRUE(socket_data.AllReadDataConsumed()); |
| EXPECT_TRUE(socket_data.AllWriteDataConsumed()); |
| } |
| } |
| |
| // Tests that the connection is established to the proxy. |
| TEST_P(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; |
| std::unique_ptr<net::URLRequestContext> context; |
| if (is_direct) { |
| context = std::make_unique<TestURLRequestContextWithProxy>( |
| "DIRECT", &mock_client_socket_factory_); |
| } else { |
| context = std::make_unique<TestURLRequestContextWithProxy>( |
| base::StringPrintf("PROXY myproxy.com:%d", kProxyPort), |
| &mock_client_socket_factory_); |
| } |
| 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::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); |
| |
| net::StaticSocketDataProvider socket_data(reads, 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)); |
| mock_client_socket_factory_.AddSocketDataProvider(&socket_data); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = 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 = 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 = |
| 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); |
| |
| mock_client_socket_factory_.AddSocketDataProvider(&socket_data); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); |
| |
| auto context = std::make_unique<TestURLRequestContextWithProxy>( |
| "DIRECT", &mock_client_socket_factory_); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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_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. |
| 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) { |
| std::unique_ptr<net::URLRequestContext> context; |
| if (test.is_direct) { |
| context = std::make_unique<TestURLRequestContextWithProxy>( |
| "DIRECT", &mock_client_socket_factory_); |
| } else { |
| context = std::make_unique<TestURLRequestContextWithProxy>( |
| base::StringPrintf("PROXY myproxy.com:%d", kProxyPort), |
| &mock_client_socket_factory_); |
| } |
| 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, writes); |
| 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; |
| net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); |
| mock_client_socket_factory_.AddSocketDataProvider(&socket_data); |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = 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 = 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 = |
| socket->socket_handle_->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"); |
| auto read_buffer = base::MakeRefCounted<net::IOBufferWithSize>(10); |
| auto write_buffer = |
| base::MakeRefCounted<net::StringIOBuffer>(test_data_string); |
| if (test.is_read_error) { |
| read_write_result = |
| socket->Read(read_buffer.get(), 10, read_write_callback.callback()); |
| } else { |
| read_write_result = 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()); |
| EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed()); |
| } |
| } |
| |
| TEST_P(ProxyResolvingClientSocketTest, ReportsBadProxies) { |
| const GURL kDestination("https://example.com:443"); |
| |
| net::StaticSocketDataProvider socket_data1; |
| socket_data1.set_connect_data( |
| net::MockConnect(net::ASYNC, net::ERR_ADDRESS_UNREACHABLE)); |
| mock_client_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, writes); |
| socket_data2.set_connect_data(net::MockConnect(net::ASYNC, net::OK)); |
| mock_client_socket_factory_.AddSocketDataProvider(&socket_data2); |
| net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = 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_with_proxy_.proxy_resolution_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()); |
| EXPECT_EQ(use_tls_, ssl_socket.ConnectDataConsumed()); |
| } |
| |
| TEST_P(ProxyResolvingClientSocketTest, ResetSocketAfterTunnelAuth) { |
| 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, kConnectWrites1); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData1); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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) { |
| 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, kConnectWrites1); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData1); |
| |
| net::StaticSocketDataProvider kSocketData2(kConnectReads2, kConnectWrites2); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData2); |
| net::StaticSocketDataProvider kSocketData3(kConnectReads3, kConnectWrites3); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData3); |
| |
| net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_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( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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) { |
| 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, kConnectWrites1); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData1); |
| |
| net::StaticSocketDataProvider kSocketData2(kConnectReads2, kConnectWrites2); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData2); |
| net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); |
| |
| net::HttpAuthCache* auth_cache = |
| context_with_proxy_.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()); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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_P(ProxyResolvingClientSocketTest, FactoryUsesLatestHTTPAuthCache) { |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| |
| // After creating |socket_factory|, updates the auth cache with credentials. |
| // New socket connections should pick up this change. |
| net::HttpAuthCache* auth_cache = |
| context_with_proxy_.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()); |
| |
| const GURL kDestination("https://example.com:443"); |
| |
| // Initial connect without credentials. The server responds with a 407. |
| 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::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 407 Proxy Authentication Required\r\n" |
| "Proxy-Authenticate: Basic realm=\"test_realm\"\r\n" |
| "Proxy-Connection: keep-alive\r\n" |
| "Content-Length: 0\r\n" |
| "\r\n"), |
| net::MockRead("HTTP/1.1 200 Success\r\n\r\n")}; |
| |
| net::StaticSocketDataProvider kSocketData(kConnectReads, kConnectWrites); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData); |
| net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_socket); |
| |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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_Preemptive) { |
| 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, kConnectWrites); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData); |
| net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); |
| mock_client_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")), |
| "/"); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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_NoCredentials) { |
| 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, kConnectWrites); |
| mock_client_socket_factory_.AddSocketDataProvider(&kSocketData); |
| |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| |
| net::TestCompletionCallback callback; |
| int status = 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_P(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::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( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(url, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = 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()); |
| } |
| |
| // 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( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(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(); |
| } |
| |
| // Regression test for crbug.com/849300. If proxy resolution is successful but |
| // the proxy scheme is not supported, do not continue with connection |
| // establishment. |
| TEST_P(ProxyResolvingClientSocketTest, NoSupportedProxies) { |
| const GURL kDestination("https://example.com:443"); |
| |
| auto context = std::make_unique<net::TestURLRequestContext>(true); |
| net::ProxyConfig proxy_config; |
| // Use an unsupported proxy scheme. |
| proxy_config.proxy_rules().ParseFromString("quic://foopy:8080"); |
| auto proxy_resolver_factory = |
| std::make_unique<net::MockAsyncProxyResolverFactory>(false); |
| 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( |
| context.get()); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = socket->Connect(callback.callback()); |
| status = callback.GetResult(status); |
| EXPECT_EQ(net::ERR_NO_SUPPORTED_PROXIES, status); |
| } |
| |
| class ReconsiderProxyAfterErrorTest |
| : public testing::Test, |
| public testing::WithParamInterface<::testing::tuple<bool, bool, int>> { |
| public: |
| ReconsiderProxyAfterErrorTest() |
| : context_with_proxy_( |
| "HTTPS badproxy:99; HTTPS badfallbackproxy:98; DIRECT", |
| &mock_client_socket_factory_), |
| use_tls_(::testing::get<0>(GetParam())) {} |
| |
| ~ReconsiderProxyAfterErrorTest() override {} |
| |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| net::MockClientSocketFactory mock_client_socket_factory_; |
| TestURLRequestContextWithProxy context_with_proxy_; |
| const bool use_tls_; |
| }; |
| |
| // List of errors that are used in the proxy resolution tests. |
| // |
| // Note: ProxyResolvingClientSocket currently removes |
| // net::ProxyServer::SCHEME_QUIC, so this list excludes errors that are |
| // retryable only for QUIC proxies. |
| 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_SOCKS_CONNECTION_FAILED, |
| net::ERR_TIMED_OUT, |
| net::ERR_CONNECTION_TIMED_OUT, |
| net::ERR_PROXY_CERTIFICATE_INVALID, |
| net::ERR_SSL_PROTOCOL_ERROR}; |
| |
| INSTANTIATE_TEST_CASE_P( |
| /* no prefix */, |
| ReconsiderProxyAfterErrorTest, |
| testing::Combine(testing::Bool(), |
| testing::Bool(), |
| testing::ValuesIn(kProxyTestMockErrors))); |
| |
| TEST_P(ReconsiderProxyAfterErrorTest, ReconsiderProxyAfterError) { |
| net::IoMode io_mode = |
| ::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() |
| ->proxy_retry_info() |
| .empty()) |
| << mock_error; |
| |
| mock_client_socket_factory_.UseMockProxyClientSockets(); |
| |
| net::ProxyClientSocketDataProvider proxy_data(io_mode, mock_error); |
| |
| // Connect to first broken proxy. |
| net::StaticSocketDataProvider data1; |
| net::SSLSocketDataProvider ssl_data1(io_mode, net::OK); |
| data1.set_connect_data(net::MockConnect(io_mode, net::OK)); |
| mock_client_socket_factory_.AddSocketDataProvider(&data1); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_data1); |
| mock_client_socket_factory_.AddProxyClientSocketDataProvider(&proxy_data); |
| |
| // Connect to second broken proxy. |
| net::StaticSocketDataProvider data2; |
| net::SSLSocketDataProvider ssl_data2(io_mode, net::OK); |
| data2.set_connect_data(net::MockConnect(io_mode, net::OK)); |
| mock_client_socket_factory_.AddSocketDataProvider(&data2); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_data2); |
| mock_client_socket_factory_.AddProxyClientSocketDataProvider(&proxy_data); |
| |
| // Connect using direct. |
| net::StaticSocketDataProvider data3; |
| net::SSLSocketDataProvider ssl_data3(io_mode, net::OK); |
| data3.set_connect_data(net::MockConnect(io_mode, net::OK)); |
| mock_client_socket_factory_.AddSocketDataProvider(&data3); |
| mock_client_socket_factory_.AddSSLSocketDataProvider(&ssl_data3); |
| |
| const GURL kDestination("https://example.com:443"); |
| ProxyResolvingClientSocketFactory proxy_resolving_socket_factory( |
| &context_with_proxy_); |
| std::unique_ptr<ProxyResolvingClientSocket> socket = |
| proxy_resolving_socket_factory.CreateSocket(kDestination, use_tls_); |
| net::TestCompletionCallback callback; |
| int status = 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_with_proxy_.proxy_resolution_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")); |
| // Should always use HTTPS to talk to HTTPS proxy. |
| EXPECT_TRUE(ssl_data1.ConnectDataConsumed()); |
| EXPECT_TRUE(ssl_data2.ConnectDataConsumed()); |
| // This depends on whether the consumer has requested to use TLS. |
| EXPECT_EQ(use_tls_, ssl_data3.ConnectDataConsumed()); |
| } |
| |
| } // namespace network |