Serenity Operating System
1/*
2 * Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
3 * Copyright (c) 2022, the SerenityOS developers.
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#include <AK/Base64.h>
9#include <AK/Random.h>
10#include <LibCrypto/Hash/HashManager.h>
11#include <LibWebSocket/Impl/WebSocketImplSerenity.h>
12#include <LibWebSocket/WebSocket.h>
13#include <unistd.h>
14
15namespace WebSocket {
16
17// Note : The websocket protocol is defined by RFC 6455, found at https://tools.ietf.org/html/rfc6455
18// In this file, section numbers will refer to the RFC 6455
19
20NonnullRefPtr<WebSocket> WebSocket::create(ConnectionInfo connection, RefPtr<WebSocketImpl> impl)
21{
22 return adopt_ref(*new WebSocket(move(connection), move(impl)));
23}
24
25WebSocket::WebSocket(ConnectionInfo connection, RefPtr<WebSocketImpl> impl)
26 : m_connection(move(connection))
27 , m_impl(move(impl))
28{
29}
30
31void WebSocket::start()
32{
33 VERIFY(m_state == WebSocket::InternalState::NotStarted);
34
35 if (!m_impl)
36 m_impl = adopt_ref(*new WebSocketImplSerenity);
37
38 m_impl->on_connection_error = [this] {
39 dbgln("WebSocket: Connection error (underlying socket)");
40 fatal_error(WebSocket::Error::CouldNotEstablishConnection);
41 };
42 m_impl->on_connected = [this] {
43 if (m_state != WebSocket::InternalState::EstablishingProtocolConnection)
44 return;
45 m_state = WebSocket::InternalState::SendingClientHandshake;
46 send_client_handshake();
47 drain_read();
48 };
49 m_impl->on_ready_to_read = [this] {
50 drain_read();
51 };
52 m_state = WebSocket::InternalState::EstablishingProtocolConnection;
53 m_impl->connect(m_connection);
54}
55
56ReadyState WebSocket::ready_state()
57{
58 switch (m_state) {
59 case WebSocket::InternalState::NotStarted:
60 case WebSocket::InternalState::EstablishingProtocolConnection:
61 case WebSocket::InternalState::SendingClientHandshake:
62 case WebSocket::InternalState::WaitingForServerHandshake:
63 return ReadyState::Connecting;
64 case WebSocket::InternalState::Open:
65 return ReadyState::Open;
66 case WebSocket::InternalState::Closing:
67 return ReadyState::Closing;
68 case WebSocket::InternalState::Closed:
69 case WebSocket::InternalState::Errored:
70 return ReadyState::Closed;
71 default:
72 VERIFY_NOT_REACHED();
73 return ReadyState::Closed;
74 }
75}
76
77DeprecatedString WebSocket::subprotocol_in_use()
78{
79 return m_subprotocol_in_use;
80}
81
82void WebSocket::send(Message const& message)
83{
84 // Calling send on a socket that is not opened is not allowed
85 VERIFY(m_state == WebSocket::InternalState::Open);
86 VERIFY(m_impl);
87 if (message.is_text())
88 send_frame(WebSocket::OpCode::Text, message.data(), true);
89 else
90 send_frame(WebSocket::OpCode::Binary, message.data(), true);
91}
92
93void WebSocket::close(u16 code, DeprecatedString const& message)
94{
95 VERIFY(m_impl);
96
97 switch (m_state) {
98 case InternalState::NotStarted:
99 case InternalState::EstablishingProtocolConnection:
100 case InternalState::SendingClientHandshake:
101 case InternalState::WaitingForServerHandshake:
102 // FIXME: Fail the connection.
103 m_state = InternalState::Closing;
104 break;
105 case InternalState::Open: {
106 auto message_bytes = message.bytes();
107 auto close_payload = ByteBuffer::create_uninitialized(message_bytes.size() + 2).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation.
108 close_payload.overwrite(0, (u8*)&code, 2);
109 close_payload.overwrite(2, message_bytes.data(), message_bytes.size());
110 send_frame(WebSocket::OpCode::ConnectionClose, close_payload, true);
111 m_state = InternalState::Closing;
112 break;
113 }
114 default:
115 break;
116 }
117}
118
119void WebSocket::drain_read()
120{
121 if (m_impl->eof()) {
122 // The connection got closed by the server
123 m_state = WebSocket::InternalState::Closed;
124 notify_close(m_last_close_code, m_last_close_message, true);
125 discard_connection();
126 return;
127 }
128
129 switch (m_state) {
130 case InternalState::NotStarted:
131 case InternalState::EstablishingProtocolConnection:
132 case InternalState::SendingClientHandshake: {
133 auto initializing_bytes = m_impl->read(1024);
134 if (!initializing_bytes.is_error())
135 dbgln("drain_read() was called on a websocket that isn't opened yet. Read {} bytes from the socket.", initializing_bytes.value().size());
136 } break;
137 case InternalState::WaitingForServerHandshake: {
138 read_server_handshake();
139 } break;
140 case InternalState::Open:
141 case InternalState::Closing: {
142 auto result = m_impl->read(65536);
143 if (result.is_error()) {
144 fatal_error(WebSocket::Error::ServerClosedSocket);
145 return;
146 }
147 auto bytes = result.release_value();
148 m_buffered_data.append(bytes.data(), bytes.size());
149 read_frame();
150 } break;
151 case InternalState::Closed:
152 case InternalState::Errored: {
153 auto closed_bytes = m_impl->read(1024);
154 if (!closed_bytes.is_error())
155 dbgln("drain_read() was called on a closed websocket. Read {} bytes from the socket.", closed_bytes.value().size());
156 } break;
157 default:
158 VERIFY_NOT_REACHED();
159 }
160}
161
162// The client handshake message is defined in the second list of section 4.1
163void WebSocket::send_client_handshake()
164{
165 VERIFY(m_impl);
166 VERIFY(m_state == WebSocket::InternalState::SendingClientHandshake);
167 StringBuilder builder;
168
169 // 2. and 3. GET /resource name/ HTTP 1.1
170 builder.appendff("GET {} HTTP/1.1\r\n", m_connection.resource_name());
171
172 // 4. Host
173 auto url = m_connection.url();
174 builder.appendff("Host: {}", url.host());
175 if (!m_connection.is_secure() && url.port_or_default() != 80)
176 builder.appendff(":{}", url.port_or_default());
177 else if (m_connection.is_secure() && url.port_or_default() != 443)
178 builder.appendff(":{}", url.port_or_default());
179 builder.append("\r\n"sv);
180
181 // 5. and 6. Connection Upgrade
182 builder.append("Upgrade: websocket\r\n"sv);
183 builder.append("Connection: Upgrade\r\n"sv);
184
185 // 7. 16-byte nonce encoded as Base64
186 u8 nonce_data[16];
187 fill_with_random(nonce_data, 16);
188 // FIXME: change to TRY() and make method fallible
189 m_websocket_key = MUST(encode_base64({ nonce_data, 16 })).to_deprecated_string();
190 builder.appendff("Sec-WebSocket-Key: {}\r\n", m_websocket_key);
191
192 // 8. Origin (optional field)
193 if (!m_connection.origin().is_empty()) {
194 builder.appendff("Origin: {}\r\n", m_connection.origin());
195 }
196
197 // 9. Websocket version
198 builder.append("Sec-WebSocket-Version: 13\r\n"sv);
199
200 // 10. Websocket protocol (optional field)
201 if (!m_connection.protocols().is_empty()) {
202 builder.append("Sec-WebSocket-Protocol: "sv);
203 builder.join(',', m_connection.protocols());
204 builder.append("\r\n"sv);
205 }
206
207 // 11. Websocket extensions (optional field)
208 if (!m_connection.extensions().is_empty()) {
209 builder.append("Sec-WebSocket-Extensions: "sv);
210 builder.join(',', m_connection.extensions());
211 builder.append("\r\n"sv);
212 }
213
214 // 12. Additional headers
215 for (auto& header : m_connection.headers()) {
216 builder.appendff("{}: {}\r\n", header.name, header.value);
217 }
218
219 builder.append("\r\n"sv);
220
221 m_state = WebSocket::InternalState::WaitingForServerHandshake;
222 auto success = m_impl->send(builder.to_deprecated_string().bytes());
223 VERIFY(success);
224}
225
226// The server handshake message is defined in the third list of section 4.1
227void WebSocket::read_server_handshake()
228{
229 VERIFY(m_impl);
230 VERIFY(m_state == WebSocket::InternalState::WaitingForServerHandshake);
231 // Read the server handshake
232 if (!m_impl->can_read_line())
233 return;
234
235 if (!m_has_read_server_handshake_first_line) {
236 auto header = m_impl->read_line(PAGE_SIZE).release_value_but_fixme_should_propagate_errors();
237 auto parts = header.split(' ');
238 if (parts.size() < 2) {
239 dbgln("WebSocket: Server HTTP Handshake contained HTTP header was malformed");
240 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
241 discard_connection();
242 return;
243 }
244 if (parts[0] != "HTTP/1.1") {
245 dbgln("WebSocket: Server HTTP Handshake contained HTTP header {} which isn't supported", parts[0]);
246 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
247 discard_connection();
248 return;
249 }
250 if (parts[1] != "101") {
251 // 1. If the status code is not 101, handle as per HTTP procedures.
252 // FIXME : This could be a redirect or a 401 authentication request, which we do not handle.
253 dbgln("WebSocket: Server HTTP Handshake return status {} which isn't supported", parts[1]);
254 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
255 return;
256 }
257 m_has_read_server_handshake_first_line = true;
258 }
259
260 // Read the rest of the reply until we find an empty line
261 while (m_impl->can_read_line()) {
262 auto line = m_impl->read_line(PAGE_SIZE).release_value_but_fixme_should_propagate_errors();
263 if (line.is_whitespace()) {
264 // We're done with the HTTP headers.
265 // Fail the connection if we're missing any of the following:
266 if (!m_has_read_server_handshake_upgrade) {
267 // 2. |Upgrade| should be present
268 dbgln("WebSocket: Server HTTP Handshake didn't contain an |Upgrade| header");
269 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
270 return;
271 }
272 if (!m_has_read_server_handshake_connection) {
273 // 2. |Connection| should be present
274 dbgln("WebSocket: Server HTTP Handshake didn't contain a |Connection| header");
275 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
276 return;
277 }
278 if (!m_has_read_server_handshake_accept) {
279 // 2. |Sec-WebSocket-Accept| should be present
280 dbgln("WebSocket: Server HTTP Handshake didn't contain a |Sec-WebSocket-Accept| header");
281 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
282 return;
283 }
284
285 m_state = WebSocket::InternalState::Open;
286 notify_open();
287 return;
288 }
289
290 auto parts = line.split(':');
291 if (parts.size() < 2) {
292 // The header field is not valid
293 dbgln("WebSocket: Got invalid header line {} in the Server HTTP handshake", line);
294 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
295 return;
296 }
297
298 auto header_name = parts[0];
299
300 if (header_name.equals_ignoring_ascii_case("Upgrade"sv)) {
301 // 2. |Upgrade| should be case-insensitive "websocket"
302 if (!parts[1].trim_whitespace().equals_ignoring_ascii_case("websocket"sv)) {
303 dbgln("WebSocket: Server HTTP Handshake Header |Upgrade| should be 'websocket', got '{}'. Failing connection.", parts[1]);
304 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
305 return;
306 }
307
308 m_has_read_server_handshake_upgrade = true;
309 continue;
310 }
311
312 if (header_name.equals_ignoring_ascii_case("Connection"sv)) {
313 // 3. |Connection| should be case-insensitive "Upgrade"
314 if (!parts[1].trim_whitespace().equals_ignoring_ascii_case("Upgrade"sv)) {
315 dbgln("WebSocket: Server HTTP Handshake Header |Connection| should be 'Upgrade', got '{}'. Failing connection.", parts[1]);
316 return;
317 }
318
319 m_has_read_server_handshake_connection = true;
320 continue;
321 }
322
323 if (header_name.equals_ignoring_ascii_case("Sec-WebSocket-Accept"sv)) {
324 // 4. |Sec-WebSocket-Accept| should be base64(SHA1(|Sec-WebSocket-Key| + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
325 auto expected_content = DeprecatedString::formatted("{}258EAFA5-E914-47DA-95CA-C5AB0DC85B11", m_websocket_key);
326
327 Crypto::Hash::Manager hash;
328 hash.initialize(Crypto::Hash::HashKind::SHA1);
329 hash.update(expected_content);
330 auto expected_sha1 = hash.digest();
331 // FIXME: change to TRY() and make method fallible
332 auto expected_sha1_string = MUST(encode_base64({ expected_sha1.immutable_data(), expected_sha1.data_length() }));
333 if (!parts[1].trim_whitespace().equals_ignoring_ascii_case(expected_sha1_string)) {
334 dbgln("WebSocket: Server HTTP Handshake Header |Sec-Websocket-Accept| should be '{}', got '{}'. Failing connection.", expected_sha1_string, parts[1]);
335 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
336 return;
337 }
338
339 m_has_read_server_handshake_accept = true;
340 continue;
341 }
342
343 if (header_name.equals_ignoring_ascii_case("Sec-WebSocket-Extensions"sv)) {
344 // 5. |Sec-WebSocket-Extensions| should not contain an extension that doesn't appear in m_connection->extensions()
345 auto server_extensions = parts[1].split(',');
346 for (auto const& extension : server_extensions) {
347 auto trimmed_extension = extension.trim_whitespace();
348 bool found_extension = false;
349 for (auto const& supported_extension : m_connection.extensions()) {
350 if (trimmed_extension.equals_ignoring_ascii_case(supported_extension)) {
351 found_extension = true;
352 }
353 }
354 if (!found_extension) {
355 dbgln("WebSocket: Server HTTP Handshake Header |Sec-WebSocket-Extensions| contains '{}', which is not supported by the client. Failing connection.", trimmed_extension);
356 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
357 return;
358 }
359 }
360 continue;
361 }
362
363 if (header_name.equals_ignoring_ascii_case("Sec-WebSocket-Protocol"sv)) {
364 // 6. If the response includes a |Sec-WebSocket-Protocol| header field and this header field indicates the use of a subprotocol that was not present in the client's handshake (the server has indicated a subprotocol not requested by the client), the client MUST _Fail the WebSocket Connection_.
365 // Additionally, Section 4.2.2 says this is "Either a single value representing the subprotocol the server is ready to use or null."
366 auto server_protocol = parts[1].trim_whitespace();
367 bool found_protocol = false;
368 for (auto const& supported_protocol : m_connection.protocols()) {
369 if (server_protocol.equals_ignoring_ascii_case(supported_protocol)) {
370 found_protocol = true;
371 }
372 }
373 if (!found_protocol) {
374 dbgln("WebSocket: Server HTTP Handshake Header |Sec-WebSocket-Protocol| contains '{}', which is not supported by the client. Failing connection.", server_protocol);
375 fatal_error(WebSocket::Error::ConnectionUpgradeFailed);
376 return;
377 }
378 m_subprotocol_in_use = server_protocol;
379 continue;
380 }
381 }
382
383 // If needed, we will keep reading the header on the next drain_read call
384}
385
386void WebSocket::read_frame()
387{
388 VERIFY(m_impl);
389 VERIFY(m_state == WebSocket::InternalState::Open || m_state == WebSocket::InternalState::Closing);
390
391 size_t cursor = 0;
392 auto get_buffered_bytes = [&](size_t count) -> ReadonlyBytes {
393 if (cursor + count > m_buffered_data.size())
394 return {};
395 auto bytes = m_buffered_data.span().slice(cursor, count);
396 cursor += count;
397 return bytes;
398 };
399
400 auto head_bytes = get_buffered_bytes(2);
401 if (head_bytes.is_null() || head_bytes.is_empty()) {
402 // The connection got closed.
403 m_state = WebSocket::InternalState::Closed;
404 notify_close(m_last_close_code, m_last_close_message, true);
405 discard_connection();
406 return;
407 }
408
409 bool is_final_frame = head_bytes[0] & 0x80;
410 if (!is_final_frame) {
411 // FIXME: Support fragmented frames
412 TODO();
413 }
414
415 auto op_code = (WebSocket::OpCode)(head_bytes[0] & 0x0f);
416 bool is_masked = head_bytes[1] & 0x80;
417
418 // Parse the payload length.
419 size_t payload_length;
420 auto payload_length_bits = head_bytes[1] & 0x7f;
421 if (payload_length_bits == 127) {
422 // A code of 127 means that the next 8 bytes contains the payload length
423 auto actual_bytes = get_buffered_bytes(8);
424 if (actual_bytes.is_null())
425 return;
426 u64 full_payload_length = (u64)((u64)(actual_bytes[0] & 0xff) << 56)
427 | (u64)((u64)(actual_bytes[1] & 0xff) << 48)
428 | (u64)((u64)(actual_bytes[2] & 0xff) << 40)
429 | (u64)((u64)(actual_bytes[3] & 0xff) << 32)
430 | (u64)((u64)(actual_bytes[4] & 0xff) << 24)
431 | (u64)((u64)(actual_bytes[5] & 0xff) << 16)
432 | (u64)((u64)(actual_bytes[6] & 0xff) << 8)
433 | (u64)((u64)(actual_bytes[7] & 0xff) << 0);
434 VERIFY(full_payload_length <= NumericLimits<size_t>::max());
435 payload_length = (size_t)full_payload_length;
436 } else if (payload_length_bits == 126) {
437 // A code of 126 means that the next 2 bytes contains the payload length
438 auto actual_bytes = get_buffered_bytes(2);
439 if (actual_bytes.is_null())
440 return;
441 payload_length = (size_t)((size_t)(actual_bytes[0] & 0xff) << 8)
442 | (size_t)((size_t)(actual_bytes[1] & 0xff) << 0);
443 } else {
444 payload_length = (size_t)payload_length_bits;
445 }
446
447 // Parse the mask, if it exists.
448 // Note : this is technically non-conformant with Section 5.1 :
449 // > A server MUST NOT mask any frames that it sends to the client.
450 // > A client MUST close a connection if it detects a masked frame.
451 // > (These rules might be relaxed in a future specification.)
452 // But because it doesn't cost much, we can support receiving masked frames anyways.
453 u8 masking_key[4];
454 if (is_masked) {
455 auto masking_key_data = get_buffered_bytes(4);
456 if (masking_key_data.is_null())
457 return;
458 masking_key[0] = masking_key_data[0];
459 masking_key[1] = masking_key_data[1];
460 masking_key[2] = masking_key_data[2];
461 masking_key[3] = masking_key_data[3];
462 }
463
464 auto payload = ByteBuffer::create_uninitialized(payload_length).release_value_but_fixme_should_propagate_errors(); // FIXME: Handle possible OOM situation.
465 u64 read_length = 0;
466 while (read_length < payload_length) {
467 auto payload_part = get_buffered_bytes(payload_length - read_length);
468 if (payload_part.is_null())
469 return;
470 // We read at most "actual_length - read" bytes, so this is safe to do.
471 payload.overwrite(read_length, payload_part.data(), payload_part.size());
472 read_length += payload_part.size();
473 }
474
475 if (cursor == m_buffered_data.size()) {
476 m_buffered_data.clear();
477 } else {
478 Vector<u8> new_buffered_data;
479 new_buffered_data.append(m_buffered_data.data() + cursor, m_buffered_data.size() - cursor);
480 m_buffered_data = move(new_buffered_data);
481 }
482
483 if (is_masked) {
484 // Unmask the payload
485 for (size_t i = 0; i < payload.size(); ++i) {
486 payload[i] = payload[i] ^ (masking_key[i % 4]);
487 }
488 }
489
490 if (op_code == WebSocket::OpCode::ConnectionClose) {
491 if (payload.size() > 1) {
492 m_last_close_code = (((u16)(payload[0] & 0xff) << 8) | ((u16)(payload[1] & 0xff)));
493 m_last_close_message = DeprecatedString(ReadonlyBytes(payload.offset_pointer(2), payload.size() - 2));
494 }
495 m_state = WebSocket::InternalState::Closing;
496 return;
497 }
498 if (op_code == WebSocket::OpCode::Ping) {
499 // Immediately send a pong frame as a reply, with the given payload.
500 send_frame(WebSocket::OpCode::Pong, payload, true);
501 return;
502 }
503 if (op_code == WebSocket::OpCode::Pong) {
504 // We can safely ignore the pong
505 return;
506 }
507 if (op_code == WebSocket::OpCode::Continuation) {
508 // FIXME: Support fragmented frames
509 TODO();
510 }
511 if (op_code == WebSocket::OpCode::Text) {
512 notify_message(Message(payload, true));
513 return;
514 }
515 if (op_code == WebSocket::OpCode::Binary) {
516 notify_message(Message(payload, false));
517 return;
518 }
519 dbgln("Websocket: Found unknown opcode {}", (u8)op_code);
520}
521
522void WebSocket::send_frame(WebSocket::OpCode op_code, ReadonlyBytes payload, bool is_final)
523{
524 VERIFY(m_impl);
525 VERIFY(m_state == WebSocket::InternalState::Open);
526 u8 frame_head[1] = { (u8)((is_final ? 0x80 : 0x00) | ((u8)(op_code)&0xf)) };
527 m_impl->send(ReadonlyBytes(frame_head, 1));
528 // Section 5.1 : a client MUST mask all frames that it sends to the server
529 bool has_mask = true;
530 // FIXME: If the payload has a size > size_t max on a 32-bit platform, we could
531 // technically stream it via non-final packets. However, the size was already
532 // truncated earlier in the call stack when stuffing into a ReadonlyBytes
533 if (payload.size() > NumericLimits<u16>::max()) {
534 // Send (the 'mask' flag + 127) + the 8-byte payload length
535 if constexpr (sizeof(size_t) >= 8) {
536 u8 payload_length[9] = {
537 (u8)((has_mask ? 0x80 : 0x00) | 127),
538 (u8)((payload.size() >> 56) & 0xff),
539 (u8)((payload.size() >> 48) & 0xff),
540 (u8)((payload.size() >> 40) & 0xff),
541 (u8)((payload.size() >> 32) & 0xff),
542 (u8)((payload.size() >> 24) & 0xff),
543 (u8)((payload.size() >> 16) & 0xff),
544 (u8)((payload.size() >> 8) & 0xff),
545 (u8)((payload.size() >> 0) & 0xff),
546 };
547 m_impl->send(ReadonlyBytes(payload_length, 9));
548 } else {
549 u8 payload_length[9] = {
550 (u8)((has_mask ? 0x80 : 0x00) | 127),
551 0,
552 0,
553 0,
554 0,
555 (u8)((payload.size() >> 24) & 0xff),
556 (u8)((payload.size() >> 16) & 0xff),
557 (u8)((payload.size() >> 8) & 0xff),
558 (u8)((payload.size() >> 0) & 0xff),
559 };
560 m_impl->send(ReadonlyBytes(payload_length, 9));
561 }
562 } else if (payload.size() >= 126) {
563 // Send (the 'mask' flag + 126) + the 2-byte payload length
564 u8 payload_length[3] = {
565 (u8)((has_mask ? 0x80 : 0x00) | 126),
566 (u8)((payload.size() >> 8) & 0xff),
567 (u8)((payload.size() >> 0) & 0xff),
568 };
569 m_impl->send(ReadonlyBytes(payload_length, 3));
570 } else {
571 // Send the mask flag + the payload in a single byte
572 u8 payload_length[1] = {
573 (u8)((has_mask ? 0x80 : 0x00) | (u8)(payload.size() & 0x7f)),
574 };
575 m_impl->send(ReadonlyBytes(payload_length, 1));
576 }
577 if (has_mask) {
578 // Section 10.3 :
579 // > Clients MUST choose a new masking key for each frame, using an algorithm
580 // > that cannot be predicted by end applications that provide data
581 u8 masking_key[4];
582 fill_with_random(masking_key, 4);
583 m_impl->send(ReadonlyBytes(masking_key, 4));
584 // don't try to send empty payload
585 if (payload.size() == 0)
586 return;
587 // Mask the payload
588 auto buffer_result = ByteBuffer::create_uninitialized(payload.size());
589 if (!buffer_result.is_error()) {
590 auto& masked_payload = buffer_result.value();
591 for (size_t i = 0; i < payload.size(); ++i) {
592 masked_payload[i] = payload[i] ^ (masking_key[i % 4]);
593 }
594 m_impl->send(masked_payload);
595 }
596 } else if (payload.size() > 0) {
597 m_impl->send(payload);
598 }
599}
600
601void WebSocket::fatal_error(WebSocket::Error error)
602{
603 m_state = WebSocket::InternalState::Errored;
604 notify_error(error);
605 discard_connection();
606}
607
608void WebSocket::discard_connection()
609{
610 deferred_invoke([this] {
611 VERIFY(m_impl);
612 m_impl->discard_connection();
613 m_impl->on_connection_error = nullptr;
614 m_impl->on_connected = nullptr;
615 m_impl->on_ready_to_read = nullptr;
616 m_impl = nullptr;
617 });
618}
619
620void WebSocket::notify_open()
621{
622 if (!on_open)
623 return;
624 on_open();
625}
626
627void WebSocket::notify_close(u16 code, DeprecatedString reason, bool was_clean)
628{
629 if (!on_close)
630 return;
631 on_close(code, move(reason), was_clean);
632}
633
634void WebSocket::notify_error(WebSocket::Error error)
635{
636 if (!on_error)
637 return;
638 on_error(error);
639}
640
641void WebSocket::notify_message(Message message)
642{
643 if (!on_message)
644 return;
645 on_message(move(message));
646}
647
648}