Show Pages in chrome://md-settings
[chromium-blink-merge.git] / net / websockets / websocket_end_to_end_test.cc
blob2fbc20b6ae65f7a984412a22482aa1f2f2aa9e76
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/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/run_loop.h"
19 #include "base/strings/string_piece.h"
20 #include "net/base/auth.h"
21 #include "net/base/network_delegate.h"
22 #include "net/base/test_data_directory.h"
23 #include "net/proxy/proxy_service.h"
24 #include "net/test/spawned_test_server/spawned_test_server.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "net/websockets/websocket_channel.h"
27 #include "net/websockets/websocket_event_interface.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "url/origin.h"
31 namespace net {
33 namespace {
35 static const char kEchoServer[] = "echo-with-no-extension";
37 // Simplify changing URL schemes.
38 GURL ReplaceUrlScheme(const GURL& in_url, const base::StringPiece& scheme) {
39 GURL::Replacements replacements;
40 replacements.SetSchemeStr(scheme);
41 return in_url.ReplaceComponents(replacements);
44 // An implementation of WebSocketEventInterface that waits for and records the
45 // results of the connect.
46 class ConnectTestingEventInterface : public WebSocketEventInterface {
47 public:
48 ConnectTestingEventInterface();
50 void WaitForResponse();
52 bool failed() const { return failed_; }
54 // Only set if the handshake failed, otherwise empty.
55 std::string failure_message() const;
57 std::string selected_subprotocol() const;
59 std::string extensions() const;
61 // Implementation of WebSocketEventInterface.
62 ChannelState OnAddChannelResponse(const std::string& selected_subprotocol,
63 const std::string& extensions) override;
65 ChannelState OnDataFrame(bool fin,
66 WebSocketMessageType type,
67 const std::vector<char>& data) override;
69 ChannelState OnFlowControl(int64 quota) override;
71 ChannelState OnClosingHandshake() override;
73 ChannelState OnDropChannel(bool was_clean,
74 uint16 code,
75 const std::string& reason) override;
77 ChannelState OnFailChannel(const std::string& message) override;
79 ChannelState OnStartOpeningHandshake(
80 scoped_ptr<WebSocketHandshakeRequestInfo> request) override;
82 ChannelState OnFinishOpeningHandshake(
83 scoped_ptr<WebSocketHandshakeResponseInfo> response) override;
85 ChannelState OnSSLCertificateError(
86 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
87 const GURL& url,
88 const SSLInfo& ssl_info,
89 bool fatal) override;
91 private:
92 void QuitNestedEventLoop();
94 // failed_ is true if the handshake failed (ie. OnFailChannel was called).
95 bool failed_;
96 std::string selected_subprotocol_;
97 std::string extensions_;
98 std::string failure_message_;
99 base::RunLoop run_loop_;
101 DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface);
104 ConnectTestingEventInterface::ConnectTestingEventInterface() : failed_(false) {
107 void ConnectTestingEventInterface::WaitForResponse() {
108 run_loop_.Run();
111 std::string ConnectTestingEventInterface::failure_message() const {
112 return failure_message_;
115 std::string ConnectTestingEventInterface::selected_subprotocol() const {
116 return selected_subprotocol_;
119 std::string ConnectTestingEventInterface::extensions() const {
120 return extensions_;
123 // Make the function definitions below less verbose.
124 typedef ConnectTestingEventInterface::ChannelState ChannelState;
126 ChannelState ConnectTestingEventInterface::OnAddChannelResponse(
127 const std::string& selected_subprotocol,
128 const std::string& extensions) {
129 selected_subprotocol_ = selected_subprotocol;
130 extensions_ = extensions;
131 QuitNestedEventLoop();
132 return CHANNEL_ALIVE;
135 ChannelState ConnectTestingEventInterface::OnDataFrame(
136 bool fin,
137 WebSocketMessageType type,
138 const std::vector<char>& data) {
139 return CHANNEL_ALIVE;
142 ChannelState ConnectTestingEventInterface::OnFlowControl(int64 quota) {
143 return CHANNEL_ALIVE;
146 ChannelState ConnectTestingEventInterface::OnClosingHandshake() {
147 return CHANNEL_ALIVE;
150 ChannelState ConnectTestingEventInterface::OnDropChannel(
151 bool was_clean,
152 uint16 code,
153 const std::string& reason) {
154 return CHANNEL_DELETED;
157 ChannelState ConnectTestingEventInterface::OnFailChannel(
158 const std::string& message) {
159 failed_ = true;
160 failure_message_ = message;
161 QuitNestedEventLoop();
162 return CHANNEL_DELETED;
165 ChannelState ConnectTestingEventInterface::OnStartOpeningHandshake(
166 scoped_ptr<WebSocketHandshakeRequestInfo> request) {
167 return CHANNEL_ALIVE;
170 ChannelState ConnectTestingEventInterface::OnFinishOpeningHandshake(
171 scoped_ptr<WebSocketHandshakeResponseInfo> response) {
172 return CHANNEL_ALIVE;
175 ChannelState ConnectTestingEventInterface::OnSSLCertificateError(
176 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks,
177 const GURL& url,
178 const SSLInfo& ssl_info,
179 bool fatal) {
180 base::MessageLoop::current()->PostTask(
181 FROM_HERE, base::Bind(&SSLErrorCallbacks::CancelSSLRequest,
182 base::Owned(ssl_error_callbacks.release()),
183 ERR_SSL_PROTOCOL_ERROR, &ssl_info));
184 return CHANNEL_ALIVE;
187 void ConnectTestingEventInterface::QuitNestedEventLoop() {
188 run_loop_.Quit();
191 // A subclass of TestNetworkDelegate that additionally implements the
192 // OnResolveProxy callback and records the information passed to it.
193 class TestNetworkDelegateWithProxyInfo : public TestNetworkDelegate {
194 public:
195 TestNetworkDelegateWithProxyInfo() {}
197 struct ResolvedProxyInfo {
198 GURL url;
199 ProxyInfo proxy_info;
202 const ResolvedProxyInfo& resolved_proxy_info() const {
203 return resolved_proxy_info_;
206 protected:
207 void OnResolveProxy(const GURL& url,
208 int load_flags,
209 const ProxyService& proxy_service,
210 ProxyInfo* result) override {
211 resolved_proxy_info_.url = url;
212 resolved_proxy_info_.proxy_info = *result;
215 private:
216 ResolvedProxyInfo resolved_proxy_info_;
218 DISALLOW_COPY_AND_ASSIGN(TestNetworkDelegateWithProxyInfo);
221 class WebSocketEndToEndTest : public ::testing::Test {
222 protected:
223 WebSocketEndToEndTest()
224 : event_interface_(),
225 network_delegate_(new TestNetworkDelegateWithProxyInfo),
226 context_(true),
227 channel_(),
228 initialised_context_(false) {}
230 // Initialise the URLRequestContext. Normally done automatically by
231 // ConnectAndWait(). This method is for the use of tests that need the
232 // URLRequestContext initialised before calling ConnectAndWait().
233 void InitialiseContext() {
234 context_.set_network_delegate(network_delegate_.get());
235 context_.Init();
236 initialised_context_ = true;
239 // Send the connect request to |socket_url| and wait for a response. Returns
240 // true if the handshake succeeded.
241 bool ConnectAndWait(const GURL& socket_url) {
242 if (!initialised_context_) {
243 InitialiseContext();
245 url::Origin origin("http://localhost");
246 event_interface_ = new ConnectTestingEventInterface;
247 channel_.reset(
248 new WebSocketChannel(make_scoped_ptr(event_interface_), &context_));
249 channel_->SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin);
250 event_interface_->WaitForResponse();
251 return !event_interface_->failed();
254 ConnectTestingEventInterface* event_interface_; // owned by channel_
255 scoped_ptr<TestNetworkDelegateWithProxyInfo> network_delegate_;
256 TestURLRequestContext context_;
257 scoped_ptr<WebSocketChannel> channel_;
258 std::vector<std::string> sub_protocols_;
259 bool initialised_context_;
262 // None of these tests work on Android.
263 // TODO(ricea): Make these tests work on Android. See crbug.com/441711.
264 #if defined(OS_ANDROID)
265 #define DISABLED_ON_ANDROID(test) DISABLED_##test
266 #else
267 #define DISABLED_ON_ANDROID(test) test
268 #endif
270 // Basic test of connectivity. If this test fails, nothing else can be expected
271 // to work.
272 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) {
273 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
274 SpawnedTestServer::kLocalhost,
275 GetWebSocketTestDataDirectory());
276 ASSERT_TRUE(ws_server.Start());
277 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
280 // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via
281 // authenticated proxy times out"
282 // TODO(ricea): Enable this when the issue is fixed.
283 TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) {
284 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
285 SpawnedTestServer::kLocalhost,
286 base::FilePath());
287 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
288 SpawnedTestServer::kLocalhost,
289 GetWebSocketTestDataDirectory());
290 ASSERT_TRUE(proxy_server.StartInBackground());
291 ASSERT_TRUE(ws_server.StartInBackground());
292 ASSERT_TRUE(proxy_server.BlockUntilStarted());
293 ASSERT_TRUE(ws_server.BlockUntilStarted());
294 std::string proxy_config =
295 "https=" + proxy_server.host_port_pair().ToString();
296 scoped_ptr<ProxyService> proxy_service(
297 ProxyService::CreateFixed(proxy_config));
298 ASSERT_TRUE(proxy_service);
299 context_.set_proxy_service(proxy_service.get());
300 EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer)));
301 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
304 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) {
305 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
306 SpawnedTestServer::kLocalhost,
307 base::FilePath());
308 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS,
309 SpawnedTestServer::kLocalhost,
310 GetWebSocketTestDataDirectory());
311 ASSERT_TRUE(proxy_server.StartInBackground());
312 ASSERT_TRUE(wss_server.StartInBackground());
313 ASSERT_TRUE(proxy_server.BlockUntilStarted());
314 ASSERT_TRUE(wss_server.BlockUntilStarted());
315 std::string proxy_config =
316 "https=" + proxy_server.host_port_pair().ToString();
317 scoped_ptr<ProxyService> proxy_service(
318 ProxyService::CreateFixed(proxy_config));
319 ASSERT_TRUE(proxy_service);
320 context_.set_proxy_service(proxy_service.get());
321 EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer)));
322 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message());
325 // Regression test for crbug/426736 "WebSocket connections not using configured
326 // system HTTPS Proxy".
327 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) {
328 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY,
329 SpawnedTestServer::kLocalhost,
330 base::FilePath());
331 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
332 SpawnedTestServer::kLocalhost,
333 GetWebSocketTestDataDirectory());
334 ASSERT_TRUE(proxy_server.StartInBackground());
335 ASSERT_TRUE(ws_server.StartInBackground());
336 ASSERT_TRUE(proxy_server.BlockUntilStarted());
337 ASSERT_TRUE(ws_server.BlockUntilStarted());
338 std::string proxy_config = "https=" +
339 proxy_server.host_port_pair().ToString() + ";" +
340 "http=" + proxy_server.host_port_pair().ToString();
341 scoped_ptr<ProxyService> proxy_service(
342 ProxyService::CreateFixed(proxy_config));
343 context_.set_proxy_service(proxy_service.get());
344 InitialiseContext();
346 // The test server doesn't have an unauthenticated proxy mode. WebSockets
347 // cannot provide auth information that isn't already cached, so it's
348 // necessary to preflight an HTTP request to authenticate against the proxy.
349 // It doesn't matter what the URL is, as long as it is an HTTP navigation.
350 GURL http_page =
351 ReplaceUrlScheme(ws_server.GetURL("connect_check.html"), "http");
352 TestDelegate delegate;
353 delegate.set_credentials(
354 AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar")));
356 scoped_ptr<URLRequest> request(
357 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL));
358 request->Start();
359 // TestDelegate exits the message loop when the request completes by
360 // default.
361 base::RunLoop().Run();
362 EXPECT_TRUE(delegate.auth_required_called());
365 GURL ws_url = ws_server.GetURL(kEchoServer);
366 EXPECT_TRUE(ConnectAndWait(ws_url));
367 const TestNetworkDelegateWithProxyInfo::ResolvedProxyInfo& info =
368 network_delegate_->resolved_proxy_info();
369 EXPECT_EQ(ws_url, info.url);
370 EXPECT_TRUE(info.proxy_info.is_http());
373 // This is a regression test for crbug.com/408061 Crash in
374 // net::WebSocketBasicHandshakeStream::Upgrade.
375 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(TruncatedResponse)) {
376 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
377 SpawnedTestServer::kLocalhost,
378 GetWebSocketTestDataDirectory());
379 ASSERT_TRUE(ws_server.Start());
380 InitialiseContext();
382 GURL ws_url = ws_server.GetURL("truncated-headers");
383 EXPECT_FALSE(ConnectAndWait(ws_url));
386 // Regression test for crbug.com/455215 "HSTS not applied to WebSocket"
387 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsHttpsToWebSocket)) {
388 SpawnedTestServer::SSLOptions ssl_options;
389 SpawnedTestServer https_server(
390 SpawnedTestServer::TYPE_HTTPS, ssl_options,
391 base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
392 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
393 GetWebSocketTestDataDirectory());
394 ASSERT_TRUE(https_server.StartInBackground());
395 ASSERT_TRUE(wss_server.StartInBackground());
396 ASSERT_TRUE(https_server.BlockUntilStarted());
397 ASSERT_TRUE(wss_server.BlockUntilStarted());
398 InitialiseContext();
399 // Set HSTS via https:
400 TestDelegate delegate;
401 GURL https_page = https_server.GetURL("files/hsts-headers.html");
402 scoped_ptr<URLRequest> request(
403 context_.CreateRequest(https_page, DEFAULT_PRIORITY, &delegate, NULL));
404 request->Start();
405 // TestDelegate exits the message loop when the request completes.
406 base::RunLoop().Run();
407 EXPECT_TRUE(request->status().is_success());
409 // Check HSTS with ws:
410 // Change the scheme from wss: to ws: to verify that it is switched back.
411 GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
412 EXPECT_TRUE(ConnectAndWait(ws_url));
415 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToHttps)) {
416 SpawnedTestServer::SSLOptions ssl_options;
417 SpawnedTestServer https_server(
418 SpawnedTestServer::TYPE_HTTPS, ssl_options,
419 base::FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
420 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
421 GetWebSocketTestDataDirectory());
422 ASSERT_TRUE(https_server.StartInBackground());
423 ASSERT_TRUE(wss_server.StartInBackground());
424 ASSERT_TRUE(https_server.BlockUntilStarted());
425 ASSERT_TRUE(wss_server.BlockUntilStarted());
426 InitialiseContext();
427 // Set HSTS via wss:
428 GURL wss_url = wss_server.GetURL("set-hsts");
429 EXPECT_TRUE(ConnectAndWait(wss_url));
431 // Verify via http:
432 TestDelegate delegate;
433 GURL http_page =
434 ReplaceUrlScheme(https_server.GetURL("files/simple.html"), "http");
435 scoped_ptr<URLRequest> request(
436 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL));
437 request->Start();
438 // TestDelegate exits the message loop when the request completes.
439 base::RunLoop().Run();
440 EXPECT_TRUE(request->status().is_success());
441 EXPECT_TRUE(request->url().SchemeIs("https"));
444 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HstsWebSocketToWebSocket)) {
445 SpawnedTestServer::SSLOptions ssl_options;
446 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, ssl_options,
447 GetWebSocketTestDataDirectory());
448 ASSERT_TRUE(wss_server.Start());
449 InitialiseContext();
450 // Set HSTS via wss:
451 GURL wss_url = wss_server.GetURL("set-hsts");
452 EXPECT_TRUE(ConnectAndWait(wss_url));
454 // Verify via wss:
455 GURL ws_url = ReplaceUrlScheme(wss_server.GetURL(kEchoServer), "ws");
456 EXPECT_TRUE(ConnectAndWait(ws_url));
459 // Regression test for crbug.com/180504 "WebSocket handshake fails when HTTP
460 // headers have trailing LWS".
461 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(TrailingWhitespace)) {
462 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS,
463 SpawnedTestServer::kLocalhost,
464 GetWebSocketTestDataDirectory());
465 ASSERT_TRUE(ws_server.Start());
467 GURL ws_url = ws_server.GetURL("trailing-whitespace");
468 sub_protocols_.push_back("sip");
469 EXPECT_TRUE(ConnectAndWait(ws_url));
470 EXPECT_EQ("sip", event_interface_->selected_subprotocol());
473 } // namespace
475 } // namespace net