Home | History | Annotate | Download | only in net
      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