| Index: net/websockets/websocket_channel.cc
|
| diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
|
| index 598b5e64363ec1f7953c22a1875cad55c75a4344..30abb2db90fe8eb8c67fa3dc898d26a5a6003b61 100644
|
| --- a/net/websockets/websocket_channel.cc
|
| +++ b/net/websockets/websocket_channel.cc
|
| @@ -44,9 +44,16 @@ using base::StreamingUtf8Validator;
|
| const int kDefaultSendQuotaLowWaterMark = 1 << 16;
|
| const int kDefaultSendQuotaHighWaterMark = 1 << 17;
|
| const size_t kWebSocketCloseCodeLength = 2;
|
| -// This timeout is based on TCPMaximumSegmentLifetime * 2 from
|
| -// MainThreadWebSocketChannel.cpp in Blink.
|
| -const int kClosingHandshakeTimeoutSeconds = 2 * 2 * 60;
|
| +// Timeout for waiting for the server to acknowledge a closing handshake.
|
| +const int kClosingHandshakeTimeoutSeconds = 60;
|
| +// We wait for the server to close the underlying connection as recommended in
|
| +// https://tools.ietf.org/html/rfc6455#section-7.1.1
|
| +// We don't use 2MSL since there're server implementations that don't follow
|
| +// the recommendation and wait for the client to close the underlying
|
| +// connection. It leads to unnecessarily long time before CloseEvent
|
| +// invocation. We want to avoid this rather than strictly following the spec
|
| +// recommendation.
|
| +const int kUnderlyingConnectionCloseTimeoutSeconds = 2;
|
|
|
| typedef WebSocketEventInterface::ChannelState ChannelState;
|
| const ChannelState CHANNEL_ALIVE = WebSocketEventInterface::CHANNEL_ALIVE;
|
| @@ -296,7 +303,10 @@ WebSocketChannel::WebSocketChannel(
|
| send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
|
| current_send_quota_(0),
|
| current_receive_quota_(0),
|
| - timeout_(base::TimeDelta::FromSeconds(kClosingHandshakeTimeoutSeconds)),
|
| + closing_handshake_timeout_(base::TimeDelta::FromSeconds(
|
| + kClosingHandshakeTimeoutSeconds)),
|
| + underlying_connection_close_timeout_(base::TimeDelta::FromSeconds(
|
| + kUnderlyingConnectionCloseTimeoutSeconds)),
|
| has_received_close_frame_(false),
|
| received_close_code_(0),
|
| state_(FRESHLY_CONSTRUCTED),
|
| @@ -312,7 +322,7 @@ WebSocketChannel::~WebSocketChannel() {
|
| stream_.reset();
|
| // The timer may have a callback pointing back to us, so stop it just in case
|
| // someone decides to run the event loop from their destructor.
|
| - timer_.Stop();
|
| + close_timer_.Stop();
|
| }
|
|
|
| void WebSocketChannel::SendAddChannelRequest(
|
| @@ -478,6 +488,15 @@ void WebSocketChannel::StartClosingHandshake(uint16 code,
|
| NOTREACHED() << "StartClosingHandshake() called in state " << state_;
|
| return;
|
| }
|
| +
|
| + DCHECK(!close_timer_.IsRunning());
|
| + // This use of base::Unretained() is safe because we stop the timer in the
|
| + // destructor.
|
| + close_timer_.Start(
|
| + FROM_HERE,
|
| + closing_handshake_timeout_,
|
| + base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
|
| +
|
| // Javascript actually only permits 1000 and 3000-4999, but the implementation
|
| // itself may produce different codes. The length of |reason| is also checked
|
| // by Javascript.
|
| @@ -513,7 +532,12 @@ void WebSocketChannel::SendAddChannelRequestForTesting(
|
|
|
| void WebSocketChannel::SetClosingHandshakeTimeoutForTesting(
|
| base::TimeDelta delay) {
|
| - timeout_ = delay;
|
| + closing_handshake_timeout_ = delay;
|
| +}
|
| +
|
| +void WebSocketChannel::SetUnderlyingConnectionCloseTimeoutForTesting(
|
| + base::TimeDelta delay) {
|
| + underlying_connection_close_timeout_ = delay;
|
| }
|
|
|
| void WebSocketChannel::SendAddChannelRequestWithSuppliedCreator(
|
| @@ -839,10 +863,20 @@ ChannelState WebSocketChannel::HandleFrameByState(
|
| switch (state_) {
|
| case CONNECTED:
|
| SetState(RECV_CLOSED);
|
| +
|
| if (SendClose(code, reason) == CHANNEL_DELETED)
|
| return CHANNEL_DELETED;
|
| DCHECK_EQ(RECV_CLOSED, state_);
|
| +
|
| SetState(CLOSE_WAIT);
|
| + DCHECK(!close_timer_.IsRunning());
|
| + // This use of base::Unretained() is safe because we stop the timer
|
| + // in the destructor.
|
| + close_timer_.Start(
|
| + FROM_HERE,
|
| + underlying_connection_close_timeout_,
|
| + base::Bind(
|
| + &WebSocketChannel::CloseTimeout, base::Unretained(this)));
|
|
|
| if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED)
|
| return CHANNEL_DELETED;
|
| @@ -853,6 +887,16 @@ ChannelState WebSocketChannel::HandleFrameByState(
|
|
|
| case SEND_CLOSED:
|
| SetState(CLOSE_WAIT);
|
| + DCHECK(close_timer_.IsRunning());
|
| + close_timer_.Stop();
|
| + // This use of base::Unretained() is safe because we stop the timer
|
| + // in the destructor.
|
| + close_timer_.Start(
|
| + FROM_HERE,
|
| + underlying_connection_close_timeout_,
|
| + base::Bind(
|
| + &WebSocketChannel::CloseTimeout, base::Unretained(this)));
|
| +
|
| // From RFC6455 section 7.1.5: "Each endpoint
|
| // will see the status code sent by the other end as _The WebSocket
|
| // Connection Close Code_."
|
| @@ -1027,12 +1071,6 @@ ChannelState WebSocketChannel::SendClose(uint16 code,
|
| std::copy(
|
| reason.begin(), reason.end(), body->data() + kWebSocketCloseCodeLength);
|
| }
|
| - // This use of base::Unretained() is safe because we stop the timer in the
|
| - // destructor.
|
| - timer_.Start(
|
| - FROM_HERE,
|
| - timeout_,
|
| - base::Bind(&WebSocketChannel::CloseTimeout, base::Unretained(this)));
|
| if (SendFrameFromIOBuffer(
|
| true, WebSocketFrameHeader::kOpCodeClose, body, size) ==
|
| CHANNEL_DELETED)
|
|
|