| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
| 10 #include "base/string_tokenizer.h" | 10 #include "base/string_tokenizer.h" |
| 11 #include "googleurl/src/gurl.h" | 11 #include "googleurl/src/gurl.h" |
| 12 #include "net/base/net_errors.h" | 12 #include "net/base/net_errors.h" |
| 13 #include "net/base/net_log.h" | 13 #include "net/base/net_log.h" |
| 14 #include "net/base/cookie_store.h" | 14 #include "net/base/cookie_store.h" |
| 15 #include "net/base/io_buffer.h" | 15 #include "net/base/io_buffer.h" |
| 16 #include "net/http/http_network_session.h" | 16 #include "net/http/http_network_session.h" |
| 17 #include "net/http/http_transaction_factory.h" | 17 #include "net/http/http_transaction_factory.h" |
| 18 #include "net/http/http_util.h" | 18 #include "net/http/http_util.h" |
| 19 #include "net/spdy/spdy_session.h" | 19 #include "net/spdy/spdy_session.h" |
| 20 #include "net/spdy/spdy_session_pool.h" | 20 #include "net/spdy/spdy_session_pool.h" |
| 21 #include "net/url_request/url_request_context.h" | 21 #include "net/url_request/url_request_context.h" |
| 22 #include "net/websockets/websocket_frame_handler.h" | 22 #include "net/websockets/websocket_frame_handler.h" |
| 23 #include "net/websockets/websocket_handshake_handler.h" | 23 #include "net/websockets/websocket_handshake_handler.h" |
| 24 #include "net/websockets/websocket_net_log_params.h" | 24 #include "net/websockets/websocket_net_log_params.h" |
| 25 #include "net/websockets/websocket_throttle.h" | 25 #include "net/websockets/websocket_throttle.h" |
| 26 | 26 |
| 27 static const int kMaxPendingSendAllowed = 32768; // 32 kilobytes. |
| 28 |
| 27 namespace { | 29 namespace { |
| 28 | 30 |
| 29 // lower-case header names. | 31 // lower-case header names. |
| 30 const char* const kCookieHeaders[] = { | 32 const char* const kCookieHeaders[] = { |
| 31 "cookie", "cookie2" | 33 "cookie", "cookie2" |
| 32 }; | 34 }; |
| 33 const char* const kSetCookieHeaders[] = { | 35 const char* const kSetCookieHeaders[] = { |
| 34 "set-cookie", "set-cookie2" | 36 "set-cookie", "set-cookie2" |
| 35 }; | 37 }; |
| 36 | 38 |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 171 Release(); // Balanced with OnStartOpenConnection(). | 173 Release(); // Balanced with OnStartOpenConnection(). |
| 172 } | 174 } |
| 173 } | 175 } |
| 174 | 176 |
| 175 int WebSocketJob::OnStartOpenConnection( | 177 int WebSocketJob::OnStartOpenConnection( |
| 176 SocketStream* socket, CompletionCallback* callback) { | 178 SocketStream* socket, CompletionCallback* callback) { |
| 177 DCHECK(!callback_); | 179 DCHECK(!callback_); |
| 178 state_ = CONNECTING; | 180 state_ = CONNECTING; |
| 179 addresses_ = socket->address_list(); | 181 addresses_ = socket->address_list(); |
| 180 WebSocketThrottle::GetInstance()->PutInQueue(this); | 182 WebSocketThrottle::GetInstance()->PutInQueue(this); |
| 181 if (!waiting_) { | 183 if (waiting_) { |
| 182 int result = TrySpdyStream(); | 184 // PutInQueue() may set |waiting_| true for throttling. In this case, |
| 183 if (result != ERR_IO_PENDING) | 185 // Wakeup() will be called later. |
| 184 return result; | 186 callback_ = callback; |
| 187 AddRef(); // Balanced when callback_ becomes NULL. |
| 188 return ERR_IO_PENDING; |
| 185 } | 189 } |
| 186 callback_ = callback; | 190 return TrySpdyStream(); |
| 187 AddRef(); // Balanced when callback_ becomes NULL. | |
| 188 return ERR_IO_PENDING; // Wakeup will be called later. | |
| 189 } | 191 } |
| 190 | 192 |
| 191 void WebSocketJob::OnConnected( | 193 void WebSocketJob::OnConnected( |
| 192 SocketStream* socket, int max_pending_send_allowed) { | 194 SocketStream* socket, int max_pending_send_allowed) { |
| 193 if (state_ == CLOSED) | 195 if (state_ == CLOSED) |
| 194 return; | 196 return; |
| 195 DCHECK_EQ(CONNECTING, state_); | 197 DCHECK_EQ(CONNECTING, state_); |
| 196 if (delegate_) | 198 if (delegate_) |
| 197 delegate_->OnConnected(socket, max_pending_send_allowed); | 199 delegate_->OnConnected(socket, max_pending_send_allowed); |
| 198 } | 200 } |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 delegate->OnClose(socket); | 271 delegate->OnClose(socket); |
| 270 } | 272 } |
| 271 | 273 |
| 272 void WebSocketJob::OnAuthRequired( | 274 void WebSocketJob::OnAuthRequired( |
| 273 SocketStream* socket, AuthChallengeInfo* auth_info) { | 275 SocketStream* socket, AuthChallengeInfo* auth_info) { |
| 274 if (delegate_) | 276 if (delegate_) |
| 275 delegate_->OnAuthRequired(socket, auth_info); | 277 delegate_->OnAuthRequired(socket, auth_info); |
| 276 } | 278 } |
| 277 | 279 |
| 278 void WebSocketJob::OnError(const SocketStream* socket, int error) { | 280 void WebSocketJob::OnError(const SocketStream* socket, int error) { |
| 281 if (delegate_ && error != ERR_PROTOCOL_SWITCHED) |
| 282 delegate_->OnError(socket, error); |
| 283 } |
| 284 |
| 285 void WebSocketJob::OnCreatedSpdyStream(int result) { |
| 286 DCHECK(spdy_websocket_stream_.get()); |
| 287 DCHECK(socket_.get()); |
| 288 DCHECK_NE(ERR_IO_PENDING, result); |
| 289 |
| 290 if (state_ == CLOSED) { |
| 291 result = ERR_ABORTED; |
| 292 } else if (result == OK) { |
| 293 state_ = CONNECTING; |
| 294 result = ERR_PROTOCOL_SWITCHED; |
| 295 } else { |
| 296 spdy_websocket_stream_.reset(); |
| 297 } |
| 298 |
| 299 CompleteIO(result); |
| 300 } |
| 301 |
| 302 void WebSocketJob::OnSentSpdyHeaders(int result) { |
| 303 DCHECK_NE(INITIALIZED, state_); |
| 304 if (state_ != CONNECTING) |
| 305 return; |
| 279 if (delegate_) | 306 if (delegate_) |
| 280 delegate_->OnError(socket, error); | 307 delegate_->OnSentData(socket_, handshake_request_->original_length()); |
| 308 handshake_request_.reset(); |
| 309 } |
| 310 |
| 311 int WebSocketJob::OnReceivedSpdyResponseHeader( |
| 312 const spdy::SpdyHeaderBlock& headers, int status) { |
| 313 DCHECK_NE(INITIALIZED, state_); |
| 314 if (state_ != CONNECTING) |
| 315 return status; |
| 316 if (status != OK) |
| 317 return status; |
| 318 // TODO(toyoshim): Fallback to non-spdy connection? |
| 319 handshake_response_->ParseResponseHeaderBlock(headers, challenge_); |
| 320 |
| 321 SaveCookiesAndNotifyHeaderComplete(); |
| 322 return OK; |
| 323 } |
| 324 |
| 325 void WebSocketJob::OnSentSpdyData(int amount_sent) { |
| 326 DCHECK_NE(INITIALIZED, state_); |
| 327 DCHECK_NE(CONNECTING, state_); |
| 328 if (state_ == CLOSED) |
| 329 return; |
| 330 if (!spdy_websocket_stream_.get()) |
| 331 return; |
| 332 OnSentData(socket_, amount_sent); |
| 333 } |
| 334 |
| 335 void WebSocketJob::OnReceivedSpdyData(const char* data, int length) { |
| 336 DCHECK_NE(INITIALIZED, state_); |
| 337 DCHECK_NE(CONNECTING, state_); |
| 338 if (state_ == CLOSED) |
| 339 return; |
| 340 if (!spdy_websocket_stream_.get()) |
| 341 return; |
| 342 OnReceivedData(socket_, data, length); |
| 343 } |
| 344 |
| 345 void WebSocketJob::OnCloseSpdyStream() { |
| 346 spdy_websocket_stream_.reset(); |
| 347 OnClose(socket_); |
| 281 } | 348 } |
| 282 | 349 |
| 283 bool WebSocketJob::SendHandshakeRequest(const char* data, int len) { | 350 bool WebSocketJob::SendHandshakeRequest(const char* data, int len) { |
| 284 DCHECK_EQ(state_, CONNECTING); | 351 DCHECK_EQ(state_, CONNECTING); |
| 285 if (started_to_send_handshake_request_) | 352 if (started_to_send_handshake_request_) |
| 286 return false; | 353 return false; |
| 287 if (!handshake_request_->ParseRequest(data, len)) | 354 if (!handshake_request_->ParseRequest(data, len)) |
| 288 return false; | 355 return false; |
| 289 | 356 |
| 290 // handshake message is completed. | 357 // handshake message is completed. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 310 CookieOptions cookie_options; | 377 CookieOptions cookie_options; |
| 311 cookie_options.set_include_httponly(); | 378 cookie_options.set_include_httponly(); |
| 312 std::string cookie = | 379 std::string cookie = |
| 313 socket_->context()->cookie_store()->GetCookiesWithOptions( | 380 socket_->context()->cookie_store()->GetCookiesWithOptions( |
| 314 GetURLForCookies(), cookie_options); | 381 GetURLForCookies(), cookie_options); |
| 315 if (!cookie.empty()) | 382 if (!cookie.empty()) |
| 316 handshake_request_->AppendHeaderIfMissing("Cookie", cookie); | 383 handshake_request_->AppendHeaderIfMissing("Cookie", cookie); |
| 317 } | 384 } |
| 318 } | 385 } |
| 319 | 386 |
| 320 const std::string& handshake_request = handshake_request_->GetRawRequest(); | 387 if (spdy_websocket_stream_.get()) { |
| 321 handshake_request_sent_ = 0; | 388 linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); |
| 322 socket_->net_log()->AddEvent( | 389 handshake_request_->GetRequestHeaderBlock( |
| 323 NetLog::TYPE_WEB_SOCKET_SEND_REQUEST_HEADERS, | 390 socket_->url(), headers.get(), &challenge_); |
| 324 make_scoped_refptr( | 391 spdy_websocket_stream_->SendRequest(headers); |
| 325 new NetLogWebSocketHandshakeParameter(handshake_request))); | 392 } else { |
| 326 socket_->SendData(handshake_request.data(), | 393 const std::string& handshake_request = |
| 327 handshake_request.size()); | 394 handshake_request_->GetRawRequest(); |
| 395 handshake_request_sent_ = 0; |
| 396 socket_->net_log()->AddEvent( |
| 397 NetLog::TYPE_WEB_SOCKET_SEND_REQUEST_HEADERS, |
| 398 make_scoped_refptr( |
| 399 new NetLogWebSocketHandshakeParameter(handshake_request))); |
| 400 socket_->SendData(handshake_request.data(), |
| 401 handshake_request.size()); |
| 402 } |
| 328 } | 403 } |
| 329 } | 404 } |
| 330 | 405 |
| 331 void WebSocketJob::OnSentHandshakeRequest( | 406 void WebSocketJob::OnSentHandshakeRequest( |
| 332 SocketStream* socket, int amount_sent) { | 407 SocketStream* socket, int amount_sent) { |
| 333 DCHECK_EQ(state_, CONNECTING); | 408 DCHECK_EQ(state_, CONNECTING); |
| 334 handshake_request_sent_ += amount_sent; | 409 handshake_request_sent_ += amount_sent; |
| 335 DCHECK_LE(handshake_request_sent_, handshake_request_->raw_length()); | 410 DCHECK_LE(handshake_request_sent_, handshake_request_->raw_length()); |
| 336 if (handshake_request_sent_ >= handshake_request_->raw_length()) { | 411 if (handshake_request_sent_ >= handshake_request_->raw_length()) { |
| 337 // handshake request has been sent. | 412 // handshake request has been sent. |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 445 } | 520 } |
| 446 | 521 |
| 447 const AddressList& WebSocketJob::address_list() const { | 522 const AddressList& WebSocketJob::address_list() const { |
| 448 return addresses_; | 523 return addresses_; |
| 449 } | 524 } |
| 450 | 525 |
| 451 int WebSocketJob::TrySpdyStream() { | 526 int WebSocketJob::TrySpdyStream() { |
| 452 if (!socket_.get()) | 527 if (!socket_.get()) |
| 453 return ERR_FAILED; | 528 return ERR_FAILED; |
| 454 | 529 |
| 455 if (websocket_over_spdy_enabled_) { | 530 if (!websocket_over_spdy_enabled_) |
| 456 // Check if we have a SPDY session available. | 531 return OK; |
| 457 // If so, use it to create the websocket stream. | 532 |
| 458 HttpTransactionFactory* factory = | 533 // Check if we have a SPDY session available. |
| 459 socket_->context()->http_transaction_factory(); | 534 HttpTransactionFactory* factory = |
| 460 if (factory) { | 535 socket_->context()->http_transaction_factory(); |
| 461 scoped_refptr<HttpNetworkSession> session = factory->GetSession(); | 536 if (!factory) |
| 462 if (session.get()) { | 537 return OK; |
| 463 SpdySessionPool* spdy_pool = session->spdy_session_pool(); | 538 scoped_refptr<HttpNetworkSession> session = factory->GetSession(); |
| 464 const HostPortProxyPair pair(HostPortPair::FromURL(socket_->url()), | 539 if (!session.get()) |
| 465 socket_->proxy_server()); | 540 return OK; |
| 466 if (spdy_pool->HasSession(pair)) { | 541 SpdySessionPool* spdy_pool = session->spdy_session_pool(); |
| 467 // TODO(toyoshim): Switch to SpdyWebSocketStream here by returning | 542 const HostPortProxyPair pair(HostPortPair::FromURL(socket_->url()), |
| 468 // ERR_PROTOCOL_SWITCHED. | 543 socket_->proxy_server()); |
| 469 } | 544 if (!spdy_pool->HasSession(pair)) |
| 470 } | 545 return OK; |
| 471 } | 546 |
| 547 // Forbid wss downgrade to SPDY without SSL. |
| 548 // TODO(toyoshim): Does it realize the same policy with HTTP? |
| 549 scoped_refptr<SpdySession> spdy_session = |
| 550 spdy_pool->Get(pair, *socket_->net_log()); |
| 551 SSLInfo ssl_info; |
| 552 bool was_npn_negotiated; |
| 553 bool use_ssl = spdy_session->GetSSLInfo(&ssl_info, &was_npn_negotiated); |
| 554 if (socket_->is_secure() && !use_ssl) |
| 555 return OK; |
| 556 |
| 557 // Create SpdyWebSocketStream. |
| 558 spdy_websocket_stream_.reset(new SpdyWebSocketStream(spdy_session, this)); |
| 559 |
| 560 int result = spdy_websocket_stream_->InitializeStream( |
| 561 socket_->url(), MEDIUM, *socket_->net_log()); |
| 562 if (result == OK) { |
| 563 OnConnected(socket_, kMaxPendingSendAllowed); |
| 564 return ERR_PROTOCOL_SWITCHED; |
| 472 } | 565 } |
| 473 // No SPDY session was available. | 566 if (result != ERR_IO_PENDING) { |
| 474 // Fallback to connecting a new socket. | 567 spdy_websocket_stream_.reset(); |
| 475 return OK; | 568 return OK; |
| 569 } |
| 570 |
| 571 return ERR_IO_PENDING; |
| 476 } | 572 } |
| 477 | 573 |
| 478 void WebSocketJob::SetWaiting() { | 574 void WebSocketJob::SetWaiting() { |
| 479 waiting_ = true; | 575 waiting_ = true; |
| 480 } | 576 } |
| 481 | 577 |
| 482 bool WebSocketJob::IsWaiting() const { | 578 bool WebSocketJob::IsWaiting() const { |
| 483 return waiting_; | 579 return waiting_; |
| 484 } | 580 } |
| 485 | 581 |
| 486 void WebSocketJob::Wakeup() { | 582 void WebSocketJob::Wakeup() { |
| 487 if (!waiting_) | 583 if (!waiting_) |
| 488 return; | 584 return; |
| 489 waiting_ = false; | 585 waiting_ = false; |
| 490 DCHECK(callback_); | 586 DCHECK(callback_); |
| 491 MessageLoopForIO::current()->PostTask( | 587 MessageLoopForIO::current()->PostTask( |
| 492 FROM_HERE, | 588 FROM_HERE, |
| 493 NewRunnableMethod(this, &WebSocketJob::RetryPendingIO)); | 589 NewRunnableMethod(this, &WebSocketJob::RetryPendingIO)); |
| 494 } | 590 } |
| 495 | 591 |
| 496 void WebSocketJob::RetryPendingIO() { | 592 void WebSocketJob::RetryPendingIO() { |
| 497 int result = TrySpdyStream(); | 593 int result = TrySpdyStream(); |
| 594 |
| 595 // In the case of ERR_IO_PENDING, CompleteIO() will be called from |
| 596 // OnCreatedSpdyStream(). |
| 597 if (result != ERR_IO_PENDING) |
| 598 CompleteIO(result); |
| 599 } |
| 600 |
| 601 void WebSocketJob::CompleteIO(int result) { |
| 498 // |callback_| may be NULL if OnClose() or DetachDelegate() was called. | 602 // |callback_| may be NULL if OnClose() or DetachDelegate() was called. |
| 499 if (callback_) { | 603 if (callback_) { |
| 500 net::CompletionCallback* callback = callback_; | 604 net::CompletionCallback* callback = callback_; |
| 501 callback_ = NULL; | 605 callback_ = NULL; |
| 502 callback->Run(result); | 606 callback->Run(result); |
| 503 Release(); // Balanced with OnStartOpenConnection(). | 607 Release(); // Balanced with OnStartOpenConnection(). |
| 504 } | 608 } |
| 505 } | 609 } |
| 506 | 610 |
| 507 bool WebSocketJob::SendDataInternal(const char* data, int length) { | 611 bool WebSocketJob::SendDataInternal(const char* data, int length) { |
| 508 // TODO(toyoshim): Call protocol specific SendData(). | 612 if (spdy_websocket_stream_.get()) |
| 613 return ERR_IO_PENDING == spdy_websocket_stream_->SendData(data, length); |
| 509 return socket_->SendData(data, length); | 614 return socket_->SendData(data, length); |
| 510 } | 615 } |
| 511 | 616 |
| 512 void WebSocketJob::CloseInternal() { | 617 void WebSocketJob::CloseInternal() { |
| 513 // TODO(toyoshim): Call protocol specific Close(). | 618 if (spdy_websocket_stream_.get()) |
| 619 spdy_websocket_stream_->Close(); |
| 514 socket_->Close(); | 620 socket_->Close(); |
| 515 } | 621 } |
| 516 | 622 |
| 517 void WebSocketJob::SendPending() { | 623 void WebSocketJob::SendPending() { |
| 518 if (current_buffer_) | 624 if (current_buffer_) |
| 519 return; | 625 return; |
| 520 // Current buffer is done. Try next buffer if any. | 626 // Current buffer is done. Try next buffer if any. |
| 521 // Don't buffer sending data. See comment on case OPEN in SendData(). | 627 // Don't buffer sending data. See comment on case OPEN in SendData(). |
| 522 if (send_frame_handler_->UpdateCurrentBuffer(false) <= 0) { | 628 if (send_frame_handler_->UpdateCurrentBuffer(false) <= 0) { |
| 523 // No more data to send. | 629 // No more data to send. |
| 524 if (state_ == CLOSING) | 630 if (state_ == CLOSING) |
| 525 CloseInternal(); | 631 CloseInternal(); |
| 526 return; | 632 return; |
| 527 } | 633 } |
| 528 current_buffer_ = new DrainableIOBuffer( | 634 current_buffer_ = new DrainableIOBuffer( |
| 529 send_frame_handler_->GetCurrentBuffer(), | 635 send_frame_handler_->GetCurrentBuffer(), |
| 530 send_frame_handler_->GetCurrentBufferSize()); | 636 send_frame_handler_->GetCurrentBufferSize()); |
| 531 SendDataInternal(current_buffer_->data(), current_buffer_->BytesRemaining()); | 637 SendDataInternal(current_buffer_->data(), current_buffer_->BytesRemaining()); |
| 532 } | 638 } |
| 533 | 639 |
| 534 } // namespace net | 640 } // namespace net |
| OLD | NEW |