blob: 772cf7d6cbe7428ef85110fac5c66a814d060a9a [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"
dgozmana6e70092014-12-12 14:46:2114#include "net/base/io_buffer.h"
yhiranoa10dd4e2015-09-28 09:06:3415#include "net/websockets/websocket_deflate_parameters.h"
16#include "net/websockets/websocket_extension.h"
dgozmana6e70092014-12-12 14:46:2117#include "net/websockets/websocket_extension_parser.h"
18
19namespace net {
20
21const char WebSocketEncoder::kClientExtensions[] =
Eugene Ostroukhovdea96372017-11-22 23:16:0222 "permessage-deflate; client_max_window_bits";
dgozmana6e70092014-12-12 14:46:2123
24namespace {
25
26const int kInflaterChunkSize = 16 * 1024;
27
28// Constants for hybi-10 frame format.
29
30typedef int OpCode;
31
32const OpCode kOpCodeContinuation = 0x0;
33const OpCode kOpCodeText = 0x1;
34const OpCode kOpCodeBinary = 0x2;
35const OpCode kOpCodeClose = 0x8;
36const OpCode kOpCodePing = 0x9;
37const OpCode kOpCodePong = 0xA;
38
39const unsigned char kFinalBit = 0x80;
40const unsigned char kReserved1Bit = 0x40;
41const unsigned char kReserved2Bit = 0x20;
42const unsigned char kReserved3Bit = 0x10;
43const unsigned char kOpCodeMask = 0xF;
44const unsigned char kMaskBit = 0x80;
45const unsigned char kPayloadLengthMask = 0x7F;
46
47const size_t kMaxSingleBytePayloadLength = 125;
48const size_t kTwoBytePayloadLengthField = 126;
49const size_t kEightBytePayloadLengthField = 127;
50const size_t kMaskingKeyWidthInBytes = 4;
51
52WebSocket::ParseResult DecodeFrameHybi17(const base::StringPiece& frame,
53 bool client_frame,
54 int* bytes_consumed,
55 std::string* output,
56 bool* compressed) {
57 size_t data_length = frame.length();
58 if (data_length < 2)
59 return WebSocket::FRAME_INCOMPLETE;
60
61 const char* buffer_begin = const_cast<char*>(frame.data());
62 const char* p = buffer_begin;
63 const char* buffer_end = p + data_length;
64
65 unsigned char first_byte = *p++;
66 unsigned char second_byte = *p++;
67
68 bool final = (first_byte & kFinalBit) != 0;
69 bool reserved1 = (first_byte & kReserved1Bit) != 0;
70 bool reserved2 = (first_byte & kReserved2Bit) != 0;
71 bool reserved3 = (first_byte & kReserved3Bit) != 0;
72 int op_code = first_byte & kOpCodeMask;
73 bool masked = (second_byte & kMaskBit) != 0;
74 *compressed = reserved1;
75 if (!final || reserved2 || reserved3)
76 return WebSocket::FRAME_ERROR; // Only compression extension is supported.
77
78 bool closed = false;
79 switch (op_code) {
80 case kOpCodeClose:
81 closed = true;
82 break;
83 case kOpCodeText:
84 break;
85 case kOpCodeBinary: // We don't support binary frames yet.
86 case kOpCodeContinuation: // We don't support binary frames yet.
87 case kOpCodePing: // We don't support binary frames yet.
88 case kOpCodePong: // We don't support binary frames yet.
89 default:
90 return WebSocket::FRAME_ERROR;
91 }
92
ellyjonesc7a5c502015-06-26 18:55:2093 if (client_frame && !masked) // In Hybi-17 spec client MUST mask its frame.
dgozmana6e70092014-12-12 14:46:2194 return WebSocket::FRAME_ERROR;
95
Avi Drissman13fc8932015-12-20 04:40:4696 uint64_t payload_length64 = second_byte & kPayloadLengthMask;
dgozmana6e70092014-12-12 14:46:2197 if (payload_length64 > kMaxSingleBytePayloadLength) {
98 int extended_payload_length_size;
thestigcf729772016-11-19 04:35:0799 if (payload_length64 == kTwoBytePayloadLengthField) {
dgozmana6e70092014-12-12 14:46:21100 extended_payload_length_size = 2;
thestigcf729772016-11-19 04:35:07101 } else {
dgozmana6e70092014-12-12 14:46:21102 DCHECK(payload_length64 == kEightBytePayloadLengthField);
103 extended_payload_length_size = 8;
104 }
105 if (buffer_end - p < extended_payload_length_size)
106 return WebSocket::FRAME_INCOMPLETE;
107 payload_length64 = 0;
108 for (int i = 0; i < extended_payload_length_size; ++i) {
109 payload_length64 <<= 8;
110 payload_length64 |= static_cast<unsigned char>(*p++);
111 }
112 }
113
114 size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
Avi Drissman13fc8932015-12-20 04:40:46115 static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
dgozmana6e70092014-12-12 14:46:21116 static size_t max_length = std::numeric_limits<size_t>::max();
117 if (payload_length64 > max_payload_length ||
118 payload_length64 + actual_masking_key_length > max_length) {
119 // WebSocket frame length too large.
120 return WebSocket::FRAME_ERROR;
121 }
122 size_t payload_length = static_cast<size_t>(payload_length64);
123
124 size_t total_length = actual_masking_key_length + payload_length;
125 if (static_cast<size_t>(buffer_end - p) < total_length)
126 return WebSocket::FRAME_INCOMPLETE;
127
128 if (masked) {
129 output->resize(payload_length);
130 const char* masking_key = p;
131 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
132 for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
133 (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
134 } else {
135 output->assign(p, p + payload_length);
136 }
137
138 size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
139 *bytes_consumed = pos;
140 return closed ? WebSocket::FRAME_CLOSE : WebSocket::FRAME_OK;
141}
142
Johannes Henkelda8b9d32019-03-15 16:15:33143void EncodeFrameHybi17(base::StringPiece message,
dgozmana6e70092014-12-12 14:46:21144 int masking_key,
145 bool compressed,
146 std::string* output) {
147 std::vector<char> frame;
148 OpCode op_code = kOpCodeText;
149 size_t data_length = message.length();
150
151 int reserved1 = compressed ? kReserved1Bit : 0;
152 frame.push_back(kFinalBit | op_code | reserved1);
153 char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
brucedawsonef128242015-12-01 04:26:36154 if (data_length <= kMaxSingleBytePayloadLength) {
155 frame.push_back(static_cast<char>(data_length) | mask_key_bit);
156 } else if (data_length <= 0xFFFF) {
dgozmana6e70092014-12-12 14:46:21157 frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
158 frame.push_back((data_length & 0xFF00) >> 8);
159 frame.push_back(data_length & 0xFF);
160 } else {
161 frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
162 char extended_payload_length[8];
163 size_t remaining = data_length;
164 // Fill the length into extended_payload_length in the network byte order.
165 for (int i = 0; i < 8; ++i) {
166 extended_payload_length[7 - i] = remaining & 0xFF;
167 remaining >>= 8;
168 }
169 frame.insert(frame.end(), extended_payload_length,
170 extended_payload_length + 8);
171 DCHECK(!remaining);
172 }
173
174 const char* data = const_cast<char*>(message.data());
175 if (masking_key != 0) {
176 const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
177 frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
178 for (size_t i = 0; i < data_length; ++i) // Mask the payload.
179 frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
180 } else {
181 frame.insert(frame.end(), data, data + data_length);
182 }
183 *output = std::string(&frame[0], frame.size());
184}
185
186} // anonymous namespace
187
188// static
danakja9850e12016-04-18 22:28:08189std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer() {
190 return base::WrapUnique(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34191}
dgozmana6e70092014-12-12 14:46:21192
yhiranoa10dd4e2015-09-28 09:06:34193// static
danakja9850e12016-04-18 22:28:08194std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer(
yhiranoa10dd4e2015-09-28 09:06:34195 const std::string& extensions,
196 WebSocketDeflateParameters* deflate_parameters) {
197 WebSocketExtensionParser parser;
198 if (!parser.Parse(extensions)) {
199 // Failed to parse Sec-WebSocket-Extensions header. We MUST fail the
200 // connection.
201 return nullptr;
dgozmana6e70092014-12-12 14:46:21202 }
yhiranoa10dd4e2015-09-28 09:06:34203
204 for (const auto& extension : parser.extensions()) {
205 std::string failure_message;
206 WebSocketDeflateParameters offer;
207 if (!offer.Initialize(extension, &failure_message) ||
208 !offer.IsValidAsRequest(&failure_message)) {
209 // We decline unknown / malformed extensions.
210 continue;
211 }
212
213 WebSocketDeflateParameters response = offer;
214 if (offer.is_client_max_window_bits_specified() &&
215 !offer.has_client_max_window_bits_value()) {
216 // We need to choose one value for the response.
217 response.SetClientMaxWindowBits(15);
218 }
219 DCHECK(response.IsValidAsResponse());
220 DCHECK(offer.IsCompatibleWith(response));
Jeremy Roman0579ed62017-08-29 15:56:19221 auto deflater = std::make_unique<WebSocketDeflater>(
ricea2deef682016-09-09 08:04:07222 response.server_context_take_over_mode());
Jeremy Roman0579ed62017-08-29 15:56:19223 auto inflater = std::make_unique<WebSocketInflater>(kInflaterChunkSize,
ricea2deef682016-09-09 08:04:07224 kInflaterChunkSize);
yhiranoa10dd4e2015-09-28 09:06:34225 if (!deflater->Initialize(response.PermissiveServerMaxWindowBits()) ||
226 !inflater->Initialize(response.PermissiveClientMaxWindowBits())) {
227 // For some reason we cannot accept the parameters.
228 continue;
229 }
230 *deflate_parameters = response;
danakja9850e12016-04-18 22:28:08231 return base::WrapUnique(new WebSocketEncoder(
232 FOR_SERVER, std::move(deflater), std::move(inflater)));
yhiranoa10dd4e2015-09-28 09:06:34233 }
234
235 // We cannot find an acceptable offer.
danakja9850e12016-04-18 22:28:08236 return base::WrapUnique(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21237}
238
239// static
danakja9850e12016-04-18 22:28:08240std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateClient(
dgozmana6e70092014-12-12 14:46:21241 const std::string& response_extensions) {
yhiranoa10dd4e2015-09-28 09:06:34242 // TODO(yhirano): Add a way to return an error.
dgozmana6e70092014-12-12 14:46:21243
dgozmana6e70092014-12-12 14:46:21244 WebSocketExtensionParser parser;
yhiranoa10dd4e2015-09-28 09:06:34245 if (!parser.Parse(response_extensions)) {
246 // Parse error. Note that there are two cases here.
247 // 1) There is no Sec-WebSocket-Extensions header.
248 // 2) There is a malformed Sec-WebSocketExtensions header.
249 // We should return a deflate-disabled encoder for the former case and
250 // fail the connection for the latter case.
danakja9850e12016-04-18 22:28:08251 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21252 }
yhiranoa10dd4e2015-09-28 09:06:34253 if (parser.extensions().size() != 1) {
254 // Only permessage-deflate extension is supported.
255 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08256 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34257 }
258 const auto& extension = parser.extensions()[0];
259 WebSocketDeflateParameters params;
260 std::string failure_message;
261 if (!params.Initialize(extension, &failure_message) ||
262 !params.IsValidAsResponse(&failure_message)) {
263 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08264 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34265 }
dgozmana6e70092014-12-12 14:46:21266
Jeremy Roman0579ed62017-08-29 15:56:19267 auto deflater = std::make_unique<WebSocketDeflater>(
ricea2deef682016-09-09 08:04:07268 params.client_context_take_over_mode());
Jeremy Roman0579ed62017-08-29 15:56:19269 auto inflater = std::make_unique<WebSocketInflater>(kInflaterChunkSize,
ricea2deef682016-09-09 08:04:07270 kInflaterChunkSize);
yhiranoa10dd4e2015-09-28 09:06:34271 if (!deflater->Initialize(params.PermissiveClientMaxWindowBits()) ||
272 !inflater->Initialize(params.PermissiveServerMaxWindowBits())) {
273 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08274 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21275 }
yhiranoa10dd4e2015-09-28 09:06:34276
danakja9850e12016-04-18 22:28:08277 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, std::move(deflater),
278 std::move(inflater)));
dgozmana6e70092014-12-12 14:46:21279}
280
yhiranoa10dd4e2015-09-28 09:06:34281WebSocketEncoder::WebSocketEncoder(Type type,
danakja9850e12016-04-18 22:28:08282 std::unique_ptr<WebSocketDeflater> deflater,
283 std::unique_ptr<WebSocketInflater> inflater)
dchengc7eeda422015-12-26 03:56:48284 : type_(type),
285 deflater_(std::move(deflater)),
286 inflater_(std::move(inflater)) {}
yhiranoa10dd4e2015-09-28 09:06:34287
Chris Watkins7a41d3552017-12-01 02:13:27288WebSocketEncoder::~WebSocketEncoder() = default;
dgozmana6e70092014-12-12 14:46:21289
290WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
291 const base::StringPiece& frame,
292 int* bytes_consumed,
293 std::string* output) {
294 bool compressed;
yhiranoa10dd4e2015-09-28 09:06:34295 WebSocket::ParseResult result = DecodeFrameHybi17(
296 frame, type_ == FOR_SERVER, bytes_consumed, output, &compressed);
dgozmana6e70092014-12-12 14:46:21297 if (result == WebSocket::FRAME_OK && compressed) {
298 if (!Inflate(output))
299 result = WebSocket::FRAME_ERROR;
300 }
301 return result;
302}
303
Johannes Henkelda8b9d32019-03-15 16:15:33304void WebSocketEncoder::EncodeFrame(base::StringPiece frame,
dgozmana6e70092014-12-12 14:46:21305 int masking_key,
306 std::string* output) {
307 std::string compressed;
308 if (Deflate(frame, &compressed))
309 EncodeFrameHybi17(compressed, masking_key, true, output);
310 else
311 EncodeFrameHybi17(frame, masking_key, false, output);
312}
313
314bool WebSocketEncoder::Inflate(std::string* message) {
315 if (!inflater_)
316 return false;
317 if (!inflater_->AddBytes(message->data(), message->length()))
318 return false;
319 if (!inflater_->Finish())
320 return false;
321
322 std::vector<char> output;
323 while (inflater_->CurrentOutputSize() > 0) {
324 scoped_refptr<IOBufferWithSize> chunk =
325 inflater_->GetOutput(inflater_->CurrentOutputSize());
326 if (!chunk.get())
327 return false;
328 output.insert(output.end(), chunk->data(), chunk->data() + chunk->size());
329 }
330
331 *message =
332 output.size() ? std::string(&output[0], output.size()) : std::string();
333 return true;
334}
335
Johannes Henkelda8b9d32019-03-15 16:15:33336bool WebSocketEncoder::Deflate(base::StringPiece message, std::string* output) {
dgozmana6e70092014-12-12 14:46:21337 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