| Index: net/server/web_socket_frame.cc
|
| diff --git a/net/server/web_socket_frame.cc b/net/server/web_socket_frame.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..db4666636cb8bfc7a34b29bd297e86a905832f58
|
| --- /dev/null
|
| +++ b/net/server/web_socket_frame.cc
|
| @@ -0,0 +1,143 @@
|
| +// Copyright (c) 2011 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/server/web_socket_frame.h"
|
| +
|
| +#include "base/rand_util.h"
|
| +#include "base/logging.h"
|
| +
|
| +#include <limits>
|
| +
|
| +namespace net {
|
| +
|
| +// Constants for hybi-10 frame format.
|
| +const WebSocketFrame::OpCode WebSocketFrame::kOpCodeContinuation = 0x0;
|
| +const WebSocketFrame::OpCode WebSocketFrame::kOpCodeText = 0x1;
|
| +const WebSocketFrame::OpCode WebSocketFrame::kOpCodeBinary = 0x2;
|
| +const WebSocketFrame::OpCode WebSocketFrame::kOpCodeClose = 0x8;
|
| +const WebSocketFrame::OpCode WebSocketFrame::kOpCodePing = 0x9;
|
| +const WebSocketFrame::OpCode WebSocketFrame::kOpCodePong = 0xA;
|
| +
|
| +const unsigned char WebSocketFrame::kFinalBit = 0x80;
|
| +const unsigned char WebSocketFrame::kReserved1Bit = 0x40;
|
| +const unsigned char WebSocketFrame::kReserved2Bit = 0x20;
|
| +const unsigned char WebSocketFrame::kReserved3Bit = 0x10;
|
| +const unsigned char WebSocketFrame::kOpCodeMask = 0xF;
|
| +const unsigned char WebSocketFrame::kMaskBit = 0x80;
|
| +const unsigned char WebSocketFrame::kPayloadLengthMask = 0x7F;
|
| +
|
| +const size_t WebSocketFrame::kMaxSingleBytePayloadLength = 125;
|
| +const size_t WebSocketFrame::kTwoBytePayloadLengthField = 126;
|
| +const size_t WebSocketFrame::kEightBytePayloadLengthField = 127;
|
| +const size_t WebSocketFrame::kMaskingKeyWidthInBytes = 4;
|
| +
|
| +WebSocketFrame::ParseResult WebSocketFrame::DecodeFrame(
|
| + const char* data, size_t data_length) {
|
| + const char* p = data;
|
| + const char* buffer_end = data + data_length;
|
| +
|
| + if (data_length < 2)
|
| + return FrameIncomplete;
|
| +
|
| + unsigned char first_byte = *p++;
|
| + unsigned char second_byte = *p++;
|
| +
|
| + final_ = first_byte & kFinalBit;
|
| + reserved1_ = first_byte & kReserved1Bit;
|
| + reserved2_ = first_byte & kReserved2Bit;
|
| + reserved3_ = first_byte & kReserved3Bit;
|
| + op_code_ = first_byte & kOpCodeMask;
|
| + masked_ = second_byte & kMaskBit;
|
| +
|
| + uint64_t payload_length64 = second_byte & kPayloadLengthMask;
|
| + if (payload_length64 > kMaxSingleBytePayloadLength) {
|
| + int extended_payload_length_size;
|
| + if (payload_length64 == kTwoBytePayloadLengthField)
|
| + extended_payload_length_size = 2;
|
| + else {
|
| + DCHECK(payload_length64 == kEightBytePayloadLengthField);
|
| + extended_payload_length_size = 8;
|
| + }
|
| + if (buffer_end - p < extended_payload_length_size)
|
| + return FrameIncomplete;
|
| + payload_length64 = 0;
|
| + for (int i = 0; i < extended_payload_length_size; ++i) {
|
| + payload_length64 <<= 8;
|
| + payload_length64 |= static_cast<unsigned char>(*p++);
|
| + }
|
| + }
|
| +
|
| + static const uint64_t max_payload_ength = 0x7FFFFFFFFFFFFFFFull;
|
| + size_t masking_key_length = masked_ ? kMaskingKeyWidthInBytes : 0;
|
| + static size_t max_length = std::numeric_limits<size_t>::max();
|
| + if (payload_length64 > max_payload_ength ||
|
| + payload_length64 + masking_key_length > max_length) {
|
| + // WebSocket frame length too large.
|
| + return FrameError;
|
| + }
|
| + payload_length_ = static_cast<size_t>(payload_length64);
|
| +
|
| + size_t total_length = masking_key_length + payload_length_;
|
| + if (static_cast<size_t>(buffer_end - p) < total_length)
|
| + return FrameIncomplete;
|
| +
|
| + if (masked_) {
|
| + const char* masking_key = p;
|
| + char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
|
| + for (size_t i = 0; i < payload_length_; ++i) // Unmask the payload.
|
| + payload[i] ^= masking_key[i % kMaskingKeyWidthInBytes];
|
| + }
|
| +
|
| + payload_ = p + masking_key_length;
|
| + frame_end_ = p + masking_key_length + payload_length_;
|
| + return FrameOK;
|
| +}
|
| +
|
| +std::vector<char> WebSocketFrame::EncodeFrame(
|
| + OpCode op_code, const char* data, size_t data_length) {
|
| + std::vector<char> frame;
|
| +
|
| + // Checks whether "op_code" fits in the range of opCodes.
|
| + DCHECK(!(op_code & ~kOpCodeMask));
|
| + frame.push_back(kFinalBit | op_code);
|
| + if (data_length <= kMaxSingleBytePayloadLength)
|
| + frame.push_back(kMaskBit | data_length);
|
| + else if (data_length <= 0xFFFF) {
|
| + frame.push_back(kMaskBit | kTwoBytePayloadLengthField);
|
| + frame.push_back((data_length & 0xFF00) >> 8);
|
| + frame.push_back(data_length & 0xFF);
|
| + } else {
|
| + frame.push_back(kMaskBit | kEightBytePayloadLengthField);
|
| + char extended_payload_length[8];
|
| + size_t remaining = data_length;
|
| + // Fill the length into extended_payload_length in the network byte order.
|
| + for (int i = 0; i < 8; ++i) {
|
| + extended_payload_length[7 - i] = remaining & 0xFF;
|
| + remaining >>= 8;
|
| + }
|
| + frame.insert(frame.end(),
|
| + extended_payload_length,
|
| + extended_payload_length + 8);
|
| + CHECK(!remaining);
|
| + }
|
| +
|
| + // Mask the frame.
|
| + size_t masking_key_start = frame.size();
|
| + // Add placeholder for masking key. Will be overwritten.
|
| + frame.resize(frame.size() + kMaskingKeyWidthInBytes);
|
| + size_t payload_start = frame.size();
|
| + frame.insert(frame.end(), data, data + data_length);
|
| +
|
| + base::RandBytes(&frame[0] + masking_key_start,
|
| + kMaskingKeyWidthInBytes);
|
| + for (size_t i = 0; i < data_length; ++i) {
|
| + frame[payload_start + i] ^=
|
| + frame[masking_key_start + i % kMaskingKeyWidthInBytes];
|
| + }
|
| +
|
| + return frame;
|
| +}
|
| +
|
| +
|
| +} // namespace net
|
|
|