blob: 812ab737d5f453c43a67a56894fb8dc582dea65f [file] [log] [blame]
Avi Drissman64595482022-09-14 20:52:291// Copyright 2014 The Chromium Authors
dgozmana6e70092014-12-12 14:46:212// 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
Hans Wennborg0924470b2020-04-27 21:08:0510#include "base/check.h"
danakja9850e12016-04-18 22:28:0811#include "base/memory/ptr_util.h"
Shiho Nodabc5f6fe02021-09-13 10:01:3512#include "base/strings/strcat.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"
Shiho Noda79b790fc42021-09-16 06:16:3918#include "net/websockets/websocket_frame.h"
dgozmana6e70092014-12-12 14:46:2119
20namespace net {
21
22const char WebSocketEncoder::kClientExtensions[] =
Eugene Ostroukhovdea96372017-11-22 23:16:0223 "permessage-deflate; client_max_window_bits";
dgozmana6e70092014-12-12 14:46:2124
25namespace {
26
27const int kInflaterChunkSize = 16 * 1024;
28
29// Constants for hybi-10 frame format.
30
dgozmana6e70092014-12-12 14:46:2131const unsigned char kFinalBit = 0x80;
32const unsigned char kReserved1Bit = 0x40;
33const unsigned char kReserved2Bit = 0x20;
34const unsigned char kReserved3Bit = 0x10;
35const unsigned char kOpCodeMask = 0xF;
36const unsigned char kMaskBit = 0x80;
37const unsigned char kPayloadLengthMask = 0x7F;
38
39const size_t kMaxSingleBytePayloadLength = 125;
40const size_t kTwoBytePayloadLengthField = 126;
41const size_t kEightBytePayloadLengthField = 127;
42const size_t kMaskingKeyWidthInBytes = 4;
43
David Benjaminb6c2dd162022-10-24 10:46:0644WebSocket::ParseResult DecodeFrameHybi17(base::StringPiece frame,
dgozmana6e70092014-12-12 14:46:2145 bool client_frame,
46 int* bytes_consumed,
47 std::string* output,
48 bool* compressed) {
49 size_t data_length = frame.length();
50 if (data_length < 2)
51 return WebSocket::FRAME_INCOMPLETE;
52
53 const char* buffer_begin = const_cast<char*>(frame.data());
54 const char* p = buffer_begin;
55 const char* buffer_end = p + data_length;
56
57 unsigned char first_byte = *p++;
58 unsigned char second_byte = *p++;
59
60 bool final = (first_byte & kFinalBit) != 0;
61 bool reserved1 = (first_byte & kReserved1Bit) != 0;
62 bool reserved2 = (first_byte & kReserved2Bit) != 0;
63 bool reserved3 = (first_byte & kReserved3Bit) != 0;
64 int op_code = first_byte & kOpCodeMask;
65 bool masked = (second_byte & kMaskBit) != 0;
66 *compressed = reserved1;
Shiho Nodabc5f6fe02021-09-13 10:01:3567 if (reserved2 || reserved3)
dgozmana6e70092014-12-12 14:46:2168 return WebSocket::FRAME_ERROR; // Only compression extension is supported.
69
70 bool closed = false;
71 switch (op_code) {
Shiho Noda79b790fc42021-09-16 06:16:3972 case WebSocketFrameHeader::OpCodeEnum::kOpCodeClose:
dgozmana6e70092014-12-12 14:46:2173 closed = true;
74 break;
Shiho Nodabc5f6fe02021-09-13 10:01:3575
Shiho Noda79b790fc42021-09-16 06:16:3976 case WebSocketFrameHeader::OpCodeEnum::kOpCodeText:
Shiho Noda79b790fc42021-09-16 06:16:3977 case WebSocketFrameHeader::OpCodeEnum::
78 kOpCodeContinuation: // Treated in the same as kOpCodeText.
Shiho Noda79b790fc42021-09-16 06:16:3979 case WebSocketFrameHeader::OpCodeEnum::kOpCodePing:
Shiho Noda2e39de62021-09-17 04:39:0180 case WebSocketFrameHeader::OpCodeEnum::kOpCodePong:
Shiho Noda79b790fc42021-09-16 06:16:3981 break;
82
83 case WebSocketFrameHeader::OpCodeEnum::kOpCodeBinary: // We don't support
84 // binary frames yet.
dgozmana6e70092014-12-12 14:46:2185 default:
86 return WebSocket::FRAME_ERROR;
87 }
88
ellyjonesc7a5c502015-06-26 18:55:2089 if (client_frame && !masked) // In Hybi-17 spec client MUST mask its frame.
dgozmana6e70092014-12-12 14:46:2190 return WebSocket::FRAME_ERROR;
91
Avi Drissman13fc8932015-12-20 04:40:4692 uint64_t payload_length64 = second_byte & kPayloadLengthMask;
dgozmana6e70092014-12-12 14:46:2193 if (payload_length64 > kMaxSingleBytePayloadLength) {
94 int extended_payload_length_size;
thestigcf729772016-11-19 04:35:0795 if (payload_length64 == kTwoBytePayloadLengthField) {
dgozmana6e70092014-12-12 14:46:2196 extended_payload_length_size = 2;
thestigcf729772016-11-19 04:35:0797 } else {
dgozmana6e70092014-12-12 14:46:2198 DCHECK(payload_length64 == kEightBytePayloadLengthField);
99 extended_payload_length_size = 8;
100 }
101 if (buffer_end - p < extended_payload_length_size)
102 return WebSocket::FRAME_INCOMPLETE;
103 payload_length64 = 0;
104 for (int i = 0; i < extended_payload_length_size; ++i) {
105 payload_length64 <<= 8;
106 payload_length64 |= static_cast<unsigned char>(*p++);
107 }
108 }
109
110 size_t actual_masking_key_length = masked ? kMaskingKeyWidthInBytes : 0;
Avi Drissman13fc8932015-12-20 04:40:46111 static const uint64_t max_payload_length = 0x7FFFFFFFFFFFFFFFull;
dgozmana6e70092014-12-12 14:46:21112 static size_t max_length = std::numeric_limits<size_t>::max();
113 if (payload_length64 > max_payload_length ||
114 payload_length64 + actual_masking_key_length > max_length) {
115 // WebSocket frame length too large.
116 return WebSocket::FRAME_ERROR;
117 }
118 size_t payload_length = static_cast<size_t>(payload_length64);
119
120 size_t total_length = actual_masking_key_length + payload_length;
121 if (static_cast<size_t>(buffer_end - p) < total_length)
122 return WebSocket::FRAME_INCOMPLETE;
123
124 if (masked) {
125 output->resize(payload_length);
126 const char* masking_key = p;
127 char* payload = const_cast<char*>(p + kMaskingKeyWidthInBytes);
128 for (size_t i = 0; i < payload_length; ++i) // Unmask the payload.
129 (*output)[i] = payload[i] ^ masking_key[i % kMaskingKeyWidthInBytes];
130 } else {
131 output->assign(p, p + payload_length);
132 }
133
134 size_t pos = p + actual_masking_key_length + payload_length - buffer_begin;
135 *bytes_consumed = pos;
Shiho Nodabc5f6fe02021-09-13 10:01:35136
Shiho Noda79b790fc42021-09-16 06:16:39137 if (op_code == WebSocketFrameHeader::OpCodeEnum::kOpCodePing)
138 return WebSocket::FRAME_PING;
Shiho Noda2e39de62021-09-17 04:39:01139
140 if (op_code == WebSocketFrameHeader::OpCodeEnum::kOpCodePong)
141 return WebSocket::FRAME_PONG;
142
Shiho Nodabc5f6fe02021-09-13 10:01:35143 if (closed)
144 return WebSocket::FRAME_CLOSE;
Shiho Noda2e39de62021-09-17 04:39:01145
Shiho Nodabc5f6fe02021-09-13 10:01:35146 return final ? WebSocket::FRAME_OK_FINAL : WebSocket::FRAME_OK_MIDDLE;
dgozmana6e70092014-12-12 14:46:21147}
148
Johannes Henkelda8b9d32019-03-15 16:15:33149void EncodeFrameHybi17(base::StringPiece message,
dgozmana6e70092014-12-12 14:46:21150 int masking_key,
151 bool compressed,
Shiho Noda79b790fc42021-09-16 06:16:39152 WebSocketFrameHeader::OpCodeEnum op_code,
dgozmana6e70092014-12-12 14:46:21153 std::string* output) {
154 std::vector<char> frame;
dgozmana6e70092014-12-12 14:46:21155 size_t data_length = message.length();
156
157 int reserved1 = compressed ? kReserved1Bit : 0;
158 frame.push_back(kFinalBit | op_code | reserved1);
159 char mask_key_bit = masking_key != 0 ? kMaskBit : 0;
brucedawsonef128242015-12-01 04:26:36160 if (data_length <= kMaxSingleBytePayloadLength) {
161 frame.push_back(static_cast<char>(data_length) | mask_key_bit);
162 } else if (data_length <= 0xFFFF) {
dgozmana6e70092014-12-12 14:46:21163 frame.push_back(kTwoBytePayloadLengthField | mask_key_bit);
164 frame.push_back((data_length & 0xFF00) >> 8);
165 frame.push_back(data_length & 0xFF);
166 } else {
167 frame.push_back(kEightBytePayloadLengthField | mask_key_bit);
168 char extended_payload_length[8];
169 size_t remaining = data_length;
170 // Fill the length into extended_payload_length in the network byte order.
171 for (int i = 0; i < 8; ++i) {
172 extended_payload_length[7 - i] = remaining & 0xFF;
173 remaining >>= 8;
174 }
175 frame.insert(frame.end(), extended_payload_length,
176 extended_payload_length + 8);
177 DCHECK(!remaining);
178 }
179
180 const char* data = const_cast<char*>(message.data());
181 if (masking_key != 0) {
182 const char* mask_bytes = reinterpret_cast<char*>(&masking_key);
183 frame.insert(frame.end(), mask_bytes, mask_bytes + 4);
184 for (size_t i = 0; i < data_length; ++i) // Mask the payload.
185 frame.push_back(data[i] ^ mask_bytes[i % kMaskingKeyWidthInBytes]);
186 } else {
187 frame.insert(frame.end(), data, data + data_length);
188 }
David Benjamin739ef112019-11-01 04:21:09189 *output = std::string(frame.data(), frame.size());
dgozmana6e70092014-12-12 14:46:21190}
191
192} // anonymous namespace
193
194// static
danakja9850e12016-04-18 22:28:08195std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer() {
196 return base::WrapUnique(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34197}
dgozmana6e70092014-12-12 14:46:21198
yhiranoa10dd4e2015-09-28 09:06:34199// static
danakja9850e12016-04-18 22:28:08200std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer(
yhiranoa10dd4e2015-09-28 09:06:34201 const std::string& extensions,
202 WebSocketDeflateParameters* deflate_parameters) {
203 WebSocketExtensionParser parser;
204 if (!parser.Parse(extensions)) {
205 // Failed to parse Sec-WebSocket-Extensions header. We MUST fail the
206 // connection.
207 return nullptr;
dgozmana6e70092014-12-12 14:46:21208 }
yhiranoa10dd4e2015-09-28 09:06:34209
210 for (const auto& extension : parser.extensions()) {
211 std::string failure_message;
212 WebSocketDeflateParameters offer;
213 if (!offer.Initialize(extension, &failure_message) ||
214 !offer.IsValidAsRequest(&failure_message)) {
215 // We decline unknown / malformed extensions.
216 continue;
217 }
218
219 WebSocketDeflateParameters response = offer;
220 if (offer.is_client_max_window_bits_specified() &&
221 !offer.has_client_max_window_bits_value()) {
222 // We need to choose one value for the response.
223 response.SetClientMaxWindowBits(15);
224 }
225 DCHECK(response.IsValidAsResponse());
226 DCHECK(offer.IsCompatibleWith(response));
Jeremy Roman0579ed62017-08-29 15:56:19227 auto deflater = std::make_unique<WebSocketDeflater>(
ricea2deef682016-09-09 08:04:07228 response.server_context_take_over_mode());
Jeremy Roman0579ed62017-08-29 15:56:19229 auto inflater = std::make_unique<WebSocketInflater>(kInflaterChunkSize,
ricea2deef682016-09-09 08:04:07230 kInflaterChunkSize);
yhiranoa10dd4e2015-09-28 09:06:34231 if (!deflater->Initialize(response.PermissiveServerMaxWindowBits()) ||
232 !inflater->Initialize(response.PermissiveClientMaxWindowBits())) {
233 // For some reason we cannot accept the parameters.
234 continue;
235 }
236 *deflate_parameters = response;
danakja9850e12016-04-18 22:28:08237 return base::WrapUnique(new WebSocketEncoder(
238 FOR_SERVER, std::move(deflater), std::move(inflater)));
yhiranoa10dd4e2015-09-28 09:06:34239 }
240
241 // We cannot find an acceptable offer.
danakja9850e12016-04-18 22:28:08242 return base::WrapUnique(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21243}
244
245// static
danakja9850e12016-04-18 22:28:08246std::unique_ptr<WebSocketEncoder> WebSocketEncoder::CreateClient(
dgozmana6e70092014-12-12 14:46:21247 const std::string& response_extensions) {
yhiranoa10dd4e2015-09-28 09:06:34248 // TODO(yhirano): Add a way to return an error.
dgozmana6e70092014-12-12 14:46:21249
dgozmana6e70092014-12-12 14:46:21250 WebSocketExtensionParser parser;
yhiranoa10dd4e2015-09-28 09:06:34251 if (!parser.Parse(response_extensions)) {
252 // Parse error. Note that there are two cases here.
253 // 1) There is no Sec-WebSocket-Extensions header.
254 // 2) There is a malformed Sec-WebSocketExtensions header.
255 // We should return a deflate-disabled encoder for the former case and
256 // fail the connection for the latter case.
danakja9850e12016-04-18 22:28:08257 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21258 }
yhiranoa10dd4e2015-09-28 09:06:34259 if (parser.extensions().size() != 1) {
260 // Only permessage-deflate extension is supported.
261 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08262 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34263 }
264 const auto& extension = parser.extensions()[0];
265 WebSocketDeflateParameters params;
266 std::string failure_message;
267 if (!params.Initialize(extension, &failure_message) ||
268 !params.IsValidAsResponse(&failure_message)) {
269 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08270 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
yhiranoa10dd4e2015-09-28 09:06:34271 }
dgozmana6e70092014-12-12 14:46:21272
Jeremy Roman0579ed62017-08-29 15:56:19273 auto deflater = std::make_unique<WebSocketDeflater>(
ricea2deef682016-09-09 08:04:07274 params.client_context_take_over_mode());
Jeremy Roman0579ed62017-08-29 15:56:19275 auto inflater = std::make_unique<WebSocketInflater>(kInflaterChunkSize,
ricea2deef682016-09-09 08:04:07276 kInflaterChunkSize);
yhiranoa10dd4e2015-09-28 09:06:34277 if (!deflater->Initialize(params.PermissiveClientMaxWindowBits()) ||
278 !inflater->Initialize(params.PermissiveServerMaxWindowBits())) {
279 // TODO (yhirano): Fail the connection.
danakja9850e12016-04-18 22:28:08280 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr));
dgozmana6e70092014-12-12 14:46:21281 }
yhiranoa10dd4e2015-09-28 09:06:34282
danakja9850e12016-04-18 22:28:08283 return base::WrapUnique(new WebSocketEncoder(FOR_CLIENT, std::move(deflater),
284 std::move(inflater)));
dgozmana6e70092014-12-12 14:46:21285}
286
yhiranoa10dd4e2015-09-28 09:06:34287WebSocketEncoder::WebSocketEncoder(Type type,
danakja9850e12016-04-18 22:28:08288 std::unique_ptr<WebSocketDeflater> deflater,
289 std::unique_ptr<WebSocketInflater> inflater)
dchengc7eeda422015-12-26 03:56:48290 : type_(type),
291 deflater_(std::move(deflater)),
292 inflater_(std::move(inflater)) {}
yhiranoa10dd4e2015-09-28 09:06:34293
Chris Watkins7a41d3552017-12-01 02:13:27294WebSocketEncoder::~WebSocketEncoder() = default;
dgozmana6e70092014-12-12 14:46:21295
David Benjaminb6c2dd162022-10-24 10:46:06296WebSocket::ParseResult WebSocketEncoder::DecodeFrame(base::StringPiece frame,
297 int* bytes_consumed,
298 std::string* output) {
dgozmana6e70092014-12-12 14:46:21299 bool compressed;
Shiho Nodabc5f6fe02021-09-13 10:01:35300 std::string current_output;
yhiranoa10dd4e2015-09-28 09:06:34301 WebSocket::ParseResult result = DecodeFrameHybi17(
Shiho Nodabc5f6fe02021-09-13 10:01:35302 frame, type_ == FOR_SERVER, bytes_consumed, &current_output, &compressed);
Adam Rice16be071d2021-10-27 09:13:20303 switch (result) {
304 case WebSocket::FRAME_OK_FINAL:
305 case WebSocket::FRAME_OK_MIDDLE: {
306 if (continuation_message_frames_.empty())
307 is_current_message_compressed_ = compressed;
308 continuation_message_frames_.push_back(current_output);
309
310 if (result == WebSocket::FRAME_OK_FINAL) {
311 *output = base::StrCat(continuation_message_frames_);
312 continuation_message_frames_.clear();
313 if (is_current_message_compressed_ && !Inflate(output)) {
314 return WebSocket::FRAME_ERROR;
315 }
316 }
317 break;
Shiho Nodabc5f6fe02021-09-13 10:01:35318 }
Adam Rice16be071d2021-10-27 09:13:20319
320 case WebSocket::FRAME_PING:
321 *output = current_output;
322 break;
323
324 default:
325 // This function doesn't need special handling for other parse results.
326 break;
Shiho Nodabc5f6fe02021-09-13 10:01:35327 }
Adam Rice16be071d2021-10-27 09:13:20328
dgozmana6e70092014-12-12 14:46:21329 return result;
330}
331
Shiho Noda79b790fc42021-09-16 06:16:39332void WebSocketEncoder::EncodeTextFrame(base::StringPiece frame,
333 int masking_key,
334 std::string* output) {
dgozmana6e70092014-12-12 14:46:21335 std::string compressed;
Shiho Noda79b790fc42021-09-16 06:16:39336 constexpr auto op_code = WebSocketFrameHeader::OpCodeEnum::kOpCodeText;
dgozmana6e70092014-12-12 14:46:21337 if (Deflate(frame, &compressed))
Shiho Noda79b790fc42021-09-16 06:16:39338 EncodeFrameHybi17(compressed, masking_key, true, op_code, output);
dgozmana6e70092014-12-12 14:46:21339 else
Shiho Noda79b790fc42021-09-16 06:16:39340 EncodeFrameHybi17(frame, masking_key, false, op_code, output);
341}
342
343void WebSocketEncoder::EncodePongFrame(base::StringPiece frame,
344 int masking_key,
345 std::string* output) {
346 constexpr auto op_code = WebSocketFrameHeader::OpCodeEnum::kOpCodePong;
347 EncodeFrameHybi17(frame, masking_key, false, op_code, output);
dgozmana6e70092014-12-12 14:46:21348}
349
350bool WebSocketEncoder::Inflate(std::string* message) {
351 if (!inflater_)
352 return false;
353 if (!inflater_->AddBytes(message->data(), message->length()))
354 return false;
355 if (!inflater_->Finish())
356 return false;
357
358 std::vector<char> output;
359 while (inflater_->CurrentOutputSize() > 0) {
360 scoped_refptr<IOBufferWithSize> chunk =
361 inflater_->GetOutput(inflater_->CurrentOutputSize());
362 if (!chunk.get())
363 return false;
364 output.insert(output.end(), chunk->data(), chunk->data() + chunk->size());
365 }
366
367 *message =
David Benjamin739ef112019-11-01 04:21:09368 output.size() ? std::string(output.data(), output.size()) : std::string();
dgozmana6e70092014-12-12 14:46:21369 return true;
370}
371
Johannes Henkelda8b9d32019-03-15 16:15:33372bool WebSocketEncoder::Deflate(base::StringPiece message, std::string* output) {
dgozmana6e70092014-12-12 14:46:21373 if (!deflater_)
374 return false;
375 if (!deflater_->AddBytes(message.data(), message.length())) {
376 deflater_->Finish();
377 return false;
378 }
379 if (!deflater_->Finish())
380 return false;
381 scoped_refptr<IOBufferWithSize> buffer =
382 deflater_->GetOutput(deflater_->CurrentOutputSize());
383 if (!buffer.get())
384 return false;
385 *output = std::string(buffer->data(), buffer->size());
386 return true;
387}
388
389} // namespace net