OLD | NEW |
| (Empty) |
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 "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 } | |
OLD | NEW |