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 | |
|
rvargas (doing something else)
2015/01/21 00:47:04
I don't think this file (net) should be talking ab
Adam Rice
2015/01/21 09:45:56
I think the end-to-end tests need to be viewed hol
rvargas (doing something else)
2015/01/21 19:46:53
I don't agree with the conclusion. net_unittests s
Adam Rice
2015/01/22 12:06:28
I didn't find out about the PPAPI tests until I'd
| |
| 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 | |
|
rvargas (doing something else)
2015/01/21 00:47:05
This comment makes sense by itself: when it is ok
Adam Rice
2015/01/22 12:06:28
Okay. This comment is now by itself.
| |
| 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 | |
|
rvargas (doing something else)
2015/01/21 00:47:04
isn't this always true?
Adam Rice
2015/01/21 09:45:56
Yes. But you don't have to look far to find pointl
rvargas (doing something else)
2015/01/21 19:46:53
To be honest, I don't think a line saying don't du
Adam Rice
2015/01/22 12:06:28
Removed.
| |
| 23 // regression tests and basic smoke tests. | |
| 24 | |
| 25 #include <string> | |
| 26 | |
| 27 #include "base/bind.h" | |
| 28 #include "base/bind_helpers.h" | |
| 29 #include "base/callback.h" | |
| 30 #include "base/memory/scoped_ptr.h" | |
| 31 #include "base/message_loop/message_loop.h" | |
| 32 #include "base/run_loop.h" | |
| 33 #include "net/base/auth.h" | |
| 34 #include "net/base/network_delegate.h" | |
| 35 #include "net/base/test_data_directory.h" | |
| 36 #include "net/proxy/proxy_service.h" | |
| 37 #include "net/test/spawned_test_server/spawned_test_server.h" | |
| 38 #include "net/url_request/url_request_test_util.h" | |
| 39 #include "net/websockets/websocket_channel.h" | |
| 40 #include "net/websockets/websocket_event_interface.h" | |
| 41 #include "testing/gtest/include/gtest/gtest.h" | |
| 42 #include "url/origin.h" | |
| 43 | |
| 44 namespace net { | |
| 45 | |
| 46 namespace { | |
| 47 | |
| 48 static const char kEchoServer[] = "echo-with-no-extension"; | |
| 49 | |
| 50 // An implementation of WebSocketEventInterface that waits for and records the | |
| 51 // results of the connect. | |
| 52 class ConnectTestingEventInterface : public WebSocketEventInterface { | |
|
rvargas (doing something else)
2015/01/21 00:47:04
Nit: I think this class is slightly too long to ha
Adam Rice
2015/01/21 09:45:56
Done.
| |
| 53 public: | |
| 54 ConnectTestingEventInterface() : fail_(true) {} | |
| 55 | |
| 56 void WaitForResponse() { run_loop_.Run(); } | |
| 57 | |
| 58 // fail() is true if the handshake failed (ie. OnFailChannel was called). | |
|
rvargas (doing something else)
2015/01/21 00:47:04
Shouldn't it be called failed_ then? And the comme
Adam Rice
2015/01/21 09:45:55
Okay. I made just this definition online, so that
| |
| 59 bool fail() const { return fail_; } | |
| 60 | |
| 61 // Only set if the handshake failed, otherwise empty. | |
| 62 // Failure messages appear on the console and are checked by the layout tests, | |
|
rvargas (doing something else)
2015/01/21 00:47:04
layout tests?
Adam Rice
2015/01/21 09:45:56
Blink layout tests. I clarified the comment slight
rvargas (doing something else)
2015/01/21 19:46:53
My point is that this file should not know what a
Adam Rice
2015/01/22 12:06:28
Done.
| |
| 63 // so they are expected to stay reasonably stable across versions. | |
| 64 std::string failure_message() const { return failure_message_; } | |
| 65 | |
| 66 std::string selected_subprotocol() const { return selected_subprotocol_; } | |
| 67 | |
| 68 std::string extensions() const { return extensions_; } | |
| 69 | |
| 70 ChannelState OnAddChannelResponse(bool fail, | |
| 71 const std::string& selected_subprotocol, | |
| 72 const std::string& extensions) override { | |
| 73 fail_ = fail; | |
| 74 selected_subprotocol_ = selected_subprotocol; | |
| 75 extensions_ = extensions; | |
| 76 QuitNestedEventLoop(); | |
| 77 return fail ? CHANNEL_DELETED : CHANNEL_ALIVE; | |
| 78 } | |
| 79 | |
| 80 ChannelState OnDataFrame(bool fin, | |
| 81 WebSocketMessageType type, | |
| 82 const std::vector<char>& data) override { | |
| 83 return CHANNEL_ALIVE; | |
| 84 } | |
| 85 | |
| 86 ChannelState OnFlowControl(int64 quota) override { return CHANNEL_ALIVE; } | |
| 87 | |
| 88 ChannelState OnClosingHandshake() override { return CHANNEL_ALIVE; } | |
| 89 | |
| 90 ChannelState OnDropChannel(bool was_clean, | |
| 91 uint16 code, | |
| 92 const std::string& reason) override { | |
| 93 return CHANNEL_DELETED; | |
| 94 } | |
| 95 | |
| 96 ChannelState OnFailChannel(const std::string& message) override { | |
| 97 fail_ = true; | |
| 98 failure_message_ = message; | |
| 99 QuitNestedEventLoop(); | |
| 100 return CHANNEL_DELETED; | |
| 101 } | |
| 102 | |
| 103 ChannelState OnStartOpeningHandshake( | |
| 104 scoped_ptr<WebSocketHandshakeRequestInfo> request) override { | |
| 105 return CHANNEL_ALIVE; | |
| 106 } | |
| 107 | |
| 108 ChannelState OnFinishOpeningHandshake( | |
| 109 scoped_ptr<WebSocketHandshakeResponseInfo> response) override { | |
| 110 return CHANNEL_ALIVE; | |
| 111 } | |
| 112 | |
| 113 ChannelState OnSSLCertificateError( | |
| 114 scoped_ptr<SSLErrorCallbacks> ssl_error_callbacks, | |
| 115 const GURL& url, | |
| 116 const SSLInfo& ssl_info, | |
| 117 bool fatal) override { | |
| 118 base::MessageLoop::current()->PostTask( | |
| 119 FROM_HERE, base::Bind(&SSLErrorCallbacks::CancelSSLRequest, | |
| 120 base::Owned(ssl_error_callbacks.release()), | |
| 121 ERR_SSL_PROTOCOL_ERROR, &ssl_info)); | |
| 122 return CHANNEL_ALIVE; | |
| 123 } | |
| 124 | |
| 125 private: | |
| 126 void QuitNestedEventLoop() { run_loop_.Quit(); } | |
| 127 | |
| 128 bool fail_; | |
| 129 std::string selected_subprotocol_; | |
| 130 std::string extensions_; | |
| 131 std::string failure_message_; | |
| 132 base::RunLoop run_loop_; | |
| 133 | |
| 134 DISALLOW_COPY_AND_ASSIGN(ConnectTestingEventInterface); | |
| 135 }; | |
| 136 | |
| 137 // A subclass of TestNetworkDelegate that additionally implements the | |
| 138 // OnResolveProxy callback and records the information passed to it. | |
| 139 class TestNetworkDelegateWithProxyInfo : public TestNetworkDelegate { | |
| 140 public: | |
| 141 TestNetworkDelegateWithProxyInfo() {} | |
| 142 | |
| 143 struct OnResolveProxyInfo { | |
|
rvargas (doing something else)
2015/01/21 00:47:04
nit: Don't declare a type named OnFoo. That patter
Adam Rice
2015/01/21 09:45:55
Thanks!
| |
| 144 GURL url; | |
| 145 ProxyInfo proxy_info; | |
| 146 }; | |
| 147 | |
| 148 const OnResolveProxyInfo& on_resolve_proxy_info() const { | |
| 149 return on_resolve_proxy_info_; | |
| 150 } | |
| 151 | |
| 152 protected: | |
| 153 void OnResolveProxy(const GURL& url, | |
| 154 int load_flags, | |
| 155 const ProxyService& proxy_service, | |
| 156 ProxyInfo* result) override { | |
| 157 on_resolve_proxy_info_.url = url; | |
| 158 on_resolve_proxy_info_.proxy_info = *result; | |
| 159 } | |
| 160 | |
| 161 private: | |
| 162 OnResolveProxyInfo on_resolve_proxy_info_; | |
| 163 | |
| 164 DISALLOW_COPY_AND_ASSIGN(TestNetworkDelegateWithProxyInfo); | |
| 165 }; | |
| 166 | |
| 167 class WebSocketEndToEndTest : public ::testing::Test { | |
| 168 protected: | |
| 169 WebSocketEndToEndTest() | |
| 170 : event_interface_(new ConnectTestingEventInterface), | |
| 171 network_delegate_(new TestNetworkDelegateWithProxyInfo), | |
| 172 context_(true), | |
| 173 channel_(make_scoped_ptr(event_interface_), &context_), | |
| 174 initialised_context_(false) {} | |
| 175 | |
| 176 // Initialise the URLRequestContext. Normally done automatically by | |
| 177 // ConnectAndWait(). This method is for the use of tests that need the | |
| 178 // URLRequestContext initialised before calling ConnectAndWait(). | |
| 179 void InitialiseContext() { | |
| 180 context_.set_network_delegate(network_delegate_.get()); | |
| 181 context_.Init(); | |
| 182 initialised_context_ = true; | |
| 183 } | |
| 184 | |
| 185 // Send the connect request to |socket_url| and wait for a response. Returns | |
| 186 // true if the handshake succeeded. | |
| 187 bool ConnectAndWait(const GURL& socket_url) { | |
| 188 if (!initialised_context_) { | |
| 189 InitialiseContext(); | |
| 190 } | |
| 191 std::vector<std::string> sub_protocols; | |
| 192 url::Origin origin("http://localhost"); | |
| 193 channel_.SendAddChannelRequest(GURL(socket_url), sub_protocols, origin); | |
| 194 event_interface_->WaitForResponse(); | |
| 195 return !event_interface_->fail(); | |
| 196 } | |
| 197 | |
| 198 ConnectTestingEventInterface* event_interface_; // owned by channel_ | |
| 199 scoped_ptr<TestNetworkDelegateWithProxyInfo> network_delegate_; | |
| 200 TestURLRequestContext context_; | |
| 201 WebSocketChannel channel_; | |
| 202 bool initialised_context_; | |
| 203 }; | |
| 204 | |
| 205 // None of these tests work on Android. | |
| 206 // TODO(ricea): Make these tests work on Android. See crbug.com/441711. | |
| 207 #if defined(OS_ANDROID) | |
| 208 #define DISABLED_ON_ANDROID(test) DISABLED_##test | |
| 209 #else | |
| 210 #define DISABLED_ON_ANDROID(test) test | |
| 211 #endif | |
| 212 | |
| 213 // Basic test of connectivity. If this test fails, nothing else can be expected | |
| 214 // to work. | |
| 215 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(BasicSmokeTest)) { | |
| 216 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
| 217 SpawnedTestServer::kLocalhost, | |
| 218 GetWebSocketTestDataDirectory()); | |
| 219 ASSERT_TRUE(ws_server.Start()); | |
| 220 EXPECT_TRUE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
| 221 } | |
| 222 | |
| 223 // Test for issue crbug.com/433695 "Unencrypted WebSocket connection via | |
| 224 // authenticated proxy times out" | |
| 225 // TODO(ricea): Enable this when the issue is fixed. | |
| 226 TEST_F(WebSocketEndToEndTest, DISABLED_HttpsProxyUnauthedFails) { | |
| 227 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
| 228 SpawnedTestServer::kLocalhost, | |
| 229 base::FilePath()); | |
| 230 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
| 231 SpawnedTestServer::kLocalhost, | |
| 232 GetWebSocketTestDataDirectory()); | |
| 233 ASSERT_TRUE(proxy_server.StartInBackground()); | |
| 234 ASSERT_TRUE(ws_server.StartInBackground()); | |
| 235 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
| 236 ASSERT_TRUE(ws_server.BlockUntilStarted()); | |
| 237 std::string proxy_config = | |
| 238 "https=" + proxy_server.host_port_pair().ToString(); | |
| 239 scoped_ptr<ProxyService> proxy_service( | |
| 240 ProxyService::CreateFixed(proxy_config)); | |
| 241 ASSERT_TRUE(proxy_service); | |
| 242 context_.set_proxy_service(proxy_service.get()); | |
| 243 EXPECT_FALSE(ConnectAndWait(ws_server.GetURL(kEchoServer))); | |
| 244 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); | |
| 245 } | |
| 246 | |
| 247 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsWssProxyUnauthedFails)) { | |
| 248 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
| 249 SpawnedTestServer::kLocalhost, | |
| 250 base::FilePath()); | |
| 251 SpawnedTestServer wss_server(SpawnedTestServer::TYPE_WSS, | |
| 252 SpawnedTestServer::kLocalhost, | |
| 253 GetWebSocketTestDataDirectory()); | |
| 254 ASSERT_TRUE(proxy_server.StartInBackground()); | |
| 255 ASSERT_TRUE(wss_server.StartInBackground()); | |
| 256 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
| 257 ASSERT_TRUE(wss_server.BlockUntilStarted()); | |
| 258 std::string proxy_config = | |
| 259 "https=" + proxy_server.host_port_pair().ToString(); | |
| 260 scoped_ptr<ProxyService> proxy_service( | |
| 261 ProxyService::CreateFixed(proxy_config)); | |
| 262 ASSERT_TRUE(proxy_service); | |
| 263 context_.set_proxy_service(proxy_service.get()); | |
| 264 EXPECT_FALSE(ConnectAndWait(wss_server.GetURL(kEchoServer))); | |
| 265 EXPECT_EQ("Proxy authentication failed", event_interface_->failure_message()); | |
| 266 } | |
| 267 | |
| 268 // Regression test for crbug/426736 "WebSocket connections not using configured | |
| 269 // system HTTPS Proxy". | |
| 270 TEST_F(WebSocketEndToEndTest, DISABLED_ON_ANDROID(HttpsProxyUsed)) { | |
| 271 SpawnedTestServer proxy_server(SpawnedTestServer::TYPE_BASIC_AUTH_PROXY, | |
| 272 SpawnedTestServer::kLocalhost, | |
| 273 base::FilePath()); | |
| 274 SpawnedTestServer ws_server(SpawnedTestServer::TYPE_WS, | |
| 275 SpawnedTestServer::kLocalhost, | |
| 276 GetWebSocketTestDataDirectory()); | |
| 277 ASSERT_TRUE(proxy_server.StartInBackground()); | |
| 278 ASSERT_TRUE(ws_server.StartInBackground()); | |
| 279 ASSERT_TRUE(proxy_server.BlockUntilStarted()); | |
| 280 ASSERT_TRUE(ws_server.BlockUntilStarted()); | |
| 281 std::string proxy_config = "https=" + | |
| 282 proxy_server.host_port_pair().ToString() + ";" + | |
| 283 "http=" + proxy_server.host_port_pair().ToString(); | |
| 284 scoped_ptr<ProxyService> proxy_service( | |
| 285 ProxyService::CreateFixed(proxy_config)); | |
| 286 context_.set_proxy_service(proxy_service.get()); | |
| 287 InitialiseContext(); | |
| 288 | |
| 289 // The test server doesn't have an unauthenticated proxy mode. WebSockets | |
| 290 // cannot provide auth information that isn't already cached, so it's | |
| 291 // necessary to preflight an HTTP request to authenticate against the proxy. | |
| 292 std::string scheme("http"); | |
| 293 GURL::Replacements replacements; | |
| 294 replacements.SetSchemeStr(scheme); | |
| 295 // It doesn't matter what the URL is, as long as it is an HTTP navigation. | |
| 296 GURL http_page = | |
| 297 ws_server.GetURL("connect_check.html").ReplaceComponents(replacements); | |
| 298 TestDelegate delegate; | |
| 299 delegate.set_credentials( | |
| 300 AuthCredentials(base::ASCIIToUTF16("foo"), base::ASCIIToUTF16("bar"))); | |
| 301 { | |
| 302 scoped_ptr<URLRequest> request( | |
| 303 context_.CreateRequest(http_page, DEFAULT_PRIORITY, &delegate, NULL)); | |
| 304 request->Start(); | |
| 305 // TestDelegate exits the message loop when the request completes by | |
| 306 // default. | |
| 307 base::RunLoop().Run(); | |
| 308 EXPECT_TRUE(delegate.auth_required_called()); | |
| 309 } | |
| 310 | |
| 311 GURL ws_url = ws_server.GetURL(kEchoServer); | |
| 312 EXPECT_TRUE(ConnectAndWait(ws_url)); | |
| 313 const TestNetworkDelegateWithProxyInfo::OnResolveProxyInfo& info = | |
| 314 network_delegate_->on_resolve_proxy_info(); | |
| 315 EXPECT_EQ(ws_url, info.url); | |
| 316 EXPECT_TRUE(info.proxy_info.is_http()); | |
| 317 } | |
| 318 | |
| 319 } // namespace | |
| 320 | |
| 321 } // namespace net | |
| OLD | NEW |