xref: /aosp_15_r20/external/cronet/net/websockets/websocket_end_to_end_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // End-to-end tests for WebSocket.
6 //
7 // A python server is (re)started for each test, which is moderately
8 // inefficient. However, it makes these tests a good fit for scenarios which
9 // require special server configurations.
10 
11 #include <stdint.h>
12 
13 #include <memory>
14 #include <optional>
15 #include <string>
16 #include <string_view>
17 #include <utility>
18 #include <vector>
19 
20 #include "base/check.h"
21 #include "base/containers/span.h"
22 #include "base/files/file_path.h"
23 #include "base/functional/bind.h"
24 #include "base/functional/callback.h"
25 #include "base/location.h"
26 #include "base/memory/raw_ptr.h"
27 #include "base/memory/scoped_refptr.h"
28 #include "base/run_loop.h"
29 #include "base/strings/strcat.h"
30 #include "base/strings/string_number_conversions.h"
31 #include "base/strings/stringprintf.h"
32 #include "base/task/single_thread_task_runner.h"
33 #include "base/test/scoped_feature_list.h"
34 #include "build/build_config.h"
35 #include "net/base/auth.h"
36 #include "net/base/connection_endpoint_metadata.h"
37 #include "net/base/features.h"
38 #include "net/base/host_port_pair.h"
39 #include "net/base/ip_address.h"
40 #include "net/base/ip_endpoint.h"
41 #include "net/base/isolation_info.h"
42 #include "net/base/net_errors.h"
43 #include "net/base/proxy_chain.h"
44 #include "net/base/proxy_delegate.h"
45 #include "net/base/request_priority.h"
46 #include "net/base/url_util.h"
47 #include "net/cookies/site_for_cookies.h"
48 #include "net/dns/host_resolver.h"
49 #include "net/dns/mock_host_resolver.h"
50 #include "net/dns/public/host_resolver_results.h"
51 #include "net/http/http_request_headers.h"
52 #include "net/log/net_log.h"
53 #include "net/proxy_resolution/configured_proxy_resolution_service.h"
54 #include "net/proxy_resolution/proxy_bypass_rules.h"
55 #include "net/proxy_resolution/proxy_config.h"
56 #include "net/proxy_resolution/proxy_config_service.h"
57 #include "net/proxy_resolution/proxy_config_service_fixed.h"
58 #include "net/proxy_resolution/proxy_config_with_annotation.h"
59 #include "net/proxy_resolution/proxy_info.h"
60 #include "net/proxy_resolution/proxy_resolution_service.h"
61 #include "net/proxy_resolution/proxy_retry_info.h"
62 #include "net/ssl/ssl_server_config.h"
63 #include "net/test/embedded_test_server/embedded_test_server.h"
64 #include "net/test/embedded_test_server/http_request.h"
65 #include "net/test/embedded_test_server/http_response.h"
66 #include "net/test/spawned_test_server/spawned_test_server.h"
67 #include "net/test/ssl_test_util.h"
68 #include "net/test/test_data_directory.h"
69 #include "net/test/test_with_task_environment.h"
70 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
71 #include "net/url_request/url_request.h"
72 #include "net/url_request/url_request_context.h"
73 #include "net/url_request/url_request_context_builder.h"
74 #include "net/url_request/url_request_test_util.h"
75 #include "net/websockets/websocket_channel.h"
76 #include "net/websockets/websocket_event_interface.h"
77 #include "net/websockets/websocket_handshake_response_info.h"
78 #include "testing/gtest/include/gtest/gtest.h"
79 #include "third_party/abseil-cpp/absl/types/variant.h"
80 #include "url/gurl.h"
81 #include "url/origin.h"
82 #include "url/url_constants.h"
83 
84 namespace net {
85 class HttpResponseHeaders;
86 class ProxyServer;
87 class SSLInfo;
88 struct WebSocketHandshakeRequestInfo;
89 
90 namespace {
91 
92 using test_server::BasicHttpResponse;
93 using test_server::HttpRequest;
94 using test_server::HttpResponse;
95 
96 static constexpr char kEchoServer[] = "echo-with-no-extension";
97 
98 // Simplify changing URL schemes.
ReplaceUrlScheme(const GURL & in_url,std::string_view scheme)99 GURL ReplaceUrlScheme(const GURL& in_url, std::string_view scheme) {
100   GURL::Replacements replacements;
101   replacements.SetSchemeStr(scheme);
102   return in_url.ReplaceComponents(replacements);
103 }
104 
105 // An implementation of WebSocketEventInterface that waits for and records the
106 // results of the connect.
107 class ConnectTestingEventInterface : public WebSocketEventInterface {
108  public:
109   ConnectTestingEventInterface();
110 
111   ConnectTestingEventInterface(const ConnectTestingEventInterface&) = delete;
112   ConnectTestingEventInterface& operator=(const ConnectTestingEventInterface&) =
113       delete;
114 
115   void WaitForResponse();
116 
failed() const117   bool failed() const { return failed_; }
118 
response() const119   const std::unique_ptr<WebSocketHandshakeResponseInfo>& response() const {
120     return response_;
121   }
122 
123   // Only set if the handshake failed, otherwise empty.
124   std::string failure_message() const;
125 
126   std::string selected_subprotocol() const;
127 
128   std::string extensions() const;
129 
130   // Implementation of WebSocketEventInterface.
OnCreateURLRequest(URLRequest * request)131   void OnCreateURLRequest(URLRequest* request) override {}
132 
OnURLRequestConnected(net::URLRequest * request,const net::TransportInfo & info)133   void OnURLRequestConnected(net::URLRequest* request,
134                              const net::TransportInfo& info) override {}
135 
136   void OnAddChannelResponse(
137       std::unique_ptr<WebSocketHandshakeResponseInfo> response,
138       const std::string& selected_subprotocol,
139       const std::string& extensions) override;
140 
141   void OnDataFrame(bool fin,
142                    WebSocketMessageType type,
143                    base::span<const char> payload) override;
144 
HasPendingDataFrames()145   bool HasPendingDataFrames() override { return false; }
146 
147   void OnSendDataFrameDone() override;
148 
149   void OnClosingHandshake() override;
150 
151   void OnDropChannel(bool was_clean,
152                      uint16_t code,
153                      const std::string& reason) override;
154 
155   void OnFailChannel(const std::string& message,
156                      int net_error,
157                      std::optional<int> response_code) override;
158 
159   void OnStartOpeningHandshake(
160       std::unique_ptr<WebSocketHandshakeRequestInfo> request) override;
161 
162   void OnSSLCertificateError(
163       std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
164       const GURL& url,
165       int net_error,
166       const SSLInfo& ssl_info,
167       bool fatal) override;
168 
169   int OnAuthRequired(const AuthChallengeInfo& auth_info,
170                      scoped_refptr<HttpResponseHeaders> response_headers,
171                      const IPEndPoint& remote_endpoint,
172                      base::OnceCallback<void(const AuthCredentials*)> callback,
173                      std::optional<AuthCredentials>* credentials) override;
174 
175  private:
176   void QuitNestedEventLoop();
177 
178   // failed_ is true if the handshake failed (ie. OnFailChannel was called).
179   bool failed_ = false;
180   std::unique_ptr<WebSocketHandshakeResponseInfo> response_;
181   std::string selected_subprotocol_;
182   std::string extensions_;
183   std::string failure_message_;
184   base::RunLoop run_loop_;
185 };
186 
187 ConnectTestingEventInterface::ConnectTestingEventInterface() = default;
188 
WaitForResponse()189 void ConnectTestingEventInterface::WaitForResponse() {
190   run_loop_.Run();
191 }
192 
failure_message() const193 std::string ConnectTestingEventInterface::failure_message() const {
194   return failure_message_;
195 }
196 
selected_subprotocol() const197 std::string ConnectTestingEventInterface::selected_subprotocol() const {
198   return selected_subprotocol_;
199 }
200 
extensions() const201 std::string ConnectTestingEventInterface::extensions() const {
202   return extensions_;
203 }
204 
OnAddChannelResponse(std::unique_ptr<WebSocketHandshakeResponseInfo> response,const std::string & selected_subprotocol,const std::string & extensions)205 void ConnectTestingEventInterface::OnAddChannelResponse(
206     std::unique_ptr<WebSocketHandshakeResponseInfo> response,
207     const std::string& selected_subprotocol,
208     const std::string& extensions) {
209   response_ = std::move(response);
210   selected_subprotocol_ = selected_subprotocol;
211   extensions_ = extensions;
212   QuitNestedEventLoop();
213 }
214 
OnDataFrame(bool fin,WebSocketMessageType type,base::span<const char> payload)215 void ConnectTestingEventInterface::OnDataFrame(bool fin,
216                                                WebSocketMessageType type,
217                                                base::span<const char> payload) {
218 }
219 
OnSendDataFrameDone()220 void ConnectTestingEventInterface::OnSendDataFrameDone() {}
221 
OnClosingHandshake()222 void ConnectTestingEventInterface::OnClosingHandshake() {}
223 
OnDropChannel(bool was_clean,uint16_t code,const std::string & reason)224 void ConnectTestingEventInterface::OnDropChannel(bool was_clean,
225                                                  uint16_t code,
226                                                  const std::string& reason) {}
227 
OnFailChannel(const std::string & message,int net_error,std::optional<int> response_code)228 void ConnectTestingEventInterface::OnFailChannel(
229     const std::string& message,
230     int net_error,
231     std::optional<int> response_code) {
232   failed_ = true;
233   failure_message_ = message;
234   QuitNestedEventLoop();
235 }
236 
OnStartOpeningHandshake(std::unique_ptr<WebSocketHandshakeRequestInfo> request)237 void ConnectTestingEventInterface::OnStartOpeningHandshake(
238     std::unique_ptr<WebSocketHandshakeRequestInfo> request) {}
239 
OnSSLCertificateError(std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,const GURL & url,int net_error,const SSLInfo & ssl_info,bool fatal)240 void ConnectTestingEventInterface::OnSSLCertificateError(
241     std::unique_ptr<SSLErrorCallbacks> ssl_error_callbacks,
242     const GURL& url,
243     int net_error,
244     const SSLInfo& ssl_info,
245     bool fatal) {
246   base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
247       FROM_HERE, base::BindOnce(&SSLErrorCallbacks::CancelSSLRequest,
248                                 base::Owned(ssl_error_callbacks.release()),
249                                 ERR_SSL_PROTOCOL_ERROR, &ssl_info));
250 }
251 
OnAuthRequired(const AuthChallengeInfo & auth_info,scoped_refptr<HttpResponseHeaders> response_headers,const IPEndPoint & remote_endpoint,base::OnceCallback<void (const AuthCredentials *)> callback,std::optional<AuthCredentials> * credentials)252 int ConnectTestingEventInterface::OnAuthRequired(
253     const AuthChallengeInfo& auth_info,
254     scoped_refptr<HttpResponseHeaders> response_headers,
255     const IPEndPoint& remote_endpoint,
256     base::OnceCallback<void(const AuthCredentials*)> callback,
257     std::optional<AuthCredentials>* credentials) {
258   *credentials = std::nullopt;
259   return OK;
260 }
261 
QuitNestedEventLoop()262 void ConnectTestingEventInterface::QuitNestedEventLoop() {
263   run_loop_.Quit();
264 }
265 
266 // A subclass of TestNetworkDelegate that additionally implements the
267 // OnResolveProxy callback and records the information passed to it.
268 class TestProxyDelegateWithProxyInfo : public ProxyDelegate {
269  public:
270   TestProxyDelegateWithProxyInfo() = default;
271 
272   TestProxyDelegateWithProxyInfo(const TestProxyDelegateWithProxyInfo&) =
273       delete;
274   TestProxyDelegateWithProxyInfo& operator=(
275       const TestProxyDelegateWithProxyInfo&) = delete;
276 
277   struct ResolvedProxyInfo {
278     GURL url;
279     ProxyInfo proxy_info;
280   };
281 
resolved_proxy_info() const282   const ResolvedProxyInfo& resolved_proxy_info() const {
283     return resolved_proxy_info_;
284   }
285 
286  protected:
OnResolveProxy(const GURL & url,const NetworkAnonymizationKey & network_anonymization_key,const std::string & method,const ProxyRetryInfoMap & proxy_retry_info,ProxyInfo * result)287   void OnResolveProxy(const GURL& url,
288                       const NetworkAnonymizationKey& network_anonymization_key,
289                       const std::string& method,
290                       const ProxyRetryInfoMap& proxy_retry_info,
291                       ProxyInfo* result) override {
292     resolved_proxy_info_.url = url;
293     resolved_proxy_info_.proxy_info = *result;
294   }
295 
OnSuccessfulRequestAfterFailures(const ProxyRetryInfoMap & proxy_retry_info)296   void OnSuccessfulRequestAfterFailures(
297       const ProxyRetryInfoMap& proxy_retry_info) override {}
298 
OnFallback(const ProxyChain & bad_chain,int net_error)299   void OnFallback(const ProxyChain& bad_chain, int net_error) override {}
300 
OnBeforeTunnelRequest(const ProxyChain & proxy_chain,size_t chain_index,HttpRequestHeaders * extra_headers)301   void OnBeforeTunnelRequest(const ProxyChain& proxy_chain,
302                              size_t chain_index,
303                              HttpRequestHeaders* extra_headers) override {}
304 
OnTunnelHeadersReceived(const ProxyChain & proxy_chain,size_t chain_index,const HttpResponseHeaders & response_headers)305   Error OnTunnelHeadersReceived(
306       const ProxyChain& proxy_chain,
307       size_t chain_index,
308       const HttpResponseHeaders& response_headers) override {
309     return OK;
310   }
311 
SetProxyResolutionService(ProxyResolutionService * proxy_resolution_service)312   void SetProxyResolutionService(
313       ProxyResolutionService* proxy_resolution_service) override {}
314 
315  private:
316   ResolvedProxyInfo resolved_proxy_info_;
317 };
318 
319 class WebSocketEndToEndTest : public TestWithTaskEnvironment {
320  protected:
WebSocketEndToEndTest()321   WebSocketEndToEndTest()
322       : event_interface_(),
323         proxy_delegate_(std::make_unique<TestProxyDelegateWithProxyInfo>()),
324         context_builder_(CreateTestURLRequestContextBuilder()) {}
325 
326   // Initialise the URLRequestContext. Normally done automatically by
327   // ConnectAndWait(). This method is for the use of tests that need the
328   // URLRequestContext initialised before calling ConnectAndWait().
InitialiseContext()329   void InitialiseContext() {
330     DCHECK(!context_);
331     context_ = context_builder_->Build();
332     context_->proxy_resolution_service()->SetProxyDelegate(
333         proxy_delegate_.get());
334   }
335 
336   // Send the connect request to |socket_url| and wait for a response. Returns
337   // true if the handshake succeeded.
ConnectAndWait(const GURL & socket_url)338   bool ConnectAndWait(const GURL& socket_url) {
339     if (!context_) {
340       InitialiseContext();
341     }
342     url::Origin origin = url::Origin::Create(GURL("http://localhost"));
343     net::SiteForCookies site_for_cookies =
344         net::SiteForCookies::FromOrigin(origin);
345     IsolationInfo isolation_info =
346         IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin,
347                               origin, SiteForCookies::FromOrigin(origin));
348     auto event_interface = std::make_unique<ConnectTestingEventInterface>();
349     event_interface_ = event_interface.get();
350     channel_ = std::make_unique<WebSocketChannel>(std::move(event_interface),
351                                                   context_.get());
352     channel_->SendAddChannelRequest(
353         GURL(socket_url), sub_protocols_, origin, site_for_cookies,
354         /*has_storage_access=*/false, isolation_info, HttpRequestHeaders(),
355         TRAFFIC_ANNOTATION_FOR_TESTS);
356     event_interface_->WaitForResponse();
357     return !event_interface_->failed();
358   }
359 
360   raw_ptr<ConnectTestingEventInterface, DanglingUntriaged>
361       event_interface_;  // owned by channel_
362   std::unique_ptr<TestProxyDelegateWithProxyInfo> proxy_delegate_;
363   std::unique_ptr<URLRequestContextBuilder> context_builder_;
364   std::unique_ptr<URLRequestContext> context_;
365   std::unique_ptr<WebSocketChannel> channel_;
366   std::vector<std::string> sub_protocols_;
367 };
368 
369 // Basic test of connectivity. If this test fails, nothing else can be expected
370 // to work.
TEST_F(WebSocketEndToEndTest,BasicSmokeTest)371 TEST_F(WebSocketEndToEndTest, BasicSmokeTest) {
372   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
373                               GetWebSocketTestDataDirectory());
374   ASSERT_TRUE(ws_server.Start());
375   EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
376 }
377 
378 // These test are not compatible with RemoteTestServer because RemoteTestServer
379 // doesn't support TYPE_BASIC_AUTH_PROXY.
380 // TODO(ricea): Make these tests work. See crbug.com/441711.
381 #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
382 #define MAYBE_HttpsProxyUnauthedFails DISABLED_HttpsProxyUnauthedFails
383 #define MAYBE_HttpsWssProxyUnauthedFails DISABLED_HttpsWssProxyUnauthedFails
384 #define MAYBE_HttpsProxyUsed DISABLED_HttpsProxyUsed
385 #else
386 #define MAYBE_HttpsProxyUnauthedFails HttpsProxyUnauthedFails
387 #define MAYBE_HttpsWssProxyUnauthedFails HttpsWssProxyUnauthedFails
388 #define MAYBE_HttpsProxyUsed HttpsProxyUsed
389 #endif
390 
391 // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via
392 // authenticated proxy times out".
TEST_F(WebSocketEndToEndTest,MAYBE_HttpsProxyUnauthedFails)393 TEST_F(WebSocketEndToEndTest, MAYBE_HttpsProxyUnauthedFails) {
394   SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
395                                  base::FilePath());
396   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
397                               GetWebSocketTestDataDirectory());
398   ASSERT_TRUE(proxy_server.StartInBackground());
399   ASSERT_TRUE(ws_server.StartInBackground());
400   ASSERT_TRUE(proxy_server.BlockUntilStarted());
401   ASSERT_TRUE(ws_server.BlockUntilStarted());
402   ProxyConfig proxy_config;
403   proxy_config.proxy_rules().ParseFromString(
404       "https=" + proxy_server.host_port_pair().ToString());
405   // TODO(https://crbug.com/901896): Don't rely on proxying localhost.
406   proxy_config.proxy_rules().bypass_rules.AddRulesToSubtractImplicit();
407 
408   std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
409       ConfiguredProxyResolutionService::CreateFixedForTest(
410           ProxyConfigWithAnnotation(proxy_config,
411                                     TRAFFIC_ANNOTATION_FOR_TESTS)));
412   ASSERT_TRUE(proxy_resolution_service);
413   context_builder_->set_proxy_resolution_service(
414       std::move(proxy_resolution_service));
415 
416   EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
417   EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
418 }
419 
TEST_F(WebSocketEndToEndTest,MAYBE_HttpsWssProxyUnauthedFails)420 TEST_F(WebSocketEndToEndTest, MAYBE_HttpsWssProxyUnauthedFails) {
421   SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
422                                  base::FilePath());
423   SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
424                                GetWebSocketTestDataDirectory());
425   ASSERT_TRUE(proxy_server.StartInBackground());
426   ASSERT_TRUE(wss_server.StartInBackground());
427   ASSERT_TRUE(proxy_server.BlockUntilStarted());
428   ASSERT_TRUE(wss_server.BlockUntilStarted());
429   ProxyConfig proxy_config;
430   proxy_config.proxy_rules().ParseFromString(
431       "https=" + proxy_server.host_port_pair().ToString());
432   // TODO(https://crbug.com/901896): Don't rely on proxying localhost.
433   proxy_config.proxy_rules().bypass_rules.AddRulesToSubtractImplicit();
434 
435   std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
436       ConfiguredProxyResolutionService::CreateFixedForTest(
437           ProxyConfigWithAnnotation(proxy_config,
438                                     TRAFFIC_ANNOTATION_FOR_TESTS)));
439   ASSERT_TRUE(proxy_resolution_service);
440   context_builder_->set_proxy_resolution_service(
441       std::move(proxy_resolution_service));
442   EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer)));
443   EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
444 }
445 
446 // Regression test for crbug/426736 "WebSocket connections not using configured
447 // system HTTPS Proxy".
TEST_F(WebSocketEndToEndTest,MAYBE_HttpsProxyUsed)448 TEST_F(WebSocketEndToEndTest, MAYBE_HttpsProxyUsed) {
449   SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_PROXY,
450                                  base::FilePath());
451   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
452                               GetWebSocketTestDataDirectory());
453   ASSERT_TRUE(proxy_server.StartInBackground());
454   ASSERT_TRUE(ws_server.StartInBackground());
455   ASSERT_TRUE(proxy_server.BlockUntilStarted());
456   ASSERT_TRUE(ws_server.BlockUntilStarted());
457   ProxyConfig proxy_config;
458   proxy_config.proxy_rules().ParseFromString(
459       "https=" + proxy_server.host_port_pair().ToString() + ";" +
460       "http=" + proxy_server.host_port_pair().ToString());
461   // TODO(https://crbug.com/901896): Don't rely on proxying localhost.
462   proxy_config.proxy_rules().bypass_rules.AddRulesToSubtractImplicit();
463 
464   std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
465       ConfiguredProxyResolutionService::CreateFixedForTest(
466           ProxyConfigWithAnnotation(proxy_config,
467                                     TRAFFIC_ANNOTATION_FOR_TESTS)));
468   context_builder_->set_proxy_resolution_service(
469       std::move(proxy_resolution_service));
470   InitialiseContext();
471 
472   GURL ws_url = ws_server.GetURL(kEchoServer);
473   EXPECT_TRUE(ConnectAndWait(ws_url));
474   const TestProxyDelegateWithProxyInfo::ResolvedProxyInfo& info =
475       proxy_delegate_->resolved_proxy_info();
476   EXPECT_EQ(ws_url, info.url);
477   EXPECT_EQ(info.proxy_info.ToDebugString(),
478             base::StrCat({"PROXY ", proxy_server.host_port_pair().ToString()}));
479 }
480 
ProxyPacHandler(const HttpRequest & request)481 std::unique_ptr<HttpResponse> ProxyPacHandler(const HttpRequest& request) {
482   GURL url = request.GetURL();
483   EXPECT_EQ(url.path_piece(), "/proxy.pac");
484   EXPECT_TRUE(url.has_query());
485   std::string proxy;
486   EXPECT_TRUE(GetValueForKeyInQuery(url, "proxy", &proxy));
487   auto response = std::make_unique<BasicHttpResponse>();
488   response->set_content_type("application/x-ns-proxy-autoconfig");
489   response->set_content(
490       base::StringPrintf("function FindProxyForURL(url, host) {\n"
491                          "  return 'PROXY %s';\n"
492                          "}\n",
493                          proxy.c_str()));
494   return response;
495 }
496 
497 // This tests the proxy.pac resolver that is built into the system. This is not
498 // the one that Chrome normally uses. Chrome's normal implementation is defined
499 // as a mojo service. It is outside //net and we can't use it from here. This
500 // tests the alternative implementations that are selected when the
501 // --winhttp-proxy-resolver flag is provided to Chrome. These only exist on OS X
502 // and Windows.
503 // TODO(ricea): Remove this test if --winhttp-proxy-resolver flag is removed.
504 // See crbug.com/644030.
505 
506 #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE)
507 #define MAYBE_ProxyPacUsed ProxyPacUsed
508 #else
509 #define MAYBE_ProxyPacUsed DISABLED_ProxyPacUsed
510 #endif
511 
TEST_F(WebSocketEndToEndTest,MAYBE_ProxyPacUsed)512 TEST_F(WebSocketEndToEndTest, MAYBE_ProxyPacUsed) {
513   EmbeddedTestServer proxy_pac_server(net::EmbeddedTestServer::Type::TYPE_HTTP);
514   SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_PROXY,
515                                  base::FilePath());
516   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
517                               GetWebSocketTestDataDirectory());
518   proxy_pac_server.RegisterRequestHandler(base::BindRepeating(ProxyPacHandler));
519   proxy_server.set_redirect_connect_to_localhost(true);
520 
521   ASSERT_TRUE(proxy_pac_server.Start());
522   ASSERT_TRUE(proxy_server.StartInBackground());
523   ASSERT_TRUE(ws_server.StartInBackground());
524   ASSERT_TRUE(proxy_server.BlockUntilStarted());
525   ASSERT_TRUE(ws_server.BlockUntilStarted());
526 
527   ProxyConfig proxy_config =
528       ProxyConfig::CreateFromCustomPacURL(proxy_pac_server.GetURL(base::StrCat(
529           {"/proxy.pac?proxy=", proxy_server.host_port_pair().ToString()})));
530   proxy_config.set_pac_mandatory(true);
531   auto proxy_config_service = std::make_unique<ProxyConfigServiceFixed>(
532       ProxyConfigWithAnnotation(proxy_config, TRAFFIC_ANNOTATION_FOR_TESTS));
533   std::unique_ptr<ProxyResolutionService> proxy_resolution_service(
534       ConfiguredProxyResolutionService::CreateUsingSystemProxyResolver(
535           std::move(proxy_config_service), NetLog::Get(),
536           /*quick_check_enabled=*/true));
537   ASSERT_EQ(ws_server.host_port_pair().host(), "127.0.0.1");
538   context_builder_->set_proxy_resolution_service(
539       std::move(proxy_resolution_service));
540   InitialiseContext();
541 
542   // Use a name other than localhost, since localhost implicitly bypasses the
543   // use of proxy.pac.
544   HostPortPair fake_ws_host_port_pair("stealth-localhost",
545                                       ws_server.host_port_pair().port());
546 
547   GURL ws_url(base::StrCat(
548       {"ws://", fake_ws_host_port_pair.ToString(), "/", kEchoServer}));
549   EXPECT_TRUE(ConnectAndWait(ws_url));
550   const auto& info = proxy_delegate_->resolved_proxy_info();
551   EXPECT_EQ(ws_url, info.url);
552   EXPECT_EQ(info.proxy_info.ToDebugString(),
553             base::StrCat({"PROXY ", proxy_server.host_port_pair().ToString()}));
554 }
555 
556 // This is a regression test for crbug.com/408061 Crash in
557 // net::WebSocketBasicHandshakeStream::Upgrade.
TEST_F(WebSocketEndToEndTest,TruncatedResponse)558 TEST_F(WebSocketEndToEndTest, TruncatedResponse) {
559   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
560                               GetWebSocketTestDataDirectory());
561   ASSERT_TRUE(ws_server.Start());
562   InitialiseContext();
563 
564   GURL ws_url = ws_server.GetURL("truncated-headers");
565   EXPECT_FALSE(ConnectAndWait(ws_url));
566 }
567 
568 // Regression test for crbug.com/455215 "HSTS not applied to WebSocket"
TEST_F(WebSocketEndToEndTest,HstsHttpsToWebSocket)569 TEST_F(WebSocketEndToEndTest, HstsHttpsToWebSocket) {
570   EmbeddedTestServer https_server(net::EmbeddedTestServer::Type::TYPE_HTTPS);
571   https_server.SetSSLConfig(
572       net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
573   https_server.ServeFilesFromSourceDirectory("net/data/url_request_unittest");
574 
575   SpawnedTestServer::SSLOptions ssl_options(
576       SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
577   SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
578                                GetWebSocketTestDataDirectory());
579 
580   ASSERT_TRUE(https_server.Start());
581   ASSERT_TRUE(wss_server.Start());
582   InitialiseContext();
583   // Set HSTS via https:
584   TestDelegate delegate;
585   GURL https_page = https_server.GetURL("/hsts-headers.html");
586   std::unique_ptr<URLRequest> request(context_->CreateRequest(
587       https_page, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
588   request->Start();
589   delegate.RunUntilComplete();
590   EXPECT_EQ(OK, delegate.request_status());
591 
592   // Check HSTS with ws:
593   // Change the scheme from wss: to ws: to verify that it is switched back.
594   GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
595   EXPECT_TRUE(ConnectAndWait(ws_url));
596 }
597 
TEST_F(WebSocketEndToEndTest,HstsWebSocketToHttps)598 TEST_F(WebSocketEndToEndTest, HstsWebSocketToHttps) {
599   EmbeddedTestServer https_server(net::EmbeddedTestServer::Type::TYPE_HTTPS);
600   https_server.SetSSLConfig(
601       net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
602   https_server.ServeFilesFromSourceDirectory("net/data/url_request_unittest");
603 
604   SpawnedTestServer::SSLOptions ssl_options(
605       SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
606   SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
607                                GetWebSocketTestDataDirectory());
608   ASSERT_TRUE(https_server.Start());
609   ASSERT_TRUE(wss_server.Start());
610   InitialiseContext();
611   // Set HSTS via wss:
612   GURL wss_url = wss_server.GetURL("set-hsts");
613   EXPECT_TRUE(ConnectAndWait(wss_url));
614 
615   // Verify via http:
616   TestDelegate delegate;
617   GURL http_page =
618       ReplaceUrlScheme(https_server.GetURL("/simple.html"), "http");
619   std::unique_ptr<URLRequest> request(context_->CreateRequest(
620       http_page, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
621   request->Start();
622   delegate.RunUntilComplete();
623   EXPECT_EQ(OK, delegate.request_status());
624   EXPECT_TRUE(request->url().SchemeIs("https"));
625 }
626 
TEST_F(WebSocketEndToEndTest,HstsWebSocketToWebSocket)627 TEST_F(WebSocketEndToEndTest, HstsWebSocketToWebSocket) {
628   SpawnedTestServer::SSLOptions ssl_options(
629       SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
630   SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
631                                GetWebSocketTestDataDirectory());
632   ASSERT_TRUE(wss_server.Start());
633   InitialiseContext();
634   // Set HSTS via wss:
635   GURL wss_url = wss_server.GetURL("set-hsts");
636   EXPECT_TRUE(ConnectAndWait(wss_url));
637 
638   // Verify via wss:
639   GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
640   EXPECT_TRUE(ConnectAndWait(ws_url));
641 }
642 
643 // Regression test for crbug.com/180504 "WebSocket handshake fails when HTTP
644 // headers have trailing LWS".
TEST_F(WebSocketEndToEndTest,TrailingWhitespace)645 TEST_F(WebSocketEndToEndTest, TrailingWhitespace) {
646   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
647                               GetWebSocketTestDataDirectory());
648   ASSERT_TRUE(ws_server.Start());
649 
650   GURL ws_url = ws_server.GetURL("trailing-whitespace");
651   sub_protocols_.push_back("sip");
652   EXPECT_TRUE(ConnectAndWait(ws_url));
653   EXPECT_EQ("sip", event_interface_->selected_subprotocol());
654 }
655 
656 // This is a regression test for crbug.com/169448 "WebSockets should support
657 // header continuations"
658 // TODO(ricea): HTTP continuation headers have been deprecated by RFC7230.  If
659 // support for continuation headers is removed from Chrome, then this test will
660 // break and should be removed.
TEST_F(WebSocketEndToEndTest,HeaderContinuations)661 TEST_F(WebSocketEndToEndTest, HeaderContinuations) {
662   SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
663                               GetWebSocketTestDataDirectory());
664   ASSERT_TRUE(ws_server.Start());
665 
666   GURL ws_url = ws_server.GetURL("header-continuation");
667 
668   EXPECT_TRUE(ConnectAndWait(ws_url));
669   EXPECT_EQ("permessage-deflate; server_max_window_bits=10",
670             event_interface_->extensions());
671 }
672 
673 // Test that ws->wss scheme upgrade is supported on receiving a DNS HTTPS
674 // record.
TEST_F(WebSocketEndToEndTest,DnsSchemeUpgradeSupported)675 TEST_F(WebSocketEndToEndTest, DnsSchemeUpgradeSupported) {
676   SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
677                                SpawnedTestServer::SSLOptions(base::FilePath(
678                                    FILE_PATH_LITERAL("test_names.pem"))),
679                                GetWebSocketTestDataDirectory());
680   ASSERT_TRUE(wss_server.Start());
681 
682   GURL wss_url("wss://a.test:" +
683                base::NumberToString(wss_server.host_port_pair().port()) + "/" +
684                kEchoServer);
685   GURL::Replacements replacements;
686   replacements.SetSchemeStr(url::kWsScheme);
687   GURL ws_url = wss_url.ReplaceComponents(replacements);
688 
689   // Note that due to socket pool behavior, HostResolver will see the ws/wss
690   // requests as http/https.
691   auto host_resolver = std::make_unique<MockHostResolver>();
692   MockHostResolverBase::RuleResolver::RuleKey unencrypted_resolve_key;
693   unencrypted_resolve_key.scheme = url::kHttpScheme;
694   host_resolver->rules()->AddRule(std::move(unencrypted_resolve_key),
695                                   ERR_DNS_NAME_HTTPS_ONLY);
696   MockHostResolverBase::RuleResolver::RuleKey encrypted_resolve_key;
697   encrypted_resolve_key.scheme = url::kHttpsScheme;
698   host_resolver->rules()->AddRule(std::move(encrypted_resolve_key),
699                                   "127.0.0.1");
700   context_builder_->set_host_resolver(std::move(host_resolver));
701 
702   EXPECT_TRUE(ConnectAndWait(ws_url));
703 
704   // Expect request to have reached the server using the upgraded URL.
705   EXPECT_EQ(event_interface_->response()->url, wss_url);
706 }
707 
708 // Test that wss connections can use HostResolverEndpointResults from DNS.
TEST_F(WebSocketEndToEndTest,HostResolverEndpointResult)709 TEST_F(WebSocketEndToEndTest, HostResolverEndpointResult) {
710   base::test::ScopedFeatureList features;
711   features.InitAndEnableFeature(features::kUseDnsHttpsSvcb);
712 
713   SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
714                                SpawnedTestServer::SSLOptions(base::FilePath(
715                                    FILE_PATH_LITERAL("test_names.pem"))),
716                                GetWebSocketTestDataDirectory());
717   ASSERT_TRUE(wss_server.Start());
718 
719   uint16_t port = wss_server.host_port_pair().port();
720   GURL wss_url("wss://a.test:" + base::NumberToString(port) + "/" +
721                kEchoServer);
722 
723   auto host_resolver = std::make_unique<MockHostResolver>();
724   MockHostResolverBase::RuleResolver::RuleKey resolve_key;
725   // The DNS query itself is made with the https scheme rather than wss.
726   resolve_key.scheme = url::kHttpsScheme;
727   resolve_key.hostname_pattern = "a.test";
728   resolve_key.port = port;
729   HostResolverEndpointResult result;
730   result.ip_endpoints = {IPEndPoint(IPAddress::IPv4Localhost(), port)};
731   result.metadata.supported_protocol_alpns = {"http/1.1"};
732   host_resolver->rules()->AddRule(
733       std::move(resolve_key),
734       MockHostResolverBase::RuleResolver::RuleResult(std::vector{result}));
735   context_builder_->set_host_resolver(std::move(host_resolver));
736 
737   EXPECT_TRUE(ConnectAndWait(wss_url));
738 
739   // Expect request to have reached the server using the upgraded URL.
740   EXPECT_EQ(event_interface_->response()->url, wss_url);
741 }
742 
743 // Test that wss connections can use EncryptedClientHello.
TEST_F(WebSocketEndToEndTest,EncryptedClientHello)744 TEST_F(WebSocketEndToEndTest, EncryptedClientHello) {
745   base::test::ScopedFeatureList features;
746   features.InitAndEnableFeature(features::kUseDnsHttpsSvcb);
747 
748   // SpawnedTestServer does not support ECH, while EmbeddedTestServer does not
749   // support WebSockets (https://crbug.com/1281277). Until that is fixed, test
750   // ECH by configuring a non-WebSockets HTTPS server. The WebSockets handshake
751   // will fail, but getting that far tests that ECH worked.
752 
753   // Configure a test server that speaks ECH.
754   static constexpr char kRealName[] = "secret.example";
755   static constexpr char kPublicName[] = "public.example";
756   EmbeddedTestServer::ServerCertificateConfig server_cert_config;
757   server_cert_config.dns_names = {kRealName};
758   SSLServerConfig ssl_server_config;
759   std::vector<uint8_t> ech_config_list;
760   ssl_server_config.ech_keys =
761       MakeTestEchKeys(kPublicName, /*max_name_len=*/128, &ech_config_list);
762   ASSERT_TRUE(ssl_server_config.ech_keys);
763 
764   EmbeddedTestServer test_server(EmbeddedTestServer::TYPE_HTTPS);
765   test_server.SetSSLConfig(server_cert_config, ssl_server_config);
766   ASSERT_TRUE(test_server.Start());
767 
768   GURL https_url = test_server.GetURL(kRealName, "/");
769   GURL::Replacements replacements;
770   replacements.SetSchemeStr(url::kWssScheme);
771   GURL wss_url = https_url.ReplaceComponents(replacements);
772 
773   auto host_resolver = std::make_unique<MockHostResolver>();
774   MockHostResolverBase::RuleResolver::RuleKey resolve_key;
775   // The DNS query itself is made with the https scheme rather than wss.
776   resolve_key.scheme = url::kHttpsScheme;
777   resolve_key.hostname_pattern = wss_url.host();
778   resolve_key.port = wss_url.IntPort();
779   HostResolverEndpointResult result;
780   result.ip_endpoints = {
781       IPEndPoint(IPAddress::IPv4Localhost(), wss_url.IntPort())};
782   result.metadata.supported_protocol_alpns = {"http/1.1"};
783   result.metadata.ech_config_list = ech_config_list;
784   host_resolver->rules()->AddRule(
785       std::move(resolve_key),
786       MockHostResolverBase::RuleResolver::RuleResult(std::vector{result}));
787   context_builder_->set_host_resolver(std::move(host_resolver));
788 
789   EXPECT_FALSE(ConnectAndWait(wss_url));
790   EXPECT_EQ("Error during WebSocket handshake: Unexpected response code: 404",
791             event_interface_->failure_message());
792 }
793 }  // namespace
794 
795 }  // namespace net
796