blob: 6d59713d9b325e84df51def4a730ce08cd2958d3 [file] [log] [blame]
dgozmana6e70092014-12-12 14:46:211// 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.
4
5#include "net/server/web_socket_encoder.h"
6
dchengc7eeda422015-12-26 03:56:487#include <utility>
yhiranoa10dd4e2015-09-28 09:06:348#include <vector>
9
dgozmana6e70092014-12-12 14:46:2110#include "base/logging.h"
11#include "base/strings/string_number_conversions.h"
12#include "base/strings/stringprintf.h"
13#include "net/base/io_buffer.h"
yhiranoa10dd4e2015-09-28 09:06:3414#include "net/websockets/websocket_deflate_parameters.h"
15#include "net/websockets/websocket_extension.h"
dgozmana6e70092014-12-12 14:46:2116#include "net/websockets/websocket_extension_parser.h"
17
18namespace net {
19
20const char WebSocketEncoder::kClientExtensions[] =
21 "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits";
22
23namespace {
24
25const int kInflaterChunkSize = 16 * 1024;
26
27// Constants for hybi-10 frame format.
28
29typedef int OpCode;
30
31const OpCode kOpCodeContinuation = 0x0;
32const OpCode kOpCodeText = 0x1;
33const OpCode kOpCodeBinary = 0x2;
34const OpCode kOpCodeClose = 0x8;
35const OpCode kOpCodePing = 0x9;
36const OpCode kOpCodePong = 0xA;
37
38const unsigned char kFinalBit = 0x80;
39const unsigned char kReserved1Bit = 0x40;
40const unsigned char kReserved2Bit = 0x20;
41const unsigned char kReserved3Bit = 0x10;
42const unsigned char kOpCodeMask = 0xF;
43const unsigned char kMaskBit = 0x80;
44const unsigned char kPayloadLengthMask = 0x7F;
45
46const size_t kMaxSingleBytePayloadLength = 125;
47const size_t kTwoBytePayloadLengthField = 126;
48const size_t kEightBytePayloadLengthField = 127;
49const size_t kMaskingKeyWidthInBytes = 4;
50
51WebSocket::ParseResult DecodeFrameHybi17(const base::StringPiece& frame,
52 bool client_frame,
53 int* bytes_consumed,
54 std::string* output,
55 bool* compressed) {
56 size_t data_length = frame.length();
57 if (data_length < 2)
58 return WebSocket::FRAME_INCOMPLETE;
59
60 const char* buffer_begin = const_cast<char*>(frame.data());
61 const char* p = buffer_begin;
62 const char* buffer_end = p + data_length;
63
64 unsigned char first_byte = *p++;
65 unsigned char second_byte = *p++;
66
67 bool final = (first_byte & kFinalBit) != 0;
68 bool reserved1 = (first_byte & kReserved1Bit) != 0;
69 bool reserved2 = (first_byte & kReserved2Bit) != 0;
70 bool reserved3 = (first_byte & kReserved3Bit) != 0;
71 int op_code = first_byte & kOpCodeMask;
72 bool masked = (second_byte & kMaskBit) != 0;
73 *compressed = reserved1;
74 if (!final || reserved2 || reserved3)
75 return WebSocket::FRAME_ERROR; // Only compression extension is supported.
76
77 bool closed = false;
78 switch (op_code) {
79 case kOpCodeClose:
80 closed = true;
81 break;
82 case kOpCodeText:
83 break;
84 case kOpCodeBinary: // We don't support binary frames yet.
85 case kOpCodeContinuation: // We don't support binary frames yet.
86 case kOpCodePing: // We don't support binary frames yet.
87 case kOpCodePong: // We don't support binary frames yet.
88 default:
89 return WebSocket::FRAME_ERROR;
90 }
91
ellyjonesc7a5c502015-06-26 18:55:2092 if (client_frame && !masked) // In Hybi-17 spec client MUST mask its frame.
dgozmana6e70092014-12-12 14:46:2193 return WebSocket::FRAME_ERROR;
94
Avi Drissman13fc8932015-12-20 04:40:4695 uint64_t payload_length64 = second_byte & kPayloadLengthMask;
dgozmana6e70092014-12-12 14:46:2196 if (payload_length64 > kMaxSingleBytePayloadLength) {
97 int extended_payload_length_size;
98 if (payload_length64 == kTwoBytePayloadLengthField)
99 extended_payload_length_size = 2;
100 else {
101 DCHECK(payload_length64 == kEightBytePayloadLengthField);
102 extended_payload_length_size = 8;
103 }
104 if (buffer_end - p < extended_payload_length_size)
105 return WebSocket::FRAME_INCOMPLETE;
106 payload_length64 = 0;
107 for (int i = 0; i < extended_payload_length_size; ++i) {
108 payload_length64 <<= 8;
109 payload_length64 |= static_cast<unsigned char>(*p++);
110 }
111 }
112
113 size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
Avi Drissman13fc8932015-12-20 04:40:46114 static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
dgozmana6e70092014-12-12 14:46:21115 static size_t max_length = std::numeric_limits<size_t>::max();
116 if (payload_length64 > max_payload_length ||
117 payload_length64 + actual_masking_key_length > max_length) {
118 // WebSocket frame length too large.
119 return WebSocket::FRAME_ERROR;
120 }
121 size_t payload_length = static_cast<size_t>(payload_length64);
122
123 size_t total_length = actual_masking_key_length + payload_length;
124 if (static_cast<size_t>(buffer_end - p) < total_length)
125 return WebSocket::FRAME_INCOMPLETE;
126
127 if (masked) {
128 output->resize(payload_length);
129 const char* masking_key = p;
130 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
131 for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
132 (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
133 } else {
134 output->assign(p, p + payload_length);
135 }
136
137 size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
138 *bytes_consumed = pos;
139 return closed ? WebSocket::FRAME_CLOSE : WebSocket::FRAME_OK;
140}
141
142void EncodeFrameHybi17(const std::string& message,
143 int masking_key,
144 bool compressed,
145 std::string* output) {
146 std::vector<char> frame;
147 OpCode op_code = kOpCodeText;
148 size_t data_length = message.length();
149
150 int reserved1 = compressed ? kReserved1Bit : 0;
151 frame.push_back(kFinalBit | op_code | reserved1);
152 char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
brucedawsonef128242015-12-01 04:26:36153 if (data_length <= kMaxSingleBytePayloadLength) {
154 frame.push_back(static_cast<char>(data_length) | mask_key_bit);
155 } else if (data_length <= 0xFFFF) {
dgozmana6e70092014-12-12 14:46:21156 frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
157 frame.push_back((data_length & 0xFF00) >> 8);
158 frame.push_back(data_length & 0xFF);
159 } else {
160 frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
161 char extended_payload_length[8];
162 size_t remaining = data_length;
163 // Fill the length into extended_payload_length in the network byte order.
164 for (int i = 0; i < 8; ++i) {
165 extended_payload_length[7 - i] = remaining & 0xFF;
166 remaining >>= 8;
167 }
168 frame.insert(frame.end(), extended_payload_length,
169 extended_payload_length + 8);
170 DCHECK(!remaining);
171 }
172
173 const char* data = const_cast<char*>(message.data());
174 if (masking_key != 0) {
175 const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
176 frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
177 for (size_t i = 0; i < data_length; ++i) // Mask the payload.
178 frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
179 } else {
180 frame.insert(frame.end(), data, data + data_length);
181 }
182 *output = std::string(&frame[0], frame.size());
183}
184
185} // anonymous namespace
186
187// static
yhiranoa10dd4e2015-09-28 09:06:34188scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer() {
189 return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
190}
dgozmana6e70092014-12-12 14:46:21191
yhiranoa10dd4e2015-09-28 09:06:34192// static
193scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer(
194 const std::string& extensions,
195 WebSocketDeflateParameters* deflate_parameters) {
196 WebSocketExtensionParser parser;
197 if (!parser.Parse(extensions)) {
198 // Failed to parse Sec-WebSocket-Extensions header. We MUST fail the
199 // connection.
200 return nullptr;
dgozmana6e70092014-12-12 14:46:21201 }
yhiranoa10dd4e2015-09-28 09:06:34202
203 for (const auto& extension : parser.extensions()) {
204 std::string failure_message;
205 WebSocketDeflateParameters offer;
206 if (!offer.Initialize(extension, &failure_message) ||
207 !offer.IsValidAsRequest(&failure_message)) {
208 // We decline unknown / malformed extensions.
209 continue;
210 }
211
212 WebSocketDeflateParameters response = offer;
213 if (offer.is_client_max_window_bits_specified() &&
214 !offer.has_client_max_window_bits_value()) {
215 // We need to choose one value for the response.
216 response.SetClientMaxWindowBits(15);
217 }
218 DCHECK(response.IsValidAsResponse());
219 DCHECK(offer.IsCompatibleWith(response));
220 auto deflater = make_scoped_ptr(
221 new WebSocketDeflater(response.server_context_take_over_mode()));
222 auto inflater = make_scoped_ptr(
223 new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
224 if (!deflater->Initialize(response.PermissiveServerMaxWindowBits()) ||
225 !inflater->Initialize(response.PermissiveClientMaxWindowBits())) {
226 // For some reason we cannot accept the parameters.
227 continue;
228 }
229 *deflate_parameters = response;
dchengc7eeda422015-12-26 03:56:48230 return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, std::move(deflater),
231 std::move(inflater)));
yhiranoa10dd4e2015-09-28 09:06:34232 }
233
234 // We cannot find an acceptable offer.
235 return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21236}
237
238// static
olli.raula46a1acf2015-11-04 08:45:52239scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateClient(
dgozmana6e70092014-12-12 14:46:21240 const std::string& response_extensions) {
yhiranoa10dd4e2015-09-28 09:06:34241 // TODO(yhirano): Add a way to return an error.
dgozmana6e70092014-12-12 14:46:21242
dgozmana6e70092014-12-12 14:46:21243 WebSocketExtensionParser parser;
yhiranoa10dd4e2015-09-28 09:06:34244 if (!parser.Parse(response_extensions)) {
245 // Parse error. Note that there are two cases here.
246 // 1) There is no Sec-WebSocket-Extensions header.
247 // 2) There is a malformed Sec-WebSocketExtensions header.
248 // We should return a deflate-disabled encoder for the former case and
249 // fail the connection for the latter case.
olli.raula46a1acf2015-11-04 08:45:52250 return make_scoped_ptr(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21251 }
yhiranoa10dd4e2015-09-28 09:06:34252 if (parser.extensions().size() != 1) {
253 // Only permessage-deflate extension is supported.
254 // TODO (yhirano): Fail the connection.
olli.raula46a1acf2015-11-04 08:45:52255 return make_scoped_ptr(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34256 }
257 const auto& extension = parser.extensions()[0];
258 WebSocketDeflateParameters params;
259 std::string failure_message;
260 if (!params.Initialize(extension, &failure_message) ||
261 !params.IsValidAsResponse(&failure_message)) {
262 // TODO (yhirano): Fail the connection.
olli.raula46a1acf2015-11-04 08:45:52263 return make_scoped_ptr(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34264 }
dgozmana6e70092014-12-12 14:46:21265
yhiranoa10dd4e2015-09-28 09:06:34266 auto deflater = make_scoped_ptr(
267 new WebSocketDeflater(params.client_context_take_over_mode()));
268 auto inflater = make_scoped_ptr(
dgozmana6e70092014-12-12 14:46:21269 new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
yhiranoa10dd4e2015-09-28 09:06:34270 if (!deflater->Initialize(params.PermissiveClientMaxWindowBits()) ||
271 !inflater->Initialize(params.PermissiveServerMaxWindowBits())) {
272 // TODO (yhirano): Fail the connection.
olli.raula46a1acf2015-11-04 08:45:52273 return make_scoped_ptr(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21274 }
yhiranoa10dd4e2015-09-28 09:06:34275
dchengc7eeda422015-12-26 03:56:48276 return make_scoped_ptr(new WebSocketEncoder(FOR_CLIENT, std::move(deflater),
277 std::move(inflater)));
dgozmana6e70092014-12-12 14:46:21278}
279
yhiranoa10dd4e2015-09-28 09:06:34280WebSocketEncoder::WebSocketEncoder(Type type,
281 scoped_ptr<WebSocketDeflater> deflater,
282 scoped_ptr<WebSocketInflater> inflater)
dchengc7eeda422015-12-26 03:56:48283 : type_(type),
284 deflater_(std::move(deflater)),
285 inflater_(std::move(inflater)) {}
yhiranoa10dd4e2015-09-28 09:06:34286
287WebSocketEncoder::~WebSocketEncoder() {}
dgozmana6e70092014-12-12 14:46:21288
289WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
290 const base::StringPiece& frame,
291 int* bytes_consumed,
292 std::string* output) {
293 bool compressed;
yhiranoa10dd4e2015-09-28 09:06:34294 WebSocket::ParseResult result = DecodeFrameHybi17(
295 frame, type_ == FOR_SERVER, bytes_consumed, output, &compressed);
dgozmana6e70092014-12-12 14:46:21296 if (result == WebSocket::FRAME_OK && compressed) {
297 if (!Inflate(output))
298 result = WebSocket::FRAME_ERROR;
299 }
300 return result;
301}
302
303void WebSocketEncoder::EncodeFrame(const std::string& frame,
304 int masking_key,
305 std::string* output) {
306 std::string compressed;
307 if (Deflate(frame, &compressed))
308 EncodeFrameHybi17(compressed, masking_key, true, output);
309 else
310 EncodeFrameHybi17(frame, masking_key, false, output);
311}
312
313bool WebSocketEncoder::Inflate(std::string* message) {
314 if (!inflater_)
315 return false;
316 if (!inflater_->AddBytes(message->data(), message->length()))
317 return false;
318 if (!inflater_->Finish())
319 return false;
320
321 std::vector<char> output;
322 while (inflater_->CurrentOutputSize() > 0) {
323 scoped_refptr<IOBufferWithSize> chunk =
324 inflater_->GetOutput(inflater_->CurrentOutputSize());
325 if (!chunk.get())
326 return false;
327 output.insert(output.end(), chunk->data(), chunk->data() + chunk->size());
328 }
329
330 *message =
331 output.size() ? std::string(&output[0], output.size()) : std::string();
332 return true;
333}
334
335bool WebSocketEncoder::Deflate(const std::string& message,
336 std::string* output) {
337 if (!deflater_)
338 return false;
339 if (!deflater_->AddBytes(message.data(), message.length())) {
340 deflater_->Finish();
341 return false;
342 }
343 if (!deflater_->Finish())
344 return false;
345 scoped_refptr<IOBufferWithSize> buffer =
346 deflater_->GetOutput(deflater_->CurrentOutputSize());
347 if (!buffer.get())
348 return false;
349 *output = std::string(buffer->data(), buffer->size());
350 return true;
351}
352
353} // namespace net