1 // Copyright (c) 2012 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 #include <string> 6 7 #include "base/strings/string_util.h" 8 #include "base/strings/utf_string_conversions.h" 9 #include "chrome/browser/chrome_notification_types.h" 10 #include "chrome/browser/ui/browser.h" 11 #include "chrome/browser/ui/login/login_prompt.h" 12 #include "chrome/browser/ui/login/login_prompt_test_utils.h" 13 #include "chrome/browser/ui/tabs/tab_strip_model.h" 14 #include "chrome/test/base/in_process_browser_test.h" 15 #include "chrome/test/base/ui_test_utils.h" 16 #include "content/public/browser/navigation_controller.h" 17 #include "content/public/browser/notification_details.h" 18 #include "content/public/browser/notification_registrar.h" 19 #include "content/public/browser/notification_source.h" 20 #include "content/public/browser/web_contents.h" 21 #include "content/public/test/browser_test_utils.h" 22 #include "net/base/test_data_directory.h" 23 #include "net/test/spawned_test_server/spawned_test_server.h" 24 #include "url/gurl.h" 25 26 namespace { 27 28 class WebSocketBrowserTest : public InProcessBrowserTest { 29 public: 30 WebSocketBrowserTest() 31 : ws_server_(net::SpawnedTestServer::TYPE_WS, 32 net::SpawnedTestServer::kLocalhost, 33 net::GetWebSocketTestDataDirectory()), 34 wss_server_(net::SpawnedTestServer::TYPE_WSS, 35 SSLOptions(SSLOptions::CERT_OK), 36 net::GetWebSocketTestDataDirectory()) {} 37 38 protected: 39 void NavigateToHTTP(const std::string& path) { 40 // Visit a HTTP page for testing. 41 std::string scheme("http"); 42 GURL::Replacements replacements; 43 replacements.SetSchemeStr(scheme); 44 ui_test_utils::NavigateToURL( 45 browser(), ws_server_.GetURL(path).ReplaceComponents(replacements)); 46 } 47 48 void NavigateToHTTPS(const std::string& path) { 49 // Visit a HTTPS page for testing. 50 std::string scheme("https"); 51 GURL::Replacements replacements; 52 replacements.SetSchemeStr(scheme); 53 ui_test_utils::NavigateToURL( 54 browser(), wss_server_.GetURL(path).ReplaceComponents(replacements)); 55 } 56 57 // Prepare the title watcher. 58 virtual void SetUpOnMainThread() OVERRIDE { 59 watcher_.reset(new content::TitleWatcher( 60 browser()->tab_strip_model()->GetActiveWebContents(), 61 base::ASCIIToUTF16("PASS"))); 62 watcher_->AlsoWaitForTitle(base::ASCIIToUTF16("FAIL")); 63 } 64 65 virtual void TearDownOnMainThread() OVERRIDE { watcher_.reset(); } 66 67 std::string WaitAndGetTitle() { 68 return base::UTF16ToUTF8(watcher_->WaitAndGetTitle()); 69 } 70 71 net::SpawnedTestServer ws_server_; 72 net::SpawnedTestServer wss_server_; 73 74 private: 75 typedef net::SpawnedTestServer::SSLOptions SSLOptions; 76 scoped_ptr<content::TitleWatcher> watcher_; 77 78 DISALLOW_COPY_AND_ASSIGN(WebSocketBrowserTest); 79 }; 80 81 // Framework for tests using the connect_to.html page served by a separate HTTP 82 // server. 83 class WebSocketBrowserConnectToTest : public WebSocketBrowserTest { 84 protected: 85 WebSocketBrowserConnectToTest() 86 : http_server_(net::SpawnedTestServer::TYPE_HTTP, 87 net::SpawnedTestServer::kLocalhost, 88 net::GetWebSocketTestDataDirectory()) {} 89 90 // The title watcher and HTTP server are set up automatically by the test 91 // framework. Each test case still needs to configure and start the 92 // WebSocket server(s) it needs. 93 virtual void SetUpOnMainThread() OVERRIDE { 94 WebSocketBrowserTest::SetUpOnMainThread(); 95 ASSERT_TRUE(http_server_.StartInBackground()); 96 } 97 98 // Supply a ws: or wss: URL to connect to. 99 void ConnectTo(GURL url) { 100 ASSERT_TRUE(http_server_.BlockUntilStarted()); 101 std::string query("url=" + url.spec()); 102 GURL::Replacements replacements; 103 replacements.SetQueryStr(query); 104 ui_test_utils::NavigateToURL(browser(), 105 http_server_.GetURL("files/connect_to.html") 106 .ReplaceComponents(replacements)); 107 } 108 109 private: 110 net::SpawnedTestServer http_server_; 111 }; 112 113 // Automatically fill in any login prompts that appear with the supplied 114 // credentials. 115 class AutoLogin : public content::NotificationObserver { 116 public: 117 AutoLogin(const std::string& username, 118 const std::string& password, 119 content::NavigationController* navigation_controller) 120 : username_(base::UTF8ToUTF16(username)), 121 password_(base::UTF8ToUTF16(password)), 122 logged_in_(false) { 123 registrar_.Add( 124 this, 125 chrome::NOTIFICATION_AUTH_NEEDED, 126 content::Source<content::NavigationController>(navigation_controller)); 127 } 128 129 // NotificationObserver implementation 130 virtual void Observe(int type, 131 const content::NotificationSource& source, 132 const content::NotificationDetails& details) OVERRIDE { 133 DCHECK_EQ(chrome::NOTIFICATION_AUTH_NEEDED, type); 134 scoped_refptr<LoginHandler> login_handler = 135 content::Details<LoginNotificationDetails>(details)->handler(); 136 login_handler->SetAuth(username_, password_); 137 logged_in_ = true; 138 } 139 140 bool logged_in() const { return logged_in_; } 141 142 private: 143 const base::string16 username_; 144 const base::string16 password_; 145 bool logged_in_; 146 147 content::NotificationRegistrar registrar_; 148 149 DISALLOW_COPY_AND_ASSIGN(AutoLogin); 150 }; 151 152 // Test that the browser can handle a WebSocket frame split into multiple TCP 153 // segments. 154 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, WebSocketSplitSegments) { 155 // Launch a WebSocket server. 156 ASSERT_TRUE(ws_server_.Start()); 157 158 NavigateToHTTP("split_packet_check.html"); 159 160 EXPECT_EQ("PASS", WaitAndGetTitle()); 161 } 162 163 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, SecureWebSocketSplitRecords) { 164 // Launch a secure WebSocket server. 165 ASSERT_TRUE(wss_server_.Start()); 166 167 NavigateToHTTPS("split_packet_check.html"); 168 169 EXPECT_EQ("PASS", WaitAndGetTitle()); 170 } 171 172 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, SendCloseFrameWhenTabIsClosed) { 173 // Launch a WebSocket server. 174 ASSERT_TRUE(ws_server_.Start()); 175 176 { 177 // Create a new tab, establish a WebSocket connection and close the tab. 178 content::WebContents* tab = 179 browser()->tab_strip_model()->GetActiveWebContents(); 180 content::WebContents* new_tab = content::WebContents::Create( 181 content::WebContents::CreateParams(tab->GetBrowserContext())); 182 browser()->tab_strip_model()->AppendWebContents(new_tab, true); 183 ASSERT_EQ(new_tab, browser()->tab_strip_model()->GetWebContentsAt(1)); 184 185 content::TitleWatcher connected_title_watcher( 186 new_tab, base::ASCIIToUTF16("CONNECTED")); 187 connected_title_watcher.AlsoWaitForTitle(base::ASCIIToUTF16("CLOSED")); 188 NavigateToHTTP("counted_connection.html"); 189 const base::string16 result = connected_title_watcher.WaitAndGetTitle(); 190 EXPECT_TRUE(EqualsASCII(result, "CONNECTED")); 191 192 content::WebContentsDestroyedWatcher destroyed_watcher(new_tab); 193 browser()->tab_strip_model()->CloseWebContentsAt(1, 0); 194 destroyed_watcher.Wait(); 195 } 196 197 NavigateToHTTP("count_connection.html"); 198 EXPECT_EQ("PASS", WaitAndGetTitle()); 199 } 200 201 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, WebSocketBasicAuthInHTTPURL) { 202 // Launch a basic-auth-protected WebSocket server. 203 ws_server_.set_websocket_basic_auth(true); 204 ASSERT_TRUE(ws_server_.Start()); 205 206 // Open connect_check.html via HTTP with credentials in the URL. 207 std::string scheme("http"); 208 GURL::Replacements replacements; 209 replacements.SetSchemeStr(scheme); 210 ui_test_utils::NavigateToURL( 211 browser(), 212 ws_server_.GetURLWithUserAndPassword("connect_check.html", "test", "test") 213 .ReplaceComponents(replacements)); 214 215 EXPECT_EQ("PASS", WaitAndGetTitle()); 216 } 217 218 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, WebSocketBasicAuthInHTTPSURL) { 219 // Launch a basic-auth-protected secure WebSocket server. 220 wss_server_.set_websocket_basic_auth(true); 221 ASSERT_TRUE(wss_server_.Start()); 222 223 // Open connect_check.html via HTTPS with credentials in the URL. 224 std::string scheme("https"); 225 GURL::Replacements replacements; 226 replacements.SetSchemeStr(scheme); 227 ui_test_utils::NavigateToURL( 228 browser(), 229 wss_server_.GetURLWithUserAndPassword( 230 "connect_check.html", "test", "test") 231 .ReplaceComponents(replacements)); 232 233 EXPECT_EQ("PASS", WaitAndGetTitle()); 234 } 235 236 // This test verifies that login details entered by the user into the login 237 // prompt to authenticate the main page are re-used for WebSockets from the same 238 // origin. 239 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, 240 ReuseMainPageBasicAuthCredentialsForWebSocket) { 241 // Launch a basic-auth-protected WebSocket server. 242 ws_server_.set_websocket_basic_auth(true); 243 ASSERT_TRUE(ws_server_.Start()); 244 245 content::NavigationController* navigation_controller = 246 &browser()->tab_strip_model()->GetActiveWebContents()->GetController(); 247 AutoLogin auto_login("test", "test", navigation_controller); 248 249 WindowedAuthNeededObserver auth_needed_waiter(navigation_controller); 250 NavigateToHTTP("connect_check.html"); 251 auth_needed_waiter.Wait(); 252 253 EXPECT_TRUE(auto_login.logged_in()); 254 EXPECT_EQ("PASS", WaitAndGetTitle()); 255 } 256 257 IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, 258 WebSocketBasicAuthInWSURL) { 259 // Launch a basic-auth-protected WebSocket server. 260 ws_server_.set_websocket_basic_auth(true); 261 ASSERT_TRUE(ws_server_.Start()); 262 263 ConnectTo(ws_server_.GetURLWithUserAndPassword( 264 "echo-with-no-extension", "test", "test")); 265 266 EXPECT_EQ("PASS", WaitAndGetTitle()); 267 } 268 269 IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, 270 WebSocketBasicAuthInWSURLBadCreds) { 271 // Launch a basic-auth-protected WebSocket server. 272 ws_server_.set_websocket_basic_auth(true); 273 ASSERT_TRUE(ws_server_.Start()); 274 275 ConnectTo(ws_server_.GetURLWithUserAndPassword( 276 "echo-with-no-extension", "wrong-user", "wrong-password")); 277 278 EXPECT_EQ("FAIL", WaitAndGetTitle()); 279 } 280 281 IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest, 282 WebSocketBasicAuthNoCreds) { 283 // Launch a basic-auth-protected WebSocket server. 284 ws_server_.set_websocket_basic_auth(true); 285 ASSERT_TRUE(ws_server_.Start()); 286 287 ConnectTo(ws_server_.GetURL("echo-with-no-extension")); 288 289 EXPECT_EQ("FAIL", WaitAndGetTitle()); 290 } 291 292 // HTTPS connection limits should not be applied to wss:. This is only tested 293 // for secure connections here because the unencrypted case is tested in the 294 // Blink layout tests, and browser tests are expensive to run. 295 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest, SSLConnectionLimit) { 296 ASSERT_TRUE(wss_server_.Start()); 297 298 NavigateToHTTPS("multiple-connections.html"); 299 300 EXPECT_EQ("PASS", WaitAndGetTitle()); 301 } 302 303 } // namespace 304