Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / websockets / websocket_end_to_end_test.cc
blob0dfa60cc6712e9d5b22b77448e18aaf33af0bc13
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
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.
11 #include <string>
13 #include "base/bind.h"
14 #include "base/bind_helpers.h"
15 #include "base/callback.h"
16 #include "base/location.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/run_loop.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/strings/string_piece.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "net/base/auth.h"
23 #include "net/base/network_delegate.h"
24 #include "net/base/test_data_directory.h"
25 #include "net/proxy/proxy_service.h"
26 #include "net/test/spawned_test_server/spawned_test_server.h"
27 #include "net/url_request/url_request_test_util.h"
28 #include "net/websockets/websocket_channel.h"
29 #include "net/websockets/websocket_event_interface.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31 #include "url/origin.h"
33 namespace net {
35 namespace {
37 static const char kEchoServer[] = "echo-with-no-extension";
39 // Simplify changing URL schemes.
40 GURL ReplaceUrlScheme(const GURL& in_url, const base::StringPiece& scheme) {
41 GURL::Replacements replacements;
42 replacements.SetSchemeStr(scheme);
43 return in_url.ReplaceComponents(replacements);
46 // An implementation of WebSocketEventInterface that waits for and records the
47 // results of the connect.
48 class ConnectTestingEventInterface : public WebSocketEventInterface {
49 public:
50 ConnectTestingEventInterface();
52 void WaitForResponse();
54 bool failed() const { return failed_; }
56 // Only set if the handshake failed, otherwise empty.
57 std::string failure_message() const;
59 std::string selected_subprotocol() const;
61 std::string extensions() const;
63 // Implementation of WebSocketEventInterface.
64 ChannelState OnAddChannelResponse(const std::string& selected_subprotocol,
65 const std::string& extensions) override;
67 ChannelState OnDataFrame(bool fin,
68 WebSocketMessageType type,
69 const std::vector<char>& data) override;
71 ChannelState OnFlowControl(int64 quota) override;
73 ChannelState OnClosingHandshake() override;
75 ChannelState OnDropChannel(bool was_clean,
76 uint16 code,
77 const std::string& reason) override;
79 ChannelState OnFailChannel(const std::string& message) override;
81 ChannelState OnStartOpeningHandshake(
82 scoped_ptr<WebSocketHandshakeRequestInfo> request) override;
84 ChannelState OnFinishOpeningHandshake(
85 scoped_ptr<WebSocketHandshakeResponseInfo> response) override;
87 ChannelState OnSSLCertificateError(
88 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
89 const GURL& url,
90 const SSLInfo& ssl_info,
91 bool fatal) override;
93 private:
94 void QuitNestedEventLoop();
96 // failed_ is true if the handshake failed (ie. OnFailChannel was called).
97 bool failed_;
98 std::string selected_subprotocol_;
99 std::string extensions_;
100 std::string failure_message_;
101 base::RunLoop run_loop_;
103 DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface);
106 ConnectTestingEventInterface::ConnectTestingEventInterface() : failed_(false) {
109 void ConnectTestingEventInterface::WaitForResponse() {
110 run_loop_.Run();
113 std::string ConnectTestingEventInterface::failure_message() const {
114 return failure_message_;
117 std::string ConnectTestingEventInterface::selected_subprotocol() const {
118 return selected_subprotocol_;
121 std::string ConnectTestingEventInterface::extensions() const {
122 return extensions_;
125 // Make the function definitions below less verbose.
126 typedef ConnectTestingEventInterface::ChannelState ChannelState;
128 ChannelState ConnectTestingEventInterface::OnAddChannelResponse(
129 const std::string& selected_subprotocol,
130 const std::string& extensions) {
131 selected_subprotocol_ = selected_subprotocol;
132 extensions_ = extensions;
133 QuitNestedEventLoop();
134 return CHANNEL_ALIVE;
137 ChannelState ConnectTestingEventInterface::OnDataFrame(
138 bool fin,
139 WebSocketMessageType type,
140 const std::vector<char>& data) {
141 return CHANNEL_ALIVE;
144 ChannelState ConnectTestingEventInterface::OnFlowControl(int64 quota) {
145 return CHANNEL_ALIVE;
148 ChannelState ConnectTestingEventInterface::OnClosingHandshake() {
149 return CHANNEL_ALIVE;
152 ChannelState ConnectTestingEventInterface::OnDropChannel(
153 bool was_clean,
154 uint16 code,
155 const std::string& reason) {
156 return CHANNEL_DELETED;
159 ChannelState ConnectTestingEventInterface::OnFailChannel(
160 const std::string& message) {
161 failed_ = true;
162 failure_message_ = message;
163 QuitNestedEventLoop();
164 return CHANNEL_DELETED;
167 ChannelState ConnectTestingEventInterface::OnStartOpeningHandshake(
168 scoped_ptr<WebSocketHandshakeRequestInfo> request) {
169 return CHANNEL_ALIVE;
172 ChannelState ConnectTestingEventInterface::OnFinishOpeningHandshake(
173 scoped_ptr<WebSocketHandshakeResponseInfo> response) {
174 return CHANNEL_ALIVE;
177 ChannelState ConnectTestingEventInterface::OnSSLCertificateError(
178 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
179 const GURL& url,
180 const SSLInfo& ssl_info,
181 bool fatal) {
182 base::ThreadTaskRunnerHandle::Get()->PostTask(
183 FROM_HERE, base::Bind(&SSLErrorCallbacks::CancelSSLRequest,
184 base::Owned(ssl_error_callbacks.release()),
185 ERR_SSL_PROTOCOL_ERROR, &ssl_info));
186 return CHANNEL_ALIVE;
189 void ConnectTestingEventInterface::QuitNestedEventLoop() {
190 run_loop_.Quit();
193 // A subclass of TestNetworkDelegate that additionally implements the
194 // OnResolveProxy callback and records the information passed to it.
195 class TestNetworkDelegateWithProxyInfo : public TestNetworkDelegate {
196 public:
197 TestNetworkDelegateWithProxyInfo() {}
199 struct ResolvedProxyInfo {
200 GURL url;
201 ProxyInfo proxy_info;
204 const ResolvedProxyInfo& resolved_proxy_info() const {
205 return resolved_proxy_info_;
208 protected:
209 void OnResolveProxy(const GURL& url,
210 int load_flags,
211 const ProxyService& proxy_service,
212 ProxyInfo* result) override {
213 resolved_proxy_info_.url = url;
214 resolved_proxy_info_.proxy_info = *result;
217 private:
218 ResolvedProxyInfo resolved_proxy_info_;
220 DISALLOW_COPY_AND_ASSIGN(TestNetworkDelegateWithProxyInfo);
223 class WebSocketEndToEndTest : public ::testing::Test {
224 protected:
225 WebSocketEndToEndTest()
226 : event_interface_(),
227 network_delegate_(new TestNetworkDelegateWithProxyInfo),
228 context_(true),
229 channel_(),
230 initialised_context_(false) {}
232 // Initialise the URLRequestContext. Normally done automatically by
233 // ConnectAndWait(). This method is for the use of tests that need the
234 // URLRequestContext initialised before calling ConnectAndWait().
235 void InitialiseContext() {
236 context_.set_network_delegate(network_delegate_.get());
237 context_.Init();
238 initialised_context_ = true;
241 // Send the connect request to |socket_url| and wait for a response. Returns
242 // true if the handshake succeeded.
243 bool ConnectAndWait(const GURL& socket_url) {
244 if (!initialised_context_) {
245 InitialiseContext();
247 url::Origin origin(GURL("http://localhost"));
248 event_interface_ = new ConnectTestingEventInterface;
249 channel_.reset(
250 new WebSocketChannel(make_scoped_ptr(event_interface_), &context_));
251 channel_->SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin);
252 event_interface_->WaitForResponse();
253 return !event_interface_->failed();
256 ConnectTestingEventInterface* event_interface_; // owned by channel_
257 scoped_ptr<TestNetworkDelegateWithProxyInfo> network_delegate_;
258 TestURLRequestContext context_;
259 scoped_ptr<WebSocketChannel> channel_;
260 std::vector<std::string> sub_protocols_;
261 bool initialised_context_;
264 // None of these tests work on Android.
265 // TODO(ricea): Make these tests work on Android. See crbug.com/441711.
266 #if defined(OS_ANDROID)
267 #define DISABLED_ON_ANDROID(test) DISABLED_##test
268 #else
269 #define DISABLED_ON_ANDROID(test) test
270 #endif
272 // Basic test of connectivity. If this test fails, nothing else can be expected
273 // to work.
274 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) {
275 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
276 SpawnedTestServer::kLocalhost,
277 GetWebSocketTestDataDirectory());
278 ASSERT_TRUE(ws_server.Start());
279 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
282 // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via
283 // authenticated proxy times out"
284 // TODO(ricea): Enable this when the issue is fixed.
285 TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) {
286 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
287 SpawnedTestServer::kLocalhost,
288 base::FilePath());
289 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
290 SpawnedTestServer::kLocalhost,
291 GetWebSocketTestDataDirectory());
292 ASSERT_TRUE(proxy_server.StartInBackground());
293 ASSERT_TRUE(ws_server.StartInBackground());
294 ASSERT_TRUE(proxy_server.BlockUntilStarted());
295 ASSERT_TRUE(ws_server.BlockUntilStarted());
296 std::string proxy_config =
297 "https=" + proxy_server.host_port_pair().ToString();
298 scoped_ptr<ProxyService> proxy_service(
299 ProxyService::CreateFixed(proxy_config));
300 ASSERT_TRUE(proxy_service);
301 context_.set_proxy_service(proxy_service.get());
302 EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
303 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
306 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) {
307 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
308 SpawnedTestServer::kLocalhost,
309 base::FilePath());
310 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
311 SpawnedTestServer::kLocalhost,
312 GetWebSocketTestDataDirectory());
313 ASSERT_TRUE(proxy_server.StartInBackground());
314 ASSERT_TRUE(wss_server.StartInBackground());
315 ASSERT_TRUE(proxy_server.BlockUntilStarted());
316 ASSERT_TRUE(wss_server.BlockUntilStarted());
317 std::string proxy_config =
318 "https=" + proxy_server.host_port_pair().ToString();
319 scoped_ptr<ProxyService> proxy_service(
320 ProxyService::CreateFixed(proxy_config));
321 ASSERT_TRUE(proxy_service);
322 context_.set_proxy_service(proxy_service.get());
323 EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer)));
324 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
327 // Regression test for crbug/426736 "WebSocket connections not using configured
328 // system HTTPS Proxy".
329 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) {
330 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
331 SpawnedTestServer::kLocalhost,
332 base::FilePath());
333 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
334 SpawnedTestServer::kLocalhost,
335 GetWebSocketTestDataDirectory());
336 ASSERT_TRUE(proxy_server.StartInBackground());
337 ASSERT_TRUE(ws_server.StartInBackground());
338 ASSERT_TRUE(proxy_server.BlockUntilStarted());
339 ASSERT_TRUE(ws_server.BlockUntilStarted());
340 std::string proxy_config = "https=" +
341 proxy_server.host_port_pair().ToString() + ";" +
342 "http=" + proxy_server.host_port_pair().ToString();
343 scoped_ptr<ProxyService> proxy_service(
344 ProxyService::CreateFixed(proxy_config));
345 context_.set_proxy_service(proxy_service.get());
346 InitialiseContext();
348 // The test server doesn't have an unauthenticated proxy mode. WebSockets
349 // cannot provide auth information that isn't already cached, so it's
350 // necessary to preflight an HTTP request to authenticate against the proxy.
351 // It doesn't matter what the URL is, as long as it is an HTTP navigation.
352 GURL http_page =
353 ReplaceUrlScheme(ws_server.GetURL("connect_check.html"), "http");
354 TestDelegate delegate;
355 delegate.set_credentials(
356 AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar")));
358 scoped_ptr<URLRequest> request(
359 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate));
360 request->Start();
361 // TestDelegate exits the message loop when the request completes by
362 // default.
363 base::RunLoop().Run();
364 EXPECT_TRUE(delegate.auth_required_called());
367 GURL ws_url = ws_server.GetURL(kEchoServer);
368 EXPECT_TRUE(ConnectAndWait(ws_url));
369 const TestNetworkDelegateWithProxyInfo::ResolvedProxyInfo& info =
370 network_delegate_->resolved_proxy_info();
371 EXPECT_EQ(ws_url, info.url);
372 EXPECT_TRUE(info.proxy_info.is_http());
375 // This is a regression test for crbug.com/408061 Crash in
376 // net::WebSocketBasicHandshakeStream::Upgrade.
377 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(TruncatedResponse)) {
378 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
379 SpawnedTestServer::kLocalhost,
380 GetWebSocketTestDataDirectory());
381 ASSERT_TRUE(ws_server.Start());
382 InitialiseContext();
384 GURL ws_url = ws_server.GetURL("truncated-headers");
385 EXPECT_FALSE(ConnectAndWait(ws_url));
388 // Regression test for crbug.com/455215 "HSTS not applied to WebSocket"
389 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsHttpsToWebSocket)) {
390 SpawnedTestServer::SSLOptions ssl_options(
391 SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
392 SpawnedTestServer https_server(
393 SpawnedTestServer::TYPE_HTTPS, ssl_options,
394 base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
395 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
396 GetWebSocketTestDataDirectory());
398 ASSERT_TRUE(https_server.StartInBackground());
399 ASSERT_TRUE(wss_server.StartInBackground());
400 ASSERT_TRUE(https_server.BlockUntilStarted());
401 ASSERT_TRUE(wss_server.BlockUntilStarted());
402 InitialiseContext();
403 // Set HSTS via https:
404 TestDelegate delegate;
405 GURL https_page = https_server.GetURL("files/hsts-headers.html");
406 scoped_ptr<URLRequest> request(
407 context_.CreateRequest(https_page, DEFAULT_PRIORITY, &delegate));
408 request->Start();
409 // TestDelegate exits the message loop when the request completes.
410 base::RunLoop().Run();
411 EXPECT_TRUE(request->status().is_success());
413 // Check HSTS with ws:
414 // Change the scheme from wss: to ws: to verify that it is switched back.
415 GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
416 EXPECT_TRUE(ConnectAndWait(ws_url));
419 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToHttps)) {
420 SpawnedTestServer::SSLOptions ssl_options(
421 SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
422 SpawnedTestServer https_server(
423 SpawnedTestServer::TYPE_HTTPS, ssl_options,
424 base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
425 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
426 GetWebSocketTestDataDirectory());
427 ASSERT_TRUE(https_server.StartInBackground());
428 ASSERT_TRUE(wss_server.StartInBackground());
429 ASSERT_TRUE(https_server.BlockUntilStarted());
430 ASSERT_TRUE(wss_server.BlockUntilStarted());
431 InitialiseContext();
432 // Set HSTS via wss:
433 GURL wss_url = wss_server.GetURL("set-hsts");
434 EXPECT_TRUE(ConnectAndWait(wss_url));
436 // Verify via http:
437 TestDelegate delegate;
438 GURL http_page =
439 ReplaceUrlScheme(https_server.GetURL("files/simple.html"), "http");
440 scoped_ptr<URLRequest> request(
441 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate));
442 request->Start();
443 // TestDelegate exits the message loop when the request completes.
444 base::RunLoop().Run();
445 EXPECT_TRUE(request->status().is_success());
446 EXPECT_TRUE(request->url().SchemeIs("https"));
449 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToWebSocket)) {
450 SpawnedTestServer::SSLOptions ssl_options(
451 SpawnedTestServer::SSLOptions::CERT_COMMON_NAME_IS_DOMAIN);
452 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
453 GetWebSocketTestDataDirectory());
454 ASSERT_TRUE(wss_server.Start());
455 InitialiseContext();
456 // Set HSTS via wss:
457 GURL wss_url = wss_server.GetURL("set-hsts");
458 EXPECT_TRUE(ConnectAndWait(wss_url));
460 // Verify via wss:
461 GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
462 EXPECT_TRUE(ConnectAndWait(ws_url));
465 // Regression test for crbug.com/180504 "WebSocket handshake fails when HTTP
466 // headers have trailing LWS".
467 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(TrailingWhitespace)) {
468 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
469 SpawnedTestServer::kLocalhost,
470 GetWebSocketTestDataDirectory());
471 ASSERT_TRUE(ws_server.Start());
473 GURL ws_url = ws_server.GetURL("trailing-whitespace");
474 sub_protocols_.push_back("sip");
475 EXPECT_TRUE(ConnectAndWait(ws_url));
476 EXPECT_EQ("sip", event_interface_->selected_subprotocol());
479 // This is a regression test for crbug.com/169448 "WebSockets should support
480 // header continuations"
481 // TODO(ricea): HTTP continuation headers have been deprecated by RFC7230. If
482 // support for continuation headers is removed from Chrome, then this test will
483 // break and should be removed.
484 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HeaderContinuations)) {
485 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
486 SpawnedTestServer::kLocalhost,
487 GetWebSocketTestDataDirectory());
488 ASSERT_TRUE(ws_server.Start());
490 GURL ws_url = ws_server.GetURL("header-continuation");
492 EXPECT_TRUE(ConnectAndWait(ws_url));
493 EXPECT_EQ("permessage-deflate; server_max_window_bits=10",
494 event_interface_->extensions());
497 } // namespace
499 } // namespace net