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