| Index: net/websockets/websocket_channel.cc
|
| diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
|
| index be364141a3989f74e9ab5dd6d3d030e5a0555130..81186117879fdb72426756d5bf7443a0472fcacf 100644
|
| --- a/net/websockets/websocket_channel.cc
|
| +++ b/net/websockets/websocket_channel.cc
|
| @@ -8,6 +8,7 @@
|
|
|
| #include "base/basictypes.h" // for size_t
|
| #include "base/bind.h"
|
| +#include "base/compiler_specific.h"
|
| #include "base/safe_numerics.h"
|
| #include "base/strings/string_util.h"
|
| #include "base/time/time.h"
|
| @@ -31,6 +32,10 @@ const size_t kWebSocketCloseCodeLength = 2;
|
| // MainThreadWebSocketChannel.cpp in Blink.
|
| const int kClosingHandshakeTimeoutSeconds = 2 * 2 * 60;
|
|
|
| +typedef WebSocketEventInterface::ChannelState ChannelState;
|
| +const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE;
|
| +const ChannelState CHANNEL_DELETED = WebSocketEventInterface::CHANNEL_DELETED;
|
| +
|
| // Maximum close reason length = max control frame payload -
|
| // status code length
|
| // = 125 - 2
|
| @@ -62,6 +67,9 @@ bool IsStrictlyValidCloseStatusCode(int code) {
|
| return ((upper - kInvalidRanges) % 2) == 0;
|
| }
|
|
|
| +// This function avoids a bunch of boilerplate code.
|
| +void AllowUnused(ChannelState ALLOW_UNUSED unused) {}
|
| +
|
| } // namespace
|
|
|
| // A class to encapsulate a set of frames and information about the size of
|
| @@ -100,10 +108,12 @@ class WebSocketChannel::ConnectDelegate
|
|
|
| virtual void OnSuccess(scoped_ptr<WebSocketStream> stream) OVERRIDE {
|
| creator_->OnConnectSuccess(stream.Pass());
|
| + // |this| may have been deleted.
|
| }
|
|
|
| virtual void OnFailure(uint16 websocket_error) OVERRIDE {
|
| creator_->OnConnectFailure(websocket_error);
|
| + // |this| has been deleted.
|
| }
|
|
|
| private:
|
| @@ -180,9 +190,10 @@ void WebSocketChannel::SendFrame(bool fin,
|
| return;
|
| }
|
| if (data.size() > base::checked_numeric_cast<size_t>(current_send_quota_)) {
|
| - FailChannel(SEND_GOING_AWAY,
|
| - kWebSocketMuxErrorSendQuotaViolation,
|
| - "Send quota exceeded");
|
| + AllowUnused(FailChannel(SEND_GOING_AWAY,
|
| + kWebSocketMuxErrorSendQuotaViolation,
|
| + "Send quota exceeded"));
|
| + // |this| is deleted here.
|
| return;
|
| }
|
| if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
|
| @@ -199,7 +210,8 @@ void WebSocketChannel::SendFrame(bool fin,
|
| // TODO(ricea): For kOpCodeText, do UTF-8 validation?
|
| scoped_refptr<IOBuffer> buffer(new IOBuffer(data.size()));
|
| std::copy(data.begin(), data.end(), buffer->data());
|
| - SendIOBuffer(fin, op_code, buffer, data.size());
|
| + AllowUnused(SendIOBuffer(fin, op_code, buffer, data.size()));
|
| + // |this| may have been deleted.
|
| }
|
|
|
| void WebSocketChannel::SendFlowControl(int64 quota) {
|
| @@ -228,11 +240,13 @@ void WebSocketChannel::StartClosingHandshake(uint16 code,
|
| // errata 3227 to RFC6455. If the renderer is sending us an invalid code or
|
| // reason it must be malfunctioning in some way, and based on that we
|
| // interpret this as an internal error.
|
| - SendClose(kWebSocketErrorInternalServerError, "Internal Error");
|
| + AllowUnused(
|
| + SendClose(kWebSocketErrorInternalServerError, "Internal Error"));
|
| + // |this| may have been deleted.
|
| return;
|
| }
|
| - // TODO(ricea): Check that |reason| is valid UTF-8.
|
| - SendClose(code, reason); // Sets state_ to SEND_CLOSED
|
| + AllowUnused(SendClose(code, IsStringUTF8(reason) ? reason : std::string()));
|
| + // |this| may have been deleted.
|
| }
|
|
|
| void WebSocketChannel::SendAddChannelRequestForTesting(
|
| @@ -272,41 +286,50 @@ void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
|
| DCHECK_EQ(CONNECTING, state_);
|
| stream_ = stream.Pass();
|
| state_ = CONNECTED;
|
| - event_interface_->OnAddChannelResponse(false, stream_->GetSubProtocol());
|
| + if (event_interface_->OnAddChannelResponse(
|
| + false, stream_->GetSubProtocol()) == CHANNEL_DELETED)
|
| + return;
|
|
|
| // TODO(ricea): Get flow control information from the WebSocketStream once we
|
| // have a multiplexing WebSocketStream.
|
| current_send_quota_ = send_quota_high_water_mark_;
|
| - event_interface_->OnFlowControl(send_quota_high_water_mark_);
|
| + if (event_interface_->OnFlowControl(send_quota_high_water_mark_) ==
|
| + CHANNEL_DELETED)
|
| + return;
|
|
|
| // |stream_request_| is not used once the connection has succeeded.
|
| stream_request_.reset();
|
| - ReadFrames();
|
| + AllowUnused(ReadFrames());
|
| + // |this| may have been deleted.
|
| }
|
|
|
| void WebSocketChannel::OnConnectFailure(uint16 websocket_error) {
|
| DCHECK_EQ(CONNECTING, state_);
|
| state_ = CLOSED;
|
| stream_request_.reset();
|
| - event_interface_->OnAddChannelResponse(true, "");
|
| + AllowUnused(event_interface_->OnAddChannelResponse(true, ""));
|
| + // |this| has been deleted.
|
| }
|
|
|
| -void WebSocketChannel::WriteFrames() {
|
| +ChannelState WebSocketChannel::WriteFrames() {
|
| int result = OK;
|
| do {
|
| // This use of base::Unretained is safe because this object owns the
|
| // WebSocketStream and destroying it cancels all callbacks.
|
| result = stream_->WriteFrames(
|
| data_being_sent_->frames(),
|
| - base::Bind(
|
| - &WebSocketChannel::OnWriteDone, base::Unretained(this), false));
|
| + base::Bind(base::IgnoreResult(&WebSocketChannel::OnWriteDone),
|
| + base::Unretained(this),
|
| + false));
|
| if (result != ERR_IO_PENDING) {
|
| - OnWriteDone(true, result);
|
| + if (OnWriteDone(true, result) == CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| }
|
| } while (result == OK && data_being_sent_);
|
| + return CHANNEL_ALIVE;
|
| }
|
|
|
| -void WebSocketChannel::OnWriteDone(bool synchronous, int result) {
|
| +ChannelState WebSocketChannel::OnWriteDone(bool synchronous, int result) {
|
| DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
|
| DCHECK_NE(CONNECTING, state_);
|
| DCHECK_NE(ERR_IO_PENDING, result);
|
| @@ -315,9 +338,8 @@ void WebSocketChannel::OnWriteDone(bool synchronous, int result) {
|
| case OK:
|
| if (data_to_send_next_) {
|
| data_being_sent_ = data_to_send_next_.Pass();
|
| - if (!synchronous) {
|
| - WriteFrames();
|
| - }
|
| + if (!synchronous)
|
| + return WriteFrames();
|
| } else {
|
| data_being_sent_.reset();
|
| if (current_send_quota_ < send_quota_low_water_mark_) {
|
| @@ -332,10 +354,10 @@ void WebSocketChannel::OnWriteDone(bool synchronous, int result) {
|
| // server, if the protocol in use supports quota.
|
| int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
|
| current_send_quota_ += fresh_quota;
|
| - event_interface_->OnFlowControl(fresh_quota);
|
| + return event_interface_->OnFlowControl(fresh_quota);
|
| }
|
| }
|
| - return;
|
| + return CHANNEL_ALIVE;
|
|
|
| // If a recoverable error condition existed, it would go here.
|
|
|
| @@ -343,16 +365,14 @@ void WebSocketChannel::OnWriteDone(bool synchronous, int result) {
|
| DCHECK_LT(result, 0)
|
| << "WriteFrames() should only return OK or ERR_ codes";
|
| stream_->Close();
|
| - if (state_ != CLOSED) {
|
| - state_ = CLOSED;
|
| - event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
|
| - "Abnormal Closure");
|
| - }
|
| - return;
|
| + DCHECK_NE(CLOSED, state_);
|
| + state_ = CLOSED;
|
| + return event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
|
| + "Abnormal Closure");
|
| }
|
| }
|
|
|
| -void WebSocketChannel::ReadFrames() {
|
| +ChannelState WebSocketChannel::ReadFrames() {
|
| int result = OK;
|
| do {
|
| // This use of base::Unretained is safe because this object owns the
|
| @@ -360,15 +380,19 @@ void WebSocketChannel::ReadFrames() {
|
| // destroyed.
|
| result = stream_->ReadFrames(
|
| &read_frames_,
|
| - base::Bind(
|
| - &WebSocketChannel::OnReadDone, base::Unretained(this), false));
|
| + base::Bind(base::IgnoreResult(&WebSocketChannel::OnReadDone),
|
| + base::Unretained(this),
|
| + false));
|
| if (result != ERR_IO_PENDING) {
|
| - OnReadDone(true, result);
|
| + if (OnReadDone(true, result) == CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| }
|
| - } while (result == OK && state_ != CLOSED);
|
| + DCHECK_NE(CLOSED, state_);
|
| + } while (result == OK);
|
| + return CHANNEL_ALIVE;
|
| }
|
|
|
| -void WebSocketChannel::OnReadDone(bool synchronous, int result) {
|
| +ChannelState WebSocketChannel::OnReadDone(bool synchronous, int result) {
|
| DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
|
| DCHECK_NE(CONNECTING, state_);
|
| DCHECK_NE(ERR_IO_PENDING, result);
|
| @@ -381,74 +405,69 @@ void WebSocketChannel::OnReadDone(bool synchronous, int result) {
|
| for (size_t i = 0; i < read_frames_.size(); ++i) {
|
| scoped_ptr<WebSocketFrame> frame(read_frames_[i]);
|
| read_frames_[i] = NULL;
|
| - ProcessFrame(frame.Pass());
|
| + if (ProcessFrame(frame.Pass()) == CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| }
|
| read_frames_.clear();
|
| // There should always be a call to ReadFrames pending.
|
| // TODO(ricea): Unless we are out of quota.
|
| - if (!synchronous && state_ != CLOSED) {
|
| - ReadFrames();
|
| - }
|
| - return;
|
| + DCHECK_NE(CLOSED, state_);
|
| + if (!synchronous)
|
| + return ReadFrames();
|
| + return CHANNEL_ALIVE;
|
|
|
| case ERR_WS_PROTOCOL_ERROR:
|
| - FailChannel(SEND_REAL_ERROR,
|
| - kWebSocketErrorProtocolError,
|
| - "WebSocket Protocol Error");
|
| - return;
|
| + return FailChannel(SEND_REAL_ERROR,
|
| + kWebSocketErrorProtocolError,
|
| + "WebSocket Protocol Error");
|
|
|
| default:
|
| DCHECK_LT(result, 0)
|
| << "ReadFrames() should only return OK or ERR_ codes";
|
| stream_->Close();
|
| - if (state_ != CLOSED) {
|
| - state_ = CLOSED;
|
| - uint16 code = kWebSocketErrorAbnormalClosure;
|
| - std::string reason = "Abnormal Closure";
|
| - if (closing_code_ != 0) {
|
| - code = closing_code_;
|
| - reason = closing_reason_;
|
| - }
|
| - event_interface_->OnDropChannel(code, reason);
|
| + DCHECK_NE(CLOSED, state_);
|
| + state_ = CLOSED;
|
| + uint16 code = kWebSocketErrorAbnormalClosure;
|
| + std::string reason = "Abnormal Closure";
|
| + if (closing_code_ != 0) {
|
| + code = closing_code_;
|
| + reason = closing_reason_;
|
| }
|
| - return;
|
| + return event_interface_->OnDropChannel(code, reason);
|
| }
|
| }
|
|
|
| -void WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) {
|
| +ChannelState WebSocketChannel::ProcessFrame(scoped_ptr<WebSocketFrame> frame) {
|
| if (frame->header.masked) {
|
| // RFC6455 Section 5.1 "A client MUST close a connection if it detects a
|
| // masked frame."
|
| - FailChannel(SEND_REAL_ERROR,
|
| - kWebSocketErrorProtocolError,
|
| - "Masked frame from server");
|
| - return;
|
| + return FailChannel(SEND_REAL_ERROR,
|
| + kWebSocketErrorProtocolError,
|
| + "Masked frame from server");
|
| }
|
| const WebSocketFrameHeader::OpCode opcode = frame->header.opcode;
|
| if (WebSocketFrameHeader::IsKnownControlOpCode(opcode) &&
|
| !frame->header.final) {
|
| - FailChannel(SEND_REAL_ERROR,
|
| - kWebSocketErrorProtocolError,
|
| - "Control message with FIN bit unset received");
|
| - return;
|
| + return FailChannel(SEND_REAL_ERROR,
|
| + kWebSocketErrorProtocolError,
|
| + "Control message with FIN bit unset received");
|
| }
|
|
|
| // Respond to the frame appropriately to its type.
|
| - HandleFrame(
|
| + return HandleFrame(
|
| opcode, frame->header.final, frame->data, frame->header.payload_length);
|
| }
|
|
|
| -void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode,
|
| - bool final,
|
| - const scoped_refptr<IOBuffer>& data_buffer,
|
| - size_t size) {
|
| +ChannelState WebSocketChannel::HandleFrame(
|
| + const WebSocketFrameHeader::OpCode opcode,
|
| + bool final,
|
| + const scoped_refptr<IOBuffer>& data_buffer,
|
| + size_t size) {
|
| DCHECK_NE(RECV_CLOSED, state_)
|
| << "HandleFrame() does not support being called re-entrantly from within "
|
| "SendClose()";
|
| - if (state_ == CLOSED || state_ == CLOSE_WAIT) {
|
| - DVLOG_IF(1, state_ == CLOSED) << "A frame was received while in the CLOSED "
|
| - "state. This is possible after a channel "
|
| - "failed, but should be very rare.";
|
| + DCHECK_NE(CLOSED, state_);
|
| + if (state_ == CLOSE_WAIT) {
|
| std::string frame_name;
|
| switch (opcode) {
|
| case WebSocketFrameHeader::kOpCodeText: // fall-thru
|
| @@ -475,10 +494,9 @@ void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode,
|
| }
|
| // SEND_REAL_ERROR makes no difference here, as FailChannel() won't send
|
| // another Close frame.
|
| - FailChannel(SEND_REAL_ERROR,
|
| - kWebSocketErrorProtocolError,
|
| - frame_name + " received after close");
|
| - return;
|
| + return FailChannel(SEND_REAL_ERROR,
|
| + kWebSocketErrorProtocolError,
|
| + frame_name + " received after close");
|
| }
|
| switch (opcode) {
|
| case WebSocketFrameHeader::kOpCodeText: // fall-thru
|
| @@ -497,26 +515,23 @@ void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode,
|
| // cause of receiving very large chunks.
|
|
|
| // Sends the received frame to the renderer process.
|
| - event_interface_->OnDataFrame(final, opcode, data);
|
| - } else {
|
| - VLOG(3) << "Ignored data packet received in state " << state_;
|
| + return event_interface_->OnDataFrame(final, opcode, data);
|
| }
|
| - return;
|
| + VLOG(3) << "Ignored data packet received in state " << state_;
|
| + return CHANNEL_ALIVE;
|
|
|
| case WebSocketFrameHeader::kOpCodePing:
|
| VLOG(1) << "Got Ping of size " << size;
|
| - if (state_ == CONNECTED) {
|
| - SendIOBuffer(
|
| + if (state_ == CONNECTED)
|
| + return SendIOBuffer(
|
| true, WebSocketFrameHeader::kOpCodePong, data_buffer, size);
|
| - } else {
|
| - VLOG(3) << "Ignored ping in state " << state_;
|
| - }
|
| - return;
|
| + VLOG(3) << "Ignored ping in state " << state_;
|
| + return CHANNEL_ALIVE;
|
|
|
| case WebSocketFrameHeader::kOpCodePong:
|
| VLOG(1) << "Got Pong of size " << size;
|
| // There is no need to do anything with pong messages.
|
| - return;
|
| + return CHANNEL_ALIVE;
|
|
|
| case WebSocketFrameHeader::kOpCodeClose: {
|
| uint16 code = kWebSocketNormalClosure;
|
| @@ -528,17 +543,16 @@ void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode,
|
| switch (state_) {
|
| case CONNECTED:
|
| state_ = RECV_CLOSED;
|
| - SendClose(code, reason); // Sets state_ to CLOSE_WAIT
|
| - event_interface_->OnClosingHandshake();
|
| + if (SendClose(code, reason) == // Sets state_ to CLOSE_WAIT
|
| + CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| + if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| closing_code_ = code;
|
| closing_reason_ = reason;
|
| break;
|
|
|
| case SEND_CLOSED:
|
| - // Give them another few minutes to actually close the connection.
|
| - DCHECK(timer_.IsRunning());
|
| - timer_.Reset();
|
| -
|
| state_ = CLOSE_WAIT;
|
| // From RFC6455 section 7.1.5: "Each endpoint
|
| // will see the status code sent by the other end as _The WebSocket
|
| @@ -551,20 +565,20 @@ void WebSocketChannel::HandleFrame(const WebSocketFrameHeader::OpCode opcode,
|
| LOG(DFATAL) << "Got Close in unexpected state " << state_;
|
| break;
|
| }
|
| - return;
|
| + return CHANNEL_ALIVE;
|
| }
|
|
|
| default:
|
| - FailChannel(
|
| + return FailChannel(
|
| SEND_REAL_ERROR, kWebSocketErrorProtocolError, "Unknown opcode");
|
| - return;
|
| }
|
| }
|
|
|
| -void WebSocketChannel::SendIOBuffer(bool fin,
|
| - WebSocketFrameHeader::OpCode op_code,
|
| - const scoped_refptr<IOBuffer>& buffer,
|
| - size_t size) {
|
| +ChannelState WebSocketChannel::SendIOBuffer(
|
| + bool fin,
|
| + WebSocketFrameHeader::OpCode op_code,
|
| + const scoped_refptr<IOBuffer>& buffer,
|
| + size_t size) {
|
| DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
|
| DCHECK(stream_);
|
| scoped_ptr<WebSocketFrame> frame(new WebSocketFrame(op_code));
|
| @@ -581,20 +595,20 @@ void WebSocketChannel::SendIOBuffer(bool fin,
|
| if (!data_to_send_next_)
|
| data_to_send_next_.reset(new SendBuffer);
|
| data_to_send_next_->AddFrame(frame.Pass());
|
| - } else {
|
| - data_being_sent_.reset(new SendBuffer);
|
| - data_being_sent_->AddFrame(frame.Pass());
|
| - WriteFrames();
|
| + return CHANNEL_ALIVE;
|
| }
|
| + data_being_sent_.reset(new SendBuffer);
|
| + data_being_sent_->AddFrame(frame.Pass());
|
| + return WriteFrames();
|
| }
|
|
|
| -void WebSocketChannel::FailChannel(ExposeError expose,
|
| - uint16 code,
|
| - const std::string& reason) {
|
| +ChannelState WebSocketChannel::FailChannel(ExposeError expose,
|
| + uint16 code,
|
| + const std::string& reason) {
|
| DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
|
| DCHECK_NE(CONNECTING, state_);
|
| + DCHECK_NE(CLOSED, state_);
|
| // TODO(ricea): Logging.
|
| - State old_state = state_;
|
| if (state_ == CONNECTED) {
|
| uint16 send_code = kWebSocketErrorGoingAway;
|
| std::string send_reason = "Internal Error";
|
| @@ -602,7 +616,9 @@ void WebSocketChannel::FailChannel(ExposeError expose,
|
| send_code = code;
|
| send_reason = reason;
|
| }
|
| - SendClose(send_code, send_reason); // Sets state_ to SEND_CLOSED
|
| + if (SendClose(send_code, send_reason) == // Sets state_ to SEND_CLOSED
|
| + CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| }
|
| // Careful study of RFC6455 section 7.1.7 and 7.1.1 indicates the browser
|
| // should close the connection itself without waiting for the closing
|
| @@ -610,12 +626,11 @@ void WebSocketChannel::FailChannel(ExposeError expose,
|
| stream_->Close();
|
| state_ = CLOSED;
|
|
|
| - if (old_state != CLOSED) {
|
| - event_interface_->OnDropChannel(code, reason);
|
| - }
|
| + return event_interface_->OnDropChannel(code, reason);
|
| }
|
|
|
| -void WebSocketChannel::SendClose(uint16 code, const std::string& reason) {
|
| +ChannelState WebSocketChannel::SendClose(uint16 code,
|
| + const std::string& reason) {
|
| DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
|
| DCHECK_LE(reason.size(), kMaximumCloseReasonLength);
|
| scoped_refptr<IOBuffer> body;
|
| @@ -640,8 +655,13 @@ void WebSocketChannel::SendClose(uint16 code, const std::string& reason) {
|
| FROM_HERE,
|
| timeout_,
|
| base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
|
| - SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size);
|
| + if (SendIOBuffer(true, WebSocketFrameHeader::kOpCodeClose, body, size) ==
|
| + CHANNEL_DELETED)
|
| + return CHANNEL_DELETED;
|
| + // SendIOBuffer() checks |state_|, so it is best not to change it until after
|
| + // SendIOBuffer() returns.
|
| state_ = (state_ == CONNECTED) ? SEND_CLOSED : CLOSE_WAIT;
|
| + return CHANNEL_ALIVE;
|
| }
|
|
|
| void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer,
|
| @@ -674,8 +694,8 @@ void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer,
|
| *code = kWebSocketErrorAbnormalClosure;
|
| }
|
| std::string text(data + kWebSocketCloseCodeLength, data + size);
|
| - // TODO(ricea): Is this check strict enough? In particular, check the
|
| - // "Security Considerations" from RFC3629.
|
| + // IsStringUTF8() blocks surrogate pairs and non-characters, so it is strictly
|
| + // stronger than required by RFC3629.
|
| if (IsStringUTF8(text)) {
|
| reason->swap(text);
|
| }
|
| @@ -683,13 +703,11 @@ void WebSocketChannel::ParseClose(const scoped_refptr<IOBuffer>& buffer,
|
|
|
| void WebSocketChannel::CloseTimeout() {
|
| stream_->Close();
|
| - // TODO(ricea): This becomes a DCHECK() once
|
| - // https://codereview.chromium.org/26544003/ has landed.
|
| - if (state_ != CLOSED) {
|
| - state_ = CLOSED;
|
| - event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
|
| - "Abnormal Closure");
|
| - }
|
| + DCHECK_NE(CLOSED, state_);
|
| + state_ = CLOSED;
|
| + AllowUnused(event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
|
| + "Abnormal Closure"));
|
| + // |this| has been deleted.
|
| }
|
|
|
| } // namespace net
|
|
|