Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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. | |
| 4 | |
| 5 // End-to-end tests for WebSocket. Usage guidelines: | |
| 6 // There are at least 4 sets of end-to-end tests in the source tree: | |
| 7 // 1) Blink layout tests. These are easy to write, and efficient because the | |
| 8 // client and server processes are both re-used. They are easy to write as | |
| 9 // they are just Javascript. They should be used for the majority of | |
| 10 // functional tests. | |
| 11 // 2) Browser tests in //chrome/browser/net/websocket_browsertest.cc. These | |
| 12 // are inefficient because both the server and client are restarted for each | |
| 13 // test. They are also hard to write and hard to debug. They should generally | |
| 14 // only be used for testing UI-related features. | |
| 15 // 3) PPAPI tests in //chrome/test/ppapi/ppapi_browsertest.cc. These are | |
| 16 // very hard to write and debug, and should only be used for verifying the | |
| 17 // operation of the Pepper API. | |
| 18 // 4) These tests. They are somewhat inefficient because the server is restarted | |
| 19 // for each test. They are well-suited for tests which require special server | |
| 20 // configurations. | |
| 21 // | |
| 22 // Tests should generally not be duplicated in the different places, except for | |
| 23 // regression tests and basic smoke tests. | |
| 24 | |
|
tyoshino (SeeGerritForStatus)
2015/01/05 08:45:48
string
Adam Rice
2015/01/06 12:27:15
Done.
| |
| 25 #include "base/callback.h" | |
| 26 #include "base/memory/scoped_ptr.h" | |
| 27 #include "base/run_loop.h" | |
| 28 #include "net/base/auth.h" | |
| 29 #include "net/base/network_delegate.h" | |
| 30 #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.
| |
| 31 #include "net/test/spawned_test_server/spawned_test_server.h" | |
| 32 #include "net/url_request/url_request_test_util.h" | |
| 33 #include "net/websockets/websocket_channel.h" | |
| 34 #include "net/websockets/websocket_event_interface.h" | |
| 35 #include "testing/gtest/include/gtest/gtest.h" | |
| 36 #include "url/origin.h" | |
| 37 | |
| 38 namespace net { | |
| 39 | |
| 40 namespace { | |
| 41 | |
| 42 static const char kEchoServer[] = "echo-with-no-extension"; | |
| 43 | |
| 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() : fail_(true) {} | |
| 49 | |
| 50 void WaitForResponse() { | |
| 51 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
| |
| 52 run_loop_.Run(); | |
| 53 } | |
| 54 | |
|
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
| |
| 55 bool fail() const { return fail_; } | |
| 56 | |
| 57 // Only set if the handshake failed, otherwise empty. | |
| 58 // Failure messages appear on the console and are checked by the layout tests, | |
| 59 // so they are expected to stay reasonably stable across versions. | |
| 60 std::string failure_message() const { return failure_message_; } | |
| 61 | |
| 62 std::string selected_subprotocol() const { return selected_subprotocol_; } | |
| 63 | |
| 64 std::string extensions() const { return extensions_; } | |
| 65 | |
| 66 ChannelState OnAddChannelResponse(bool fail, | |
| 67 const std::string& selected_subprotocol, | |
| 68 const std::string& extensions) override { | |
| 69 fail_ = fail; | |
| 70 selected_subprotocol_ = selected_subprotocol; | |
| 71 extensions_ = extensions; | |
| 72 QuitNestedEventLoop(); | |
| 73 return fail ? CHANNEL_DELETED : CHANNEL_ALIVE; | |
| 74 } | |
| 75 | |
| 76 ChannelState OnDataFrame(bool fin, | |
| 77 WebSocketMessageType type, | |
| 78 const std::vector<char>& data) override { | |
| 79 return CHANNEL_ALIVE; | |
| 80 } | |
| 81 | |
| 82 ChannelState OnFlowControl(int64 quota) override { return CHANNEL_ALIVE; } | |
| 83 | |
| 84 ChannelState OnClosingHandshake() override { return CHANNEL_ALIVE; } | |
| 85 | |
| 86 ChannelState OnDropChannel(bool was_clean, | |
| 87 uint16 code, | |
| 88 const std::string& reason) override { | |
| 89 fail_ = !was_clean; | |
| 90 QuitNestedEventLoop(); | |
| 91 return CHANNEL_DELETED; | |
| 92 } | |
| 93 | |
| 94 ChannelState OnFailChannel(const std::string& message) override { | |
| 95 fail_ = true; | |
| 96 failure_message_ = message; | |
| 97 QuitNestedEventLoop(); | |
| 98 return CHANNEL_DELETED; | |
| 99 } | |
| 100 | |
| 101 ChannelState OnStartOpeningHandshake( | |
| 102 scoped_ptr<WebSocketHandshakeRequestInfo> request) override { | |
| 103 return CHANNEL_ALIVE; | |
| 104 } | |
| 105 | |
| 106 ChannelState OnFinishOpeningHandshake( | |
| 107 scoped_ptr<WebSocketHandshakeResponseInfo> response) override { | |
| 108 return CHANNEL_ALIVE; | |
| 109 } | |
| 110 | |
| 111 ChannelState OnSSLCertificateError( | |
| 112 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks, | |
| 113 const GURL& url, | |
| 114 const SSLInfo& ssl_info, | |
| 115 bool fatal) override { | |
| 116 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
| |
| 117 return CHANNEL_ALIVE; | |
| 118 } | |
| 119 | |
| 120 private: | |
| 121 void QuitNestedEventLoop() { | |
| 122 DCHECK(!quit_closure_.is_null()); | |
| 123 quit_closure_.Run(); | |
| 124 } | |
| 125 bool fail_; | |
| 126 std::string selected_subprotocol_; | |
| 127 std::string extensions_; | |
| 128 std::string failure_message_; | |
| 129 base::RunLoop run_loop_; | |
| 130 base::Closure quit_closure_; | |
| 131 | |
| 132 DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface); | |
| 133 }; | |
| 134 | |
| 135 class WebSocketEndToEndTest : public ::testing::Test { | |
| 136 protected: | |
| 137 WebSocketEndToEndTest() | |
| 138 : event_interface_(new ConnectTestingEventInterface), | |
| 139 network_delegate_(new TestNetworkDelegate), | |
| 140 context_(true), | |
| 141 channel_(make_scoped_ptr(event_interface_), &context_), | |
| 142 origin_("http://localhost"), | |
| 143 initialised_context_(false) {} | |
| 144 | |
| 145 void InitialiseContext() { | |
| 146 context_.set_network_delegate(network_delegate_.get()); | |
| 147 context_.Init(); | |
| 148 initialised_context_ = true; | |
| 149 } | |
| 150 | |
| 151 bool ConnectAndWait(const GURL& socket_url) { | |
| 152 if (!initialised_context_) { | |
| 153 InitialiseContext(); | |
| 154 } | |
| 155 channel_.SendAddChannelRequest(GURL(socket_url), sub_protocols_, origin_); | |
| 156 event_interface_->WaitForResponse(); | |
| 157 return !event_interface_->fail(); | |
| 158 } | |
| 159 | |
| 160 ConnectTestingEventInterface* event_interface_; // owned by channel_ | |
| 161 scoped_ptr<TestNetworkDelegate> network_delegate_; | |
| 162 TestURLRequestContext context_; | |
| 163 WebSocketChannel channel_; | |
| 164 std::vector<std::string> sub_protocols_; | |
| 165 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.
| |
| 166 bool initialised_context_; | |
| 167 }; | |
| 168 | |
| 169 // None of these tests work on Android. | |
| 170 // TODO(ricea): Make these tests work on Android. See crbug.com/441711. | |
| 171 #if defined(OS_ANDROID) | |
| 172 #define DISABLED_ON_ANDROID(test) DISABLED_##test | |
| 173 #else | |
| 174 #define DISABLED_ON_ANDROID(test) test | |
| 175 #endif | |
| 176 | |
| 177 // Basic test of connectivity. If this test fails, nothing else can be expected | |
| 178 // to work. | |
| 179 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) { | |
| 180 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
| 181 SpawnedTestServer::kLocalhost, | |
| 182 GetWebSocketTestDataDirectory()); | |
| 183 ASSERT_TRUE(ws_server.Start()); | |
| 184 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
| 185 } | |
| 186 | |
| 187 // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via | |
| 188 // authenticated proxy times out" | |
| 189 // TODO(ricea): Enable this when the issue is fixed. | |
| 190 TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) { | |
| 191 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
| 192 SpawnedTestServer::kLocalhost, | |
| 193 base::FilePath()); | |
| 194 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
| 195 SpawnedTestServer::kLocalhost, | |
| 196 GetWebSocketTestDataDirectory()); | |
| 197 ASSERT_TRUE(proxy_server.StartInBackground()); | |
| 198 ASSERT_TRUE(ws_server.StartInBackground()); | |
| 199 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
| 200 ASSERT_TRUE(ws_server.BlockUntilStarted()); | |
| 201 std::string proxy_config = | |
| 202 "https=" + proxy_server.host_port_pair().ToString(); | |
| 203 scoped_ptr<ProxyService> proxy_service( | |
| 204 ProxyService::CreateFixed(proxy_config)); | |
| 205 ASSERT_TRUE(proxy_service); | |
| 206 context_.set_proxy_service(proxy_service.get()); | |
| 207 EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
| 208 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); | |
| 209 } | |
| 210 | |
| 211 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) { | |
| 212 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
| 213 SpawnedTestServer::kLocalhost, | |
| 214 base::FilePath()); | |
| 215 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, | |
| 216 SpawnedTestServer::kLocalhost, | |
| 217 GetWebSocketTestDataDirectory()); | |
| 218 ASSERT_TRUE(proxy_server.StartInBackground()); | |
| 219 ASSERT_TRUE(wss_server.StartInBackground()); | |
| 220 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
| 221 ASSERT_TRUE(wss_server.BlockUntilStarted()); | |
| 222 std::string proxy_config = | |
| 223 "https=" + proxy_server.host_port_pair().ToString(); | |
| 224 scoped_ptr<ProxyService> proxy_service( | |
| 225 ProxyService::CreateFixed(proxy_config)); | |
| 226 ASSERT_TRUE(proxy_service); | |
| 227 context_.set_proxy_service(proxy_service.get()); | |
| 228 EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer))); | |
| 229 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); | |
| 230 } | |
| 231 | |
| 232 // Regression test for crbug/426736 "WebSocket connections not using configured | |
| 233 // system HTTPS Proxy". | |
| 234 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) { | |
| 235 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
| 236 SpawnedTestServer::kLocalhost, | |
| 237 base::FilePath()); | |
| 238 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
| 239 SpawnedTestServer::kLocalhost, | |
| 240 GetWebSocketTestDataDirectory()); | |
| 241 ASSERT_TRUE(proxy_server.StartInBackground()); | |
| 242 ASSERT_TRUE(ws_server.StartInBackground()); | |
| 243 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
| 244 ASSERT_TRUE(ws_server.BlockUntilStarted()); | |
| 245 std::string proxy_config = "https=" + | |
| 246 proxy_server.host_port_pair().ToString() + ";" + | |
| 247 "http=" + proxy_server.host_port_pair().ToString(); | |
| 248 scoped_ptr<ProxyService> proxy_service( | |
| 249 ProxyService::CreateFixed(proxy_config)); | |
| 250 context_.set_proxy_service(proxy_service.get()); | |
| 251 InitialiseContext(); | |
| 252 | |
| 253 // The test server doesn't have an unauthenticated proxy mode. WebSockets | |
| 254 // cannot provide auth information that isn't already cached, so it's | |
| 255 // necessary to preflight an HTTP request to authenticate against the proxy. | |
| 256 std::string scheme("http"); | |
| 257 GURL::Replacements replacements; | |
| 258 replacements.SetSchemeStr(scheme); | |
| 259 // It doesn't matter what the URL is, as long as it is an HTTP navigation. | |
| 260 GURL http_page = | |
| 261 ws_server.GetURL("connect_check.html").ReplaceComponents(replacements); | |
| 262 TestDelegate delegate; | |
| 263 delegate.set_credentials( | |
| 264 AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar"))); | |
| 265 { | |
| 266 scoped_ptr<URLRequest> request( | |
| 267 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL)); | |
| 268 request->Start(); | |
| 269 // TestDelegate exits the message loop when the request completes by | |
| 270 // default. | |
| 271 base::RunLoop().Run(); | |
| 272 EXPECT_TRUE(delegate.auth_required_called()); | |
| 273 } | |
| 274 | |
| 275 int previous_observed_before_proxy_headers_sent_callbacks = | |
| 276 network_delegate_->observed_before_proxy_headers_sent_callbacks(); | |
| 277 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
| 278 EXPECT_EQ(previous_observed_before_proxy_headers_sent_callbacks + 1, | |
| 279 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.
| |
| 280 } | |
| 281 | |
| 282 } // namespace | |
| 283 | |
| 284 } // namespace net | |
| OLD | NEW |