Chromium Code Reviews| 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 // This one is tricky because OnClose() sets WebSocketJob::_socket to NULL, | |
| 1224 // so detaching it from within DeletingSocketStreamDelegate::DeleteJobMaybe() | |
| 1225 // doesn't work. Detach it first to prevent a DCHECK() failure. | |
|
tyoshino (SeeGerritForStatus)
2014/04/03 05:21:56
Which DCHECK?
Adam Rice
2014/04/03 06:03:04
socket_stream.cc:328
I have clarified the comment
| |
| 1226 socket_->DetachDelegate(); | |
| 1227 job()->OnClose(socket_.get()); | |
| 1228 } | |
| 1229 | |
| 1230 TEST_F(WebSocketJobDeleteTest, OnAuthRequired) { | |
| 1231 SetDeleteNext(); | |
| 1232 job()->OnAuthRequired(socket_.get(), NULL); | |
| 1233 } | |
| 1234 | |
| 1235 TEST_F(WebSocketJobDeleteTest, OnSSLCertificateError) { | |
| 1236 SSLInfo ssl_info; | |
| 1237 SetDeleteNext(); | |
| 1238 job()->OnSSLCertificateError(socket_.get(), ssl_info, true); | |
| 1239 } | |
| 1240 | |
| 1241 TEST_F(WebSocketJobDeleteTest, OnError) { | |
| 1242 SetDeleteNext(); | |
| 1243 job()->OnError(socket_.get(), ERR_CONNECTION_RESET); | |
| 1244 } | |
| 1245 | |
| 1246 TEST_F(WebSocketJobDeleteTest, OnSentSpdyHeaders) { | |
| 1247 job()->Connect(); | |
| 1248 SetDeleteNext(); | |
| 1249 job()->OnSentSpdyHeaders(); | |
| 1250 } | |
| 1251 | |
| 1252 TEST_F(WebSocketJobDeleteTest, OnSentHandshakeRequest) { | |
| 1253 static const char kMinimalRequest[] = | |
| 1254 "GET /demo HTTP/1.1\r\n" | |
| 1255 "Host: example.com\r\n" | |
| 1256 "Upgrade: WebSocket\r\n" | |
| 1257 "Connection: Upgrade\r\n" | |
| 1258 "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n" | |
| 1259 "Origin: http://example.com\r\n" | |
| 1260 "Sec-WebSocket-Version: 13\r\n" | |
| 1261 "\r\n"; | |
| 1262 const size_t kMinimalRequestSize = arraysize(kMinimalRequest) - 1; | |
| 1263 job()->Connect(); | |
| 1264 job()->SendData(kMinimalRequest, kMinimalRequestSize); | |
| 1265 SetDeleteNext(); | |
| 1266 job()->OnSentData(socket_.get(), kMinimalRequestSize); | |
| 1267 } | |
| 1268 | |
| 1269 TEST_F(WebSocketJobDeleteTest, NotifyHeadersComplete) { | |
| 1270 static const char kMinimalResponse[] = | |
| 1271 "HTTP/1.1 101 Switching Protocols\r\n" | |
| 1272 "Upgrade: websocket\r\n" | |
| 1273 "Connection: Upgrade\r\n" | |
| 1274 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n" | |
| 1275 "\r\n"; | |
| 1276 job()->Connect(); | |
| 1277 SetDeleteNext(); | |
| 1278 job()->OnReceivedData( | |
| 1279 socket_.get(), kMinimalResponse, arraysize(kMinimalResponse) - 1); | |
| 1280 } | |
| 1281 | |
|
tyoshino (SeeGerritForStatus)
2014/04/03 05:21:56
let's check that delegate_->job() is NULL at the e
Adam Rice
2014/04/03 06:03:04
Done.
| |
| 1125 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. | 1282 // TODO(toyoshim): Add tests to verify throttling, SPDY stream limitation. |
| 1126 // TODO(toyoshim,yutak): Add tests to verify closing handshake. | 1283 // TODO(toyoshim,yutak): Add tests to verify closing handshake. |
| 1127 } // namespace net | 1284 } // namespace net |
| OLD | NEW |