OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/websockets/websocket_job.h" | 5 #include "net/websockets/websocket_job.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 | 309 |
310 private: | 310 private: |
311 OrderedSocketData* data_; | 311 OrderedSocketData* data_; |
312 scoped_ptr<SpdySessionDependencies> session_deps_; | 312 scoped_ptr<SpdySessionDependencies> session_deps_; |
313 scoped_refptr<HttpNetworkSession> http_session_; | 313 scoped_refptr<HttpNetworkSession> http_session_; |
314 base::WeakPtr<SpdySession> session_; | 314 base::WeakPtr<SpdySession> session_; |
315 HostPortPair host_port_pair_; | 315 HostPortPair host_port_pair_; |
316 SpdySessionKey spdy_session_key_; | 316 SpdySessionKey spdy_session_key_; |
317 }; | 317 }; |
318 | 318 |
| 319 class DeletingSocketStreamDelegate : public SocketStream::Delegate { |
| 320 public: |
| 321 DeletingSocketStreamDelegate() |
| 322 : delete_next_(false) {} |
| 323 |
| 324 // Since this class needs to be able to delete |job_|, it must be the only |
| 325 // reference holder (except for temporary references). Provide access to the |
| 326 // pointer for tests to use. |
| 327 WebSocketJob* job() { return job_.get(); } |
| 328 |
| 329 void set_job(WebSocketJob* job) { job_ = job; } |
| 330 |
| 331 // After calling this, the next call to a method on this delegate will delete |
| 332 // the WebSocketJob object. |
| 333 void set_delete_next(bool delete_next) { delete_next_ = delete_next; } |
| 334 |
| 335 void DeleteJobMaybe() { |
| 336 if (delete_next_) { |
| 337 job_->DetachContext(); |
| 338 job_->DetachDelegate(); |
| 339 job_ = NULL; |
| 340 } |
| 341 } |
| 342 |
| 343 // SocketStream::Delegate implementation |
| 344 |
| 345 // OnStartOpenConnection() is not implemented by SocketStreamDispatcherHost |
| 346 |
| 347 virtual void OnConnected(SocketStream* socket, |
| 348 int max_pending_send_allowed) OVERRIDE { |
| 349 DeleteJobMaybe(); |
| 350 } |
| 351 |
| 352 virtual void OnSentData(SocketStream* socket, int amount_sent) OVERRIDE { |
| 353 DeleteJobMaybe(); |
| 354 } |
| 355 |
| 356 virtual void OnReceivedData(SocketStream* socket, |
| 357 const char* data, |
| 358 int len) OVERRIDE { |
| 359 DeleteJobMaybe(); |
| 360 } |
| 361 |
| 362 virtual void OnClose(SocketStream* socket) OVERRIDE { DeleteJobMaybe(); } |
| 363 |
| 364 virtual void OnAuthRequired(SocketStream* socket, |
| 365 AuthChallengeInfo* auth_info) OVERRIDE { |
| 366 DeleteJobMaybe(); |
| 367 } |
| 368 |
| 369 virtual void OnSSLCertificateError(SocketStream* socket, |
| 370 const SSLInfo& ssl_info, |
| 371 bool fatal) OVERRIDE { |
| 372 DeleteJobMaybe(); |
| 373 } |
| 374 |
| 375 virtual void OnError(const SocketStream* socket, int error) OVERRIDE { |
| 376 DeleteJobMaybe(); |
| 377 } |
| 378 |
| 379 // CanGetCookies() and CanSetCookies() do not appear to be able to delete the |
| 380 // WebSocketJob object. |
| 381 |
| 382 private: |
| 383 scoped_refptr<WebSocketJob> job_; |
| 384 bool delete_next_; |
| 385 }; |
| 386 |
319 } // namespace | 387 } // namespace |
320 | 388 |
321 class WebSocketJobTest : public PlatformTest, | 389 class WebSocketJobTest : public PlatformTest, |
322 public ::testing::WithParamInterface<NextProto> { | 390 public ::testing::WithParamInterface<NextProto> { |
323 public: | 391 public: |
324 WebSocketJobTest() : spdy_util_(GetParam()) {} | 392 WebSocketJobTest() : spdy_util_(GetParam()) {} |
325 | 393 |
326 virtual void SetUp() OVERRIDE { | 394 virtual void SetUp() OVERRIDE { |
327 stream_type_ = STREAM_INVALID; | 395 stream_type_ = STREAM_INVALID; |
328 cookie_store_ = new MockCookieStore; | 396 cookie_store_ = new MockCookieStore; |
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 static const char* const kHandshakeResponseForSpdy[]; | 541 static const char* const kHandshakeResponseForSpdy[]; |
474 static const size_t kHandshakeRequestWithoutCookieLength; | 542 static const size_t kHandshakeRequestWithoutCookieLength; |
475 static const size_t kHandshakeRequestWithCookieLength; | 543 static const size_t kHandshakeRequestWithCookieLength; |
476 static const size_t kHandshakeRequestWithFilteredCookieLength; | 544 static const size_t kHandshakeRequestWithFilteredCookieLength; |
477 static const size_t kHandshakeResponseWithoutCookieLength; | 545 static const size_t kHandshakeResponseWithoutCookieLength; |
478 static const size_t kHandshakeResponseWithCookieLength; | 546 static const size_t kHandshakeResponseWithCookieLength; |
479 static const size_t kDataHelloLength; | 547 static const size_t kDataHelloLength; |
480 static const size_t kDataWorldLength; | 548 static const size_t kDataWorldLength; |
481 }; | 549 }; |
482 | 550 |
| 551 // Tests using this fixture verify that the WebSocketJob can handle being |
| 552 // deleted while calling back to the delegate correctly. These tests need to be |
| 553 // run under AddressSanitizer or other systems for detecting use-after-free |
| 554 // errors in order to find problems. |
| 555 class WebSocketJobDeleteTest : public ::testing::Test { |
| 556 protected: |
| 557 WebSocketJobDeleteTest() |
| 558 : delegate_(new DeletingSocketStreamDelegate), |
| 559 cookie_store_(new MockCookieStore), |
| 560 context_(new MockURLRequestContext(cookie_store_.get())) { |
| 561 WebSocketJob* websocket = new WebSocketJob(delegate_.get()); |
| 562 delegate_->set_job(websocket); |
| 563 |
| 564 socket_ = new MockSocketStream( |
| 565 GURL("ws://127.0.0.1/"), websocket, context_.get(), NULL); |
| 566 |
| 567 websocket->InitSocketStream(socket_.get()); |
| 568 } |
| 569 |
| 570 void SetDeleteNext() { return delegate_->set_delete_next(true); } |
| 571 WebSocketJob* job() { return delegate_->job(); } |
| 572 |
| 573 scoped_ptr<DeletingSocketStreamDelegate> delegate_; |
| 574 scoped_refptr<MockCookieStore> cookie_store_; |
| 575 scoped_ptr<MockURLRequestContext> context_; |
| 576 scoped_refptr<SocketStream> socket_; |
| 577 }; |
| 578 |
483 const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] = | 579 const char WebSocketJobTest::kHandshakeRequestWithoutCookie[] = |
484 "GET /demo HTTP/1.1\r\n" | 580 "GET /demo HTTP/1.1\r\n" |
485 "Host: example.com\r\n" | 581 "Host: example.com\r\n" |
486 "Upgrade: WebSocket\r\n" | 582 "Upgrade: WebSocket\r\n" |
487 "Connection: Upgrade\r\n" | 583 "Connection: Upgrade\r\n" |
488 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" | 584 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" |
489 "Origin: http://example.com\r\n" | 585 "Origin: http://example.com\r\n" |
490 "Sec-WebSocket-Protocol: sample\r\n" | 586 "Sec-WebSocket-Protocol: sample\r\n" |
491 "Sec-WebSocket-Version: 13\r\n" | 587 "Sec-WebSocket-Version: 13\r\n" |
492 "\r\n"; | 588 "\r\n"; |
(...skipping 622 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1115 TEST_P(WebSocketJobTest, ThrottlingSpdy) { | 1211 TEST_P(WebSocketJobTest, ThrottlingSpdy) { |
1116 WebSocketJob::set_websocket_over_spdy_enabled(false); | 1212 WebSocketJob::set_websocket_over_spdy_enabled(false); |
1117 TestConnectBySpdy(SPDY_OFF, THROTTLING_ON); | 1213 TestConnectBySpdy(SPDY_OFF, THROTTLING_ON); |
1118 } | 1214 } |
1119 | 1215 |
1120 TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) { | 1216 TEST_P(WebSocketJobTest, ThrottlingSpdySpdyEnabled) { |
1121 WebSocketJob::set_websocket_over_spdy_enabled(true); | 1217 WebSocketJob::set_websocket_over_spdy_enabled(true); |
1122 TestConnectBySpdy(SPDY_ON, THROTTLING_ON); | 1218 TestConnectBySpdy(SPDY_ON, THROTTLING_ON); |
1123 } | 1219 } |
1124 | 1220 |
| 1221 TEST_F(WebSocketJobDeleteTest, OnClose) { |
| 1222 SetDeleteNext(); |
| 1223 job()->OnClose(socket_.get()); |
| 1224 // OnClose() sets WebSocketJob::_socket to NULL before we can detach it, so |
| 1225 // socket_->delegate is still set at this point. Clear it to avoid hitting |
| 1226 // DCHECK(!delegate_) in the SocketStream destructor. SocketStream::Finish() |
| 1227 // is the only caller of this method in real code, and it also sets delegate_ |
| 1228 // to NULL. |
| 1229 socket_->DetachDelegate(); |
| 1230 EXPECT_FALSE(job()); |
| 1231 } |
| 1232 |
| 1233 TEST_F(WebSocketJobDeleteTest, OnAuthRequired) { |
| 1234 SetDeleteNext(); |
| 1235 job()->OnAuthRequired(socket_.get(), NULL); |
| 1236 EXPECT_FALSE(job()); |
| 1237 } |
| 1238 |
| 1239 TEST_F(WebSocketJobDeleteTest, OnSSLCertificateError) { |
| 1240 SSLInfo ssl_info; |
| 1241 SetDeleteNext(); |
| 1242 job()->OnSSLCertificateError(socket_.get(), ssl_info, true); |
| 1243 EXPECT_FALSE(job()); |
| 1244 } |
| 1245 |
| 1246 TEST_F(WebSocketJobDeleteTest, OnError) { |
| 1247 SetDeleteNext(); |
| 1248 job()->OnError(socket_.get(), ERR_CONNECTION_RESET); |
| 1249 EXPECT_FALSE(job()); |
| 1250 } |
| 1251 |
| 1252 TEST_F(WebSocketJobDeleteTest, OnSentSpdyHeaders) { |
| 1253 job()->Connect(); |
| 1254 SetDeleteNext(); |
| 1255 job()->OnSentSpdyHeaders(); |
| 1256 EXPECT_FALSE(job()); |
| 1257 } |
| 1258 |
| 1259 TEST_F(WebSocketJobDeleteTest, OnSentHandshakeRequest) { |
| 1260 static const char kMinimalRequest[] = |
| 1261 "GET /demo HTTP/1.1\r\n" |
| 1262 "Host: example.com\r\n" |
| 1263 "Upgrade: WebSocket\r\n" |
| 1264 "Connection: Upgrade\r\n" |
| 1265 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" |
| 1266 "Origin: http://example.com\r\n" |
| 1267 "Sec-WebSocket-Version: 13\r\n" |
| 1268 "\r\n"; |
| 1269 const size_t kMinimalRequestSize = arraysize(kMinimalRequest) - 1; |
| 1270 job()->Connect(); |
| 1271 job()->SendData(kMinimalRequest, kMinimalRequestSize); |
| 1272 SetDeleteNext(); |
| 1273 job()->OnSentData(socket_.get(), kMinimalRequestSize); |
| 1274 EXPECT_FALSE(job()); |
| 1275 } |
| 1276 |
| 1277 TEST_F(WebSocketJobDeleteTest, NotifyHeadersComplete) { |
| 1278 static const char kMinimalResponse[] = |
| 1279 "HTTP/1.1 101 Switching Protocols\r\n" |
| 1280 "Upgrade: websocket\r\n" |
| 1281 "Connection: Upgrade\r\n" |
| 1282 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" |
| 1283 "\r\n"; |
| 1284 job()->Connect(); |
| 1285 SetDeleteNext(); |
| 1286 job()->OnReceivedData( |
| 1287 socket_.get(), kMinimalResponse, arraysize(kMinimalResponse) - 1); |
| 1288 EXPECT_FALSE(job()); |
| 1289 } |
| 1290 |
1125 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. | 1291 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. |
1126 // TODO(toyoshim,yutak): Add tests to verify closing handshake. | 1292 // TODO(toyoshim,yutak): Add tests to verify closing handshake. |
1127 } // namespace net | 1293 } // namespace net |
OLD | NEW |