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
18class 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
30namespace net {
31
32class 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
79TEST_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
283TEST_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