Index: net/websockets/websocket_job.cc |
diff --git a/net/websockets/websocket_job.cc b/net/websockets/websocket_job.cc |
index 77fc0582304cef9499f3bc6f46fac3161f97fc12..b627a0710962fc62f6b04808bfabc2b3a9d6021a 100644 |
--- a/net/websockets/websocket_job.cc |
+++ b/net/websockets/websocket_job.cc |
@@ -24,6 +24,8 @@ |
#include "net/websockets/websocket_net_log_params.h" |
#include "net/websockets/websocket_throttle.h" |
+static const int kMaxPendingSendAllowed = 32768; // 32 kilobytes. |
+ |
namespace { |
// lower-case header names. |
@@ -178,14 +180,14 @@ int WebSocketJob::OnStartOpenConnection( |
state_ = CONNECTING; |
addresses_ = socket->address_list(); |
WebSocketThrottle::GetInstance()->PutInQueue(this); |
- if (!waiting_) { |
- int result = TrySpdyStream(); |
- if (result != ERR_IO_PENDING) |
- return result; |
+ if (waiting_) { |
+ // PutInQueue() may set |waiting_| true for throttling. In this case, |
+ // Wakeup() will be called later. |
+ callback_ = callback; |
+ AddRef(); // Balanced when callback_ becomes NULL. |
+ return ERR_IO_PENDING; |
} |
- callback_ = callback; |
- AddRef(); // Balanced when callback_ becomes NULL. |
- return ERR_IO_PENDING; // Wakeup will be called later. |
+ return TrySpdyStream(); |
} |
void WebSocketJob::OnConnected( |
@@ -276,10 +278,75 @@ void WebSocketJob::OnAuthRequired( |
} |
void WebSocketJob::OnError(const SocketStream* socket, int error) { |
- if (delegate_) |
+ if (delegate_ && error != ERR_PROTOCOL_SWITCHED) |
delegate_->OnError(socket, error); |
} |
+void WebSocketJob::OnCreatedSpdyStream(int result) { |
+ DCHECK(spdy_websocket_stream_.get()); |
+ DCHECK(socket_.get()); |
+ DCHECK_NE(ERR_IO_PENDING, result); |
+ |
+ if (state_ == CLOSED) { |
+ result = ERR_ABORTED; |
+ } else if (result == OK) { |
+ state_ = CONNECTING; |
+ result = ERR_PROTOCOL_SWITCHED; |
+ } else { |
+ spdy_websocket_stream_.reset(); |
+ } |
+ |
+ CompleteIO(result); |
+} |
+ |
+void WebSocketJob::OnSentSpdyHeaders(int result) { |
+ DCHECK_NE(INITIALIZED, state_); |
+ if (state_ != CONNECTING) |
+ return; |
+ if (delegate_) |
+ delegate_->OnSentData(socket_, handshake_request_->original_length()); |
+ handshake_request_.reset(); |
+} |
+ |
+int WebSocketJob::OnReceivedSpdyResponseHeader( |
+ const spdy::SpdyHeaderBlock& headers, int status) { |
+ DCHECK_NE(INITIALIZED, state_); |
+ if (state_ != CONNECTING) |
+ return status; |
+ if (status != OK) |
+ return status; |
+ // TODO(toyoshim): Fallback to non-spdy connection? |
+ handshake_response_->ParseResponseHeaderBlock(headers, challenge_); |
+ |
+ SaveCookiesAndNotifyHeaderComplete(); |
+ return OK; |
+} |
+ |
+void WebSocketJob::OnSentSpdyData(int amount_sent) { |
+ DCHECK_NE(INITIALIZED, state_); |
+ DCHECK_NE(CONNECTING, state_); |
+ if (state_ == CLOSED) |
+ return; |
+ if (!spdy_websocket_stream_.get()) |
+ return; |
+ OnSentData(socket_, amount_sent); |
+} |
+ |
+void WebSocketJob::OnReceivedSpdyData(const char* data, int length) { |
+ DCHECK_NE(INITIALIZED, state_); |
+ DCHECK_NE(CONNECTING, state_); |
+ if (state_ == CLOSED) |
+ return; |
+ if (!spdy_websocket_stream_.get()) |
+ return; |
+ OnReceivedData(socket_, data, length); |
+} |
+ |
+void WebSocketJob::OnCloseSpdyStream() { |
+ spdy_websocket_stream_.reset(); |
+ OnClose(socket_); |
+} |
+ |
bool WebSocketJob::SendHandshakeRequest(const char* data, int len) { |
DCHECK_EQ(state_, CONNECTING); |
if (started_to_send_handshake_request_) |
@@ -317,14 +384,22 @@ void WebSocketJob::AddCookieHeaderAndSend() { |
} |
} |
- const std::string& handshake_request = handshake_request_->GetRawRequest(); |
- handshake_request_sent_ = 0; |
- socket_->net_log()->AddEvent( |
- NetLog::TYPE_WEB_SOCKET_SEND_REQUEST_HEADERS, |
- make_scoped_refptr( |
- new NetLogWebSocketHandshakeParameter(handshake_request))); |
- socket_->SendData(handshake_request.data(), |
- handshake_request.size()); |
+ if (spdy_websocket_stream_.get()) { |
+ linked_ptr<spdy::SpdyHeaderBlock> headers(new spdy::SpdyHeaderBlock); |
+ handshake_request_->GetRequestHeaderBlock( |
+ socket_->url(), headers.get(), &challenge_); |
+ spdy_websocket_stream_->SendRequest(headers); |
+ } else { |
+ const std::string& handshake_request = |
+ handshake_request_->GetRawRequest(); |
+ handshake_request_sent_ = 0; |
+ socket_->net_log()->AddEvent( |
+ NetLog::TYPE_WEB_SOCKET_SEND_REQUEST_HEADERS, |
+ make_scoped_refptr( |
+ new NetLogWebSocketHandshakeParameter(handshake_request))); |
+ socket_->SendData(handshake_request.data(), |
+ handshake_request.size()); |
+ } |
} |
} |
@@ -452,27 +527,48 @@ int WebSocketJob::TrySpdyStream() { |
if (!socket_.get()) |
return ERR_FAILED; |
- if (websocket_over_spdy_enabled_) { |
- // Check if we have a SPDY session available. |
- // If so, use it to create the websocket stream. |
- HttpTransactionFactory* factory = |
- socket_->context()->http_transaction_factory(); |
- if (factory) { |
- scoped_refptr<HttpNetworkSession> session = factory->GetSession(); |
- if (session.get()) { |
- SpdySessionPool* spdy_pool = session->spdy_session_pool(); |
- const HostPortProxyPair pair(HostPortPair::FromURL(socket_->url()), |
- socket_->proxy_server()); |
- if (spdy_pool->HasSession(pair)) { |
- // TODO(toyoshim): Switch to SpdyWebSocketStream here by returning |
- // ERR_PROTOCOL_SWITCHED. |
- } |
- } |
- } |
+ if (!websocket_over_spdy_enabled_) |
+ return OK; |
+ |
+ // Check if we have a SPDY session available. |
+ HttpTransactionFactory* factory = |
+ socket_->context()->http_transaction_factory(); |
+ if (!factory) |
+ return OK; |
+ scoped_refptr<HttpNetworkSession> session = factory->GetSession(); |
+ if (!session.get()) |
+ return OK; |
+ SpdySessionPool* spdy_pool = session->spdy_session_pool(); |
+ const HostPortProxyPair pair(HostPortPair::FromURL(socket_->url()), |
+ socket_->proxy_server()); |
+ if (!spdy_pool->HasSession(pair)) |
+ return OK; |
+ |
+ // Forbid wss downgrade to SPDY without SSL. |
+ // TODO(toyoshim): Does it realize the same policy with HTTP? |
+ scoped_refptr<SpdySession> spdy_session = |
+ spdy_pool->Get(pair, *socket_->net_log()); |
+ SSLInfo ssl_info; |
+ bool was_npn_negotiated; |
+ bool use_ssl = spdy_session->GetSSLInfo(&ssl_info, &was_npn_negotiated); |
+ if (socket_->is_secure() && !use_ssl) |
+ return OK; |
+ |
+ // Create SpdyWebSocketStream. |
+ spdy_websocket_stream_.reset(new SpdyWebSocketStream(spdy_session, this)); |
+ |
+ int result = spdy_websocket_stream_->InitializeStream( |
+ socket_->url(), MEDIUM, *socket_->net_log()); |
+ if (result == OK) { |
+ OnConnected(socket_, kMaxPendingSendAllowed); |
+ return ERR_PROTOCOL_SWITCHED; |
} |
- // No SPDY session was available. |
- // Fallback to connecting a new socket. |
- return OK; |
+ if (result != ERR_IO_PENDING) { |
+ spdy_websocket_stream_.reset(); |
+ return OK; |
+ } |
+ |
+ return ERR_IO_PENDING; |
} |
void WebSocketJob::SetWaiting() { |
@@ -495,6 +591,14 @@ void WebSocketJob::Wakeup() { |
void WebSocketJob::RetryPendingIO() { |
int result = TrySpdyStream(); |
+ |
+ // In the case of ERR_IO_PENDING, CompleteIO() will be called from |
+ // OnCreatedSpdyStream(). |
+ if (result != ERR_IO_PENDING) |
+ CompleteIO(result); |
+} |
+ |
+void WebSocketJob::CompleteIO(int result) { |
// |callback_| may be NULL if OnClose() or DetachDelegate() was called. |
if (callback_) { |
net::CompletionCallback* callback = callback_; |
@@ -505,12 +609,14 @@ void WebSocketJob::RetryPendingIO() { |
} |
bool WebSocketJob::SendDataInternal(const char* data, int length) { |
- // TODO(toyoshim): Call protocol specific SendData(). |
+ if (spdy_websocket_stream_.get()) |
+ return ERR_IO_PENDING == spdy_websocket_stream_->SendData(data, length); |
return socket_->SendData(data, length); |
} |
void WebSocketJob::CloseInternal() { |
- // TODO(toyoshim): Call protocol specific Close(). |
+ if (spdy_websocket_stream_.get()) |
+ spdy_websocket_stream_->Close(); |
socket_->Close(); |
} |