[memory-inspector] Force use of https:// schema to fetch JS resources.
[chromium-blink-merge.git] / net / websockets / websocket_frame_parser.cc
blobd00a1d11ae59bf9c547d47e63fa2141ff123e119
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/websockets/websocket_frame_parser.h"
7 #include <algorithm>
8 #include <limits>
10 #include "base/basictypes.h"
11 #include "base/big_endian.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/scoped_vector.h"
16 #include "net/base/io_buffer.h"
17 #include "net/websockets/websocket_frame.h"
19 namespace {
21 const uint8 kFinalBit = 0x80;
22 const uint8 kReserved1Bit = 0x40;
23 const uint8 kReserved2Bit = 0x20;
24 const uint8 kReserved3Bit = 0x10;
25 const uint8 kOpCodeMask = 0xF;
26 const uint8 kMaskBit = 0x80;
27 const uint8 kPayloadLengthMask = 0x7F;
28 const uint64 kMaxPayloadLengthWithoutExtendedLengthField = 125;
29 const uint64 kPayloadLengthWithTwoByteExtendedLengthField = 126;
30 const uint64 kPayloadLengthWithEightByteExtendedLengthField = 127;
32 } // Unnamed namespace.
34 namespace net {
36 WebSocketFrameParser::WebSocketFrameParser()
37 : current_read_pos_(0),
38 frame_offset_(0),
39 websocket_error_(kWebSocketNormalClosure) {
40 std::fill(masking_key_.key,
41 masking_key_.key + WebSocketFrameHeader::kMaskingKeyLength,
42 '\0');
45 WebSocketFrameParser::~WebSocketFrameParser() {}
47 bool WebSocketFrameParser::Decode(
48 const char* data,
49 size_t length,
50 ScopedVector<WebSocketFrameChunk>* frame_chunks) {
51 if (websocket_error_ != kWebSocketNormalClosure)
52 return false;
53 if (!length)
54 return true;
56 // TODO(yutak): Remove copy.
57 buffer_.insert(buffer_.end(), data, data + length);
59 while (current_read_pos_ < buffer_.size()) {
60 bool first_chunk = false;
61 if (!current_frame_header_.get()) {
62 DecodeFrameHeader();
63 if (websocket_error_ != kWebSocketNormalClosure)
64 return false;
65 // If frame header is incomplete, then carry over the remaining
66 // data to the next round of Decode().
67 if (!current_frame_header_.get())
68 break;
69 first_chunk = true;
72 scoped_ptr<WebSocketFrameChunk> frame_chunk =
73 DecodeFramePayload(first_chunk);
74 DCHECK(frame_chunk.get());
75 frame_chunks->push_back(frame_chunk.release());
77 if (current_frame_header_.get()) {
78 DCHECK(current_read_pos_ == buffer_.size());
79 break;
83 // Drain unnecessary data. TODO(yutak): Remove copy. (but how?)
84 buffer_.erase(buffer_.begin(), buffer_.begin() + current_read_pos_);
85 current_read_pos_ = 0;
87 // Sanity check: the size of carried-over data should not exceed
88 // the maximum possible length of a frame header.
89 static const size_t kMaximumFrameHeaderSize =
90 WebSocketFrameHeader::kBaseHeaderSize +
91 WebSocketFrameHeader::kMaximumExtendedLengthSize +
92 WebSocketFrameHeader::kMaskingKeyLength;
93 DCHECK_LT(buffer_.size(), kMaximumFrameHeaderSize);
95 return true;
98 void WebSocketFrameParser::DecodeFrameHeader() {
99 typedef WebSocketFrameHeader::OpCode OpCode;
100 static const int kMaskingKeyLength = WebSocketFrameHeader::kMaskingKeyLength;
102 DCHECK(!current_frame_header_.get());
104 const char* start = &buffer_.front() + current_read_pos_;
105 const char* current = start;
106 const char* end = &buffer_.front() + buffer_.size();
108 // Header needs 2 bytes at minimum.
109 if (end - current < 2)
110 return;
112 uint8 first_byte = *current++;
113 uint8 second_byte = *current++;
115 bool final = (first_byte & kFinalBit) != 0;
116 bool reserved1 = (first_byte & kReserved1Bit) != 0;
117 bool reserved2 = (first_byte & kReserved2Bit) != 0;
118 bool reserved3 = (first_byte & kReserved3Bit) != 0;
119 OpCode opcode = first_byte & kOpCodeMask;
121 bool masked = (second_byte & kMaskBit) != 0;
122 uint64 payload_length = second_byte & kPayloadLengthMask;
123 if (payload_length == kPayloadLengthWithTwoByteExtendedLengthField) {
124 if (end - current < 2)
125 return;
126 uint16 payload_length_16;
127 base::ReadBigEndian(current, &payload_length_16);
128 current += 2;
129 payload_length = payload_length_16;
130 if (payload_length <= kMaxPayloadLengthWithoutExtendedLengthField)
131 websocket_error_ = kWebSocketErrorProtocolError;
132 } else if (payload_length == kPayloadLengthWithEightByteExtendedLengthField) {
133 if (end - current < 8)
134 return;
135 base::ReadBigEndian(current, &payload_length);
136 current += 8;
137 if (payload_length <= kuint16max ||
138 payload_length > static_cast<uint64>(kint64max)) {
139 websocket_error_ = kWebSocketErrorProtocolError;
140 } else if (payload_length > static_cast<uint64>(kint32max)) {
141 websocket_error_ = kWebSocketErrorMessageTooBig;
144 if (websocket_error_ != kWebSocketNormalClosure) {
145 buffer_.clear();
146 current_read_pos_ = 0;
147 current_frame_header_.reset();
148 frame_offset_ = 0;
149 return;
152 if (masked) {
153 if (end - current < kMaskingKeyLength)
154 return;
155 std::copy(current, current + kMaskingKeyLength, masking_key_.key);
156 current += kMaskingKeyLength;
157 } else {
158 std::fill(masking_key_.key, masking_key_.key + kMaskingKeyLength, '\0');
161 current_frame_header_.reset(new WebSocketFrameHeader(opcode));
162 current_frame_header_->final = final;
163 current_frame_header_->reserved1 = reserved1;
164 current_frame_header_->reserved2 = reserved2;
165 current_frame_header_->reserved3 = reserved3;
166 current_frame_header_->masked = masked;
167 current_frame_header_->payload_length = payload_length;
168 current_read_pos_ += current - start;
169 DCHECK_EQ(0u, frame_offset_);
172 scoped_ptr<WebSocketFrameChunk> WebSocketFrameParser::DecodeFramePayload(
173 bool first_chunk) {
174 // The cast here is safe because |payload_length| is already checked to be
175 // less than std::numeric_limits<int>::max() when the header is parsed.
176 int next_size = static_cast<int>(std::min(
177 static_cast<uint64>(buffer_.size() - current_read_pos_),
178 current_frame_header_->payload_length - frame_offset_));
180 scoped_ptr<WebSocketFrameChunk> frame_chunk(new WebSocketFrameChunk);
181 if (first_chunk) {
182 frame_chunk->header = current_frame_header_->Clone();
184 frame_chunk->final_chunk = false;
185 if (next_size) {
186 frame_chunk->data = new IOBufferWithSize(static_cast<int>(next_size));
187 char* io_data = frame_chunk->data->data();
188 memcpy(io_data, &buffer_.front() + current_read_pos_, next_size);
189 if (current_frame_header_->masked) {
190 // The masking function is its own inverse, so we use the same function to
191 // unmask as to mask.
192 MaskWebSocketFramePayload(
193 masking_key_, frame_offset_, io_data, next_size);
196 current_read_pos_ += next_size;
197 frame_offset_ += next_size;
200 DCHECK_LE(frame_offset_, current_frame_header_->payload_length);
201 if (frame_offset_ == current_frame_header_->payload_length) {
202 frame_chunk->final_chunk = true;
203 current_frame_header_.reset();
204 frame_offset_ = 0;
207 return frame_chunk.Pass();
210 } // namespace net