Crow  1.1
A C++ microframework for the web
 
Loading...
Searching...
No Matches
websocket.h
1#pragma once
2#include <array>
3#include <memory>
4#include <optional>
5#include <string>
6#include <thread>
7#include "crow/http_response.h"
8#include "crow/logging.h"
9#include "crow/socket_adaptors.h"
10#include "crow/http_request.h"
11#include "crow/tcp_socket_options.h"
12#include "crow/TinySHA1.hpp"
13#include "crow/utility.h"
14
15namespace crow // NOTE: Already documented in "crow/app.h"
16{
17#ifdef CROW_USE_BOOST
18 namespace asio = boost::asio;
19 using error_code = boost::system::error_code;
20#else
21 using error_code = asio::error_code;
22#endif
23
24 /**
25 * \namespace crow::websocket
26 * \brief Namespace that includes the \ref Connection class
27 * and \ref connection struct. Useful for WebSockets connection.
28 *
29 * Used specially in crow/websocket.h, crow/app.h and crow/routing.h
30 */
31 namespace websocket
32 {
33 enum class WebSocketReadState
34 {
35 MiniHeader,
36 Len16,
37 Len64,
38 Mask,
39 Payload,
40 };
41
42 // Codes taken from https://www.rfc-editor.org/rfc/rfc6455#section-7.4.1
43 enum CloseStatusCode : uint16_t {
44 NormalClosure = 1000,
45 EndpointGoingAway = 1001,
46 ProtocolError = 1002,
47 UnacceptableData = 1003,
48 InconsistentData = 1007,
49 PolicyViolated = 1008,
50 MessageTooBig = 1009,
51 ExtensionsNotNegotiated = 1010,
52 UnexpectedCondition = 1011,
53
54 // Reserved for applications only, should not send/receive these to/from clients
55 NoStatusCodePresent = 1005,
56 ClosedAbnormally = 1006,
57 TLSHandshakeFailure = 1015,
58
59 StartStatusCodesForLibraries = 3000,
60 StartStatusCodesForPrivateUse = 4000,
61 // Status code should be between 1000 and 4999 inclusive
62 StartStatusCodes = NormalClosure,
63 EndStatusCodes = 4999,
64 };
65
66 /// A base class for websocket connection.
68 {
69 virtual void send_binary(std::string msg) = 0;
70 virtual void send_text(std::string msg) = 0;
71 virtual void send_ping(std::string msg) = 0;
72 virtual void send_pong(std::string msg) = 0;
73 virtual void close(std::string const& msg = "quit", uint16_t status_code = CloseStatusCode::NormalClosure) = 0;
74 virtual std::string get_remote_ip() = 0;
75 virtual std::string get_subprotocol() const = 0;
76 virtual ~connection() = default;
77
78 void userdata(void* u) { userdata_ = u; }
79 void* userdata() { return userdata_; }
80
81 private:
82 void* userdata_;
83 };
84
85 // Modified version of the illustration in RFC6455 Section-5.2
86 //
87 //
88 // 0 1 2 3 -byte
89 // 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 -bit
90 // +-+-+-+-+-------+-+-------------+-------------------------------+
91 // |F|R|R|R| opcode|M| Payload len | Extended payload length |
92 // |I|S|S|S| (4) |A| (7) | (16/64) |
93 // |N|V|V|V| |S| | (if payload len==126/127) |
94 // | |1|2|3| |K| | |
95 // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
96 // | Extended payload length continued, if payload len == 127 |
97 // + - - - - - - - - - - - - - - - +-------------------------------+
98 // | |Masking-key, if MASK set to 1 |
99 // +-------------------------------+-------------------------------+
100 // | Masking-key (continued) | Payload Data |
101 // +-------------------------------- - - - - - - - - - - - - - - - +
102 // : Payload Data continued ... :
103 // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
104 // | Payload Data continued ... |
105 // +---------------------------------------------------------------+
106 //
107
108 /// A websocket connection.
109
110 template<typename Adaptor, typename Handler>
111 class Connection : public connection, public std::enable_shared_from_this<Connection<Adaptor, Handler>>
112 {
113 public:
114 /// Factory for a connection.
115 ///
116 /// Requires a request with an "Upgrade: websocket" header.<br>
117 /// Automatically handles the handshake.
118 static void create(const crow::request& req, Adaptor adaptor, Handler* handler,
119 uint64_t max_payload, const std::vector<std::string>& subprotocols,
120 std::function<void(crow::websocket::connection&)> open_handler,
121 std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
122 std::function<void(crow::websocket::connection&, const std::string&, uint16_t)> close_handler,
123 std::function<void(crow::websocket::connection&, const std::string&)> error_handler,
124 std::function<void(const crow::request&, std::optional<crow::response>&, void**)> accept_handler,
125 bool mirror_protocols,
126 const detail::socket::tcp_socket_options& tcp_options = {})
127 {
128 auto conn = std::shared_ptr<Connection>(new Connection(std::move(adaptor),
129 handler, max_payload,
130 std::move(open_handler),
131 std::move(message_handler),
132 std::move(close_handler),
133 std::move(error_handler),
134 std::move(accept_handler)));
135
136 // Apply TCP socket options to WebSocket connection
137 detail::socket::apply_tcp_socket_options(conn->adaptor_.socket(), tcp_options);
138
139 // Perform handshake validation
140 if (!utility::string_equals(req.get_header_value("upgrade"), "websocket"))
141 {
142 conn->adaptor_.close();
143 return;
144 }
145
146 std::string requested_subprotocols_header = req.get_header_value("Sec-WebSocket-Protocol");
147 if (!subprotocols.empty() || !requested_subprotocols_header.empty())
148 {
149 auto requested_subprotocols = utility::split(requested_subprotocols_header, ", ");
150 auto subprotocol = utility::find_first_of(subprotocols.begin(), subprotocols.end(), requested_subprotocols.begin(), requested_subprotocols.end());
151 if (subprotocol != subprotocols.end())
152 {
153 conn->subprotocol_ = *subprotocol;
154 }
155 }
156
157 if (mirror_protocols & !requested_subprotocols_header.empty())
158 {
159 conn->subprotocol_ = requested_subprotocols_header;
160 }
161
162 if (conn->accept_handler_)
163 {
164 void* ud = nullptr;
165 std::optional<crow::response> res;
166 conn->accept_handler_(req, res, &ud);
167 if (res)
168 {
169 std::vector<asio::const_buffer> buffers;
170 auto server_name = "";
171 std::string content_length_buffer;
172 res->write_header_into_buffer(buffers, content_length_buffer, req.keep_alive, server_name);
173 buffers.emplace_back(res->body.data(), res->body.size());
174 error_code ec;
175 asio::write(conn->adaptor_.socket(), buffers, ec);
176 conn->adaptor_.close();
177 return;
178 }
179 conn->userdata(ud);
180 }
181
182 // Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
183 // Sec-WebSocket-Version: 13
184 std::string magic = req.get_header_value("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
185 sha1::SHA1 s;
186 s.processBytes(magic.data(), magic.size());
187 uint8_t digest[20];
188 s.getDigestBytes(digest);
189
190 conn->handler_->add_websocket(conn);
191 conn->start(crow::utility::base64encode((unsigned char*)digest, 20));
192 }
193
194 ~Connection() noexcept override = default;
195
196 template<typename Callable>
198 {
199 Callable callable;
200 std::weak_ptr<void> watch;
201
202 void operator()()
203 {
204 if (auto anchor = watch.lock())
205 {
206 std::move(callable)();
207 }
208 }
209 };
210
211 /// Send data through the socket.
212 template<typename CompletionHandler>
213 void dispatch(CompletionHandler&& handler)
214 {
215 asio::dispatch(adaptor_.get_io_context(),
216 WeakWrappedMessage<typename std::decay<CompletionHandler>::type>{
217 std::forward<CompletionHandler>(handler), anchor_});
218 }
219
220 /// Send data through the socket and return immediately.
221 template<typename CompletionHandler>
222 void post(CompletionHandler&& handler)
223 {
224 asio::post(adaptor_.get_io_context(),
225 WeakWrappedMessage<typename std::decay<CompletionHandler>::type>{
226 std::forward<CompletionHandler>(handler), anchor_});
227 }
228
229 /// Send a "Ping" message.
230
231 ///
232 /// Usually invoked to check if the other point is still online.
233 void send_ping(std::string msg) override
234 {
235 send_data(0x9, std::move(msg));
236 }
237
238 /// Send a "Pong" message.
239
240 ///
241 /// Usually automatically invoked as a response to a "Ping" message.
242 void send_pong(std::string msg) override
243 {
244 send_data(0xA, std::move(msg));
245 }
246
247 /// Send a binary encoded message.
248 void send_binary(std::string msg) override
249 {
250 send_data(0x2, std::move(msg));
251 }
252
253 /// Send a plaintext message.
254 void send_text(std::string msg) override
255 {
256 send_data(0x1, std::move(msg));
257 }
258
259 /// Send a close signal.
260
261 ///
262 /// Sets a flag to destroy the object once the message is sent.
263 void close(std::string const& msg, uint16_t status_code) override
264 {
265 dispatch([shared_this = this->shared_from_this(), msg, status_code]() mutable {
266 shared_this->has_sent_close_ = true;
267 if (shared_this->has_recv_close_ && !shared_this->is_close_handler_called_)
268 {
269 shared_this->is_close_handler_called_ = true;
270 if (shared_this->close_handler_)
271 shared_this->close_handler_(*shared_this, msg, status_code);
272 }
273 auto header = shared_this->build_header(0x8, msg.size() + 2);
274 char status_buf[2];
275 *(uint16_t*)(status_buf) = htons(status_code);
276
277 shared_this->write_buffers_.emplace_back(std::move(header));
278 shared_this->write_buffers_.emplace_back(std::string(status_buf, 2));
279 shared_this->write_buffers_.emplace_back(msg);
280 shared_this->do_write();
281 });
282 }
283
284 std::string get_remote_ip() override
285 {
286 return adaptor_.address();
287 }
288
289 void set_max_payload_size(uint64_t payload)
290 {
291 max_payload_bytes_ = payload;
292 }
293
294 /// Returns the matching client/server subprotocol, empty string if none matched.
295 std::string get_subprotocol() const override
296 {
297 return subprotocol_;
298 }
299
300 protected:
301 /// Generate the websocket headers using an opcode and the message size (in bytes).
302 std::string build_header(int opcode, size_t size)
303 {
304 char buf[2 + 8] = "\x80\x00";
305 buf[0] += opcode;
306 if (size < 126)
307 {
308 buf[1] += static_cast<char>(size);
309 return {buf, buf + 2};
310 }
311 else if (size < 0x10000)
312 {
313 buf[1] += 126;
314 *(uint16_t*)(buf + 2) = htons(static_cast<uint16_t>(size));
315 return {buf, buf + 4};
316 }
317 else
318 {
319 buf[1] += 127;
320 *reinterpret_cast<uint64_t*>(buf + 2) = ((1 == htonl(1)) ? static_cast<uint64_t>(size) : (static_cast<uint64_t>(htonl((size)&0xFFFFFFFF)) << 32) | htonl(static_cast<uint64_t>(size) >> 32));
321 return {buf, buf + 10};
322 }
323 }
324
325 /// Send the HTTP upgrade response.
326
327 ///
328 /// Finishes the handshake process, then starts reading messages from the socket.
329 void start(std::string&& hello)
330 {
331 static const std::string header =
332 "HTTP/1.1 101 Switching Protocols\r\n"
333 "Upgrade: websocket\r\n"
334 "Connection: Upgrade\r\n"
335 "Sec-WebSocket-Accept: ";
336 write_buffers_.emplace_back(header);
337 write_buffers_.emplace_back(std::move(hello));
338 write_buffers_.emplace_back(crlf);
339 if (!subprotocol_.empty())
340 {
341 write_buffers_.emplace_back("Sec-WebSocket-Protocol: ");
342 write_buffers_.emplace_back(subprotocol_);
343 write_buffers_.emplace_back(crlf);
344 }
345 write_buffers_.emplace_back(crlf);
346 do_write();
347 if (open_handler_)
348 open_handler_(*this);
349 do_read();
350 }
351
352 /// Read a websocket message.
353
354 ///
355 /// Involves:<br>
356 /// Handling headers (opcodes, size).<br>
357 /// Unmasking the payload.<br>
358 /// Reading the actual payload.<br>
359 void do_read()
360 {
361 if (has_sent_close_ && has_recv_close_)
362 {
363 close_connection_ = true;
364 adaptor_.shutdown_readwrite();
365 adaptor_.close();
367 return;
368 }
369
370 is_reading = true;
371 switch (state_)
372 {
373 case WebSocketReadState::MiniHeader:
374 {
375 mini_header_ = 0;
376 //asio::async_read(adaptor_.socket(), asio::buffer(&mini_header_, 1),
377 adaptor_.socket().async_read_some(
378 asio::buffer(&mini_header_, 2),
379 [shared_this = this->shared_from_this()](const error_code& ec, std::size_t
380#ifdef CROW_ENABLE_DEBUG
381 bytes_transferred
382#endif
383 )
384
385 {
386 shared_this->is_reading = false;
387 shared_this->mini_header_ = ntohs(shared_this->mini_header_);
388#ifdef CROW_ENABLE_DEBUG
389
390 if (!ec && bytes_transferred != 2)
391 {
392 throw std::runtime_error("WebSocket:MiniHeader:async_read fail:asio bug?");
393 }
394#endif
395
396 if (!ec)
397 {
398 if ((shared_this->mini_header_ & 0x80) == 0x80)
399 shared_this->has_mask_ = true;
400 else //if the websocket specification is enforced and the message isn't masked, terminate the connection
401 {
402#ifndef CROW_ENFORCE_WS_SPEC
403 shared_this->has_mask_ = false;
404#else
405 shared_this->close_connection_ = true;
406 shared_this->adaptor_.shutdown_readwrite();
407 shared_this->adaptor_.close();
408 if (shared_this->error_handler_)
409 shared_this->error_handler_(*shared_this, "Client connection not masked.");
410 shared_this->check_destroy(CloseStatusCode::UnacceptableData);
411#endif
412 }
413
414 if ((shared_this->mini_header_ & 0x7f) == 127)
415 {
416 shared_this->state_ = WebSocketReadState::Len64;
417 }
418 else if ((shared_this->mini_header_ & 0x7f) == 126)
419 {
420 shared_this->state_ = WebSocketReadState::Len16;
421 }
422 else
423 {
424 shared_this->remaining_length_ = shared_this->mini_header_ & 0x7f;
425 shared_this->state_ = WebSocketReadState::Mask;
426 }
427 shared_this->do_read();
428 }
429 else
430 {
431 shared_this->close_connection_ = true;
432 shared_this->adaptor_.shutdown_readwrite();
433 shared_this->adaptor_.close();
434 if (shared_this->error_handler_)
435 shared_this->error_handler_(*shared_this, ec.message());
436 shared_this->check_destroy();
437 }
438 });
439 }
440 break;
441 case WebSocketReadState::Len16:
442 {
443 remaining_length_ = 0;
444 remaining_length16_ = 0;
445 asio::async_read(
446 adaptor_.socket(), asio::buffer(&remaining_length16_, 2),
447 [shared_this = this->shared_from_this()](const error_code& ec, std::size_t
448#ifdef CROW_ENABLE_DEBUG
449 bytes_transferred
450#endif
451 ) {
452 shared_this->is_reading = false;
453 shared_this->remaining_length16_ = ntohs(shared_this->remaining_length16_);
454 shared_this->remaining_length_ = shared_this->remaining_length16_;
455#ifdef CROW_ENABLE_DEBUG
456 if (!ec && bytes_transferred != 2)
457 {
458 throw std::runtime_error("WebSocket:Len16:async_read fail:asio bug?");
459 }
460#endif
461
462 if (!ec)
463 {
464 shared_this->state_ = WebSocketReadState::Mask;
465 shared_this->do_read();
466 }
467 else
468 {
469 shared_this->close_connection_ = true;
470 shared_this->adaptor_.shutdown_readwrite();
471 shared_this->adaptor_.close();
472 if (shared_this->error_handler_)
473 shared_this->error_handler_(*shared_this, ec.message());
474 shared_this->check_destroy();
475 }
476 });
477 }
478 break;
479 case WebSocketReadState::Len64:
480 {
481 asio::async_read(
482 adaptor_.socket(), asio::buffer(&remaining_length_, 8),
483 [shared_this = this->shared_from_this()](const error_code& ec, std::size_t
484#ifdef CROW_ENABLE_DEBUG
485 bytes_transferred
486#endif
487 ) {
488 shared_this->is_reading = false;
489 shared_this->remaining_length_ = ((1 == ntohl(1)) ? (shared_this->remaining_length_) : (static_cast<uint64_t>(ntohl((shared_this->remaining_length_)&0xFFFFFFFF)) << 32) | ntohl((shared_this->remaining_length_) >> 32));
490#ifdef CROW_ENABLE_DEBUG
491 if (!ec && bytes_transferred != 8)
492 {
493 throw std::runtime_error("WebSocket:Len16:async_read fail:asio bug?");
494 }
495#endif
496
497 if (!ec)
498 {
499 shared_this->state_ = WebSocketReadState::Mask;
500 shared_this->do_read();
501 }
502 else
503 {
504 shared_this->close_connection_ = true;
505 shared_this->adaptor_.shutdown_readwrite();
506 shared_this->adaptor_.close();
507 if (shared_this->error_handler_)
508 shared_this->error_handler_(*shared_this, ec.message());
509 shared_this->check_destroy();
510 }
511 });
512 }
513 break;
514 case WebSocketReadState::Mask:
515 if (remaining_length_ > max_payload_bytes_)
516 {
517 close_connection_ = true;
518 adaptor_.close();
519 if (error_handler_)
520 error_handler_(*this, "Message length exceeds maximum payload.");
521 check_destroy(MessageTooBig);
522 }
523 else if (has_mask_)
524 {
525 asio::async_read(
526 adaptor_.socket(), asio::buffer((char*)&mask_, 4),
527 [shared_this = this->shared_from_this()](const error_code& ec, std::size_t
528#ifdef CROW_ENABLE_DEBUG
529 bytes_transferred
530#endif
531 ) {
532 shared_this->is_reading = false;
533#ifdef CROW_ENABLE_DEBUG
534 if (!ec && bytes_transferred != 4)
535 {
536 throw std::runtime_error("WebSocket:Mask:async_read fail:asio bug?");
537 }
538#endif
539
540 if (!ec)
541 {
542 shared_this->state_ = WebSocketReadState::Payload;
543 shared_this->do_read();
544 }
545 else
546 {
547 shared_this->close_connection_ = true;
548 if (shared_this->error_handler_)
549 shared_this->error_handler_(*shared_this, ec.message());
550 shared_this->adaptor_.shutdown_readwrite();
551 shared_this->adaptor_.close();
552 shared_this->check_destroy();
553 }
554 });
555 }
556 else
557 {
558 state_ = WebSocketReadState::Payload;
559 do_read();
560 }
561 break;
562 case WebSocketReadState::Payload:
563 {
564 auto to_read = static_cast<std::uint64_t>(buffer_.size());
565 if (remaining_length_ < to_read)
566 to_read = remaining_length_;
567 adaptor_.socket().async_read_some(
568 asio::buffer(buffer_, static_cast<std::size_t>(to_read)),
569 [shared_this = this->shared_from_this()](const error_code& ec, std::size_t bytes_transferred) {
570 shared_this->is_reading = false;
571
572 if (!ec)
573 {
574 shared_this->fragment_.insert(shared_this->fragment_.end(), shared_this->buffer_.begin(), shared_this->buffer_.begin() + bytes_transferred);
575 shared_this->remaining_length_ -= bytes_transferred;
576 if (shared_this->remaining_length_ == 0)
577 {
578 if (shared_this->handle_fragment())
579 {
580 shared_this->state_ = WebSocketReadState::MiniHeader;
581 shared_this->do_read();
582 }
583 }
584 else
585 shared_this->do_read();
586 }
587 else
588 {
589 shared_this->close_connection_ = true;
590 if (shared_this->error_handler_)
591 shared_this->error_handler_(*shared_this, ec.message());
592 shared_this->adaptor_.shutdown_readwrite();
593 shared_this->adaptor_.close();
594 shared_this->check_destroy();
595 }
596 });
597 }
598 break;
599 }
600 }
601
602 /// Check if the FIN bit is set.
603 bool is_FIN()
604 {
605 return mini_header_ & 0x8000;
606 }
607
608 /// Extract the opcode from the header.
609 int opcode()
610 {
611 return (mini_header_ & 0x0f00) >> 8;
612 }
613
614 /// Process the payload fragment.
615
616 ///
617 /// Unmasks the fragment, checks the opcode, merges fragments into 1 message body, and calls the appropriate handler.
619 {
620 if (has_mask_)
621 {
622 for (decltype(fragment_.length()) i = 0; i < fragment_.length(); i++)
623 {
624 fragment_[i] ^= ((char*)&mask_)[i % 4];
625 }
626 }
627 switch (opcode())
628 {
629 case 0: // Continuation
630 {
631 message_ += fragment_;
632 if (is_FIN())
633 {
634 if (message_handler_)
635 message_handler_(*this, message_, is_binary_);
636 message_.clear();
637 }
638 }
639 break;
640 case 1: // Text
641 {
642 is_binary_ = false;
643 message_ += fragment_;
644 if (is_FIN())
645 {
646 if (message_handler_)
647 message_handler_(*this, message_, is_binary_);
648 message_.clear();
649 }
650 }
651 break;
652 case 2: // Binary
653 {
654 is_binary_ = true;
655 message_ += fragment_;
656 if (is_FIN())
657 {
658 if (message_handler_)
659 message_handler_(*this, message_, is_binary_);
660 message_.clear();
661 }
662 }
663 break;
664 case 0x8: // Close
665 {
666 has_recv_close_ = true;
667
668
669 uint16_t status_code = NoStatusCodePresent;
670 std::string::size_type message_start = 2;
671 if (fragment_.size() >= 2)
672 {
673 status_code = ntohs(((uint16_t*)fragment_.data())[0]);
674 } else {
675 // no message will crash substr
676 message_start = 0;
677 }
678
679 if (!has_sent_close_)
680 {
681 close(fragment_.substr(message_start), status_code);
682 }
683 else
684 {
685
686 close_connection_ = true;
687 if (!is_close_handler_called_)
688 {
689 if (close_handler_)
690 close_handler_(*this, fragment_.substr(message_start), status_code);
691 is_close_handler_called_ = true;
692 }
693 adaptor_.shutdown_readwrite();
694 adaptor_.close();
695
696 // Close handler must have been called at this point so code does not matter
697 check_destroy();
698 return false;
699 }
700 }
701 break;
702 case 0x9: // Ping
703 {
704 send_pong(fragment_);
705 }
706 break;
707 case 0xA: // Pong
708 {
709 pong_received_ = true;
710 }
711 break;
712 }
713
714 fragment_.clear();
715 return true;
716 }
717
718 /// Send the buffers' data through the socket.
719
720 ///
721 /// Also destroys the object if the Close flag is set.
722 void do_write()
723 {
724 if (sending_buffers_.empty()) {
725 if (write_buffers_.empty()) return;
726
727 sending_buffers_.swap(write_buffers_);
728 std::vector<asio::const_buffer> buffers;
729 buffers.reserve(sending_buffers_.size());
730 for (auto &s: sending_buffers_)
731 {
732 buffers.emplace_back(asio::buffer(s));
733 }
734 auto watch = std::weak_ptr<void>{anchor_};
735 asio::async_write(
736 adaptor_.socket(), buffers,
737 [shared_this = this->shared_from_this(), watch](const error_code &ec, std::size_t /*bytes_transferred*/) {
738 auto anchor = watch.lock();
739 if (anchor == nullptr)
740 return;
741
742 if (!ec && !shared_this->close_connection_)
743 {
744 shared_this->sending_buffers_.clear();
745 if (!shared_this->write_buffers_.empty())
746 shared_this->do_write();
747 if (shared_this->has_sent_close_)
748 shared_this->close_connection_ = true;
749 }
750 else
751 {
752 shared_this->sending_buffers_.clear();
753 shared_this->close_connection_ = true;
754 shared_this->check_destroy();
755 }
756 });
757 }
758 }
759
760 /// Destroy the Connection.
761 void check_destroy(websocket::CloseStatusCode code = CloseStatusCode::ClosedAbnormally)
762 {
763 // Note that if the close handler was not yet called at this point we did not receive a close packet (or send one)
764 // and thus we use ClosedAbnormally unless instructed otherwise
765 if (!is_close_handler_called_)
766 {
767 if (close_handler_)
768 {
769 close_handler_(*this, "uncleanly", code);
770 }
771 }
772
773 handler_->remove_websocket(this->shared_from_this());
774 }
775
776
778 {
779 std::string payload;
780 Connection* self;
781 int opcode;
782
783 void operator()()
784 {
785 self->send_data_impl(this);
786 }
787 };
788
789 void send_data_impl(SendMessageType* s)
790 {
791 auto header = build_header(s->opcode, s->payload.size());
792 write_buffers_.emplace_back(std::move(header));
793 write_buffers_.emplace_back(std::move(s->payload));
794 do_write();
795 }
796
797 void send_data(int opcode, std::string&& msg)
798 {
799 SendMessageType event_arg{
800 std::move(msg),
801 this,
802 opcode};
803
804 post(std::move(event_arg));
805 }
806
807 private:
808 Connection(Adaptor&& adaptor, Handler* handler, uint64_t max_payload,
809 std::function<void(crow::websocket::connection&)> open_handler,
810 std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler,
811 std::function<void(crow::websocket::connection&, const std::string&, uint16_t)> close_handler,
812 std::function<void(crow::websocket::connection&, const std::string&)> error_handler,
813 std::function<void(const crow::request&, std::optional<crow::response>&, void**)> accept_handler):
814 adaptor_(std::move(adaptor)),
815 handler_(handler),
816 max_payload_bytes_(max_payload),
817 open_handler_(std::move(open_handler)),
818 message_handler_(std::move(message_handler)),
819 close_handler_(std::move(close_handler)),
820 error_handler_(std::move(error_handler)),
821 accept_handler_(std::move(accept_handler))
822 {}
823
824 Adaptor adaptor_;
825 Handler* handler_;
826
827 std::vector<std::string> sending_buffers_;
828 std::vector<std::string> write_buffers_;
829
830 std::array<char, 4096> buffer_;
831 bool is_binary_;
832 std::string message_;
833 std::string fragment_;
834 WebSocketReadState state_{WebSocketReadState::MiniHeader};
835 uint16_t remaining_length16_{0};
836 uint64_t remaining_length_{0};
837 uint64_t max_payload_bytes_{UINT64_MAX};
838 std::string subprotocol_;
839 bool close_connection_{false};
840 bool is_reading{false};
841 bool has_mask_{false};
842 uint32_t mask_;
843 uint16_t mini_header_;
844 bool has_sent_close_{false};
845 bool has_recv_close_{false};
846 bool error_occurred_{false};
847 bool pong_received_{false};
848 bool is_close_handler_called_{false};
849
850 std::shared_ptr<void> anchor_ = std::make_shared<int>(); // Value is just for placeholding
851
852 std::function<void(crow::websocket::connection&)> open_handler_;
853 std::function<void(crow::websocket::connection&, const std::string&, bool)> message_handler_;
854 std::function<void(crow::websocket::connection&, const std::string&, uint16_t status_code)> close_handler_;
855 std::function<void(crow::websocket::connection&, const std::string&)> error_handler_;
856 std::function<void(const crow::request&, std::optional<crow::response>&, void**)> accept_handler_;
857 };
858 } // namespace websocket
859} // namespace crow
TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based on the implementation in ...
A websocket connection.
Definition websocket.h:112
void dispatch(CompletionHandler &&handler)
Send data through the socket.
Definition websocket.h:213
void do_read()
Read a websocket message.
Definition websocket.h:359
void send_pong(std::string msg) override
Send a "Pong" message.
Definition websocket.h:242
bool handle_fragment()
Process the payload fragment.
Definition websocket.h:618
std::string build_header(int opcode, size_t size)
Generate the websocket headers using an opcode and the message size (in bytes).
Definition websocket.h:302
void close(std::string const &msg, uint16_t status_code) override
Send a close signal.
Definition websocket.h:263
void send_text(std::string msg) override
Send a plaintext message.
Definition websocket.h:254
std::string get_subprotocol() const override
Returns the matching client/server subprotocol, empty string if none matched.
Definition websocket.h:295
int opcode()
Extract the opcode from the header.
Definition websocket.h:609
void do_write()
Send the buffers' data through the socket.
Definition websocket.h:722
void start(std::string &&hello)
Send the HTTP upgrade response.
Definition websocket.h:329
bool is_FIN()
Check if the FIN bit is set.
Definition websocket.h:603
void send_ping(std::string msg) override
Send a "Ping" message.
Definition websocket.h:233
void send_binary(std::string msg) override
Send a binary encoded message.
Definition websocket.h:248
void check_destroy(websocket::CloseStatusCode code=CloseStatusCode::ClosedAbnormally)
Destroy the Connection.
Definition websocket.h:761
static void create(const crow::request &req, Adaptor adaptor, Handler *handler, uint64_t max_payload, const std::vector< std::string > &subprotocols, std::function< void(crow::websocket::connection &)> open_handler, std::function< void(crow::websocket::connection &, const std::string &, bool)> message_handler, std::function< void(crow::websocket::connection &, const std::string &, uint16_t)> close_handler, std::function< void(crow::websocket::connection &, const std::string &)> error_handler, std::function< void(const crow::request &, std::optional< crow::response > &, void **)> accept_handler, bool mirror_protocols, const detail::socket::tcp_socket_options &tcp_options={})
Definition websocket.h:118
void post(CompletionHandler &&handler)
Send data through the socket and return immediately.
Definition websocket.h:222
A tiny SHA1 algorithm implementation used internally in the Crow server (specifically in crow/websock...
Definition TinySHA1.hpp:48
The main namespace of the library. In this namespace is defined the most important classes and functi...
Definition tcp_socket_options.h:29
An HTTP request.
Definition http_request.h:47
bool keep_alive
Whether or not the server should send a connection: Keep-Alive header to the client.
Definition http_request.h:56
A base class for websocket connection.
Definition websocket.h:68