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