1 // Copyright 2014 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/server/web_socket_encoder.h"
7 #include "base/logging.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/strings/stringprintf.h"
10 #include "net/base/io_buffer.h"
11 #include "net/websockets/websocket_extension_parser.h"
15 const char WebSocketEncoder::kClientExtensions
[] =
16 "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits";
20 const int kInflaterChunkSize
= 16 * 1024;
22 // Constants for hybi-10 frame format.
26 const OpCode kOpCodeContinuation
= 0x0;
27 const OpCode kOpCodeText
= 0x1;
28 const OpCode kOpCodeBinary
= 0x2;
29 const OpCode kOpCodeClose
= 0x8;
30 const OpCode kOpCodePing
= 0x9;
31 const OpCode kOpCodePong
= 0xA;
33 const unsigned char kFinalBit
= 0x80;
34 const unsigned char kReserved1Bit
= 0x40;
35 const unsigned char kReserved2Bit
= 0x20;
36 const unsigned char kReserved3Bit
= 0x10;
37 const unsigned char kOpCodeMask
= 0xF;
38 const unsigned char kMaskBit
= 0x80;
39 const unsigned char kPayloadLengthMask
= 0x7F;
41 const size_t kMaxSingleBytePayloadLength
= 125;
42 const size_t kTwoBytePayloadLengthField
= 126;
43 const size_t kEightBytePayloadLengthField
= 127;
44 const size_t kMaskingKeyWidthInBytes
= 4;
46 WebSocket::ParseResult
DecodeFrameHybi17(const base::StringPiece
& frame
,
51 size_t data_length
= frame
.length();
53 return WebSocket::FRAME_INCOMPLETE
;
55 const char* buffer_begin
= const_cast<char*>(frame
.data());
56 const char* p
= buffer_begin
;
57 const char* buffer_end
= p
+ data_length
;
59 unsigned char first_byte
= *p
++;
60 unsigned char second_byte
= *p
++;
62 bool final
= (first_byte
& kFinalBit
) != 0;
63 bool reserved1
= (first_byte
& kReserved1Bit
) != 0;
64 bool reserved2
= (first_byte
& kReserved2Bit
) != 0;
65 bool reserved3
= (first_byte
& kReserved3Bit
) != 0;
66 int op_code
= first_byte
& kOpCodeMask
;
67 bool masked
= (second_byte
& kMaskBit
) != 0;
68 *compressed
= reserved1
;
69 if (!final
|| reserved2
|| reserved3
)
70 return WebSocket::FRAME_ERROR
; // Only compression extension is supported.
79 case kOpCodeBinary
: // We don't support binary frames yet.
80 case kOpCodeContinuation
: // We don't support binary frames yet.
81 case kOpCodePing
: // We don't support binary frames yet.
82 case kOpCodePong
: // We don't support binary frames yet.
84 return WebSocket::FRAME_ERROR
;
87 if (client_frame
&& !masked
) // In Hybi-17 spec client MUST mask its frame.
88 return WebSocket::FRAME_ERROR
;
90 uint64 payload_length64
= second_byte
& kPayloadLengthMask
;
91 if (payload_length64
> kMaxSingleBytePayloadLength
) {
92 int extended_payload_length_size
;
93 if (payload_length64
== kTwoBytePayloadLengthField
)
94 extended_payload_length_size
= 2;
96 DCHECK(payload_length64
== kEightBytePayloadLengthField
);
97 extended_payload_length_size
= 8;
99 if (buffer_end
- p
< extended_payload_length_size
)
100 return WebSocket::FRAME_INCOMPLETE
;
101 payload_length64
= 0;
102 for (int i
= 0; i
< extended_payload_length_size
; ++i
) {
103 payload_length64
<<= 8;
104 payload_length64
|= static_cast<unsigned char>(*p
++);
108 size_t actual_masking_key_length
= masked
? kMaskingKeyWidthInBytes
: 0;
109 static const uint64 max_payload_length
= 0x7FFFFFFFFFFFFFFFull
;
110 static size_t max_length
= std::numeric_limits
<size_t>::max();
111 if (payload_length64
> max_payload_length
||
112 payload_length64
+ actual_masking_key_length
> max_length
) {
113 // WebSocket frame length too large.
114 return WebSocket::FRAME_ERROR
;
116 size_t payload_length
= static_cast<size_t>(payload_length64
);
118 size_t total_length
= actual_masking_key_length
+ payload_length
;
119 if (static_cast<size_t>(buffer_end
- p
) < total_length
)
120 return WebSocket::FRAME_INCOMPLETE
;
123 output
->resize(payload_length
);
124 const char* masking_key
= p
;
125 char* payload
= const_cast<char*>(p
+ kMaskingKeyWidthInBytes
);
126 for (size_t i
= 0; i
< payload_length
; ++i
) // Unmask the payload.
127 (*output
)[i
] = payload
[i
] ^ masking_key
[i
% kMaskingKeyWidthInBytes
];
129 output
->assign(p
, p
+ payload_length
);
132 size_t pos
= p
+ actual_masking_key_length
+ payload_length
- buffer_begin
;
133 *bytes_consumed
= pos
;
134 return closed
? WebSocket::FRAME_CLOSE
: WebSocket::FRAME_OK
;
137 void EncodeFrameHybi17(const std::string
& message
,
140 std::string
* output
) {
141 std::vector
<char> frame
;
142 OpCode op_code
= kOpCodeText
;
143 size_t data_length
= message
.length();
145 int reserved1
= compressed
? kReserved1Bit
: 0;
146 frame
.push_back(kFinalBit
| op_code
| reserved1
);
147 char mask_key_bit
= masking_key
!= 0 ? kMaskBit
: 0;
148 if (data_length
<= kMaxSingleBytePayloadLength
)
149 frame
.push_back(data_length
| mask_key_bit
);
150 else if (data_length
<= 0xFFFF) {
151 frame
.push_back(kTwoBytePayloadLengthField
| mask_key_bit
);
152 frame
.push_back((data_length
& 0xFF00) >> 8);
153 frame
.push_back(data_length
& 0xFF);
155 frame
.push_back(kEightBytePayloadLengthField
| mask_key_bit
);
156 char extended_payload_length
[8];
157 size_t remaining
= data_length
;
158 // Fill the length into extended_payload_length in the network byte order.
159 for (int i
= 0; i
< 8; ++i
) {
160 extended_payload_length
[7 - i
] = remaining
& 0xFF;
163 frame
.insert(frame
.end(), extended_payload_length
,
164 extended_payload_length
+ 8);
168 const char* data
= const_cast<char*>(message
.data());
169 if (masking_key
!= 0) {
170 const char* mask_bytes
= reinterpret_cast<char*>(&masking_key
);
171 frame
.insert(frame
.end(), mask_bytes
, mask_bytes
+ 4);
172 for (size_t i
= 0; i
< data_length
; ++i
) // Mask the payload.
173 frame
.push_back(data
[i
] ^ mask_bytes
[i
% kMaskingKeyWidthInBytes
]);
175 frame
.insert(frame
.end(), data
, data
+ data_length
);
177 *output
= std::string(&frame
[0], frame
.size());
180 } // anonymous namespace
183 WebSocketEncoder
* WebSocketEncoder::CreateServer(
184 const std::string
& request_extensions
,
185 std::string
* response_extensions
) {
187 bool has_client_window_bits
;
188 int client_window_bits
;
189 int server_window_bits
;
190 bool client_no_context_takeover
;
191 bool server_no_context_takeover
;
192 ParseExtensions(request_extensions
, &deflate
, &has_client_window_bits
,
193 &client_window_bits
, &server_window_bits
,
194 &client_no_context_takeover
, &server_no_context_takeover
);
197 *response_extensions
= base::StringPrintf(
198 "permessage-deflate; server_max_window_bits=%d%s", server_window_bits
,
199 server_no_context_takeover
? "; server_no_context_takeover" : "");
200 if (has_client_window_bits
) {
201 base::StringAppendF(response_extensions
, "; client_max_window_bits=%d",
204 DCHECK_EQ(client_window_bits
, 15);
206 return new WebSocketEncoder(true /* is_server */, server_window_bits
,
207 client_window_bits
, server_no_context_takeover
);
209 *response_extensions
= std::string();
210 return new WebSocketEncoder(true /* is_server */);
215 WebSocketEncoder
* WebSocketEncoder::CreateClient(
216 const std::string
& response_extensions
) {
218 bool has_client_window_bits
;
219 int client_window_bits
;
220 int server_window_bits
;
221 bool client_no_context_takeover
;
222 bool server_no_context_takeover
;
223 ParseExtensions(response_extensions
, &deflate
, &has_client_window_bits
,
224 &client_window_bits
, &server_window_bits
,
225 &client_no_context_takeover
, &server_no_context_takeover
);
228 return new WebSocketEncoder(false /* is_server */, client_window_bits
,
229 server_window_bits
, client_no_context_takeover
);
231 return new WebSocketEncoder(false /* is_server */);
236 void WebSocketEncoder::ParseExtensions(const std::string
& header_value
,
238 bool* has_client_window_bits
,
239 int* client_window_bits
,
240 int* server_window_bits
,
241 bool* client_no_context_takeover
,
242 bool* server_no_context_takeover
) {
244 *has_client_window_bits
= false;
245 *client_window_bits
= 15;
246 *server_window_bits
= 15;
247 *client_no_context_takeover
= false;
248 *server_no_context_takeover
= false;
250 if (header_value
.empty())
253 WebSocketExtensionParser parser
;
254 if (!parser
.Parse(header_value
))
256 const std::vector
<WebSocketExtension
>& extensions
= parser
.extensions();
257 // TODO(tyoshino): Fail if this method is used for parsing a response and
258 // there are multiple permessage-deflate extensions or there are any unknown
260 for (const auto& extension
: extensions
) {
261 if (extension
.name() != "permessage-deflate") {
265 const std::vector
<WebSocketExtension::Parameter
>& parameters
=
266 extension
.parameters();
267 for (const auto& param
: parameters
) {
268 const std::string
& name
= param
.name();
269 // TODO(tyoshino): Fail the connection when an invalid value is given.
270 if (name
== "client_max_window_bits") {
271 *has_client_window_bits
= true;
272 if (param
.HasValue()) {
274 if (base::StringToInt(param
.value(), &bits
) && bits
>= 8 &&
276 *client_window_bits
= bits
;
280 if (name
== "server_max_window_bits" && param
.HasValue()) {
282 if (base::StringToInt(param
.value(), &bits
) && bits
>= 8 && bits
<= 15)
283 *server_window_bits
= bits
;
285 if (name
== "client_no_context_takeover")
286 *client_no_context_takeover
= true;
287 if (name
== "server_no_context_takeover")
288 *server_no_context_takeover
= true;
296 WebSocketEncoder::WebSocketEncoder(bool is_server
) : is_server_(is_server
) {
299 WebSocketEncoder::WebSocketEncoder(bool is_server
,
302 bool no_context_takeover
)
303 : is_server_(is_server
) {
304 deflater_
.reset(new WebSocketDeflater(
305 no_context_takeover
? WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT
306 : WebSocketDeflater::TAKE_OVER_CONTEXT
));
308 new WebSocketInflater(kInflaterChunkSize
, kInflaterChunkSize
));
310 if (!deflater_
->Initialize(deflate_bits
) ||
311 !inflater_
->Initialize(inflate_bits
)) {
312 // Disable deflate support.
318 WebSocketEncoder::~WebSocketEncoder() {
321 WebSocket::ParseResult
WebSocketEncoder::DecodeFrame(
322 const base::StringPiece
& frame
,
324 std::string
* output
) {
326 WebSocket::ParseResult result
=
327 DecodeFrameHybi17(frame
, is_server_
, bytes_consumed
, output
, &compressed
);
328 if (result
== WebSocket::FRAME_OK
&& compressed
) {
329 if (!Inflate(output
))
330 result
= WebSocket::FRAME_ERROR
;
335 void WebSocketEncoder::EncodeFrame(const std::string
& frame
,
337 std::string
* output
) {
338 std::string compressed
;
339 if (Deflate(frame
, &compressed
))
340 EncodeFrameHybi17(compressed
, masking_key
, true, output
);
342 EncodeFrameHybi17(frame
, masking_key
, false, output
);
345 bool WebSocketEncoder::Inflate(std::string
* message
) {
348 if (!inflater_
->AddBytes(message
->data(), message
->length()))
350 if (!inflater_
->Finish())
353 std::vector
<char> output
;
354 while (inflater_
->CurrentOutputSize() > 0) {
355 scoped_refptr
<IOBufferWithSize
> chunk
=
356 inflater_
->GetOutput(inflater_
->CurrentOutputSize());
359 output
.insert(output
.end(), chunk
->data(), chunk
->data() + chunk
->size());
363 output
.size() ? std::string(&output
[0], output
.size()) : std::string();
367 bool WebSocketEncoder::Deflate(const std::string
& message
,
368 std::string
* output
) {
371 if (!deflater_
->AddBytes(message
.data(), message
.length())) {
375 if (!deflater_
->Finish())
377 scoped_refptr
<IOBufferWithSize
> buffer
=
378 deflater_
->GetOutput(deflater_
->CurrentOutputSize());
381 *output
= std::string(buffer
->data(), buffer
->size());