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.
5 #include "net/websockets/websocket_throttle.h"
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"
19 class DummySocketStreamDelegate : public net::SocketStream::Delegate {
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 {}
34 class WebSocketThrottleTest : public PlatformTest {
36 static IPEndPoint MakeAddr(int a1, int a2, int a3, int a4) {
42 return IPEndPoint(ip, 0);
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()
58 DCHECK_EQ(socket->next_state_, SocketStream::STATE_NONE);
62 TEST_F(WebSocketThrottleTest, Throttle) {
63 TestURLRequestContext context;
64 DummySocketStreamDelegate delegate;
65 // TODO(toyoshim): We need to consider both spdy-enabled and spdy-disabled
67 WebSocketJob::set_websocket_over_spdy_enabled(true);
69 // For host1: 1.2.3.4, 1.2.3.5, 1.2.3.6
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);
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()));
86 // Now connecting to host1, so waiting queue looks like
87 // Address | head -> tail
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);
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
113 // For host3: 1.2.3.5
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);
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
133 // For host4: 1.2.3.4, 1.2.3.6
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);
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
154 // For host5: 1.2.3.6
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);
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
172 // 1.2.3.6 | w1 w4 w5
174 // For host6: 1.2.3.6
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);
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
192 // 1.2.3.6 | w1 w4 w5 w6
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());
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"
213 w1->OnReceivedData(s1.get(), kHeader2, sizeof(kHeader2) - 1);
214 base::MessageLoopForIO::current()->RunUntilIdle();
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
224 // 1.2.3.6 | w4 w5 w6
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
235 // 1.2.3.6 | w4 w5 w6
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
248 s5->DetachDelegate();
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
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
269 s2->DetachDelegate();
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();
278 base::MessageLoopForIO::current()->RunUntilIdle();
281 TEST_F(WebSocketThrottleTest, NoThrottleForDuplicateAddress) {
282 TestURLRequestContext context;
283 DummySocketStreamDelegate delegate;
284 WebSocketJob::set_websocket_over_spdy_enabled(true);
286 // For localhost: 127.0.0.1, 127.0.0.1
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);
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()));
302 DVLOG(1) << "socket1 close";
303 w1->OnClose(s1.get());
304 s1->DetachDelegate();
306 base::MessageLoopForIO::current()->RunUntilIdle();
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);
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));
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()));
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));
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()));
346 DVLOG(1) << "closing socket1";
347 w1->OnClose(s1.get());
348 s1->DetachDelegate();
350 DVLOG(1) << "closing socket2";
351 w2->OnClose(s2.get());
352 s2->DetachDelegate();
354 base::MessageLoopForIO::current()->RunUntilIdle();