- add sources.
[platform/framework/web/crosswalk.git] / src / net / websockets / websocket_throttle_test.cc
1 // Copyright 2013 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 "net/websockets/websocket_throttle.h"
6
7 #include <string>
8
9 #include "base/message_loop/message_loop.h"
10 #include "net/base/address_list.h"
11 #include "net/base/test_completion_callback.h"
12 #include "net/socket_stream/socket_stream.h"
13 #include "net/url_request/url_request_test_util.h"
14 #include "net/websockets/websocket_job.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 #include "testing/platform_test.h"
17 #include "url/gurl.h"
18
19 class DummySocketStreamDelegate : public net::SocketStream::Delegate {
20  public:
21   DummySocketStreamDelegate() {}
22   virtual ~DummySocketStreamDelegate() {}
23   virtual void OnConnected(
24       net::SocketStream* socket, int max_pending_send_allowed) OVERRIDE {}
25   virtual void OnSentData(net::SocketStream* socket,
26                           int amount_sent) OVERRIDE {}
27   virtual void OnReceivedData(net::SocketStream* socket,
28                               const char* data, int len) OVERRIDE {}
29   virtual void OnClose(net::SocketStream* socket) OVERRIDE {}
30 };
31
32 namespace net {
33
34 class WebSocketThrottleTest : public PlatformTest {
35  protected:
36   static IPEndPoint MakeAddr(int a1, int a2, int a3, int a4) {
37     IPAddressNumber ip;
38     ip.push_back(a1);
39     ip.push_back(a2);
40     ip.push_back(a3);
41     ip.push_back(a4);
42     return IPEndPoint(ip, 0);
43   }
44
45   static void MockSocketStreamConnect(
46       SocketStream* socket, const AddressList& list) {
47     socket->set_addresses(list);
48     // TODO(toyoshim): We should introduce additional tests on cases via proxy.
49     socket->proxy_info_.UseDirect();
50     // In SocketStream::Connect(), it adds reference to socket, which is
51     // balanced with SocketStream::Finish() that is finally called from
52     // SocketStream::Close() or SocketStream::DetachDelegate(), when
53     // next_state_ is not STATE_NONE.
54     // If next_state_ is STATE_NONE, SocketStream::Close() or
55     // SocketStream::DetachDelegate() won't call SocketStream::Finish(),
56     // so Release() won't be called.  Thus, we don't need socket->AddRef()
57     // here.
58     DCHECK_EQ(socket->next_state_, SocketStream::STATE_NONE);
59   }
60 };
61
62 TEST_F(WebSocketThrottleTest, Throttle) {
63   TestURLRequestContext context;
64   DummySocketStreamDelegate delegate;
65   // TODO(toyoshim): We need to consider both spdy-enabled and spdy-disabled
66   // configuration.
67   WebSocketJob::set_websocket_over_spdy_enabled(true);
68
69   // For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6
70   AddressList addr;
71   addr.push_back(MakeAddr(1, 2, 3, 4));
72   addr.push_back(MakeAddr(1, 2, 3, 5));
73   addr.push_back(MakeAddr(1, 2, 3, 6));
74   scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
75   scoped_refptr<SocketStream> s1(
76       new SocketStream(GURL("ws://host1/"), w1.get()));
77   s1->set_context(&context);
78   w1->InitSocketStream(s1.get());
79   WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr);
80
81   DVLOG(1) << "socket1";
82   TestCompletionCallback callback_s1;
83   // Trying to open connection to host1 will start without wait.
84   EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback()));
85
86   // Now connecting to host1, so waiting queue looks like
87   // Address | head -> tail
88   // 1.2.3.4 | w1
89   // 1.2.3.5 | w1
90   // 1.2.3.6 | w1
91
92   // For host2: 1.2.3.4
93   addr.clear();
94   addr.push_back(MakeAddr(1, 2, 3, 4));
95   scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
96   scoped_refptr<SocketStream> s2(
97       new SocketStream(GURL("ws://host2/"), w2.get()));
98   s2->set_context(&context);
99   w2->InitSocketStream(s2.get());
100   WebSocketThrottleTest::MockSocketStreamConnect(s2.get(), addr);
101
102   DVLOG(1) << "socket2";
103   TestCompletionCallback callback_s2;
104   // Trying to open connection to host2 will wait for w1.
105   EXPECT_EQ(ERR_IO_PENDING,
106             w2->OnStartOpenConnection(s2.get(), callback_s2.callback()));
107   // Now waiting queue looks like
108   // Address | head -> tail
109   // 1.2.3.4 | w1 w2
110   // 1.2.3.5 | w1
111   // 1.2.3.6 | w1
112
113   // For host3: 1.2.3.5
114   addr.clear();
115   addr.push_back(MakeAddr(1, 2, 3, 5));
116   scoped_refptr<WebSocketJob> w3(new WebSocketJob(&delegate));
117   scoped_refptr<SocketStream> s3(
118       new SocketStream(GURL("ws://host3/"), w3.get()));
119   s3->set_context(&context);
120   w3->InitSocketStream(s3.get());
121   WebSocketThrottleTest::MockSocketStreamConnect(s3.get(), addr);
122
123   DVLOG(1) << "socket3";
124   TestCompletionCallback callback_s3;
125   // Trying to open connection to host3 will wait for w1.
126   EXPECT_EQ(ERR_IO_PENDING,
127             w3->OnStartOpenConnection(s3.get(), callback_s3.callback()));
128   // Address | head -> tail
129   // 1.2.3.4 | w1 w2
130   // 1.2.3.5 | w1    w3
131   // 1.2.3.6 | w1
132
133   // For host4: 1.2.3.4, 1.2.3.6
134   addr.clear();
135   addr.push_back(MakeAddr(1, 2, 3, 4));
136   addr.push_back(MakeAddr(1, 2, 3, 6));
137   scoped_refptr<WebSocketJob> w4(new WebSocketJob(&delegate));
138   scoped_refptr<SocketStream> s4(
139       new SocketStream(GURL("ws://host4/"), w4.get()));
140   s4->set_context(&context);
141   w4->InitSocketStream(s4.get());
142   WebSocketThrottleTest::MockSocketStreamConnect(s4.get(), addr);
143
144   DVLOG(1) << "socket4";
145   TestCompletionCallback callback_s4;
146   // Trying to open connection to host4 will wait for w1, w2.
147   EXPECT_EQ(ERR_IO_PENDING,
148             w4->OnStartOpenConnection(s4.get(), callback_s4.callback()));
149   // Address | head -> tail
150   // 1.2.3.4 | w1 w2    w4
151   // 1.2.3.5 | w1    w3
152   // 1.2.3.6 | w1       w4
153
154   // For host5: 1.2.3.6
155   addr.clear();
156   addr.push_back(MakeAddr(1, 2, 3, 6));
157   scoped_refptr<WebSocketJob> w5(new WebSocketJob(&delegate));
158   scoped_refptr<SocketStream> s5(
159       new SocketStream(GURL("ws://host5/"), w5.get()));
160   s5->set_context(&context);
161   w5->InitSocketStream(s5.get());
162   WebSocketThrottleTest::MockSocketStreamConnect(s5.get(), addr);
163
164   DVLOG(1) << "socket5";
165   TestCompletionCallback callback_s5;
166   // Trying to open connection to host5 will wait for w1, w4
167   EXPECT_EQ(ERR_IO_PENDING,
168             w5->OnStartOpenConnection(s5.get(), callback_s5.callback()));
169   // Address | head -> tail
170   // 1.2.3.4 | w1 w2    w4
171   // 1.2.3.5 | w1    w3
172   // 1.2.3.6 | w1       w4 w5
173
174   // For host6: 1.2.3.6
175   addr.clear();
176   addr.push_back(MakeAddr(1, 2, 3, 6));
177   scoped_refptr<WebSocketJob> w6(new WebSocketJob(&delegate));
178   scoped_refptr<SocketStream> s6(
179       new SocketStream(GURL("ws://host6/"), w6.get()));
180   s6->set_context(&context);
181   w6->InitSocketStream(s6.get());
182   WebSocketThrottleTest::MockSocketStreamConnect(s6.get(), addr);
183
184   DVLOG(1) << "socket6";
185   TestCompletionCallback callback_s6;
186   // Trying to open connection to host6 will wait for w1, w4, w5
187   EXPECT_EQ(ERR_IO_PENDING,
188             w6->OnStartOpenConnection(s6.get(), callback_s6.callback()));
189   // Address | head -> tail
190   // 1.2.3.4 | w1 w2    w4
191   // 1.2.3.5 | w1    w3
192   // 1.2.3.6 | w1       w4 w5 w6
193
194   // Receive partial response on w1, still connecting.
195   DVLOG(1) << "socket1 1";
196   static const char kHeader[] = "HTTP/1.1 101 WebSocket Protocol\r\n";
197   w1->OnReceivedData(s1.get(), kHeader, sizeof(kHeader) - 1);
198   EXPECT_FALSE(callback_s2.have_result());
199   EXPECT_FALSE(callback_s3.have_result());
200   EXPECT_FALSE(callback_s4.have_result());
201   EXPECT_FALSE(callback_s5.have_result());
202   EXPECT_FALSE(callback_s6.have_result());
203
204   // Receive rest of handshake response on w1.
205   DVLOG(1) << "socket1 2";
206   static const char kHeader2[] =
207       "Upgrade: WebSocket\r\n"
208       "Connection: Upgrade\r\n"
209       "Sec-WebSocket-Origin: http://www.google.com\r\n"
210       "Sec-WebSocket-Location: ws://websocket.chromium.org\r\n"
211       "\r\n"
212       "8jKS'y:G*Co,Wxa-";
213   w1->OnReceivedData(s1.get(), kHeader2, sizeof(kHeader2) - 1);
214   base::MessageLoopForIO::current()->RunUntilIdle();
215   // Now, w1 is open.
216   EXPECT_EQ(WebSocketJob::OPEN, w1->state());
217   // So, w2 and w3 can start connecting. w4 needs to wait w2 (1.2.3.4)
218   EXPECT_TRUE(callback_s2.have_result());
219   EXPECT_TRUE(callback_s3.have_result());
220   EXPECT_FALSE(callback_s4.have_result());
221   // Address | head -> tail
222   // 1.2.3.4 |    w2    w4
223   // 1.2.3.5 |       w3
224   // 1.2.3.6 |          w4 w5 w6
225
226   // Closing s1 doesn't change waiting queue.
227   DVLOG(1) << "socket1 close";
228   w1->OnClose(s1.get());
229   base::MessageLoopForIO::current()->RunUntilIdle();
230   EXPECT_FALSE(callback_s4.have_result());
231   s1->DetachDelegate();
232   // Address | head -> tail
233   // 1.2.3.4 |    w2    w4
234   // 1.2.3.5 |       w3
235   // 1.2.3.6 |          w4 w5 w6
236
237   // w5 can close while waiting in queue.
238   DVLOG(1) << "socket5 close";
239   // w5 close() closes SocketStream that change state to STATE_CLOSE, calls
240   // DoLoop(), so OnClose() callback will be called.
241   w5->OnClose(s5.get());
242   base::MessageLoopForIO::current()->RunUntilIdle();
243   EXPECT_FALSE(callback_s4.have_result());
244   // Address | head -> tail
245   // 1.2.3.4 |    w2    w4
246   // 1.2.3.5 |       w3
247   // 1.2.3.6 |          w4 w6
248   s5->DetachDelegate();
249
250   // w6 close abnormally (e.g. renderer finishes) while waiting in queue.
251   DVLOG(1) << "socket6 close abnormally";
252   w6->DetachDelegate();
253   base::MessageLoopForIO::current()->RunUntilIdle();
254   EXPECT_FALSE(callback_s4.have_result());
255   // Address | head -> tail
256   // 1.2.3.4 |    w2    w4
257   // 1.2.3.5 |       w3
258   // 1.2.3.6 |          w4
259
260   // Closing s2 kicks w4 to start connecting.
261   DVLOG(1) << "socket2 close";
262   w2->OnClose(s2.get());
263   base::MessageLoopForIO::current()->RunUntilIdle();
264   EXPECT_TRUE(callback_s4.have_result());
265   // Address | head -> tail
266   // 1.2.3.4 |          w4
267   // 1.2.3.5 |       w3
268   // 1.2.3.6 |          w4
269   s2->DetachDelegate();
270
271   DVLOG(1) << "socket3 close";
272   w3->OnClose(s3.get());
273   base::MessageLoopForIO::current()->RunUntilIdle();
274   s3->DetachDelegate();
275   w4->OnClose(s4.get());
276   s4->DetachDelegate();
277   DVLOG(1) << "Done";
278   base::MessageLoopForIO::current()->RunUntilIdle();
279 }
280
281 TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
282   TestURLRequestContext context;
283   DummySocketStreamDelegate delegate;
284   WebSocketJob::set_websocket_over_spdy_enabled(true);
285
286   // For localhost: 127.0.0.1, 127.0.0.1
287   AddressList addr;
288   addr.push_back(MakeAddr(127, 0, 0, 1));
289   addr.push_back(MakeAddr(127, 0, 0, 1));
290   scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
291   scoped_refptr<SocketStream> s1(
292       new SocketStream(GURL("ws://localhost/"), w1.get()));
293   s1->set_context(&context);
294   w1->InitSocketStream(s1.get());
295   WebSocketThrottleTest::MockSocketStreamConnect(s1.get(), addr);
296
297   DVLOG(1) << "socket1";
298   TestCompletionCallback callback_s1;
299   // Trying to open connection to localhost will start without wait.
300   EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback()));
301
302   DVLOG(1) << "socket1 close";
303   w1->OnClose(s1.get());
304   s1->DetachDelegate();
305   DVLOG(1) << "Done";
306   base::MessageLoopForIO::current()->RunUntilIdle();
307 }
308
309 // A connection should not be blocked by another connection to the same IP
310 // with a different port.
311 TEST_F(WebSocketThrottleTest, NoThrottleForDistinctPort) {
312   TestURLRequestContext context;
313   DummySocketStreamDelegate delegate;
314   IPAddressNumber localhost;
315   ParseIPLiteralToNumber("127.0.0.1", &localhost);
316   WebSocketJob::set_websocket_over_spdy_enabled(false);
317
318   // socket1: 127.0.0.1:80
319   scoped_refptr<WebSocketJob> w1(new WebSocketJob(&delegate));
320   scoped_refptr<SocketStream> s1(
321       new SocketStream(GURL("ws://localhost:80/"), w1.get()));
322   s1->set_context(&context);
323   w1->InitSocketStream(s1.get());
324   MockSocketStreamConnect(s1.get(),
325                           AddressList::CreateFromIPAddress(localhost, 80));
326
327   DVLOG(1) << "connecting socket1";
328   TestCompletionCallback callback_s1;
329   // Trying to open connection to localhost:80 will start without waiting.
330   EXPECT_EQ(OK, w1->OnStartOpenConnection(s1.get(), callback_s1.callback()));
331
332   // socket2: 127.0.0.1:81
333   scoped_refptr<WebSocketJob> w2(new WebSocketJob(&delegate));
334   scoped_refptr<SocketStream> s2(
335       new SocketStream(GURL("ws://localhost:81/"), w2.get()));
336   s2->set_context(&context);
337   w2->InitSocketStream(s2.get());
338   MockSocketStreamConnect(s2.get(),
339                           AddressList::CreateFromIPAddress(localhost, 81));
340
341   DVLOG(1) << "connecting socket2";
342   TestCompletionCallback callback_s2;
343   // Trying to open connection to localhost:81 will start without waiting.
344   EXPECT_EQ(OK, w2->OnStartOpenConnection(s2.get(), callback_s2.callback()));
345
346   DVLOG(1) << "closing socket1";
347   w1->OnClose(s1.get());
348   s1->DetachDelegate();
349
350   DVLOG(1) << "closing socket2";
351   w2->OnClose(s2.get());
352   s2->DetachDelegate();
353   DVLOG(1) << "Done";
354   base::MessageLoopForIO::current()->RunUntilIdle();
355 }
356
357 }