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 |