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 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
));
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
));
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"));
65 virtual void TearDownOnMainThread() override
{ watcher_
.reset(); }
67 std::string
WaitAndGetTitle() {
68 return base::UTF16ToUTF8(watcher_
->WaitAndGetTitle());
71 net::SpawnedTestServer ws_server_
;
72 net::SpawnedTestServer wss_server_
;
75 typedef net::SpawnedTestServer::SSLOptions SSLOptions
;
76 scoped_ptr
<content::TitleWatcher
> watcher_
;
78 DISALLOW_COPY_AND_ASSIGN(WebSocketBrowserTest
);
81 // Framework for tests using the connect_to.html page served by a separate HTTP
83 class WebSocketBrowserConnectToTest
: public WebSocketBrowserTest
{
85 WebSocketBrowserConnectToTest()
86 : http_server_(net::SpawnedTestServer::TYPE_HTTP
,
87 net::SpawnedTestServer::kLocalhost
,
88 net::GetWebSocketTestDataDirectory()) {}
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());
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
));
110 net::SpawnedTestServer http_server_
;
113 // Automatically fill in any login prompts that appear with the supplied
115 class AutoLogin
: public content::NotificationObserver
{
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
)),
125 chrome::NOTIFICATION_AUTH_NEEDED
,
126 content::Source
<content::NavigationController
>(navigation_controller
));
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_
);
140 bool logged_in() const { return logged_in_
; }
143 const base::string16 username_
;
144 const base::string16 password_
;
147 content::NotificationRegistrar registrar_
;
149 DISALLOW_COPY_AND_ASSIGN(AutoLogin
);
152 // Test that the browser can handle a WebSocket frame split into multiple TCP
154 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, WebSocketSplitSegments
) {
155 // Launch a WebSocket server.
156 ASSERT_TRUE(ws_server_
.Start());
158 NavigateToHTTP("split_packet_check.html");
160 EXPECT_EQ("PASS", WaitAndGetTitle());
163 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, SecureWebSocketSplitRecords
) {
164 // Launch a secure WebSocket server.
165 ASSERT_TRUE(wss_server_
.Start());
167 NavigateToHTTPS("split_packet_check.html");
169 EXPECT_EQ("PASS", WaitAndGetTitle());
172 IN_PROC_BROWSER_TEST_F(WebSocketBrowserTest
, SendCloseFrameWhenTabIsClosed
) {
173 // Launch a WebSocket server.
174 ASSERT_TRUE(ws_server_
.Start());
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));
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"));
192 content::WebContentsDestroyedWatcher
destroyed_watcher(new_tab
);
193 browser()->tab_strip_model()->CloseWebContentsAt(1, 0);
194 destroyed_watcher
.Wait();
197 NavigateToHTTP("count_connection.html");
198 EXPECT_EQ("PASS", WaitAndGetTitle());
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());
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(
212 ws_server_
.GetURLWithUserAndPassword("connect_check.html", "test", "test")
213 .ReplaceComponents(replacements
));
215 EXPECT_EQ("PASS", WaitAndGetTitle());
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());
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(
229 wss_server_
.GetURLWithUserAndPassword(
230 "connect_check.html", "test", "test")
231 .ReplaceComponents(replacements
));
233 EXPECT_EQ("PASS", WaitAndGetTitle());
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
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());
245 content::NavigationController
* navigation_controller
=
246 &browser()->tab_strip_model()->GetActiveWebContents()->GetController();
247 AutoLogin
auto_login("test", "test", navigation_controller
);
249 WindowedAuthNeededObserver
auth_needed_waiter(navigation_controller
);
250 NavigateToHTTP("connect_check.html");
251 auth_needed_waiter
.Wait();
253 EXPECT_TRUE(auto_login
.logged_in());
254 EXPECT_EQ("PASS", WaitAndGetTitle());
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());
263 ConnectTo(ws_server_
.GetURLWithUserAndPassword(
264 "echo-with-no-extension", "test", "test"));
266 EXPECT_EQ("PASS", WaitAndGetTitle());
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());
275 ConnectTo(ws_server_
.GetURLWithUserAndPassword(
276 "echo-with-no-extension", "wrong-user", "wrong-password"));
278 EXPECT_EQ("FAIL", WaitAndGetTitle());
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());
287 ConnectTo(ws_server_
.GetURL("echo-with-no-extension"));
289 EXPECT_EQ("FAIL", WaitAndGetTitle());
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());
298 NavigateToHTTPS("multiple-connections.html");
300 EXPECT_EQ("PASS", WaitAndGetTitle());