5#include "crow/logging.h"
6#include "crow/socket_adaptors.h"
7#include "crow/http_request.h"
9#include "crow/utility.h"
14 namespace asio = boost::asio;
15 using error_code = boost::system::error_code;
17 using error_code = asio::error_code;
29 enum class WebSocketReadState
39 enum CloseStatusCode : uint16_t {
41 EndpointGoingAway = 1001,
43 UnacceptableData = 1003,
44 InconsistentData = 1007,
45 PolicyViolated = 1008,
47 ExtensionsNotNegotiated = 1010,
48 UnexpectedCondition = 1011,
51 NoStatusCodePresent = 1005,
52 ClosedAbnormally = 1006,
53 TLSHandshakeFailure = 1015,
55 StartStatusCodesForLibraries = 3000,
56 StartStatusCodesForPrivateUse = 4000,
58 StartStatusCodes = NormalClosure,
59 EndStatusCodes = 4999,
65 virtual void send_binary(std::string msg) = 0;
66 virtual void send_text(std::string msg) = 0;
67 virtual void send_ping(std::string msg) = 0;
68 virtual void send_pong(std::string msg) = 0;
69 virtual void close(std::string
const& msg =
"quit", uint16_t status_code = CloseStatusCode::NormalClosure) = 0;
70 virtual std::string get_remote_ip() = 0;
71 virtual std::string get_subprotocol()
const = 0;
74 void userdata(
void* u) { userdata_ = u; }
75 void* userdata() {
return userdata_; }
106 template<
typename Adaptor,
typename Handler>
115 uint64_t max_payload,
const std::vector<std::string>& subprotocols,
120 std::function<
bool(
const crow::request&,
void**)> accept_handler,
121 bool mirror_protocols)
123 auto conn = std::shared_ptr<Connection>(
new Connection(std::move(adaptor),
124 handler, max_payload,
125 std::move(open_handler),
126 std::move(message_handler),
127 std::move(close_handler),
128 std::move(error_handler),
129 std::move(accept_handler)));
132 if (!utility::string_equals(req.get_header_value(
"upgrade"),
"websocket"))
134 conn->adaptor_.close();
138 std::string requested_subprotocols_header = req.get_header_value(
"Sec-WebSocket-Protocol");
139 if (!subprotocols.empty() || !requested_subprotocols_header.empty())
141 auto requested_subprotocols = utility::split(requested_subprotocols_header,
", ");
142 auto subprotocol = utility::find_first_of(subprotocols.begin(), subprotocols.end(), requested_subprotocols.begin(), requested_subprotocols.end());
143 if (subprotocol != subprotocols.end())
145 conn->subprotocol_ = *subprotocol;
149 if (mirror_protocols & !requested_subprotocols_header.empty())
151 conn->subprotocol_ = requested_subprotocols_header;
154 if (conn->accept_handler_)
157 if (!conn->accept_handler_(req, &ud))
159 conn->adaptor_.close();
167 std::string magic = req.get_header_value(
"Sec-WebSocket-Key") +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
169 s.processBytes(magic.data(), magic.size());
171 s.getDigestBytes(digest);
173 conn->handler_->add_websocket(conn);
174 conn->start(crow::utility::base64encode((
unsigned char*)digest, 20));
179 template<typename Callable>
183 std::weak_ptr<void> watch;
187 if (
auto anchor = watch.lock())
189 std::move(callable)();
195 template<
typename CompletionHandler>
198 asio::dispatch(adaptor_.get_io_context(),
200 std::forward<CompletionHandler>(handler), anchor_});
204 template<
typename CompletionHandler>
205 void post(CompletionHandler&& handler)
207 asio::post(adaptor_.get_io_context(),
209 std::forward<CompletionHandler>(handler), anchor_});
218 send_data(0x9, std::move(msg));
227 send_data(0xA, std::move(msg));
233 send_data(0x2, std::move(msg));
239 send_data(0x1, std::move(msg));
246 void close(std::string
const& msg, uint16_t status_code)
override
248 dispatch([
this, msg, status_code]()
mutable {
249 has_sent_close_ =
true;
250 if (has_recv_close_ && !is_close_handler_called_)
252 is_close_handler_called_ =
true;
254 close_handler_(*
this, msg, status_code);
258 *(uint16_t*)(status_buf) = htons(status_code);
260 write_buffers_.emplace_back(std::move(header));
261 write_buffers_.emplace_back(std::string(status_buf, 2));
262 write_buffers_.emplace_back(msg);
267 std::string get_remote_ip()
override
269 return adaptor_.address();
272 void set_max_payload_size(uint64_t payload)
274 max_payload_bytes_ = payload;
287 char buf[2 + 8] =
"\x80\x00";
291 buf[1] +=
static_cast<char>(size);
292 return {buf, buf + 2};
294 else if (size < 0x10000)
297 *(uint16_t*)(buf + 2) = htons(
static_cast<uint16_t
>(size));
298 return {buf, buf + 4};
303 *
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));
304 return {buf, buf + 10};
314 static const std::string header =
315 "HTTP/1.1 101 Switching Protocols\r\n"
316 "Upgrade: websocket\r\n"
317 "Connection: Upgrade\r\n"
318 "Sec-WebSocket-Accept: ";
319 write_buffers_.emplace_back(header);
320 write_buffers_.emplace_back(std::move(hello));
321 write_buffers_.emplace_back(crlf);
322 if (!subprotocol_.empty())
324 write_buffers_.emplace_back(
"Sec-WebSocket-Protocol: ");
325 write_buffers_.emplace_back(subprotocol_);
326 write_buffers_.emplace_back(crlf);
328 write_buffers_.emplace_back(crlf);
331 open_handler_(*
this);
344 if (has_sent_close_ && has_recv_close_)
346 close_connection_ =
true;
347 adaptor_.shutdown_readwrite();
356 case WebSocketReadState::MiniHeader:
360 adaptor_.socket().async_read_some(
361 asio::buffer(&mini_header_, 2),
362 [
this](
const error_code& ec, std::size_t
363#ifdef CROW_ENABLE_DEBUG
370 mini_header_ = ntohs(mini_header_);
371#ifdef CROW_ENABLE_DEBUG
373 if (!ec && bytes_transferred != 2)
375 throw std::runtime_error(
"WebSocket:MiniHeader:async_read fail:asio bug?");
381 if ((mini_header_ & 0x80) == 0x80)
385#ifndef CROW_ENFORCE_WS_SPEC
388 close_connection_ =
true;
389 adaptor_.shutdown_readwrite();
392 error_handler_(*
this,
"Client connection not masked.");
397 if ((mini_header_ & 0x7f) == 127)
399 state_ = WebSocketReadState::Len64;
401 else if ((mini_header_ & 0x7f) == 126)
403 state_ = WebSocketReadState::Len16;
407 remaining_length_ = mini_header_ & 0x7f;
408 state_ = WebSocketReadState::Mask;
414 close_connection_ =
true;
415 adaptor_.shutdown_readwrite();
418 error_handler_(*
this, ec.message());
424 case WebSocketReadState::Len16:
426 remaining_length_ = 0;
427 remaining_length16_ = 0;
429 adaptor_.socket(), asio::buffer(&remaining_length16_, 2),
430 [
this](
const error_code& ec, std::size_t
431#ifdef CROW_ENABLE_DEBUG
436 remaining_length16_ = ntohs(remaining_length16_);
437 remaining_length_ = remaining_length16_;
438#ifdef CROW_ENABLE_DEBUG
439 if (!ec && bytes_transferred != 2)
441 throw std::runtime_error(
"WebSocket:Len16:async_read fail:asio bug?");
447 state_ = WebSocketReadState::Mask;
452 close_connection_ =
true;
453 adaptor_.shutdown_readwrite();
456 error_handler_(*
this, ec.message());
462 case WebSocketReadState::Len64:
465 adaptor_.socket(), asio::buffer(&remaining_length_, 8),
466 [
this](
const error_code& ec, std::size_t
467#ifdef CROW_ENABLE_DEBUG
472 remaining_length_ = ((1 == ntohl(1)) ? (remaining_length_) : (static_cast<uint64_t>(ntohl((remaining_length_)&0xFFFFFFFF)) << 32) | ntohl((remaining_length_) >> 32));
473#ifdef CROW_ENABLE_DEBUG
474 if (!ec && bytes_transferred != 8)
476 throw std::runtime_error(
"WebSocket:Len16:async_read fail:asio bug?");
482 state_ = WebSocketReadState::Mask;
487 close_connection_ =
true;
488 adaptor_.shutdown_readwrite();
491 error_handler_(*
this, ec.message());
497 case WebSocketReadState::Mask:
498 if (remaining_length_ > max_payload_bytes_)
500 close_connection_ =
true;
503 error_handler_(*
this,
"Message length exceeds maximum payload.");
509 adaptor_.socket(), asio::buffer((
char*)&mask_, 4),
510 [
this](
const error_code& ec, std::size_t
511#ifdef CROW_ENABLE_DEBUG
516#ifdef CROW_ENABLE_DEBUG
517 if (!ec && bytes_transferred != 4)
519 throw std::runtime_error(
"WebSocket:Mask:async_read fail:asio bug?");
525 state_ = WebSocketReadState::Payload;
530 close_connection_ =
true;
532 error_handler_(*
this, ec.message());
533 adaptor_.shutdown_readwrite();
541 state_ = WebSocketReadState::Payload;
545 case WebSocketReadState::Payload:
547 auto to_read =
static_cast<std::uint64_t
>(buffer_.size());
548 if (remaining_length_ < to_read)
549 to_read = remaining_length_;
550 adaptor_.socket().async_read_some(
551 asio::buffer(buffer_,
static_cast<std::size_t
>(to_read)),
552 [
this](
const error_code& ec, std::size_t bytes_transferred) {
557 fragment_.insert(fragment_.end(), buffer_.begin(), buffer_.begin() + bytes_transferred);
558 remaining_length_ -= bytes_transferred;
559 if (remaining_length_ == 0)
561 if (handle_fragment())
563 state_ = WebSocketReadState::MiniHeader;
572 close_connection_ =
true;
574 error_handler_(*
this, ec.message());
575 adaptor_.shutdown_readwrite();
588 return mini_header_ & 0x8000;
594 return (mini_header_ & 0x0f00) >> 8;
605 for (
decltype(fragment_.length()) i = 0; i < fragment_.length(); i++)
607 fragment_[i] ^= ((
char*)&mask_)[i % 4];
614 message_ += fragment_;
617 if (message_handler_)
618 message_handler_(*
this, message_, is_binary_);
626 message_ += fragment_;
629 if (message_handler_)
630 message_handler_(*
this, message_, is_binary_);
638 message_ += fragment_;
641 if (message_handler_)
642 message_handler_(*
this, message_, is_binary_);
649 has_recv_close_ =
true;
652 uint16_t status_code = NoStatusCodePresent;
653 std::string::size_type message_start = 2;
654 if (fragment_.size() >= 2)
656 status_code = ntohs(((uint16_t*)fragment_.data())[0]);
662 if (!has_sent_close_)
664 close(fragment_.substr(message_start), status_code);
669 close_connection_ =
true;
670 if (!is_close_handler_called_)
673 close_handler_(*
this, fragment_.substr(message_start), status_code);
674 is_close_handler_called_ =
true;
676 adaptor_.shutdown_readwrite();
687 send_pong(fragment_);
692 pong_received_ =
true;
707 if (write_buffers_.empty())
return;
709 sending_buffers_.swap(write_buffers_);
710 std::vector<asio::const_buffer> buffers;
711 buffers.reserve(sending_buffers_.size());
712 for (
auto& s : sending_buffers_)
714 buffers.emplace_back(asio::buffer(s));
716 auto watch = std::weak_ptr<void>{anchor_};
718 adaptor_.socket(), buffers,
719 [
this, watch](
const error_code& ec, std::size_t ) {
720 auto anchor = watch.lock();
721 if (anchor == nullptr)
724 if (!ec && !close_connection_)
726 sending_buffers_.clear();
727 if (!write_buffers_.empty())
730 close_connection_ = true;
734 sending_buffers_.clear();
735 close_connection_ = true;
742 void check_destroy(websocket::CloseStatusCode code = CloseStatusCode::ClosedAbnormally)
746 if (!is_close_handler_called_)
750 close_handler_(*
this,
"uncleanly", code);
754 handler_->remove_websocket(this->shared_from_this());
766 self->send_data_impl(
this);
772 auto header = build_header(s->opcode, s->payload.size());
773 write_buffers_.emplace_back(std::move(header));
774 write_buffers_.emplace_back(std::move(s->payload));
778 void send_data(
int opcode, std::string&& msg)
780 SendMessageType event_arg{
785 post(std::move(event_arg));
789 Connection(Adaptor&& adaptor, Handler* handler, uint64_t max_payload,
794 std::function<
bool(
const crow::request&,
void**)> accept_handler):
795 adaptor_(std::move(adaptor)),
797 max_payload_bytes_(max_payload),
798 open_handler_(std::move(open_handler)),
799 message_handler_(std::move(message_handler)),
800 close_handler_(std::move(close_handler)),
801 error_handler_(std::move(error_handler)),
802 accept_handler_(std::move(accept_handler))
808 std::vector<std::string> sending_buffers_;
809 std::vector<std::string> write_buffers_;
811 std::array<char, 4096> buffer_;
813 std::string message_;
814 std::string fragment_;
815 WebSocketReadState state_{WebSocketReadState::MiniHeader};
816 uint16_t remaining_length16_{0};
817 uint64_t remaining_length_{0};
818 uint64_t max_payload_bytes_{UINT64_MAX};
819 std::string subprotocol_;
820 bool close_connection_{
false};
821 bool is_reading{
false};
822 bool has_mask_{
false};
824 uint16_t mini_header_;
825 bool has_sent_close_{
false};
826 bool has_recv_close_{
false};
827 bool error_occurred_{
false};
828 bool pong_received_{
false};
829 bool is_close_handler_called_{
false};
831 std::shared_ptr<void> anchor_ = std::make_shared<int>();
837 std::function<bool(
const crow::request&,
void**)> accept_handler_;
TinySHA1 - a header only implementation of the SHA1 algorithm in C++. Based on the implementation in ...
A websocket connection.
Definition websocket.h:108
void dispatch(CompletionHandler &&handler)
Send data through the socket.
Definition websocket.h:196
void do_read()
Read a websocket message.
Definition websocket.h:342
void send_pong(std::string msg) override
Send a "Pong" message.
Definition websocket.h:225
bool handle_fragment()
Process the payload fragment.
Definition websocket.h:601
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:285
void close(std::string const &msg, uint16_t status_code) override
Send a close signal.
Definition websocket.h:246
void send_text(std::string msg) override
Send a plaintext message.
Definition websocket.h:237
std::string get_subprotocol() const override
Returns the matching client/server subprotocol, empty string if none matched.
Definition websocket.h:278
int opcode()
Extract the opcode from the header.
Definition websocket.h:592
void do_write()
Send the buffers' data through the socket.
Definition websocket.h:705
void start(std::string &&hello)
Send the HTTP upgrade response.
Definition websocket.h:312
bool is_FIN()
Check if the FIN bit is set.
Definition websocket.h:586
void send_ping(std::string msg) override
Send a "Ping" message.
Definition websocket.h:216
void send_binary(std::string msg) override
Send a binary encoded message.
Definition websocket.h:231
void check_destroy(websocket::CloseStatusCode code=CloseStatusCode::ClosedAbnormally)
Destroy the Connection.
Definition websocket.h:742
void post(CompletionHandler &&handler)
Send data through the socket and return immediately.
Definition websocket.h:205
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< bool(const crow::request &, void **)> accept_handler, bool mirror_protocols)
Definition websocket.h:114
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...
An HTTP request.
Definition http_request.h:36
Definition websocket.h:759
Definition websocket.h:181
A base class for websocket connection.
Definition websocket.h:64