123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765 |
- // Copyright 2014 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.
- // End-to-end tests for WebSocket.
- //
- // A python server is (re)started for each test, which is moderately
- // inefficient. However, it makes these tests a good fit for scenarios which
- // require special server configurations.
- #include <stdint.h>
- #include <memory>
- #include <string>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/callback_helpers.h"
- #include "base/files/file_path.h"
- #include "base/location.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/raw_ptr.h"
- #include "base/memory/scoped_refptr.h"
- #include "base/run_loop.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/single_thread_task_runner.h"
- #include "base/test/scoped_feature_list.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "build/build_config.h"
- #include "net/base/auth.h"
- #include "net/base/features.h"
- #include "net/base/host_port_pair.h"
- #include "net/base/ip_endpoint.h"
- #include "net/base/isolation_info.h"
- #include "net/base/net_errors.h"
- #include "net/base/proxy_delegate.h"
- #include "net/base/url_util.h"
- #include "net/cert/ct_policy_status.h"
- #include "net/dns/mock_host_resolver.h"
- #include "net/http/http_request_headers.h"
- #include "net/http/transport_security_state.h"
- #include "net/log/net_log.h"
- #include "net/proxy_resolution/configured_proxy_resolution_service.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/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/ssl_test_util.h"
- #include "net/test/test_data_directory.h"
- #include "net/test/test_with_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_context_builder.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"
- #include "url/url_constants.h"
- namespace net {
- class URLRequest;
- 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.
- GURL ReplaceUrlScheme(const GURL& in_url, const base::StringPiece& scheme) {
- GURL::Replacements replacements;
- replacements.SetSchemeStr(scheme);
- return in_url.ReplaceComponents(replacements);
- }
- // An implementation of WebSocketEventInterface that waits for and records the
- // results of the connect.
- class ConnectTestingEventInterface : public WebSocketEventInterface {
- public:
- ConnectTestingEventInterface();
- ConnectTestingEventInterface(const ConnectTestingEventInterface&) = delete;
- ConnectTestingEventInterface& operator=(const ConnectTestingEventInterface&) =
- delete;
- void WaitForResponse();
- bool failed() const { return failed_; }
- const std::unique_ptr<WebSocketHandshakeResponseInfo>& response() const {
- return response_;
- }
- // Only set if the handshake failed, otherwise empty.
- std::string failure_message() const;
- std::string selected_subprotocol() const;
- std::string extensions() const;
- // Implementation of WebSocketEventInterface.
- void OnCreateURLRequest(URLRequest* request) override {}
- void OnAddChannelResponse(
- std::unique_ptr<WebSocketHandshakeResponseInfo> response,
- const std::string& selected_subprotocol,
- const std::string& extensions) override;
- void OnDataFrame(bool fin,
- WebSocketMessageType type,
- base::span<const char> payload) override;
- bool HasPendingDataFrames() override { return false; }
- void OnSendDataFrameDone() override;
- void OnClosingHandshake() override;
- void OnDropChannel(bool was_clean,
- uint16_t code,
- const std::string& reason) override;
- void OnFailChannel(const std::string& message,
- int net_error,
- absl::optional<int> response_code) override;
- void OnStartOpeningHandshake(
- std::unique_ptr<WebSocketHandshakeRequestInfo> request) override;
- void OnSSLCertificateError(
- std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
- const GURL& url,
- int net_error,
- const SSLInfo& ssl_info,
- bool fatal) override;
- int OnAuthRequired(const AuthChallengeInfo& auth_info,
- scoped_refptr<HttpResponseHeaders> response_headers,
- const IPEndPoint& remote_endpoint,
- base::OnceCallback<void(const AuthCredentials*)> callback,
- absl::optional<AuthCredentials>* credentials) override;
- private:
- void QuitNestedEventLoop();
- // failed_ is true if the handshake failed (ie. OnFailChannel was called).
- bool failed_ = false;
- std::unique_ptr<WebSocketHandshakeResponseInfo> response_;
- std::string selected_subprotocol_;
- std::string extensions_;
- std::string failure_message_;
- base::RunLoop run_loop_;
- };
- ConnectTestingEventInterface::ConnectTestingEventInterface() = default;
- void ConnectTestingEventInterface::WaitForResponse() {
- run_loop_.Run();
- }
- std::string ConnectTestingEventInterface::failure_message() const {
- return failure_message_;
- }
- std::string ConnectTestingEventInterface::selected_subprotocol() const {
- return selected_subprotocol_;
- }
- std::string ConnectTestingEventInterface::extensions() const {
- return extensions_;
- }
- void ConnectTestingEventInterface::OnAddChannelResponse(
- std::unique_ptr<WebSocketHandshakeResponseInfo> response,
- const std::string& selected_subprotocol,
- const std::string& extensions) {
- response_ = std::move(response);
- selected_subprotocol_ = selected_subprotocol;
- extensions_ = extensions;
- QuitNestedEventLoop();
- }
- void ConnectTestingEventInterface::OnDataFrame(bool fin,
- WebSocketMessageType type,
- base::span<const char> payload) {
- }
- void ConnectTestingEventInterface::OnSendDataFrameDone() {}
- void ConnectTestingEventInterface::OnClosingHandshake() {}
- void ConnectTestingEventInterface::OnDropChannel(bool was_clean,
- uint16_t code,
- const std::string& reason) {}
- void ConnectTestingEventInterface::OnFailChannel(
- const std::string& message,
- int net_error,
- absl::optional<int> response_code) {
- failed_ = true;
- failure_message_ = message;
- QuitNestedEventLoop();
- }
- void ConnectTestingEventInterface::OnStartOpeningHandshake(
- std::unique_ptr<WebSocketHandshakeRequestInfo> request) {}
- void ConnectTestingEventInterface::OnSSLCertificateError(
- std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
- const GURL& url,
- int net_error,
- const SSLInfo& ssl_info,
- bool fatal) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE, base::BindOnce(&SSLErrorCallbacks::CancelSSLRequest,
- base::Owned(ssl_error_callbacks.release()),
- ERR_SSL_PROTOCOL_ERROR, &ssl_info));
- }
- int ConnectTestingEventInterface::OnAuthRequired(
- const AuthChallengeInfo& auth_info,
- scoped_refptr<HttpResponseHeaders> response_headers,
- const IPEndPoint& remote_endpoint,
- base::OnceCallback<void(const AuthCredentials*)> callback,
- absl::optional<AuthCredentials>* credentials) {
- *credentials = absl::nullopt;
- return OK;
- }
- void ConnectTestingEventInterface::QuitNestedEventLoop() {
- run_loop_.Quit();
- }
- // A subclass of TestNetworkDelegate that additionally implements the
- // OnResolveProxy callback and records the information passed to it.
- class TestProxyDelegateWithProxyInfo : public ProxyDelegate {
- public:
- TestProxyDelegateWithProxyInfo() = default;
- TestProxyDelegateWithProxyInfo(const TestProxyDelegateWithProxyInfo&) =
- delete;
- TestProxyDelegateWithProxyInfo& operator=(
- const TestProxyDelegateWithProxyInfo&) = delete;
- struct ResolvedProxyInfo {
- GURL url;
- ProxyInfo proxy_info;
- };
- const ResolvedProxyInfo& resolved_proxy_info() const {
- return resolved_proxy_info_;
- }
- protected:
- void OnResolveProxy(const GURL& url,
- const std::string& method,
- const ProxyRetryInfoMap& proxy_retry_info,
- ProxyInfo* result) override {
- resolved_proxy_info_.url = url;
- resolved_proxy_info_.proxy_info = *result;
- }
- void OnFallback(const ProxyServer& bad_proxy, int net_error) override {}
- void OnBeforeTunnelRequest(const ProxyServer& proxy_server,
- HttpRequestHeaders* extra_headers) override {}
- Error OnTunnelHeadersReceived(
- const ProxyServer& proxy_server,
- const HttpResponseHeaders& response_headers) override {
- return OK;
- }
- private:
- ResolvedProxyInfo resolved_proxy_info_;
- };
- class WebSocketEndToEndTest : public TestWithTaskEnvironment {
- protected:
- WebSocketEndToEndTest()
- : event_interface_(),
- proxy_delegate_(std::make_unique<TestProxyDelegateWithProxyInfo>()),
- context_builder_(CreateTestURLRequestContextBuilder()) {}
- // Initialise the URLRequestContext. Normally done automatically by
- // ConnectAndWait(). This method is for the use of tests that need the
- // URLRequestContext initialised before calling ConnectAndWait().
- void InitialiseContext() {
- DCHECK(!context_);
- context_ = context_builder_->Build();
- context_->proxy_resolution_service()->SetProxyDelegate(
- proxy_delegate_.get());
- }
- // Send the connect request to |socket_url| and wait for a response. Returns
- // true if the handshake succeeded.
- bool ConnectAndWait(const GURL& socket_url) {
- if (!context_) {
- InitialiseContext();
- }
- url::Origin origin = url::Origin::Create(GURL("http://localhost"));
- net::SiteForCookies site_for_cookies =
- net::SiteForCookies::FromOrigin(origin);
- IsolationInfo isolation_info =
- IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin,
- origin, SiteForCookies::FromOrigin(origin));
- auto event_interface = std::make_unique<ConnectTestingEventInterface>();
- event_interface_ = event_interface.get();
- channel_ = std::make_unique<WebSocketChannel>(std::move(event_interface),
- context_.get());
- channel_->SendAddChannelRequest(
- GURL(socket_url), sub_protocols_, origin, site_for_cookies,
- isolation_info, HttpRequestHeaders(), TRAFFIC_ANNOTATION_FOR_TESTS);
- event_interface_->WaitForResponse();
- return !event_interface_->failed();
- }
- raw_ptr<ConnectTestingEventInterface> event_interface_; // owned by channel_
- std::unique_ptr<TestProxyDelegateWithProxyInfo> proxy_delegate_;
- std::unique_ptr<URLRequestContextBuilder> context_builder_;
- std::unique_ptr<URLRequestContext> context_;
- std::unique_ptr<WebSocketChannel> channel_;
- std::vector<std::string> sub_protocols_;
- };
- // Basic test of connectivity. If this test fails, nothing else can be expected
- // to work.
- TEST_F(WebSocketEndToEndTest, BasicSmokeTest) {
- SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(ws_server.Start());
- EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
- }
- // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via
- // authenticated proxy times out"
- // TODO(ricea): Enable this when the issue is fixed.
- TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) {
- SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
- base::FilePath());
- SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(proxy_server.StartInBackground());
- ASSERT_TRUE(ws_server.StartInBackground());
- ASSERT_TRUE(proxy_server.BlockUntilStarted());
- ASSERT_TRUE(ws_server.BlockUntilStarted());
- std::string proxy_config =
- "https=" + proxy_server.host_port_pair().ToString();
- std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS));
- ASSERT_TRUE(proxy_resolution_service);
- context_builder_->set_proxy_resolution_service(
- std::move(proxy_resolution_service));
- EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
- EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
- }
- // These test are not compatible with RemoteTestServer because RemoteTestServer
- // doesn't support TYPE_BASIC_AUTH_PROXY.
- // TODO(ricea): Make these tests work. See crbug.com/441711.
- #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
- #define MAYBE_HttpsWssProxyUnauthedFails DISABLED_HttpsWssProxyUnauthedFails
- #define MAYBE_HttpsProxyUsed DISABLED_HttpsProxyUsed
- #else
- #define MAYBE_HttpsWssProxyUnauthedFails HttpsWssProxyUnauthedFails
- #define MAYBE_HttpsProxyUsed HttpsProxyUsed
- #endif
- TEST_F(WebSocketEndToEndTest, MAYBE_HttpsWssProxyUnauthedFails) {
- SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
- base::FilePath());
- SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(proxy_server.StartInBackground());
- ASSERT_TRUE(wss_server.StartInBackground());
- ASSERT_TRUE(proxy_server.BlockUntilStarted());
- ASSERT_TRUE(wss_server.BlockUntilStarted());
- ProxyConfig proxy_config;
- proxy_config.proxy_rules().ParseFromString(
- "https=" + proxy_server.host_port_pair().ToString());
- // TODO(https://crbug.com/901896): Don't rely on proxying localhost.
- proxy_config.proxy_rules().bypass_rules.AddRulesToSubtractImplicit();
- std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- ProxyConfigWithAnnotation(proxy_config,
- TRAFFIC_ANNOTATION_FOR_TESTS)));
- ASSERT_TRUE(proxy_resolution_service);
- context_builder_->set_proxy_resolution_service(
- std::move(proxy_resolution_service));
- EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer)));
- EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
- }
- // Regression test for crbug/426736 "WebSocket connections not using configured
- // system HTTPS Proxy".
- TEST_F(WebSocketEndToEndTest, MAYBE_HttpsProxyUsed) {
- SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_PROXY,
- base::FilePath());
- SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(proxy_server.StartInBackground());
- ASSERT_TRUE(ws_server.StartInBackground());
- ASSERT_TRUE(proxy_server.BlockUntilStarted());
- ASSERT_TRUE(ws_server.BlockUntilStarted());
- ProxyConfig proxy_config;
- proxy_config.proxy_rules().ParseFromString(
- "https=" + proxy_server.host_port_pair().ToString() + ";" +
- "http=" + proxy_server.host_port_pair().ToString());
- // TODO(https://crbug.com/901896): Don't rely on proxying localhost.
- proxy_config.proxy_rules().bypass_rules.AddRulesToSubtractImplicit();
- std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
- ConfiguredProxyResolutionService::CreateFixedForTest(
- ProxyConfigWithAnnotation(proxy_config,
- TRAFFIC_ANNOTATION_FOR_TESTS)));
- context_builder_->set_proxy_resolution_service(
- std::move(proxy_resolution_service));
- InitialiseContext();
- GURL ws_url = ws_server.GetURL(kEchoServer);
- EXPECT_TRUE(ConnectAndWait(ws_url));
- const TestProxyDelegateWithProxyInfo::ResolvedProxyInfo& info =
- proxy_delegate_->resolved_proxy_info();
- EXPECT_EQ(ws_url, info.url);
- 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 BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
- #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));
- std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
- ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver(
- std::move(proxy_config_service), NetLog::Get(),
- /*quick_check_enabled=*/true));
- ASSERT_EQ(ws_server.host_port_pair().host(), "127.0.0.1");
- context_builder_->set_proxy_resolution_service(
- std::move(proxy_resolution_service));
- InitialiseContext();
- // Use a name other than localhost, since localhost implicitly bypasses the
- // use of proxy.pac.
- 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) {
- SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(ws_server.Start());
- InitialiseContext();
- GURL ws_url = ws_server.GetURL("truncated-headers");
- EXPECT_FALSE(ConnectAndWait(ws_url));
- }
- // Regression test for crbug.com/455215 "HSTS not applied to WebSocket"
- TEST_F(WebSocketEndToEndTest, HstsHttpsToWebSocket) {
- EmbeddedTestServer https_server(net::EmbeddedTestServer::Type::TYPE_HTTPS);
- https_server.SetSSLConfig(
- net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
- https_server.ServeFilesFromSourceDirectory("net/data/url_request_unittest");
- SpawnedTestServer::SSLOptions ssl_options(
- SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
- SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(https_server.Start());
- ASSERT_TRUE(wss_server.Start());
- InitialiseContext();
- // Set HSTS via https:
- TestDelegate delegate;
- GURL https_page = https_server.GetURL("/hsts-headers.html");
- std::unique_ptr<URLRequest> request(context_->CreateRequest(
- https_page, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
- request->Start();
- delegate.RunUntilComplete();
- EXPECT_EQ(OK, delegate.request_status());
- // Check HSTS with ws:
- // Change the scheme from wss: to ws: to verify that it is switched back.
- GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
- EXPECT_TRUE(ConnectAndWait(ws_url));
- }
- TEST_F(WebSocketEndToEndTest, HstsWebSocketToHttps) {
- EmbeddedTestServer https_server(net::EmbeddedTestServer::Type::TYPE_HTTPS);
- https_server.SetSSLConfig(
- net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
- https_server.ServeFilesFromSourceDirectory("net/data/url_request_unittest");
- SpawnedTestServer::SSLOptions ssl_options(
- SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
- SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(https_server.Start());
- ASSERT_TRUE(wss_server.Start());
- InitialiseContext();
- // Set HSTS via wss:
- GURL wss_url = wss_server.GetURL("set-hsts");
- EXPECT_TRUE(ConnectAndWait(wss_url));
- // Verify via http:
- TestDelegate delegate;
- GURL http_page =
- ReplaceUrlScheme(https_server.GetURL("/simple.html"), "http");
- std::unique_ptr<URLRequest> request(context_->CreateRequest(
- http_page, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
- request->Start();
- delegate.RunUntilComplete();
- EXPECT_EQ(OK, delegate.request_status());
- EXPECT_TRUE(request->url().SchemeIs("https"));
- }
- TEST_F(WebSocketEndToEndTest, HstsWebSocketToWebSocket) {
- SpawnedTestServer::SSLOptions ssl_options(
- SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
- SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(wss_server.Start());
- InitialiseContext();
- // Set HSTS via wss:
- GURL wss_url = wss_server.GetURL("set-hsts");
- EXPECT_TRUE(ConnectAndWait(wss_url));
- // Verify via wss:
- GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
- EXPECT_TRUE(ConnectAndWait(ws_url));
- }
- // Regression test for crbug.com/180504 "WebSocket handshake fails when HTTP
- // headers have trailing LWS".
- TEST_F(WebSocketEndToEndTest, TrailingWhitespace) {
- SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(ws_server.Start());
- GURL ws_url = ws_server.GetURL("trailing-whitespace");
- sub_protocols_.push_back("sip");
- EXPECT_TRUE(ConnectAndWait(ws_url));
- EXPECT_EQ("sip", event_interface_->selected_subprotocol());
- }
- // This is a regression test for crbug.com/169448 "WebSockets should support
- // header continuations"
- // TODO(ricea): HTTP continuation headers have been deprecated by RFC7230. If
- // support for continuation headers is removed from Chrome, then this test will
- // break and should be removed.
- TEST_F(WebSocketEndToEndTest, HeaderContinuations) {
- SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(ws_server.Start());
- GURL ws_url = ws_server.GetURL("header-continuation");
- EXPECT_TRUE(ConnectAndWait(ws_url));
- EXPECT_EQ("permessage-deflate; server_max_window_bits=10",
- event_interface_->extensions());
- }
- // Test that ws->wss scheme upgrade is supported on receiving a DNS HTTPS
- // record.
- TEST_F(WebSocketEndToEndTest, DnsSchemeUpgradeSupported) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeatureWithParameters(
- features::kUseDnsHttpsSvcb, {{"UseDnsHttpsSvcbHttpUpgrade", "true"}});
- SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
- SpawnedTestServer::SSLOptions(base::FilePath(
- FILE_PATH_LITERAL("test_names.pem"))),
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(wss_server.Start());
- GURL wss_url("wss://a.test:" +
- base::NumberToString(wss_server.host_port_pair().port()) + "/" +
- kEchoServer);
- GURL::Replacements replacements;
- replacements.SetSchemeStr(url::kWsScheme);
- GURL ws_url = wss_url.ReplaceComponents(replacements);
- // Note that due to socket pool behavior, HostResolver will see the ws/wss
- // requests as http/https.
- auto host_resolver = std::make_unique<MockHostResolver>();
- MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key;
- unencrypted_resolve_key.scheme = url::kHttpScheme;
- host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key),
- ERR_DNS_NAME_HTTPS_ONLY);
- MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key;
- encrypted_resolve_key.scheme = url::kHttpsScheme;
- host_resolver->rules()->AddRule(std::move(encrypted_resolve_key),
- "127.0.0.1");
- context_builder_->set_host_resolver(std::move(host_resolver));
- EXPECT_TRUE(ConnectAndWait(ws_url));
- // Expect request to have reached the server using the upgraded URL.
- EXPECT_EQ(event_interface_->response()->url, wss_url);
- }
- // Test that wss connections can use HostResolverEndpointResults from DNS.
- TEST_F(WebSocketEndToEndTest, HostResolverEndpointResult) {
- base::test::ScopedFeatureList features;
- features.InitAndEnableFeature(features::kUseDnsHttpsSvcb);
- SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
- SpawnedTestServer::SSLOptions(base::FilePath(
- FILE_PATH_LITERAL("test_names.pem"))),
- GetWebSocketTestDataDirectory());
- ASSERT_TRUE(wss_server.Start());
- uint16_t port = wss_server.host_port_pair().port();
- GURL wss_url("wss://a.test:" + base::NumberToString(port) + "/" +
- kEchoServer);
- auto host_resolver = std::make_unique<MockHostResolver>();
- MockHostResolverBase::RuleResolver::RuleKey resolve_key;
- // The DNS query itself is made with the https scheme rather than wss.
- resolve_key.scheme = url::kHttpsScheme;
- resolve_key.hostname_pattern = "a.test";
- resolve_key.port = port;
- HostResolverEndpointResult result;
- result.ip_endpoints = {IPEndPoint(IPAddress::IPv4Localhost(), port)};
- result.metadata.supported_protocol_alpns = {"http/1.1"};
- host_resolver->rules()->AddRule(
- std::move(resolve_key),
- MockHostResolverBase::RuleResolver::RuleResult(std::vector{result}));
- context_builder_->set_host_resolver(std::move(host_resolver));
- EXPECT_TRUE(ConnectAndWait(wss_url));
- // Expect request to have reached the server using the upgraded URL.
- EXPECT_EQ(event_interface_->response()->url, wss_url);
- }
- // Test that wss connections can use EncryptedClientHello.
- TEST_F(WebSocketEndToEndTest, EncryptedClientHello) {
- base::test::ScopedFeatureList features;
- features.InitWithFeatures(
- {features::kUseDnsHttpsSvcb, features::kEncryptedClientHello}, {});
- // SpawnedTestServer does not support ECH, while EmbeddedTestServer does not
- // support WebSockets (https://crbug.com/1281277). Until that is fixed, test
- // ECH by configuring a non-WebSockets HTTPS server. The WebSockets handshake
- // will fail, but getting that far tests that ECH worked.
- // Configure a test server that speaks ECH.
- static constexpr char kRealName[] = "secret.example";
- static constexpr char kPublicName[] = "public.example";
- EmbeddedTestServer::ServerCertificateConfig server_cert_config;
- server_cert_config.dns_names = {kRealName};
- SSLServerConfig ssl_server_config;
- std::vector<uint8_t> ech_config_list;
- ssl_server_config.ech_keys =
- MakeTestEchKeys(kPublicName, /*max_name_len=*/128, &ech_config_list);
- ASSERT_TRUE(ssl_server_config.ech_keys);
- EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS);
- test_server.SetSSLConfig(server_cert_config, ssl_server_config);
- ASSERT_TRUE(test_server.Start());
- GURL https_url = test_server.GetURL(kRealName, "/");
- GURL::Replacements replacements;
- replacements.SetSchemeStr(url::kWssScheme);
- GURL wss_url = https_url.ReplaceComponents(replacements);
- auto host_resolver = std::make_unique<MockHostResolver>();
- MockHostResolverBase::RuleResolver::RuleKey resolve_key;
- // The DNS query itself is made with the https scheme rather than wss.
- resolve_key.scheme = url::kHttpsScheme;
- resolve_key.hostname_pattern = wss_url.host();
- resolve_key.port = wss_url.IntPort();
- HostResolverEndpointResult result;
- result.ip_endpoints = {
- IPEndPoint(IPAddress::IPv4Localhost(), wss_url.IntPort())};
- result.metadata.supported_protocol_alpns = {"http/1.1"};
- result.metadata.ech_config_list = ech_config_list;
- host_resolver->rules()->AddRule(
- std::move(resolve_key),
- MockHostResolverBase::RuleResolver::RuleResult(std::vector{result}));
- context_builder_->set_host_resolver(std::move(host_resolver));
- EXPECT_FALSE(ConnectAndWait(wss_url));
- EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 404",
- event_interface_->failure_message());
- }
- } // namespace
- } // namespace net
|