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"
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"
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.
36 WebSocketFrameParser::WebSocketFrameParser()
37 : current_read_pos_(0),
39 websocket_error_(kWebSocketNormalClosure
) {
40 std::fill(masking_key_
.key
,
41 masking_key_
.key
+ WebSocketFrameHeader::kMaskingKeyLength
,
45 WebSocketFrameParser::~WebSocketFrameParser() {}
47 bool WebSocketFrameParser::Decode(
50 ScopedVector
<WebSocketFrameChunk
>* frame_chunks
) {
51 if (websocket_error_
!= kWebSocketNormalClosure
)
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()) {
63 if (websocket_error_
!= kWebSocketNormalClosure
)
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())
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());
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
);
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)
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)
126 uint16 payload_length_16
;
127 base::ReadBigEndian(current
, &payload_length_16
);
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)
135 base::ReadBigEndian(current
, &payload_length
);
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
) {
146 current_read_pos_
= 0;
147 current_frame_header_
.reset();
153 if (end
- current
< kMaskingKeyLength
)
155 std::copy(current
, current
+ kMaskingKeyLength
, masking_key_
.key
);
156 current
+= kMaskingKeyLength
;
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(
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
);
182 frame_chunk
->header
= current_frame_header_
->Clone();
184 frame_chunk
->final_chunk
= false;
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();
207 return frame_chunk
.Pass();