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.
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"
28 class WebSocketBrowserTest
: public InProcessBrowserTest
{
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()) {}
39 void NavigateToHTTP(const std::string
& path
) {
40 // Visit a HTTP page for testing.
41 GURL::Replacements replacements
;
42 replacements
.SetSchemeStr("http");
43 ui_test_utils::NavigateToURL(
44 browser(), ws_server_
.GetURL(path
).ReplaceComponents(replacements
));
47 void NavigateToHTTPS(const std::string
& path
) {
48 // Visit a HTTPS page for testing.
49 GURL::Replacements replacements
;
50 replacements
.SetSchemeStr("https");
51 ui_test_utils::NavigateToURL(
52 browser(), wss_server_
.GetURL(path
).ReplaceComponents(replacements
));
55 // Prepare the title watcher.
56 void SetUpOnMainThread() override
{
57 watcher_
.reset(new content::TitleWatcher(
58 browser()->tab_strip_model()->GetActiveWebContents(),
59 base::ASCIIToUTF16("PASS")));
60 watcher_
->AlsoWaitForTitle(base::ASCIIToUTF16("FAIL"));
63 void TearDownOnMainThread() override
{ watcher_
.reset(); }
65 std::string
WaitAndGetTitle() {
66 return base::UTF16ToUTF8(watcher_
->WaitAndGetTitle());
69 net::SpawnedTestServer ws_server_
;
70 net::SpawnedTestServer wss_server_
;
73 typedef net::SpawnedTestServer::SSLOptions SSLOptions
;
74 scoped_ptr
<content::TitleWatcher
> watcher_
;
76 DISALLOW_COPY_AND_ASSIGN(WebSocketBrowserTest
);
79 // Framework for tests using the connect_to.html page served by a separate HTTP
81 class WebSocketBrowserConnectToTest
: public WebSocketBrowserTest
{
83 WebSocketBrowserConnectToTest()
84 : http_server_(net::SpawnedTestServer::TYPE_HTTP
,
85 net::SpawnedTestServer::kLocalhost
,
86 net::GetWebSocketTestDataDirectory()) {}
88 // The title watcher and HTTP server are set up automatically by the test
89 // framework. Each test case still needs to configure and start the
90 // WebSocket server(s) it needs.
91 void SetUpOnMainThread() override
{
92 WebSocketBrowserTest::SetUpOnMainThread();
93 ASSERT_TRUE(http_server_
.StartInBackground());
96 // Supply a ws: or wss: URL to connect to.
97 void ConnectTo(GURL url
) {
98 ASSERT_TRUE(http_server_
.BlockUntilStarted());
99 std::string
query("url=" + url
.spec());
100 GURL::Replacements replacements
;
101 replacements
.SetQueryStr(query
);
102 ui_test_utils::NavigateToURL(browser(),
103 http_server_
.GetURL("files/connect_to.html")
104 .ReplaceComponents(replacements
));
108 net::SpawnedTestServer http_server_
;
111 // Automatically fill in any login prompts that appear with the supplied
113 class AutoLogin
: public content::NotificationObserver
{
115 AutoLogin(const std::string
& username
,
116 const std::string
& password
,
117 content::NavigationController
* navigation_controller
)
118 : username_(base::UTF8ToUTF16(username
)),
119 password_(base::UTF8ToUTF16(password
)),
123 chrome::NOTIFICATION_AUTH_NEEDED
,
124 content::Source
<content::NavigationController
>(navigation_controller
));
127 // NotificationObserver implementation
128 void Observe(int type
,
129 const content::NotificationSource
& source
,
130 const content::NotificationDetails
& details
) override
{
131 DCHECK_EQ(chrome::NOTIFICATION_AUTH_NEEDED
, type
);
132 scoped_refptr
<LoginHandler
> login_handler
=
133 content::Details
<LoginNotificationDetails
>(details
)->handler();
134 login_handler
->SetAuth(username_
, password_
);
138 bool logged_in() const { return logged_in_
; }
141 const base::string16 username_
;
142 const base::string16 password_
;
145 content::NotificationRegistrar registrar_
;
147 DISALLOW_COPY_AND_ASSIGN(AutoLogin
);
150 // Test that the browser can handle a WebSocket frame split into multiple TCP
152 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, WebSocketSplitSegments
) {
153 // Launch a WebSocket server.
154 ASSERT_TRUE(ws_server_
.Start());
156 NavigateToHTTP("split_packet_check.html");
158 EXPECT_EQ("PASS", WaitAndGetTitle());
161 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, SecureWebSocketSplitRecords
) {
162 // Launch a secure WebSocket server.
163 ASSERT_TRUE(wss_server_
.Start());
165 NavigateToHTTPS("split_packet_check.html");
167 EXPECT_EQ("PASS", WaitAndGetTitle());
170 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, SendCloseFrameWhenTabIsClosed
) {
171 // Launch a WebSocket server.
172 ASSERT_TRUE(ws_server_
.Start());
175 // Create a new tab, establish a WebSocket connection and close the tab.
176 content::WebContents
* tab
=
177 browser()->tab_strip_model()->GetActiveWebContents();
178 content::WebContents
* new_tab
= content::WebContents::Create(
179 content::WebContents::CreateParams(tab
->GetBrowserContext()));
180 browser()->tab_strip_model()->AppendWebContents(new_tab
, true);
181 ASSERT_EQ(new_tab
, browser()->tab_strip_model()->GetWebContentsAt(1));
183 content::TitleWatcher
connected_title_watcher(
184 new_tab
, base::ASCIIToUTF16("CONNECTED"));
185 connected_title_watcher
.AlsoWaitForTitle(base::ASCIIToUTF16("CLOSED"));
186 NavigateToHTTP("counted_connection.html");
187 const base::string16 result
= connected_title_watcher
.WaitAndGetTitle();
188 EXPECT_TRUE(EqualsASCII(result
, "CONNECTED"));
190 content::WebContentsDestroyedWatcher
destroyed_watcher(new_tab
);
191 browser()->tab_strip_model()->CloseWebContentsAt(1, 0);
192 destroyed_watcher
.Wait();
195 NavigateToHTTP("count_connection.html");
196 EXPECT_EQ("PASS", WaitAndGetTitle());
199 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, WebSocketBasicAuthInHTTPURL
) {
200 // Launch a basic-auth-protected WebSocket server.
201 ws_server_
.set_websocket_basic_auth(true);
202 ASSERT_TRUE(ws_server_
.Start());
204 // Open connect_check.html via HTTP with credentials in the URL.
205 GURL::Replacements replacements
;
206 replacements
.SetSchemeStr("http");
207 ui_test_utils::NavigateToURL(
209 ws_server_
.GetURLWithUserAndPassword("connect_check.html", "test", "test")
210 .ReplaceComponents(replacements
));
212 EXPECT_EQ("PASS", WaitAndGetTitle());
215 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, WebSocketBasicAuthInHTTPSURL
) {
216 // Launch a basic-auth-protected secure WebSocket server.
217 wss_server_
.set_websocket_basic_auth(true);
218 ASSERT_TRUE(wss_server_
.Start());
220 // Open connect_check.html via HTTPS with credentials in the URL.
221 GURL::Replacements replacements
;
222 replacements
.SetSchemeStr("https");
223 ui_test_utils::NavigateToURL(
225 wss_server_
.GetURLWithUserAndPassword(
226 "connect_check.html", "test", "test")
227 .ReplaceComponents(replacements
));
229 EXPECT_EQ("PASS", WaitAndGetTitle());
232 // This test verifies that login details entered by the user into the login
233 // prompt to authenticate the main page are re-used for WebSockets from the same
235 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
,
236 ReuseMainPageBasicAuthCredentialsForWebSocket
) {
237 // Launch a basic-auth-protected WebSocket server.
238 ws_server_
.set_websocket_basic_auth(true);
239 ASSERT_TRUE(ws_server_
.Start());
241 content::NavigationController
* navigation_controller
=
242 &browser()->tab_strip_model()->GetActiveWebContents()->GetController();
243 AutoLogin
auto_login("test", "test", navigation_controller
);
245 WindowedAuthNeededObserver
auth_needed_waiter(navigation_controller
);
246 NavigateToHTTP("connect_check.html");
247 auth_needed_waiter
.Wait();
249 EXPECT_TRUE(auto_login
.logged_in());
250 EXPECT_EQ("PASS", WaitAndGetTitle());
253 IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest
,
254 WebSocketBasicAuthInWSURL
) {
255 // Launch a basic-auth-protected WebSocket server.
256 ws_server_
.set_websocket_basic_auth(true);
257 ASSERT_TRUE(ws_server_
.Start());
259 ConnectTo(ws_server_
.GetURLWithUserAndPassword(
260 "echo-with-no-extension", "test", "test"));
262 EXPECT_EQ("PASS", WaitAndGetTitle());
265 IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest
,
266 WebSocketBasicAuthInWSURLBadCreds
) {
267 // Launch a basic-auth-protected WebSocket server.
268 ws_server_
.set_websocket_basic_auth(true);
269 ASSERT_TRUE(ws_server_
.Start());
271 ConnectTo(ws_server_
.GetURLWithUserAndPassword(
272 "echo-with-no-extension", "wrong-user", "wrong-password"));
274 EXPECT_EQ("FAIL", WaitAndGetTitle());
277 IN_PROC_BROWSER_TEST_F(WebSocketBrowserConnectToTest
,
278 WebSocketBasicAuthNoCreds
) {
279 // Launch a basic-auth-protected WebSocket server.
280 ws_server_
.set_websocket_basic_auth(true);
281 ASSERT_TRUE(ws_server_
.Start());
283 ConnectTo(ws_server_
.GetURL("echo-with-no-extension"));
285 EXPECT_EQ("FAIL", WaitAndGetTitle());
288 // HTTPS connection limits should not be applied to wss:. This is only tested
289 // for secure connections here because the unencrypted case is tested in the
290 // Blink layout tests, and browser tests are expensive to run.
291 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, SSLConnectionLimit
) {
292 ASSERT_TRUE(wss_server_
.Start());
294 NavigateToHTTPS("multiple-connections.html");
296 EXPECT_EQ("PASS", WaitAndGetTitle());
299 // Regression test for crbug.com/903553005
300 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, WebSocketAppliesHSTS
) {
301 net::SpawnedTestServer
https_server(
302 net::SpawnedTestServer::TYPE_HTTPS
,
303 net::SpawnedTestServer::SSLOptions(),
304 base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
305 // This test sets HSTS on 127.0.0.1. To avoid being redirected to https, start
306 // the http server on "localhost" instead.
307 net::SpawnedTestServer
http_server(
308 net::SpawnedTestServer::TYPE_HTTP
,
310 base::FilePath(FILE_PATH_LITERAL("chrome/test/data")));
311 ASSERT_TRUE(https_server
.StartInBackground());
312 ASSERT_TRUE(http_server
.StartInBackground());
313 ASSERT_TRUE(wss_server_
.StartInBackground());
314 ASSERT_TRUE(https_server
.BlockUntilStarted());
316 // Set HSTS on 127.0.0.1.
317 content::TitleWatcher
title_watcher(
318 browser()->tab_strip_model()->GetActiveWebContents(),
319 base::ASCIIToUTF16("SET"));
320 ui_test_utils::NavigateToURL(
321 browser(), https_server
.GetURL("files/websocket/set-hsts.html"));
322 const base::string16 result
= title_watcher
.WaitAndGetTitle();
323 EXPECT_TRUE(EqualsASCII(result
, "SET"));
325 // Verify that it applies to WebSockets.
326 ASSERT_TRUE(wss_server_
.BlockUntilStarted());
327 GURL wss_url
= wss_server_
.GetURL("echo-with-no-extension");
328 std::string
scheme("ws");
329 GURL::Replacements scheme_replacement
;
330 scheme_replacement
.SetSchemeStr(scheme
);
331 GURL ws_url
= wss_url
.ReplaceComponents(scheme_replacement
);
333 // An https: URL won't work here here because the mixed content policy
334 // disallows connections to unencrypted WebSockets from encrypted pages.
335 ASSERT_TRUE(http_server
.BlockUntilStarted());
337 http_server
.GetURL("files/websocket/check-hsts.html#" + ws_url
.spec());
339 ui_test_utils::NavigateToURL(browser(), http_url
);
341 EXPECT_EQ("PASS", WaitAndGetTitle());