#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/message_loop/message_loop.h"
+#include "base/metrics/histogram.h"
#include "base/numerics/safe_conversions.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
base::Bind(&WebSocketStream::CreateAndConnectStream));
}
+void WebSocketChannel::SetState(State new_state) {
+ DCHECK_NE(state_, new_state);
+
+ if (new_state == CONNECTED)
+ established_on_ = base::TimeTicks::Now();
+ if (state_ == CONNECTED && !established_on_.is_null()) {
+ UMA_HISTOGRAM_LONG_TIMES(
+ "Net.WebSocket.Duration", base::TimeTicks::Now() - established_on_);
+ }
+
+ state_ = new_state;
+}
+
bool WebSocketChannel::InClosingState() const {
// The state RECV_CLOSED is not supported here, because it is only used in one
// code path and should not leak into the code in general.
return;
}
if (InClosingState()) {
- VLOG(1) << "SendFrame called in state " << state_
- << ". This may be a bug, or a harmless race.";
+ DVLOG(1) << "SendFrame called in state " << state_
+ << ". This may be a bug, or a harmless race.";
return;
}
if (state_ != CONNECTED) {
const size_t bytes_to_send =
std::min(base::checked_cast<size_t>(quota), data_size);
const bool final = front.final() && data_size == bytes_to_send;
- const char* data = front.data()->data() + front.offset();
+ const char* data = front.data() ?
+ front.data()->data() + front.offset() : NULL;
+ DCHECK(!bytes_to_send || data) << "Non empty data should not be null.";
const std::vector<char> data_vector(data, data + bytes_to_send);
DVLOG(3) << "Sending frame previously split due to quota to the "
<< "renderer: quota=" << quota << " data_size=" << data_size
void WebSocketChannel::StartClosingHandshake(uint16 code,
const std::string& reason) {
if (InClosingState()) {
- VLOG(1) << "StartClosingHandshake called in state " << state_
- << ". This may be a bug, or a harmless race.";
+ DVLOG(1) << "StartClosingHandshake called in state " << state_
+ << ". This may be a bug, or a harmless race.";
return;
}
if (state_ == CONNECTING) {
// Abort the in-progress handshake and drop the connection immediately.
stream_request_.reset();
- state_ = CLOSED;
+ SetState(CLOSED);
AllowUnused(DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""));
return;
}
// 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.
- if (SendClose(kWebSocketErrorInternalServerError, "") != CHANNEL_DELETED)
- state_ = SEND_CLOSED;
+ if (SendClose(kWebSocketErrorInternalServerError, "") != CHANNEL_DELETED) {
+ DCHECK_EQ(CONNECTED, state_);
+ SetState(SEND_CLOSED);
+ }
return;
}
if (SendClose(
StreamingUtf8Validator::Validate(reason) ? reason : std::string()) ==
CHANNEL_DELETED)
return;
- state_ = SEND_CLOSED;
+ DCHECK_EQ(CONNECTED, state_);
+ SetState(SEND_CLOSED);
}
void WebSocketChannel::SendAddChannelRequestForTesting(
url_request_context_,
BoundNetLog(),
connect_delegate.Pass());
- state_ = CONNECTING;
+ SetState(CONNECTING);
}
void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
DCHECK(stream);
DCHECK_EQ(CONNECTING, state_);
+
stream_ = stream.Pass();
- state_ = CONNECTED;
+
+ SetState(CONNECTED);
+
if (event_interface_->OnAddChannelResponse(
false, stream_->GetSubProtocol(), stream_->GetExtensions()) ==
CHANNEL_DELETED)
void WebSocketChannel::OnConnectFailure(const std::string& message) {
DCHECK_EQ(CONNECTING, state_);
- state_ = CLOSED;
+
+ SetState(CLOSED);
stream_request_.reset();
if (CHANNEL_DELETED ==
default:
DCHECK_LT(result, 0)
<< "WriteFrames() should only return OK or ERR_ codes";
+
stream_->Close();
- DCHECK_NE(CLOSED, state_);
- state_ = CLOSED;
+ SetState(CLOSED);
return DoDropChannel(false, kWebSocketErrorAbnormalClosure, "");
}
}
default:
DCHECK_LT(result, 0)
<< "ReadFrames() should only return OK or ERR_ codes";
+
stream_->Close();
- DCHECK_NE(CLOSED, state_);
- state_ = CLOSED;
+ SetState(CLOSED);
+
uint16 code = kWebSocketErrorAbnormalClosure;
std::string reason = "";
bool was_clean = false;
reason = received_close_reason_;
was_clean = (result == ERR_CONNECTION_CLOSED);
}
+
return DoDropChannel(was_clean, code, reason);
}
}
return HandleDataFrame(opcode, final, data_buffer, size);
case WebSocketFrameHeader::kOpCodePing:
- VLOG(1) << "Got Ping of size " << size;
+ DVLOG(1) << "Got Ping of size " << size;
if (state_ == CONNECTED)
return SendFrameFromIOBuffer(
true, WebSocketFrameHeader::kOpCodePong, data_buffer, size);
- VLOG(3) << "Ignored ping in state " << state_;
+ DVLOG(3) << "Ignored ping in state " << state_;
return CHANNEL_ALIVE;
case WebSocketFrameHeader::kOpCodePong:
- VLOG(1) << "Got Pong of size " << size;
+ DVLOG(1) << "Got Pong of size " << size;
// There is no need to do anything with pong messages.
return CHANNEL_ALIVE;
}
// TODO(ricea): Find a way to safely log the message from the close
// message (escape control codes and so on).
- VLOG(1) << "Got Close with code " << code;
+ DVLOG(1) << "Got Close with code " << code;
switch (state_) {
case CONNECTED:
- state_ = RECV_CLOSED;
+ SetState(RECV_CLOSED);
if (SendClose(code, reason) == CHANNEL_DELETED)
return CHANNEL_DELETED;
- state_ = CLOSE_WAIT;
+ DCHECK_EQ(RECV_CLOSED, state_);
+ SetState(CLOSE_WAIT);
if (event_interface_->OnClosingHandshake() == CHANNEL_DELETED)
return CHANNEL_DELETED;
break;
case SEND_CLOSED:
- state_ = CLOSE_WAIT;
+ SetState(CLOSE_WAIT);
// From RFC6455 section 7.1.5: "Each endpoint
// will see the status code sent by the other end as _The WebSocket
// Connection Close Code_."
DCHECK_NE(FRESHLY_CONSTRUCTED, state_);
DCHECK_NE(CONNECTING, state_);
DCHECK_NE(CLOSED, state_);
+
// TODO(ricea): Logging.
if (state_ == CONNECTED) {
if (SendClose(code, reason) == 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
// handshake.
stream_->Close();
- state_ = CLOSED;
-
+ SetState(CLOSED);
return event_interface_->OnFailChannel(message);
}
void WebSocketChannel::CloseTimeout() {
stream_->Close();
- DCHECK_NE(CLOSED, state_);
- state_ = CLOSED;
+ SetState(CLOSED);
AllowUnused(DoDropChannel(false, kWebSocketErrorAbnormalClosure, ""));
// |this| has been deleted.
}