[WebSocket] Fix proxy.pac --winhttp-proxy-resolver
Make proxy.pac resolution for WebSockets work for Windows and Mac when
the --winhttp-proxy-resolver flag is supplied. These platforms don't
support passing WebSocket URLs to the proxy.pac file. Convert ws and wss
URLs to http and https before passing them to proxy.pac. This matches
the behaviour of the vendor browsers on these platforms.
Add a WebSocket end-to-end test to verify that WebSockets can be
accessed via a proxy specified in proxy.pac.
Since preflighting proxy credentials makes the tests really complicated,
add an extra mode to testserver.py for a vanilla proxy that doesn't
require auth.
BUG=862121
Change-Id: I63b318b7f6d3083a6c21f5811bb27966d17cb02a
Reviewed-on: https://chromium-review.googlesource.com/1143093
Commit-Queue: Adam Rice <[email protected]>
Reviewed-by: Yutaka Hirano <[email protected]>
Reviewed-by: Ian Clelland <[email protected]>
Reviewed-by: Eric Roman <[email protected]>
Cr-Commit-Position: refs/heads/master@{#580196}
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc
index 066647ca..aea2424 100644
--- a/net/websockets/websocket_end_to_end_test.cc
+++ b/net/websockets/websocket_end_to_end_test.cc
@@ -12,6 +12,7 @@
#include <memory>
#include <string>
+#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -22,22 +23,37 @@
#include "base/memory/scoped_refptr.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/strings/strcat.h"
#include "base/strings/string_piece.h"
+#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "net/base/auth.h"
+#include "net/base/host_port_pair.h"
#include "net/base/proxy_delegate.h"
+#include "net/base/url_util.h"
#include "net/http/http_request_headers.h"
+#include "net/log/net_log.h"
+#include "net/proxy_resolution/proxy_config.h"
+#include "net/proxy_resolution/proxy_config_service.h"
+#include "net/proxy_resolution/proxy_config_service_fixed.h"
+#include "net/proxy_resolution/proxy_config_with_annotation.h"
+#include "net/proxy_resolution/proxy_info.h"
#include "net/proxy_resolution/proxy_resolution_service.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "net/test/embedded_test_server/http_request.h"
+#include "net/test/embedded_test_server/http_response.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "net/test/test_data_directory.h"
#include "net/test/test_with_scoped_task_environment.h"
#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_test_util.h"
#include "net/websockets/websocket_channel.h"
#include "net/websockets/websocket_event_interface.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
#include "url/origin.h"
namespace net {
@@ -46,6 +62,10 @@
namespace {
+using test_server::BasicHttpResponse;
+using test_server::HttpRequest;
+using test_server::HttpResponse;
+
static const char kEchoServer[] = "echo-with-no-extension";
// Simplify changing URL schemes.
@@ -259,7 +279,7 @@
}
url::Origin origin = url::Origin::Create(GURL("http://localhost"));
GURL site_for_cookies("http://localhost/");
- event_interface_ = new ConnectTestingEventInterface;
+ event_interface_ = new ConnectTestingEventInterface();
channel_ = std::make_unique<WebSocketChannel>(
base::WrapUnique(event_interface_), &context_);
channel_->SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin,
@@ -342,7 +362,7 @@
// Regression test for crbug/426736 "WebSocket connections not using configured
// system HTTPS Proxy".
TEST_F(WebSocketEndToEndTest, MAYBE_HttpsProxyUsed) {
- SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
+ SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_PROXY,
base::FilePath());
SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
GetWebSocketTestDataDirectory());
@@ -359,23 +379,6 @@
context_.set_proxy_resolution_service(proxy_resolution_service.get());
InitialiseContext();
- // The test server doesn't have an unauthenticated proxy mode. WebSockets
- // cannot provide auth information that isn't already cached, so it's
- // necessary to preflight an HTTP request to authenticate against the proxy.
- // It doesn't matter what the URL is, as long as it is an HTTP navigation.
- GURL http_page =
- ReplaceUrlScheme(ws_server.GetURL("connect_check.html"), "http");
- TestDelegate delegate;
- delegate.set_credentials(
- AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar")));
- {
- std::unique_ptr<URLRequest> request(context_.CreateRequest(
- http_page, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
- request->Start();
- delegate.RunUntilComplete();
- EXPECT_TRUE(delegate.auth_required_called());
- }
-
GURL ws_url = ws_server.GetURL(kEchoServer);
EXPECT_TRUE(ConnectAndWait(ws_url));
const TestProxyDelegateWithProxyInfo::ResolvedProxyInfo& info =
@@ -384,6 +387,81 @@
EXPECT_TRUE(info.proxy_info.is_http());
}
+std::unique_ptr<HttpResponse> ProxyPacHandler(const HttpRequest& request) {
+ GURL url = request.GetURL();
+ EXPECT_EQ(url.path_piece(), "/proxy.pac");
+ EXPECT_TRUE(url.has_query());
+ std::string proxy;
+ EXPECT_TRUE(GetValueForKeyInQuery(url, "proxy", &proxy));
+ auto response = std::make_unique<BasicHttpResponse>();
+ response->set_content_type("application/x-ns-proxy-autoconfig");
+ response->set_content(
+ base::StringPrintf("function FindProxyForURL(url, host) {\n"
+ " return 'PROXY %s';\n"
+ "}\n",
+ proxy.c_str()));
+ return response;
+}
+
+// This tests the proxy.pac resolver that is built into the system. This is not
+// the one that Chrome normally uses. Chrome's normal implementation is defined
+// as a mojo service. It is outside //net and we can't use it from here. This
+// tests the alternative implementations that are selected when the
+// --winhttp-proxy-resolver flag is provided to Chrome. These only exist on OS X
+// and Windows.
+// TODO(ricea): Remove this test if --winhttp-proxy-resolver flag is removed.
+// See crbug.com/644030.
+
+#if defined(OS_WIN) || defined(OS_MACOSX)
+#define MAYBE_ProxyPacUsed ProxyPacUsed
+#else
+#define MAYBE_ProxyPacUsed DISABLED_ProxyPacUsed
+#endif
+
+TEST_F(WebSocketEndToEndTest, MAYBE_ProxyPacUsed) {
+ EmbeddedTestServer proxy_pac_server(net::EmbeddedTestServer::Type::TYPE_HTTP);
+ SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_PROXY,
+ base::FilePath());
+ SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
+ GetWebSocketTestDataDirectory());
+ proxy_pac_server.RegisterRequestHandler(base::BindRepeating(ProxyPacHandler));
+ proxy_server.set_redirect_connect_to_localhost(true);
+
+ ASSERT_TRUE(proxy_pac_server.Start());
+ ASSERT_TRUE(proxy_server.StartInBackground());
+ ASSERT_TRUE(ws_server.StartInBackground());
+ ASSERT_TRUE(proxy_server.BlockUntilStarted());
+ ASSERT_TRUE(ws_server.BlockUntilStarted());
+
+ ProxyConfig proxy_config =
+ ProxyConfig::CreateFromCustomPacURL(proxy_pac_server.GetURL(base::StrCat(
+ {"/proxy.pac?proxy=", proxy_server.host_port_pair().ToString()})));
+ proxy_config.set_pac_mandatory(true);
+ auto proxy_config_service = std::make_unique<ProxyConfigServiceFixed>(
+ ProxyConfigWithAnnotation(proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS));
+ NetLog net_log;
+ std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
+ ProxyResolutionService::CreateUsingSystemProxyResolver(
+ std::move(proxy_config_service), &net_log));
+ ASSERT_EQ(ws_server.host_port_pair().host(), "127.0.0.1");
+ context_.set_proxy_resolution_service(proxy_resolution_service.get());
+ InitialiseContext();
+
+ // We need to use something that doesn't look like localhost, or Windows'
+ // resolver will send us direct regardless of what proxy.pac says.
+ HostPortPair fake_ws_host_port_pair("stealth-localhost",
+ ws_server.host_port_pair().port());
+
+ GURL ws_url(base::StrCat(
+ {"ws://", fake_ws_host_port_pair.ToString(), "/", kEchoServer}));
+ EXPECT_TRUE(ConnectAndWait(ws_url));
+ const auto& info = proxy_delegate_->resolved_proxy_info();
+ EXPECT_EQ(ws_url, info.url);
+ EXPECT_TRUE(info.proxy_info.is_http());
+ EXPECT_EQ(info.proxy_info.ToPacString(),
+ base::StrCat({"PROXY ", proxy_server.host_port_pair().ToString()}));
+}
+
// This is a regression test for crbug.com/408061 Crash in
// net::WebSocketBasicHandshakeStream::Upgrade.
TEST_F(WebSocketEndToEndTest, TruncatedResponse) {