diff options
author | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-02 03:29:37 +0000 |
---|---|---|
committer | ukai@chromium.org <ukai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-02 03:29:37 +0000 |
commit | b43f3e46472b8dc8b68d115c629f264da80e0eec (patch) | |
tree | b3467fae1da3f90c14a8dc9d58470f220657fcbd /net/websockets | |
parent | 64d3d791df850a8ed083febcdf8170b1e8d0bbe5 (diff) | |
download | chromium_src-b43f3e46472b8dc8b68d115c629f264da80e0eec.zip chromium_src-b43f3e46472b8dc8b68d115c629f264da80e0eec.tar.gz chromium_src-b43f3e46472b8dc8b68d115c629f264da80e0eec.tar.bz2 |
Implement closing handshake described in draft-hixie-thewebsocketprotocol-76.
exchange closing frame (0xFF 0x00) to close the WebSocket connection cleanly.
Land it again with eliminating LOG(ERROR), because it got arm build fail
by relocation overflow in relocation 10 at line 167 (LOG(ERROR) line)
in net/base/keygen_handler_nss.cc
TBR=rohitrao,jar
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/1605007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43442 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/websockets')
-rw-r--r-- | net/websockets/websocket.cc | 117 | ||||
-rw-r--r-- | net/websockets/websocket.h | 13 |
2 files changed, 126 insertions, 4 deletions
diff --git a/net/websockets/websocket.cc b/net/websockets/websocket.cc index 1cc3cf9..cf90fa7 100644 --- a/net/websockets/websocket.cc +++ b/net/websockets/websocket.cc @@ -13,6 +13,9 @@ namespace net { +static const char kClosingFrame[2] = {'\xff', '\x00'}; +static int64 kClosingHandshakeTimeout = 1000; // msec. + WebSocket::WebSocket(Request* request, WebSocketDelegate* delegate) : ready_state_(INITIALIZED), request_(request), @@ -23,7 +26,12 @@ WebSocket::WebSocket(Request* request, WebSocketDelegate* delegate) max_pending_send_allowed_(0), current_read_buf_(NULL), read_consumed_len_(0), - current_write_buf_(NULL) { + current_write_buf_(NULL), + server_closing_handshake_(false), + client_closing_handshake_(false), + closing_handshake_started_(false), + force_close_task_(NULL), + closing_handshake_timeout_(kClosingHandshakeTimeout) { DCHECK(request_.get()); DCHECK(delegate_); DCHECK(origin_loop_); @@ -56,6 +64,13 @@ void WebSocket::Connect() { } void WebSocket::Send(const std::string& msg) { + if (ready_state_ == CLOSED) { + return; + } + if (client_closing_handshake_) { + // We must not send any data after we start the WebSocket closing handshake. + return; + } DCHECK(ready_state_ == OPEN); DCHECK(MessageLoop::current() == origin_loop_); @@ -76,18 +91,31 @@ void WebSocket::Close() { ready_state_ = CLOSED; return; } - if (ready_state_ != CLOSED) { + if (ready_state_ == CLOSED) + return; + if (request_->version() == DRAFT75) { DCHECK(socket_stream_); socket_stream_->Close(); return; } + origin_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &WebSocket::StartClosingHandshake)); } void WebSocket::DetachDelegate() { if (!delegate_) return; delegate_ = NULL; - Close(); + if (ready_state_ == INITIALIZED) { + DCHECK(!socket_stream_); + ready_state_ = CLOSED; + return; + } + if (ready_state_ != CLOSED) { + DCHECK(socket_stream_); + socket_stream_->Close(); + } } void WebSocket::OnConnected(SocketStream* socket_stream, @@ -160,8 +188,27 @@ void WebSocket::SendPending() { DCHECK(MessageLoop::current() == origin_loop_); DCHECK(socket_stream_); if (!current_write_buf_) { - if (pending_write_bufs_.empty()) + if (pending_write_bufs_.empty()) { + if (client_closing_handshake_) { + // Already sent 0xFF and 0x00 bytes. + // *The WebSocket closing handshake has started.* + closing_handshake_started_ = true; + if (server_closing_handshake_) { + // 4.2 3-8-3 If the WebSocket connection is not already closed, + // then close the WebSocket connection. + // *The WebSocket closing handshake has finished* + socket_stream_->Close(); + } else { + // 5. Wait a user-agent-determined length of time, or until the + // WebSocket connection is closed. + force_close_task_ = + NewRunnableMethod(this, &WebSocket::DoForceCloseConnection); + origin_loop_->PostDelayedTask( + FROM_HERE, force_close_task_, closing_handshake_timeout_); + } + } return; + } current_write_buf_ = new DrainableIOBuffer( pending_write_bufs_.front(), pending_write_bufs_.front()->size()); } @@ -175,6 +222,7 @@ void WebSocket::SendPending() { void WebSocket::DoReceivedData() { DCHECK(MessageLoop::current() == origin_loop_); + scoped_refptr<WebSocket> protect(this); switch (ready_state_) { case CONNECTING: { @@ -218,6 +266,11 @@ void WebSocket::DoReceivedData() { void WebSocket::ProcessFrameData() { DCHECK(current_read_buf_); + if (server_closing_handshake_) { + // Any data on the connection after the 0xFF frame is discarded. + return; + } + scoped_refptr<WebSocket> protect(this); const char* start_frame = current_read_buf_->StartOfBuffer() + read_consumed_len_; const char* next_frame = start_frame; @@ -246,6 +299,28 @@ void WebSocket::ProcessFrameData() { if (p + length < end) { p += length; next_frame = p; + if (request_->version() != DRAFT75 && + frame_byte == 0xFF && length == 0) { + // 4.2 Data framing 3. Handle the /frame type/ byte. + // 8. If the /frame type/ is 0xFF and the /length/ was 0, then + // run the following substeps: + // 1. If the WebSocket closing handshake has not yet started, then + // start the WebSocket closing handshake. + server_closing_handshake_ = true; + if (!closing_handshake_started_) { + origin_loop_->PostTask( + FROM_HERE, + NewRunnableMethod(this, &WebSocket::StartClosingHandshake)); + } else { + // If the WebSocket closing handshake has been started and + // the WebSocket connection is not already closed, then close + // the WebSocket connection. + socket_stream_->Close(); + } + return; + } + // 4.2 3-8 Otherwise, let /error/ be true. + // TODO(ukai): report error event. } else { break; } @@ -302,8 +377,42 @@ void WebSocket::SkipReadBuffer(int len) { } } +void WebSocket::StartClosingHandshake() { + // 4.2 *start the WebSocket closing handshake*. + if (closing_handshake_started_ || client_closing_handshake_) { + // 1. If the WebSocket closing handshake has started, then abort these + // steps. + return; + } + // 2.,3. Send a 0xFF and 0x00 byte to the server. + client_closing_handshake_ = true; + IOBufferWithSize* buf = new IOBufferWithSize(2); + memcpy(buf->data(), kClosingFrame, 2); + pending_write_bufs_.push_back(buf); + SendPending(); +} + +void WebSocket::DoForceCloseConnection() { + // 4.2 *start the WebSocket closing handshake* + // 6. If the WebSocket connection is not already closed, then close the + // WebSocket connection. (If this happens, then the closing handshake + // doesn't finish.) + DCHECK(MessageLoop::current() == origin_loop_); + force_close_task_ = NULL; + if (!socket_stream_) + return; + socket_stream_->Close(); + // TODO(ukai): report error close here? +} + void WebSocket::DoClose() { DCHECK(MessageLoop::current() == origin_loop_); + if (force_close_task_) { + // WebSocket connection is closed while waiting a user-agent-determined + // length of time after *The WebSocket closing handshake has started*. + force_close_task_->Cancel(); + force_close_task_ = NULL; + } WebSocketDelegate* delegate = delegate_; delegate_ = NULL; ready_state_ = CLOSED; diff --git a/net/websockets/websocket.h b/net/websockets/websocket.h index 427af56..006362b 100644 --- a/net/websockets/websocket.h +++ b/net/websockets/websocket.h @@ -174,6 +174,8 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, // Skips |len| bytes in |current_read_buf_|. void SkipReadBuffer(int len); + void StartClosingHandshake(); + void DoForceCloseConnection(); // Handles closed connection. void DoClose(); @@ -205,6 +207,17 @@ class WebSocket : public base::RefCountedThreadSafe<WebSocket>, // Front IOBuffer is being sent via |current_write_buf_|. PendingDataQueue pending_write_bufs_; + // True when the 0xFF frame with length 0x00 is received. + bool server_closing_handshake_; + // True when trying to send 0xFF and 0x00 bytes. + bool client_closing_handshake_; + // True when send 0xFF and 0x00 bytes. + bool closing_handshake_started_; + // Task to close the connection after closing handshake has started and + // |closing_handshake_timeout_|. + CancelableTask* force_close_task_; + int64 closing_handshake_timeout_; + DISALLOW_COPY_AND_ASSIGN(WebSocket); }; |