Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(296)

Unified Diff: net/websockets/websocket_channel.cc

Issue 12764006: WebSocketChannel implementation (Closed) Base URL: http://git.chromium.org/chromium/src.git@web_socket_dispatcher
Patch Set: Add unit tests and fix bugs. Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/websockets/websocket_channel.cc
diff --git a/net/websockets/websocket_channel.cc b/net/websockets/websocket_channel.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0e90934f7bdc7f42e148a529f8deb8771fc61f16
--- /dev/null
+++ b/net/websockets/websocket_channel.cc
@@ -0,0 +1,553 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/websockets/websocket_channel.h"
+
+#include <algorithm>
+
+#include "base/basictypes.h" // for size_t
+#include "base/bind.h"
+#include "base/string_util.h"
+#include "net/base/io_buffer.h"
+#include "net/http/http_request_info.h"
+#include "net/http/http_stream_factory.h"
+#include "net/ssl/ssl_config_service.h"
+#include "net/websockets/websocket_errors.h"
+#include "net/websockets/websocket_event_interface.h"
+#include "net/websockets/websocket_frame.h"
+#include "net/websockets/websocket_mux.h"
+#include "net/websockets/websocket_stream.h"
+
+namespace net {
+
+namespace {
+
+const int kDefaultSendQuotaLowWaterMark = 1 << 16;
+const int kDefaultSendQuotaHighWaterMark = 1 << 17;
+const size_t kWebSocketErrorNetworkByteLength = 2;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 It's not always error. And the length of the field
Adam Rice 2013/06/27 17:29:49 Done.
+
+union NetworkErrorCode {
+ char code_as_char[kWebSocketErrorNetworkByteLength];
+ unsigned short code_as_network_short;
+};
+
+// A subclass of IOBufferWithSize formed by concatenating two other subclasses
+// of IOBufferWithSize.
+class ConcatenatedIOBufferWithSize : public IOBufferWithSize {
+ public:
+ ConcatenatedIOBufferWithSize(const IOBufferWithSize* part1,
+ const IOBufferWithSize* part2)
tyoshino (SeeGerritForStatus) 2013/06/26 09:40:17 you don't need a new class. you can just have a ut
Adam Rice 2013/06/27 17:29:49 I must have been confused when I wrote this. Chang
+ : IOBufferWithSize(part1->size() + part2->size()) {
+ data_ = new char[size_];
+ std::copy(GetConstData(part1), GetConstData(part1) + part1->size(), data_);
+ std::copy(GetConstData(part2),
+ GetConstData(part2) + part2->size(),
+ data_ + part1->size());
+ }
+
+ private:
+ // We take const pointers as arguments, but IOBuffer doesn't actually have a
+ // const accessor for its data member. So we implement our own.
+ static const char* GetConstData(const IOBufferWithSize* iobuffer) {
+ return const_cast<IOBufferWithSize*>(iobuffer)->data();
+ }
+};
+
+} // namespace
+
+struct WebSocketChannel::SendBuffer {
+ SendBuffer() : frames(), total_bytes(0) {}
+ ScopedVector<WebSocketFrameChunk> frames;
+ size_t total_bytes;
+};
+
+WebSocketChannel::WebSocketChannel(
+ const GURL& socket_url,
+ scoped_ptr<WebSocketEventInterface> event_interface)
+ : socket_url_(socket_url),
+ event_interface_(event_interface.Pass()),
+ currently_sending_(),
+ send_next_(),
+ read_frame_chunks_(),
+ current_frame_header_(),
+ incomplete_control_frame_body_(),
+ send_quota_low_water_mark_(kDefaultSendQuotaLowWaterMark),
+ send_quota_high_water_mark_(kDefaultSendQuotaHighWaterMark),
+ current_send_quota_(0),
+ state_(FRESHLY_CONSTRUCTED),
+ weak_factory_(this) {}
+
+WebSocketChannel::~WebSocketChannel() {
+ // The stream may hold a pointer to read_frame_chunks_, and so it needs to be
+ // destroyed first.
+ stream_.reset();
+}
+
+void WebSocketChannel::SendAddChannelRequest(
+ const std::vector<std::string>& requested_protocols,
+ const GURL& origin,
+ URLRequestContext* url_request_context) {
+ // Delegate to the tested version.
+ SendAddChannelRequestWithFactory(
+ requested_protocols,
+ origin,
+ url_request_context,
+ base::Bind(&WebSocketStream::CreateAndConnectStream));
+}
+
+void WebSocketChannel::SendAddChannelRequestWithFactory(
+ const std::vector<std::string>& requested_protocols,
+ const GURL& origin,
+ URLRequestContext* url_request_context,
+ base::Callback<void(const GURL&,
+ const std::vector<std::string>&,
+ const GURL&,
+ URLRequestContext*,
+ const WebSocketStream::SuccessCallback&,
+ const WebSocketStream::FailureCallback&)> factory) {
+ DCHECK_EQ(FRESHLY_CONSTRUCTED, state_);
+ factory.Run(
+ socket_url_,
+ requested_protocols,
+ origin,
+ url_request_context,
+ base::Bind(&WebSocketChannel::OnConnectSuccess,
+ weak_factory_.GetWeakPtr()),
+ base::Bind(&WebSocketChannel::OnConnectFailure,
+ weak_factory_.GetWeakPtr()));
+ state_ = CONNECTING;
+}
+
+void WebSocketChannel::OnConnectSuccess(scoped_ptr<WebSocketStream> stream) {
+ DCHECK(stream);
+ DCHECK_EQ(CONNECTING, state_);
+ stream_ = stream.Pass();
+ event_interface_->OnAddChannelResponse(false, stream_->Protocol());
+ // TODO(ricea): Get flow control information from the WebSocketStream once we
+ // have a multiplexing WebSocketStream.
+ event_interface_->OnFlowControl(send_quota_high_water_mark_);
+ current_send_quota_ = send_quota_high_water_mark_;
+ state_ = CONNECTED;
+ ReadFrames();
+}
+
+void WebSocketChannel::OnConnectFailure(unsigned short web_socket_error) {
+ DCHECK_EQ(CONNECTING, state_);
+ event_interface_->OnAddChannelResponse(true, "");
+ state_ = CLOSED;
+}
+
+void WebSocketChannel::SendFrame(bool fin,
+ WebSocketFrameHeader::OpCode op_code,
+ const std::vector<char>& data) {
+ if (data.size() > INT_MAX) {
+ DCHECK_LE(data.size(), static_cast<size_t>(INT_MAX));
yhirano 2013/06/26 11:33:29 What is this DCHECK_LE?
Adam Rice 2013/06/27 17:29:49 It will cause a debug build to abort if the frame
yhirano 2013/06/28 06:49:17 How about NOTREACHED?
Adam Rice 2013/06/28 07:59:29 That is much better, thank you.
+ return;
+ }
+ if (stream_ == NULL) {
+ LOG(DFATAL) << "Got SendFrame without a connection established; "
+ << "misbehaving renderer? fin=" << fin << " op_code=" << op_code
+ << " data.size()=" << data.size();
+ return;
+ }
+ if (state_ == SEND_CLOSED || state_ == RECV_CLOSED || state_ == CLOSED) {
+ VLOG(1) << "SendFrame called in state " << state_
+ << ". This may be a bug, or a harmless race.";
+ return;
+ }
+ DCHECK_EQ(CONNECTED, state_);
+ CHECK_GE(current_send_quota_, 0); // Security-critical invariant
+ typedef std::vector<char>::size_type size_type;
+ if (data.size() > static_cast<size_type>(current_send_quota_)) {
+ FailChannel(kWebSocketMuxErrorSendQuotaViolation, "Send quota exceeded");
+ return;
+ }
+ if (!WebSocketFrameHeader::IsKnownDataOpCode(op_code)) {
+ LOG(DFATAL) << "Got SendFrame with bogus op_code " << op_code
+ << "; misbehaving renderer? fin=" << fin
+ << " data.size()=" << data.size();
+ return;
+ }
+ current_send_quota_ -= data.size();
+ // TODO(ricea): If current_send_quota_ has dropped below
+ // send_quota_low_water_mark_, we may want to consider increasing the "low
+ // water mark" and "high water mark", but only if we think we are not
+ // saturating the link to the WebSocket server.
+ // TODO(ricea): For kOpCodeText, do UTF-8 validation?
+ Send(fin, op_code, data);
+}
+
+void WebSocketChannel::Send(bool fin,
+ WebSocketFrameHeader::OpCode op_code,
+ const std::vector<char>& data) {
+ scoped_refptr<IOBufferWithSize> buffer(new IOBufferWithSize(data.size()));
+ std::copy(data.begin(), data.end(), buffer->data());
+ SendIOBufferWithSize(fin, op_code, buffer);
+}
+
+void WebSocketChannel::SendIOBufferWithSize(
+ bool fin,
+ WebSocketFrameHeader::OpCode op_code,
+ const scoped_refptr<IOBufferWithSize>& buffer) {
+ DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
+ DCHECK(stream_);
+ scoped_ptr<WebSocketFrameHeader> header(new WebSocketFrameHeader(op_code));
+ header->final = fin;
+ header->payload_length = buffer->size();
+ scoped_ptr<WebSocketFrameChunk> chunk(new WebSocketFrameChunk());
+ chunk->header = header.Pass();
+ chunk->final_chunk = true;
+ chunk->data = buffer;
+ if (currently_sending_) {
+ // Either the link to the WebSocket server is saturated, or we are simply
+ // processing a batch of messages.
+ // TODO(ricea): We need to keep some statistics to work out which situation
+ // we are in and adjust quota appropriately.
+ if (!send_next_) {
+ send_next_.reset(new SendBuffer);
+ }
+ send_next_->frames.push_back(chunk.release());
+ send_next_->total_bytes += buffer->size();
+ } else {
+ currently_sending_.reset(new SendBuffer);
+ currently_sending_->frames.push_back(chunk.release());
+ currently_sending_->total_bytes += buffer->size();
+ WriteFrames();
+ }
+}
+
+void WebSocketChannel::WriteFrames() {
+ // This is safe because we own the WebSocketStream and destroying it cancels
+ // all callbacks.
+ int result = stream_->WriteFrames(
+ &(currently_sending_->frames),
+ base::Bind(&WebSocketChannel::OnWriteDone, base::Unretained(this)));
+ if (result != ERR_IO_PENDING) {
+ OnWriteDone(result);
+ }
+}
+
+void WebSocketChannel::OnWriteDone(int result) {
+ DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
+ DCHECK_NE(ERR_IO_PENDING, result);
+ DCHECK(currently_sending_);
+ switch (result) {
+ case OK:
+ if (current_send_quota_ < send_quota_low_water_mark_) {
yhirano 2013/06/26 11:33:29 I think this clause should be executed only if sen
Adam Rice 2013/06/27 17:29:49 You're probably right. I have moved it into the if
+ // TODO(ricea): Increase low_water_mark and high_water_mark if
+ // throughput is high, reduce them if throughput is low.
+ // Low water mark needs to be >= the bandwidth delay product *of the IPC
+ // channel*. Because factors like context-switch time, thread wake-up
+ // time, and bus speed come into play it is complex and probably needs
+ // to be determined empirically.
+ DCHECK_LE(send_quota_low_water_mark_, send_quota_high_water_mark_);
+ // TODO(ricea): Truncate quota by the quota specified by the remote
+ // server, if the protocol in use supports quota.
+ int fresh_quota = send_quota_high_water_mark_ - current_send_quota_;
+ event_interface_->OnFlowControl(fresh_quota);
+ current_send_quota_ += fresh_quota;
+ }
+ if (send_next_) {
+ currently_sending_ = send_next_.Pass();
+ WriteFrames();
+ } else {
+ currently_sending_.reset();
+ }
+ break;
+
+ // If a recoverable error condition existed, it would go here.
+
+ default:
+ DCHECK_LT(result, 0)
+ << "WriteFrames() should only return OK or ERR_ codes";
+ stream_->Close();
+ state_ = CLOSED;
+ event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
+ "Abnormal Closure");
+ break;
+ }
+}
+
+void WebSocketChannel::ReadFrames() {
+ // This use if base::Unretained is safe because we own the WebSocketStream,
+ // and any pending reads will be cancelled when it is destroyed.
+ int result = stream_->ReadFrames(
+ &read_frame_chunks_,
+ base::Bind(&WebSocketChannel::OnReadDone, base::Unretained(this)));
+ if (result != ERR_IO_PENDING) {
+ OnReadDone(result);
+ }
+}
+
+void WebSocketChannel::OnReadDone(int result) {
+ DCHECK(state_ != FRESHLY_CONSTRUCTED && state_ != CONNECTING);
+ DCHECK_NE(ERR_IO_PENDING, result);
+ switch (result) {
+ case OK:
+ // ReadFrames() must use ERR_CONNECTION_CLOSED for a closed connection
+ // with no data read, not an empty response.
+ DCHECK(!read_frame_chunks_.empty())
+ << "ReadFrames() returned OK, but nothing was read.";
+ for (size_t i = 0; i < read_frame_chunks_.size(); ++i) {
+ scoped_ptr<WebSocketFrameChunk> chunk(read_frame_chunks_[i]);
+ read_frame_chunks_[i] = NULL;
+ ProcessFrameChunk(chunk.Pass());
+ }
+ read_frame_chunks_.clear();
+ break;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 let's call ReadFrames() and return.
Adam Rice 2013/06/27 17:29:49 Done.
+
+ case ERR_CONNECTION_CLOSED: {
+ State old_state = state_;
+ state_ = CLOSED;
+ if (old_state != RECV_CLOSED && old_state != CLOSED) {
+ // We need to inform the render process of the unexpected closure.
+ event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
+ "Abnormal Closure");
+ }
+ break;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 let's return
Adam Rice 2013/06/27 17:29:49 Done.
+ }
+
+ default: {
+ DCHECK_LT(result, 0)
+ << "ReadFrames() should only return OK or ERR_ codes";
+ stream_->Close();
+ State old_state = state_;
+ state_ = CLOSED;
+ if (old_state != RECV_CLOSED && old_state != CLOSED) {
+ event_interface_->OnDropChannel(kWebSocketErrorAbnormalClosure,
+ "Abnormal Closure");
+ }
+ break;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 let's return
Adam Rice 2013/06/27 17:29:49 Done.
+ }
+ }
+ // We need to always keep a call to ReadFrames pending.
+ if (state_ != CLOSED) {
+ ReadFrames();
+ }
+}
+
+// TODO(ricea): This method is too long. Break it up.
+void WebSocketChannel::ProcessFrameChunk(
+ scoped_ptr<WebSocketFrameChunk> chunk) {
+ scoped_ptr<WebSocketFrameHeader> frame_header;
+ frame_header.swap(current_frame_header_);
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 Write the reason of swap.
Adam Rice 2013/06/27 17:29:49 I added a comment. I hope it helps.
+ bool first_chunk = false;
+ if (chunk->header) {
+ first_chunk = true;
+ frame_header.swap(chunk->header);
+ }
+ scoped_refptr<IOBufferWithSize> data_buffer;
+ data_buffer.swap(chunk->data);
+ if (WebSocketFrameHeader::IsKnownControlOpCode(frame_header->opcode)) {
+ if (chunk->final_chunk) {
+ if (incomplete_control_frame_body_) {
+ VLOG(2) << "Rejoining a split control frame, opcode "
+ << frame_header->opcode;
+ scoped_refptr<IOBufferWithSize> old_data_buffer;
+ old_data_buffer.swap(incomplete_control_frame_body_);
+ scoped_refptr<IOBufferWithSize> new_data_buffer;
+ new_data_buffer.swap(data_buffer);
+ data_buffer = new ConcatenatedIOBufferWithSize(old_data_buffer.get(),
+ new_data_buffer.get());
+ }
+ } else {
+ // TODO(ricea): Enforce a maximum size of 127 bytes on the control frames
+ // we accept.
+ VLOG(2) << "Encountered a split control frame, opcode "
+ << frame_header->opcode;
+ if (incomplete_control_frame_body_) {
+ // The really horrid case. We need to create a new IOBufferWithSize
+ // combining the new one and the old one. This should virtually never
+ // happen.
+ // TODO(ricea): This algorithm is O(N^2). Use a fixed 127-byte byffer
+ // instead.
+ VLOG(3) << "Hit the really horrid case";
+ scoped_refptr<IOBufferWithSize> old_body_;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 not member variable. remove last _
Adam Rice 2013/06/27 17:29:49 I have no idea why I put that _ there. Removed.
+ old_body_.swap(incomplete_control_frame_body_);
+ incomplete_control_frame_body_ = new ConcatenatedIOBufferWithSize(
+ old_body_.get(), data_buffer.get());
+ } else {
+ // The merely horrid case. Store the IOBufferWithSize to use when the
+ // rest of the control frame arrives.
+ incomplete_control_frame_body_.swap(data_buffer);
+ }
+ current_frame_header_.swap(frame_header);
+ return;
+ }
+ }
+
+ WebSocketFrameHeader::OpCode opcode = frame_header->opcode;
+ switch (frame_header->opcode) {
+ case WebSocketFrameHeader::kOpCodeText: // fall-thru
+ case WebSocketFrameHeader::kOpCodeBinary:
+ if (!first_chunk) {
+ opcode = WebSocketFrameHeader::kOpCodeContinuation;
+ }
+ // fall-thru
+ case WebSocketFrameHeader::kOpCodeContinuation: {
+ if (state_ == RECV_CLOSED) {
+ FailChannel(1002, "Data packet received after close");
+ return;
+ }
+ bool final = chunk->final_chunk && frame_header->final;
+ // TODO(ricea): Can this copy be eliminated?
+ char* data_begin = data_buffer->data();
+ char* data_end = data_begin + data_buffer->size();
+ std::vector<char> data(data_begin, data_end);
+ // TODO(ricea): Handle the (improbable) case when ReadFrames returns far
+ // more data at once than we want to send in a single IPC (in which case
+ // we need to buffer the data and return to the event loop with a
+ // callback to send the rest in 32K chunks).
+ event_interface_->OnSendFrame(final, opcode, data);
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 why this is On"Send"?
Adam Rice 2013/06/27 17:29:49 Having received the message, we will send the IPC
tyoshino (SeeGerritForStatus) 2013/06/28 07:55:27 Yes. It's so common convention that OnSomething()
Adam Rice 2013/06/28 08:28:06 Done.
+ break;
+ }
+
+ case WebSocketFrameHeader::kOpCodePing:
+ VLOG(1) << "Got Ping of size " << data_buffer->size();
+ if (state_ == RECV_CLOSED) {
+ FailChannel(1002, "Ping received after Close");
+ return;
+ }
+ SendIOBufferWithSize(
+ true, WebSocketFrameHeader::kOpCodePong, data_buffer);
+ break;
+
+ case WebSocketFrameHeader::kOpCodePong:
+ VLOG(1) << "Got Pong of size " << data_buffer->size();
yhirano 2013/06/26 11:33:29 Can you write a TODO comment that describes how to
Adam Rice 2013/06/27 17:29:49 We don't need to anything with pong messages.
+ if (state_ == RECV_CLOSED) {
+ FailChannel(1002, "Pong received after Close");
+ return;
+ }
+ break;
+
+ case WebSocketFrameHeader::kOpCodeClose: {
+ unsigned short reason;
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 initialize to some default value.
Adam Rice 2013/06/27 17:29:49 Done.
+ std::string reason_text;
+ ParseClose(*data_buffer, &reason, &reason_text);
+ // 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 reason " << reason;
+ switch (state_) {
+ case CONNECTED:
+ state_ = RECV_CLOSED;
+ SendClose(reason, reason_text);
+ event_interface_->OnDropChannel(reason, reason_text);
+ break;
+
+ case RECV_CLOSED:
+ FailChannel(1002, "Close received after Close");
+ break;
+
+ case SEND_CLOSED:
+ state_ = CLOSED;
+ event_interface_->OnDropChannel(reason, reason_text);
+ break;
+
+ default:
+ LOG(DFATAL) << "Got Close in unexpected state " << state_;
+ break;
+ }
+ break;
+ }
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ if (!chunk->final_chunk) {
+ // Preserve the frame header for the next call.
+ current_frame_header_.swap(frame_header);
+ }
+}
+
+void WebSocketChannel::SendFlowControl(int64 quota) {
+ DCHECK_EQ(CONNECTED, state_);
+ // TODO(ricea): Add interface to WebSocketStream and implement.
+ // stream_->SendFlowControl(quota);
+}
+
+void WebSocketChannel::SendDropChannel(unsigned short reason,
+ const std::string& reason_text) {
+ if (state_ == SEND_CLOSED || state_ == CLOSED) {
+ VLOG(1) << "SendDropChannel called in state " << state_
+ << ". This may be a bug, or a harmless race.";
+ return;
+ }
+ DCHECK_EQ(CONNECTED, state_);
+ // TODO(ricea): Validate reason? Check that reason_text is valid UTF-8?
+ // TODO(ricea): There should probably be a timeout for the closing handshake.
+ SendClose(reason, reason_text);
+}
+
+void WebSocketChannel::FailChannel(unsigned short code,
+ const std::string& reason) {
+ // TODO(ricea): Logging.
+ State old_state = state_;
+ if (state_ == CONNECTED) {
+ SendClose(kWebSocketErrorGoingAway, "Internal Error");
+ }
+ if (old_state != RECV_CLOSED && old_state != CLOSED) {
+ event_interface_->OnDropChannel(code, reason);
+ }
+}
+
+void WebSocketChannel::SendClose(unsigned short code,
+ const std::string& reason) {
+ DCHECK(state_ == CONNECTED || state_ == RECV_CLOSED);
+ uint64 payload_length = kWebSocketErrorNetworkByteLength + reason.length();
+ std::vector<char> data(payload_length);
+ NetworkErrorCode network_code;
+ network_code.code_as_network_short = base::HostToNet16(code);
+ std::copy(network_code.code_as_char,
+ network_code.code_as_char + kWebSocketErrorNetworkByteLength,
+ data.begin());
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 how about using net::WriteBigEndian?
Adam Rice 2013/06/27 17:29:49 Done.
+ std::copy(reason.begin(),
+ reason.end(),
+ data.begin() + kWebSocketErrorNetworkByteLength);
+ Send(true, WebSocketFrameHeader::kOpCodeClose, data);
+ // TODO(ricea): This is wrong.
+ state_ = state_ == CONNECTED ? SEND_CLOSED : CLOSED;
+}
+
+void WebSocketChannel::ParseClose(const IOBufferWithSize& buffer,
+ unsigned short* reason,
+ std::string* reason_text) {
+ const char* data = const_cast<IOBufferWithSize&>(buffer).data();
+ CHECK_GE(buffer.size(), 0); // Possibly security-critical invariant.
+ size_t size = static_cast<size_t>(buffer.size());
+ reason_text->clear();
+ if (size < kWebSocketErrorNetworkByteLength) {
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 it works but i'd code this like if (size < kWebSo
Adam Rice 2013/06/27 17:29:49 Done.
+ if (size == 1) {
+ VLOG(1) << "Close frame with 1-byte payload received "
+ << "(the byte is " << std::hex << static_cast<int>(data[0])
+ << ")";
+ }
+ *reason = 1005;
yhirano 2013/06/26 11:33:29 Explaining what 1005 error is in a comment would b
Adam Rice 2013/06/27 17:29:49 I'm not sure why I wrote 1005 instead of just usin
+ return;
+ }
+ NetworkErrorCode network_code;
+ std::copy(
+ data, data + kWebSocketErrorNetworkByteLength, network_code.code_as_char);
+ unsigned short unchecked_reason =
+ base::NetToHost16(network_code.code_as_network_short);
tyoshino (SeeGerritForStatus) 2013/06/26 09:27:06 how about using net::ReadBigEndian?
Adam Rice 2013/06/27 17:29:49 Done.
+ if (unchecked_reason >=
+ static_cast<unsigned short>(kWebSocketNormalClosure) &&
+ unchecked_reason <=
+ static_cast<unsigned short>(kWebSocketErrorPrivateReservedMax)) {
+ *reason = unchecked_reason;
+ } else {
+ VLOG(1) << "Close frame contained reason code outside of the valid range: "
+ << unchecked_reason;
+ *reason = 1008;
+ }
+ std::string text(data + kWebSocketErrorNetworkByteLength, data + size);
+ // TODO(ricea): Is this check strict enough? In particular, check the
+ // "Security Considerations" from RFC3629.
+ if (IsStringUTF8(text)) {
+ using std::swap;
+ swap(*reason_text, text);
+ }
+}
+
+} // namespace net

Powered by Google App Engine
This is Rietveld 408576698