Home | History | Annotate | Download | only in websockets
      1 // Copyright (c) 2010 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/message_loop.h"
      8 #include "googleurl/src/gurl.h"
      9 #include "net/base/address_list.h"
     10 #include "net/base/sys_addrinfo.h"
     11 #include "net/base/test_completion_callback.h"
     12 #include "net/socket_stream/socket_stream.h"
     13 #include "net/websockets/websocket_job.h"
     14 #include "net/websockets/websocket_throttle.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 #include "testing/platform_test.h"
     17 
     18 class DummySocketStreamDelegate : public net::SocketStream::Delegate {
     19  public:
     20   DummySocketStreamDelegate() {}
     21   virtual ~DummySocketStreamDelegate() {}
     22   virtual void OnConnected(
     23       net::SocketStream* socket, int max_pending_send_allowed) {}
     24   virtual void OnSentData(net::SocketStream* socket, int amount_sent) {}
     25   virtual void OnReceivedData(net::SocketStream* socket,
     26                               const char* data, int len) {}
     27   virtual void OnClose(net::SocketStream* socket) {}
     28 };
     29 
     30 namespace net {
     31 
     32 class WebSocketThrottleTest : public PlatformTest {
     33  protected:
     34   struct addrinfo *AddAddr(int a1, int a2, int a3, int a4,
     35                            struct addrinfo* next) {
     36     struct addrinfo* addrinfo = new struct addrinfo;
     37     memset(addrinfo, 0, sizeof(struct addrinfo));
     38     addrinfo->ai_family = AF_INET;
     39     int addrlen = sizeof(struct sockaddr_in);
     40     addrinfo->ai_addrlen = addrlen;
     41     addrinfo->ai_addr = reinterpret_cast<sockaddr*>(new char[addrlen]);
     42     memset(addrinfo->ai_addr, 0, sizeof(addrlen));
     43     struct sockaddr_in* addr =
     44         reinterpret_cast<sockaddr_in*>(addrinfo->ai_addr);
     45     int addrint = ((a1 & 0xff) << 24) |
     46         ((a2 & 0xff) << 16) |
     47         ((a3 & 0xff) <<  8) |
     48         ((a4 & 0xff));
     49     memcpy(&addr->sin_addr, &addrint, sizeof(int));
     50     addrinfo->ai_next = next;
     51     return addrinfo;
     52   }
     53   void DeleteAddrInfo(struct addrinfo* head) {
     54     if (!head)
     55       return;
     56     struct addrinfo* next;
     57     for (struct addrinfo* a = head; a != NULL; a = next) {
     58       next = a->ai_next;
     59       delete [] a->ai_addr;
     60       delete a;
     61     }
     62   }
     63 
     64   static void MockSocketStreamConnect(
     65       SocketStream* socket, struct addrinfo* head) {
     66     socket->CopyAddrInfo(head);
     67     // In SocketStream::Connect(), it adds reference to socket, which is
     68     // balanced with SocketStream::Finish() that is finally called from
     69     // SocketStream::Close() or SocketStream::DetachDelegate(), when
     70     // next_state_ is not STATE_NONE.
     71     // If next_state_ is STATE_NONE, SocketStream::Close() or
     72     // SocketStream::DetachDelegate() won't call SocketStream::Finish(),
     73     // so Release() won't be called.  Thus, we don't need socket->AddRef()
     74     // here.
     75     DCHECK_EQ(socket->next_state_, SocketStream::STATE_NONE);
     76   }
     77 };
     78 
     79 TEST_F(WebSocketThrottleTest, Throttle) {
     80   DummySocketStreamDelegate delegate;
     81 
     82   // For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6
     83   struct addrinfo* addr = AddAddr(1, 2, 3, 4, NULL);
     84   addr = AddAddr(1, 2, 3, 5, addr);
     85   addr = AddAddr(1, 2, 3, 6, addr);
     86   scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
     87   scoped_refptr<SocketStream> s1(
     88       new SocketStream(GURL("ws://host1/"), w1.get()));
     89   w1->InitSocketStream(s1.get());
     90   WebSocketThrottleTest::MockSocketStreamConnect(s1, addr);
     91   DeleteAddrInfo(addr);
     92 
     93   DVLOG(1) << "socket1";
     94   TestCompletionCallback callback_s1;
     95   // Trying to open connection to host1 will start without wait.
     96   EXPECT_EQ(OK, w1->OnStartOpenConnection(s1, &callback_s1));
     97 
     98   // Now connecting to host1, so waiting queue looks like
     99   // Address | head -> tail
    100   // 1.2.3.4 | w1
    101   // 1.2.3.5 | w1
    102   // 1.2.3.6 | w1
    103 
    104   // For host2: 1.2.3.4
    105   addr = AddAddr(1, 2, 3, 4, NULL);
    106   scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
    107   scoped_refptr<SocketStream> s2(
    108       new SocketStream(GURL("ws://host2/"), w2.get()));
    109   w2->InitSocketStream(s2.get());
    110   WebSocketThrottleTest::MockSocketStreamConnect(s2, addr);
    111   DeleteAddrInfo(addr);
    112 
    113   DVLOG(1) << "socket2";
    114   TestCompletionCallback callback_s2;
    115   // Trying to open connection to host2 will wait for w1.
    116   EXPECT_EQ(ERR_IO_PENDING, w2->OnStartOpenConnection(s2, &callback_s2));
    117   // Now waiting queue looks like
    118   // Address | head -> tail
    119   // 1.2.3.4 | w1 w2
    120   // 1.2.3.5 | w1
    121   // 1.2.3.6 | w1
    122 
    123   // For host3: 1.2.3.5
    124   addr = AddAddr(1, 2, 3, 5, NULL);
    125   scoped_refptr<WebSocketJob> w3(new WebSocketJob(&delegate));
    126   scoped_refptr<SocketStream> s3(
    127       new SocketStream(GURL("ws://host3/"), w3.get()));
    128   w3->InitSocketStream(s3.get());
    129   WebSocketThrottleTest::MockSocketStreamConnect(s3, addr);
    130   DeleteAddrInfo(addr);
    131 
    132   DVLOG(1) << "socket3";
    133   TestCompletionCallback callback_s3;
    134   // Trying to open connection to host3 will wait for w1.
    135   EXPECT_EQ(ERR_IO_PENDING, w3->OnStartOpenConnection(s3, &callback_s3));
    136   // Address | head -> tail
    137   // 1.2.3.4 | w1 w2
    138   // 1.2.3.5 | w1    w3
    139   // 1.2.3.6 | w1
    140 
    141   // For host4: 1.2.3.4, 1.2.3.6
    142   addr = AddAddr(1, 2, 3, 4, NULL);
    143   addr = AddAddr(1, 2, 3, 6, addr);
    144   scoped_refptr<WebSocketJob> w4(new WebSocketJob(&delegate));
    145   scoped_refptr<SocketStream> s4(
    146       new SocketStream(GURL("ws://host4/"), w4.get()));
    147   w4->InitSocketStream(s4.get());
    148   WebSocketThrottleTest::MockSocketStreamConnect(s4, addr);
    149   DeleteAddrInfo(addr);
    150 
    151   DVLOG(1) << "socket4";
    152   TestCompletionCallback callback_s4;
    153   // Trying to open connection to host4 will wait for w1, w2.
    154   EXPECT_EQ(ERR_IO_PENDING, w4->OnStartOpenConnection(s4, &callback_s4));
    155   // Address | head -> tail
    156   // 1.2.3.4 | w1 w2    w4
    157   // 1.2.3.5 | w1    w3
    158   // 1.2.3.6 | w1       w4
    159 
    160   // For host5: 1.2.3.6
    161   addr = AddAddr(1, 2, 3, 6, NULL);
    162   scoped_refptr<WebSocketJob> w5(new WebSocketJob(&delegate));
    163   scoped_refptr<SocketStream> s5(
    164       new SocketStream(GURL("ws://host5/"), w5.get()));
    165   w5->InitSocketStream(s5.get());
    166   WebSocketThrottleTest::MockSocketStreamConnect(s5, addr);
    167   DeleteAddrInfo(addr);
    168 
    169   DVLOG(1) << "socket5";
    170   TestCompletionCallback callback_s5;
    171   // Trying to open connection to host5 will wait for w1, w4
    172   EXPECT_EQ(ERR_IO_PENDING, w5->OnStartOpenConnection(s5, &callback_s5));
    173   // Address | head -> tail
    174   // 1.2.3.4 | w1 w2    w4
    175   // 1.2.3.5 | w1    w3
    176   // 1.2.3.6 | w1       w4 w5
    177 
    178   // For host6: 1.2.3.6
    179   addr = AddAddr(1, 2, 3, 6, NULL);
    180   scoped_refptr<WebSocketJob> w6(new WebSocketJob(&delegate));
    181   scoped_refptr<SocketStream> s6(
    182       new SocketStream(GURL("ws://host6/"), w6.get()));
    183   w6->InitSocketStream(s6.get());
    184   WebSocketThrottleTest::MockSocketStreamConnect(s6, addr);
    185   DeleteAddrInfo(addr);
    186 
    187   DVLOG(1) << "socket6";
    188   TestCompletionCallback callback_s6;
    189   // Trying to open connection to host6 will wait for w1, w4, w5
    190   EXPECT_EQ(ERR_IO_PENDING, w6->OnStartOpenConnection(s6, &callback_s6));
    191   // Address | head -> tail
    192   // 1.2.3.4 | w1 w2    w4
    193   // 1.2.3.5 | w1    w3
    194   // 1.2.3.6 | w1       w4 w5 w6
    195 
    196   // Receive partial response on w1, still connecting.
    197   DVLOG(1) << "socket1 1";
    198   static const char kHeader[] = "HTTP/1.1 101 WebSocket Protocol\r\n";
    199   w1->OnReceivedData(s1.get(), kHeader, sizeof(kHeader) - 1);
    200   EXPECT_FALSE(callback_s2.have_result());
    201   EXPECT_FALSE(callback_s3.have_result());
    202   EXPECT_FALSE(callback_s4.have_result());
    203   EXPECT_FALSE(callback_s5.have_result());
    204   EXPECT_FALSE(callback_s6.have_result());
    205 
    206   // Receive rest of handshake response on w1.
    207   DVLOG(1) << "socket1 2";
    208   static const char kHeader2[] =
    209       "Upgrade: WebSocket\r\n"
    210       "Connection: Upgrade\r\n"
    211       "Sec-WebSocket-Origin: http://www.google.com\r\n"
    212       "Sec-WebSocket-Location: ws://websocket.chromium.org\r\n"
    213       "\r\n"
    214       "8jKS'y:G*Co,Wxa-";
    215   w1->OnReceivedData(s1.get(), kHeader2, sizeof(kHeader2) - 1);
    216   MessageLoopForIO::current()->RunAllPending();
    217   // Now, w1 is open.
    218   EXPECT_EQ(WebSocketJob::OPEN, w1->state());
    219   // So, w2 and w3 can start connecting. w4 needs to wait w2 (1.2.3.4)
    220   EXPECT_TRUE(callback_s2.have_result());
    221   EXPECT_TRUE(callback_s3.have_result());
    222   EXPECT_FALSE(callback_s4.have_result());
    223   // Address | head -> tail
    224   // 1.2.3.4 |    w2    w4
    225   // 1.2.3.5 |       w3
    226   // 1.2.3.6 |          w4 w5 w6
    227 
    228   // Closing s1 doesn't change waiting queue.
    229   DVLOG(1) << "socket1 close";
    230   w1->OnClose(s1.get());
    231   MessageLoopForIO::current()->RunAllPending();
    232   EXPECT_FALSE(callback_s4.have_result());
    233   s1->DetachDelegate();
    234   // Address | head -> tail
    235   // 1.2.3.4 |    w2    w4
    236   // 1.2.3.5 |       w3
    237   // 1.2.3.6 |          w4 w5 w6
    238 
    239   // w5 can close while waiting in queue.
    240   DVLOG(1) << "socket5 close";
    241   // w5 close() closes SocketStream that change state to STATE_CLOSE, calls
    242   // DoLoop(), so OnClose() callback will be called.
    243   w5->OnClose(s5.get());
    244   MessageLoopForIO::current()->RunAllPending();
    245   EXPECT_FALSE(callback_s4.have_result());
    246   // Address | head -> tail
    247   // 1.2.3.4 |    w2    w4
    248   // 1.2.3.5 |       w3
    249   // 1.2.3.6 |          w4 w6
    250   s5->DetachDelegate();
    251 
    252   // w6 close abnormally (e.g. renderer finishes) while waiting in queue.
    253   DVLOG(1) << "socket6 close abnormally";
    254   w6->DetachDelegate();
    255   MessageLoopForIO::current()->RunAllPending();
    256   EXPECT_FALSE(callback_s4.have_result());
    257   // Address | head -> tail
    258   // 1.2.3.4 |    w2    w4
    259   // 1.2.3.5 |       w3
    260   // 1.2.3.6 |          w4
    261 
    262   // Closing s2 kicks w4 to start connecting.
    263   DVLOG(1) << "socket2 close";
    264   w2->OnClose(s2.get());
    265   MessageLoopForIO::current()->RunAllPending();
    266   EXPECT_TRUE(callback_s4.have_result());
    267   // Address | head -> tail
    268   // 1.2.3.4 |          w4
    269   // 1.2.3.5 |       w3
    270   // 1.2.3.6 |          w4
    271   s2->DetachDelegate();
    272 
    273   DVLOG(1) << "socket3 close";
    274   w3->OnClose(s3.get());
    275   MessageLoopForIO::current()->RunAllPending();
    276   s3->DetachDelegate();
    277   w4->OnClose(s4.get());
    278   s4->DetachDelegate();
    279   DVLOG(1) << "Done";
    280   MessageLoopForIO::current()->RunAllPending();
    281 }
    282 
    283 TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
    284   DummySocketStreamDelegate delegate;
    285 
    286   // For localhost: 127.0.0.1, 127.0.0.1
    287   struct addrinfo* addr = AddAddr(127, 0, 0, 1, NULL);
    288   addr = AddAddr(127, 0, 0, 1, addr);
    289   scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
    290   scoped_refptr<SocketStream> s1(
    291       new SocketStream(GURL("ws://localhost/"), w1.get()));
    292   w1->InitSocketStream(s1.get());
    293   WebSocketThrottleTest::MockSocketStreamConnect(s1, addr);
    294   DeleteAddrInfo(addr);
    295 
    296   DVLOG(1) << "socket1";
    297   TestCompletionCallback callback_s1;
    298   // Trying to open connection to localhost will start without wait.
    299   EXPECT_EQ(OK, w1->OnStartOpenConnection(s1, &callback_s1));
    300 
    301   DVLOG(1) << "socket1 close";
    302   w1->OnClose(s1.get());
    303   s1->DetachDelegate();
    304   DVLOG(1) << "Done";
    305   MessageLoopForIO::current()->RunAllPending();
    306 }
    307 
    308 }
    309