Index: net/websockets/websocket_end_to_end_test.cc |
diff --git a/net/websockets/websocket_end_to_end_test.cc b/net/websockets/websocket_end_to_end_test.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a9ad4e09a52ff1af74d5ad36d018d60f59ef39aa |
--- /dev/null |
+++ b/net/websockets/websocket_end_to_end_test.cc |
@@ -0,0 +1,284 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+// End-to-end tests for WebSocket. Usage guidelines: |
+// There are at least 4 sets of end-to-end tests in the source tree: |
+// 1) Blink layout tests. These are easy to write, and efficient because the |
+// client and server processes are both re-used. They are easy to write as |
+// they are just Javascript. They should be used for the majority of |
+// functional tests. |
+// 2) Browser tests in //chrome/browser/net/websocket_browsertest.cc. These |
+// are inefficient because both the server and client are restarted for each |
+// test. They are also hard to write and hard to debug. They should generally |
+// only be used for testing UI-related features. |
+// 3) PPAPI tests in //chrome/test/ppapi/ppapi_browsertest.cc. These are |
+// very hard to write and debug, and should only be used for verifying the |
+// operation of the Pepper API. |
+// 4) These tests. They are somewhat inefficient because the server is restarted |
+// for each test. They are well-suited for tests which require special server |
+// configurations. |
+// |
+// Tests should generally not be duplicated in the different places, except for |
+// regression tests and basic smoke tests. |
+ |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
string
Adam Rice
2015/01/06 12:27:15
Done.
|
+#include "base/callback.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/run_loop.h" |
+#include "net/base/auth.h" |
+#include "net/base/network_delegate.h" |
+#include "net/base/test_data_directory.h" |
tyoshino (SeeGerritForStatus)
2015/01/05 08:47:29
net/proxy/proxy_service.h
Adam Rice
2015/01/06 12:27:15
Done.
|
+#include "net/test/spawned_test_server/spawned_test_server.h" |
+#include "net/url_request/url_request_test_util.h" |
+#include "net/websockets/websocket_channel.h" |
+#include "net/websockets/websocket_event_interface.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+#include "url/origin.h" |
+ |
+namespace net { |
+ |
+namespace { |
+ |
+static const char kEchoServer[] = "echo-with-no-extension"; |
+ |
+// An implementation of WebSocketEventInterface that waits for and records the |
+// results of the connect. |
+class ConnectTestingEventInterface : public WebSocketEventInterface { |
+ public: |
+ ConnectTestingEventInterface() : fail_(true) {} |
+ |
+ void WaitForResponse() { |
+ quit_closure_ = run_loop_.QuitClosure(); |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
quit_closure_ is initialized here to make DCHECK o
Adam Rice
2015/01/06 12:27:15
No, it was a mistake. I misunderstood the API of R
|
+ run_loop_.Run(); |
+ } |
+ |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
explain that this variable is used for storing sev
Adam Rice
2015/01/06 12:27:15
I decided that setting fail_ in OnDropChannel() wa
|
+ bool fail() const { return fail_; } |
+ |
+ // Only set if the handshake failed, otherwise empty. |
+ // Failure messages appear on the console and are checked by the layout tests, |
+ // so they are expected to stay reasonably stable across versions. |
+ std::string failure_message() const { return failure_message_; } |
+ |
+ std::string selected_subprotocol() const { return selected_subprotocol_; } |
+ |
+ std::string extensions() const { return extensions_; } |
+ |
+ ChannelState OnAddChannelResponse(bool fail, |
+ const std::string& selected_subprotocol, |
+ const std::string& extensions) override { |
+ fail_ = fail; |
+ selected_subprotocol_ = selected_subprotocol; |
+ extensions_ = extensions; |
+ QuitNestedEventLoop(); |
+ return fail ? CHANNEL_DELETED : CHANNEL_ALIVE; |
+ } |
+ |
+ ChannelState OnDataFrame(bool fin, |
+ WebSocketMessageType type, |
+ const std::vector<char>& data) override { |
+ return CHANNEL_ALIVE; |
+ } |
+ |
+ ChannelState OnFlowControl(int64 quota) override { return CHANNEL_ALIVE; } |
+ |
+ ChannelState OnClosingHandshake() override { return CHANNEL_ALIVE; } |
+ |
+ ChannelState OnDropChannel(bool was_clean, |
+ uint16 code, |
+ const std::string& reason) override { |
+ fail_ = !was_clean; |
+ QuitNestedEventLoop(); |
+ return CHANNEL_DELETED; |
+ } |
+ |
+ ChannelState OnFailChannel(const std::string& message) override { |
+ fail_ = true; |
+ failure_message_ = message; |
+ QuitNestedEventLoop(); |
+ return CHANNEL_DELETED; |
+ } |
+ |
+ ChannelState OnStartOpeningHandshake( |
+ scoped_ptr<WebSocketHandshakeRequestInfo> request) override { |
+ return CHANNEL_ALIVE; |
+ } |
+ |
+ ChannelState OnFinishOpeningHandshake( |
+ scoped_ptr<WebSocketHandshakeResponseInfo> response) override { |
+ return CHANNEL_ALIVE; |
+ } |
+ |
+ ChannelState OnSSLCertificateError( |
+ scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks, |
+ const GURL& url, |
+ const SSLInfo& ssl_info, |
+ bool fatal) override { |
+ ssl_error_callbacks->CancelSSLRequest(ERR_SSL_PROTOCOL_ERROR, &ssl_info); |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
I'm a little worried about CancelSSLRequest() bein
Adam Rice
2015/01/06 12:27:15
I'm not 100% sure. I decided to call it via the Po
|
+ return CHANNEL_ALIVE; |
+ } |
+ |
+ private: |
+ void QuitNestedEventLoop() { |
+ DCHECK(!quit_closure_.is_null()); |
+ quit_closure_.Run(); |
+ } |
+ bool fail_; |
+ std::string selected_subprotocol_; |
+ std::string extensions_; |
+ std::string failure_message_; |
+ base::RunLoop run_loop_; |
+ base::Closure quit_closure_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface); |
+}; |
+ |
+class WebSocketEndToEndTest : public ::testing::Test { |
+ protected: |
+ WebSocketEndToEndTest() |
+ : event_interface_(new ConnectTestingEventInterface), |
+ network_delegate_(new TestNetworkDelegate), |
+ context_(true), |
+ channel_(make_scoped_ptr(event_interface_), &context_), |
+ origin_("http://localhost"), |
+ initialised_context_(false) {} |
+ |
+ void InitialiseContext() { |
+ context_.set_network_delegate(network_delegate_.get()); |
+ context_.Init(); |
+ initialised_context_ = true; |
+ } |
+ |
+ bool ConnectAndWait(const GURL& socket_url) { |
+ if (!initialised_context_) { |
+ InitialiseContext(); |
+ } |
+ channel_.SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin_); |
+ event_interface_->WaitForResponse(); |
+ return !event_interface_->fail(); |
+ } |
+ |
+ ConnectTestingEventInterface* event_interface_; // owned by channel_ |
+ scoped_ptr<TestNetworkDelegate> network_delegate_; |
+ TestURLRequestContext context_; |
+ WebSocketChannel channel_; |
+ std::vector<std::string> sub_protocols_; |
+ url::Origin origin_; |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
how about making sub_protocols_ and origin_ local
Adam Rice
2015/01/06 12:27:15
Done.
|
+ bool initialised_context_; |
+}; |
+ |
+// None of these tests work on Android. |
+// TODO(ricea): Make these tests work on Android. See crbug.com/441711. |
+#if defined(OS_ANDROID) |
+#define DISABLED_ON_ANDROID(test) DISABLED_##test |
+#else |
+#define DISABLED_ON_ANDROID(test) test |
+#endif |
+ |
+// Basic test of connectivity. If this test fails, nothing else can be expected |
+// to work. |
+TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) { |
+ SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, |
+ SpawnedTestServer::kLocalhost, |
+ GetWebSocketTestDataDirectory()); |
+ ASSERT_TRUE(ws_server.Start()); |
+ EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); |
+} |
+ |
+// Test for issue crbug.com/433695 "Unencrypted WebSocket connection via |
+// authenticated proxy times out" |
+// TODO(ricea): Enable this when the issue is fixed. |
+TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) { |
+ SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, |
+ SpawnedTestServer::kLocalhost, |
+ base::FilePath()); |
+ SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, |
+ SpawnedTestServer::kLocalhost, |
+ GetWebSocketTestDataDirectory()); |
+ ASSERT_TRUE(proxy_server.StartInBackground()); |
+ ASSERT_TRUE(ws_server.StartInBackground()); |
+ ASSERT_TRUE(proxy_server.BlockUntilStarted()); |
+ ASSERT_TRUE(ws_server.BlockUntilStarted()); |
+ std::string proxy_config = |
+ "https=" + proxy_server.host_port_pair().ToString(); |
+ scoped_ptr<ProxyService> proxy_service( |
+ ProxyService::CreateFixed(proxy_config)); |
+ ASSERT_TRUE(proxy_service); |
+ context_.set_proxy_service(proxy_service.get()); |
+ EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer))); |
+ EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); |
+} |
+ |
+TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) { |
+ SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, |
+ SpawnedTestServer::kLocalhost, |
+ base::FilePath()); |
+ SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, |
+ SpawnedTestServer::kLocalhost, |
+ GetWebSocketTestDataDirectory()); |
+ ASSERT_TRUE(proxy_server.StartInBackground()); |
+ ASSERT_TRUE(wss_server.StartInBackground()); |
+ ASSERT_TRUE(proxy_server.BlockUntilStarted()); |
+ ASSERT_TRUE(wss_server.BlockUntilStarted()); |
+ std::string proxy_config = |
+ "https=" + proxy_server.host_port_pair().ToString(); |
+ scoped_ptr<ProxyService> proxy_service( |
+ ProxyService::CreateFixed(proxy_config)); |
+ ASSERT_TRUE(proxy_service); |
+ context_.set_proxy_service(proxy_service.get()); |
+ EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer))); |
+ EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); |
+} |
+ |
+// Regression test for crbug/426736 "WebSocket connections not using configured |
+// system HTTPS Proxy". |
+TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) { |
+ SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, |
+ SpawnedTestServer::kLocalhost, |
+ base::FilePath()); |
+ SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, |
+ SpawnedTestServer::kLocalhost, |
+ GetWebSocketTestDataDirectory()); |
+ ASSERT_TRUE(proxy_server.StartInBackground()); |
+ ASSERT_TRUE(ws_server.StartInBackground()); |
+ ASSERT_TRUE(proxy_server.BlockUntilStarted()); |
+ ASSERT_TRUE(ws_server.BlockUntilStarted()); |
+ std::string proxy_config = "https=" + |
+ proxy_server.host_port_pair().ToString() + ";" + |
+ "http=" + proxy_server.host_port_pair().ToString(); |
+ scoped_ptr<ProxyService> proxy_service( |
+ ProxyService::CreateFixed(proxy_config)); |
+ context_.set_proxy_service(proxy_service.get()); |
+ InitialiseContext(); |
+ |
+ // The test server doesn't have an unauthenticated proxy mode. WebSockets |
+ // cannot provide auth information that isn't already cached, so it's |
+ // necessary to preflight an HTTP request to authenticate against the proxy. |
+ std::string scheme("http"); |
+ GURL::Replacements replacements; |
+ replacements.SetSchemeStr(scheme); |
+ // It doesn't matter what the URL is, as long as it is an HTTP navigation. |
+ GURL http_page = |
+ ws_server.GetURL("connect_check.html").ReplaceComponents(replacements); |
+ TestDelegate delegate; |
+ delegate.set_credentials( |
+ AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar"))); |
+ { |
+ scoped_ptr<URLRequest> request( |
+ context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL)); |
+ request->Start(); |
+ // TestDelegate exits the message loop when the request completes by |
+ // default. |
+ base::RunLoop().Run(); |
+ EXPECT_TRUE(delegate.auth_required_called()); |
+ } |
+ |
+ int previous_observed_before_proxy_headers_sent_callbacks = |
+ network_delegate_->observed_before_proxy_headers_sent_callbacks(); |
+ EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); |
+ EXPECT_EQ(previous_observed_before_proxy_headers_sent_callbacks + 1, |
+ network_delegate_->observed_before_proxy_headers_sent_callbacks()); |
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
what does this expectations add? reading the varia
Adam Rice
2015/01/06 12:27:15
It works because of a bug. I'm still looking for a
Adam Rice
2015/01/06 15:48:43
I found a way to do it. PTAL.
|
+} |
+ |
+} // namespace |
+ |
+} // namespace net |