blob: 60a9a47021c8181376504724f090c7099cdca39a [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
davidben411d3f72016-01-22 01:41:417#include <limits>
dchengc7eeda422015-12-26 03:56:488#include <utility>
yhiranoa10dd4e2015-09-28 09:06:349#include <vector>
10
dgozmana6e70092014-12-12 14:46:2111#include "base/logging.h"
danakja9850e12016-04-18 22:28:0812#include "base/memory/ptr_util.h"
dgozmana6e70092014-12-12 14:46:2113#include "base/strings/string_number_conversions.h"
14#include "base/strings/stringprintf.h"
15#include "net/base/io_buffer.h"
yhiranoa10dd4e2015-09-28 09:06:3416#include "net/websockets/websocket_deflate_parameters.h"
17#include "net/websockets/websocket_extension.h"
dgozmana6e70092014-12-12 14:46:2118#include "net/websockets/websocket_extension_parser.h"
19
20namespace net {
21
22const char WebSocketEncoder::kClientExtensions[] =
23 "Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits";
24
25namespace {
26
27const int kInflaterChunkSize = 16 * 1024;
28
29// Constants for hybi-10 frame format.
30
31typedef int OpCode;
32
33const OpCode kOpCodeContinuation = 0x0;
34const OpCode kOpCodeText = 0x1;
35const OpCode kOpCodeBinary = 0x2;
36const OpCode kOpCodeClose = 0x8;
37const OpCode kOpCodePing = 0x9;
38const OpCode kOpCodePong = 0xA;
39
40const unsigned char kFinalBit = 0x80;
41const unsigned char kReserved1Bit = 0x40;
42const unsigned char kReserved2Bit = 0x20;
43const unsigned char kReserved3Bit = 0x10;
44const unsigned char kOpCodeMask = 0xF;
45const unsigned char kMaskBit = 0x80;
46const unsigned char kPayloadLengthMask = 0x7F;
47
48const size_t kMaxSingleBytePayloadLength = 125;
49const size_t kTwoBytePayloadLengthField = 126;
50const size_t kEightBytePayloadLengthField = 127;
51const size_t kMaskingKeyWidthInBytes = 4;
52
53WebSocket::ParseResult DecodeFrameHybi17(const base::StringPiece& frame,
54 bool client_frame,
55 int* bytes_consumed,
56 std::string* output,
57 bool* compressed) {
58 size_t data_length = frame.length();
59 if (data_length < 2)
60 return WebSocket::FRAME_INCOMPLETE;
61
62 const char* buffer_begin = const_cast<char*>(frame.data());
63 const char* p = buffer_begin;
64 const char* buffer_end = p + data_length;
65
66 unsigned char first_byte = *p++;
67 unsigned char second_byte = *p++;
68
69 bool final = (first_byte & kFinalBit) != 0;
70 bool reserved1 = (first_byte & kReserved1Bit) != 0;
71 bool reserved2 = (first_byte & kReserved2Bit) != 0;
72 bool reserved3 = (first_byte & kReserved3Bit) != 0;
73 int op_code = first_byte & kOpCodeMask;
74 bool masked = (second_byte & kMaskBit) != 0;
75 *compressed = reserved1;
76 if (!final || reserved2 || reserved3)
77 return WebSocket::FRAME_ERROR; // Only compression extension is supported.
78
79 bool closed = false;
80 switch (op_code) {
81 case kOpCodeClose:
82 closed = true;
83 break;
84 case kOpCodeText:
85 break;
86 case kOpCodeBinary: // We don't support binary frames yet.
87 case kOpCodeContinuation: // We don't support binary frames yet.
88 case kOpCodePing: // We don't support binary frames yet.
89 case kOpCodePong: // We don't support binary frames yet.
90 default:
91 return WebSocket::FRAME_ERROR;
92 }
93
ellyjonesc7a5c502015-06-26 18:55:2094 if (client_frame && !masked) // In Hybi-17 spec client MUST mask its frame.
dgozmana6e70092014-12-12 14:46:2195 return WebSocket::FRAME_ERROR;
96
Avi Drissman13fc8932015-12-20 04:40:4697 uint64_t payload_length64 = second_byte & kPayloadLengthMask;
dgozmana6e70092014-12-12 14:46:2198 if (payload_length64 > kMaxSingleBytePayloadLength) {
99 int extended_payload_length_size;
100 if (payload_length64 == kTwoBytePayloadLengthField)
101 extended_payload_length_size = 2;
102 else {
103 DCHECK(payload_length64 == kEightBytePayloadLengthField);
104 extended_payload_length_size = 8;
105 }
106 if (buffer_end - p < extended_payload_length_size)
107 return WebSocket::FRAME_INCOMPLETE;
108 payload_length64 = 0;
109 for (int i = 0; i < extended_payload_length_size; ++i) {
110 payload_length64 <<= 8;
111 payload_length64 |= static_cast<unsigned char>(*p++);
112 }
113 }
114
115 size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
Avi Drissman13fc8932015-12-20 04:40:46116 static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
dgozmana6e70092014-12-12 14:46:21117 static size_t max_length = std::numeric_limits<size_t>::max();
118 if (payload_length64 > max_payload_length ||
119 payload_length64 + actual_masking_key_length > max_length) {
120 // WebSocket frame length too large.
121 return WebSocket::FRAME_ERROR;
122 }
123 size_t payload_length = static_cast<size_t>(payload_length64);
124
125 size_t total_length = actual_masking_key_length + payload_length;
126 if (static_cast<size_t>(buffer_end - p) < total_length)
127 return WebSocket::FRAME_INCOMPLETE;
128
129 if (masked) {
130 output->resize(payload_length);
131 const char* masking_key = p;
132 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
133 for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
134 (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
135 } else {
136 output->assign(p, p + payload_length);
137 }
138
139 size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
140 *bytes_consumed = pos;
141 return closed ? WebSocket::FRAME_CLOSE : WebSocket::FRAME_OK;
142}
143
144void EncodeFrameHybi17(const std::string& message,
145 int masking_key,
146 bool compressed,
147 std::string* output) {
148 std::vector<char> frame;
149 OpCode op_code = kOpCodeText;
150 size_t data_length = message.length();
151
152 int reserved1 = compressed ? kReserved1Bit : 0;
153 frame.push_back(kFinalBit | op_code | reserved1);
154 char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
brucedawsonef128242015-12-01 04:26:36155 if (data_length <= kMaxSingleBytePayloadLength) {
156 frame.push_back(static_cast<char>(data_length) | mask_key_bit);
157 } else if (data_length <= 0xFFFF) {
dgozmana6e70092014-12-12 14:46:21158 frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
159 frame.push_back((data_length & 0xFF00) >> 8);
160 frame.push_back(data_length & 0xFF);
161 } else {
162 frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
163 char extended_payload_length[8];
164 size_t remaining = data_length;
165 // Fill the length into extended_payload_length in the network byte order.
166 for (int i = 0; i < 8; ++i) {
167 extended_payload_length[7 - i] = remaining & 0xFF;
168 remaining >>= 8;
169 }
170 frame.insert(frame.end(), extended_payload_length,
171 extended_payload_length + 8);
172 DCHECK(!remaining);
173 }
174
175 const char* data = const_cast<char*>(message.data());
176 if (masking_key != 0) {
177 const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
178 frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
179 for (size_t i = 0; i < data_length; ++i) // Mask the payload.
180 frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
181 } else {
182 frame.insert(frame.end(), data, data + data_length);
183 }
184 *output = std::string(&frame[0], frame.size());
185}
186
187} // anonymous namespace
188
189// static
danakja9850e12016-04-18 22:28:08190std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer() {
191 return base::WrapUnique(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34192}
dgozmana6e70092014-12-12 14:46:21193
yhiranoa10dd4e2015-09-28 09:06:34194// static
danakja9850e12016-04-18 22:28:08195std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer(
yhiranoa10dd4e2015-09-28 09:06:34196 const std::string& extensions,
197 WebSocketDeflateParameters* deflate_parameters) {
198 WebSocketExtensionParser parser;
199 if (!parser.Parse(extensions)) {
200 // Failed to parse Sec-WebSocket-Extensions header. We MUST fail the
201 // connection.
202 return nullptr;
dgozmana6e70092014-12-12 14:46:21203 }
yhiranoa10dd4e2015-09-28 09:06:34204
205 for (const auto& extension : parser.extensions()) {
206 std::string failure_message;
207 WebSocketDeflateParameters offer;
208 if (!offer.Initialize(extension, &failure_message) ||
209 !offer.IsValidAsRequest(&failure_message)) {
210 // We decline unknown / malformed extensions.
211 continue;
212 }
213
214 WebSocketDeflateParameters response = offer;
215 if (offer.is_client_max_window_bits_specified() &&
216 !offer.has_client_max_window_bits_value()) {
217 // We need to choose one value for the response.
218 response.SetClientMaxWindowBits(15);
219 }
220 DCHECK(response.IsValidAsResponse());
221 DCHECK(offer.IsCompatibleWith(response));
danakja9850e12016-04-18 22:28:08222 auto deflater = base::WrapUnique(
yhiranoa10dd4e2015-09-28 09:06:34223 new WebSocketDeflater(response.server_context_take_over_mode()));
danakja9850e12016-04-18 22:28:08224 auto inflater = base::WrapUnique(
yhiranoa10dd4e2015-09-28 09:06:34225 new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
226 if (!deflater->Initialize(response.PermissiveServerMaxWindowBits()) ||
227 !inflater->Initialize(response.PermissiveClientMaxWindowBits())) {
228 // For some reason we cannot accept the parameters.
229 continue;
230 }
231 *deflate_parameters = response;
danakja9850e12016-04-18 22:28:08232 return base::WrapUnique(new WebSocketEncoder(
233 FOR_SERVER, std::move(deflater), std::move(inflater)));
yhiranoa10dd4e2015-09-28 09:06:34234 }
235
236 // We cannot find an acceptable offer.
danakja9850e12016-04-18 22:28:08237 return base::WrapUnique(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21238}
239
240// static
danakja9850e12016-04-18 22:28:08241std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateClient(
dgozmana6e70092014-12-12 14:46:21242 const std::string& response_extensions) {
yhiranoa10dd4e2015-09-28 09:06:34243 // TODO(yhirano): Add a way to return an error.
dgozmana6e70092014-12-12 14:46:21244
dgozmana6e70092014-12-12 14:46:21245 WebSocketExtensionParser parser;
yhiranoa10dd4e2015-09-28 09:06:34246 if (!parser.Parse(response_extensions)) {
247 // Parse error. Note that there are two cases here.
248 // 1) There is no Sec-WebSocket-Extensions header.
249 // 2) There is a malformed Sec-WebSocketExtensions header.
250 // We should return a deflate-disabled encoder for the former case and
251 // fail the connection for the latter case.
danakja9850e12016-04-18 22:28:08252 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21253 }
yhiranoa10dd4e2015-09-28 09:06:34254 if (parser.extensions().size() != 1) {
255 // Only permessage-deflate extension is supported.
256 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08257 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34258 }
259 const auto& extension = parser.extensions()[0];
260 WebSocketDeflateParameters params;
261 std::string failure_message;
262 if (!params.Initialize(extension, &failure_message) ||
263 !params.IsValidAsResponse(&failure_message)) {
264 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08265 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34266 }
dgozmana6e70092014-12-12 14:46:21267
danakja9850e12016-04-18 22:28:08268 auto deflater = base::WrapUnique(
yhiranoa10dd4e2015-09-28 09:06:34269 new WebSocketDeflater(params.client_context_take_over_mode()));
danakja9850e12016-04-18 22:28:08270 auto inflater = base::WrapUnique(
dgozmana6e70092014-12-12 14:46:21271 new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
yhiranoa10dd4e2015-09-28 09:06:34272 if (!deflater->Initialize(params.PermissiveClientMaxWindowBits()) ||
273 !inflater->Initialize(params.PermissiveServerMaxWindowBits())) {
274 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08275 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21276 }
yhiranoa10dd4e2015-09-28 09:06:34277
danakja9850e12016-04-18 22:28:08278 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, std::move(deflater),
279 std::move(inflater)));
dgozmana6e70092014-12-12 14:46:21280}
281
yhiranoa10dd4e2015-09-28 09:06:34282WebSocketEncoder::WebSocketEncoder(Type type,
danakja9850e12016-04-18 22:28:08283 std::unique_ptr<WebSocketDeflater> deflater,
284 std::unique_ptr<WebSocketInflater> inflater)
dchengc7eeda422015-12-26 03:56:48285 : type_(type),
286 deflater_(std::move(deflater)),
287 inflater_(std::move(inflater)) {}
yhiranoa10dd4e2015-09-28 09:06:34288
289WebSocketEncoder::~WebSocketEncoder() {}
dgozmana6e70092014-12-12 14:46:21290
291WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
292 const base::StringPiece& frame,
293 int* bytes_consumed,
294 std::string* output) {
295 bool compressed;
yhiranoa10dd4e2015-09-28 09:06:34296 WebSocket::ParseResult result = DecodeFrameHybi17(
297 frame, type_ == FOR_SERVER, bytes_consumed, output, &compressed);
dgozmana6e70092014-12-12 14:46:21298 if (result == WebSocket::FRAME_OK && compressed) {
299 if (!Inflate(output))
300 result = WebSocket::FRAME_ERROR;
301 }
302 return result;
303}
304
305void WebSocketEncoder::EncodeFrame(const std::string& frame,
306 int masking_key,
307 std::string* output) {
308 std::string compressed;
309 if (Deflate(frame, &compressed))
310 EncodeFrameHybi17(compressed, masking_key, true, output);
311 else
312 EncodeFrameHybi17(frame, masking_key, false, output);
313}
314
315bool WebSocketEncoder::Inflate(std::string* message) {
316 if (!inflater_)
317 return false;
318 if (!inflater_->AddBytes(message->data(), message->length()))
319 return false;
320 if (!inflater_->Finish())
321 return false;
322
323 std::vector<char> output;
324 while (inflater_->CurrentOutputSize() > 0) {
325 scoped_refptr<IOBufferWithSize> chunk =
326 inflater_->GetOutput(inflater_->CurrentOutputSize());
327 if (!chunk.get())
328 return false;
329 output.insert(output.end(), chunk->data(), chunk->data() + chunk->size());
330 }
331
332 *message =
333 output.size() ? std::string(&output[0], output.size()) : std::string();
334 return true;
335}
336
337bool WebSocketEncoder::Deflate(const std::string& message,
338 std::string* output) {
339 if (!deflater_)
340 return false;
341 if (!deflater_->AddBytes(message.data(), message.length())) {
342 deflater_->Finish();
343 return false;
344 }
345 if (!deflater_->Finish())
346 return false;
347 scoped_refptr<IOBufferWithSize> buffer =
348 deflater_->GetOutput(deflater_->CurrentOutputSize());
349 if (!buffer.get())
350 return false;
351 *output = std::string(buffer->data(), buffer->size());
352 return true;
353}
354
355} // namespace net