3 #include "crow/logging.h"
4 #include "crow/socket_adaptors.h"
5 #include "crow/http_request.h"
7 #include "crow/utility.h"
12 namespace asio = boost::asio;
13 using error_code = boost::system::error_code;
15 using error_code = asio::error_code;
27 enum class WebSocketReadState
39 virtual void send_binary(std::string msg) = 0;
40 virtual void send_text(std::string msg) = 0;
41 virtual void send_ping(std::string msg) = 0;
42 virtual void send_pong(std::string msg) = 0;
43 virtual void close(std::string
const& msg =
"quit") = 0;
44 virtual std::string get_remote_ip() = 0;
47 void userdata(
void* u) { userdata_ = u; }
48 void* userdata() {
return userdata_; }
79 template<
typename Adaptor,
typename Handler>
93 std::function<
bool(
const crow::request&,
void**)> accept_handler):
94 adaptor_(std::move(adaptor)),
96 max_payload_bytes_(max_payload),
97 open_handler_(std::move(open_handler)),
98 message_handler_(std::move(message_handler)),
99 close_handler_(std::move(close_handler)),
100 error_handler_(std::move(error_handler)),
101 accept_handler_(std::move(accept_handler))
103 if (!utility::string_equals(req.get_header_value(
"upgrade"),
"websocket"))
106 handler_->remove_websocket(
this);
114 if (!accept_handler_(req, &ud))
117 handler_->remove_websocket(
this);
126 std::string magic = req.get_header_value(
"Sec-WebSocket-Key") +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
128 s.processBytes(magic.data(), magic.size());
130 s.getDigestBytes(digest);
132 start(crow::utility::base64encode((
unsigned char*)digest, 20));
138 auto watch = std::weak_ptr<void>{anchor_};
143 while (watch.use_count() > 2)
145 std::this_thread::yield();
149 template<
typename Callable>
153 std::weak_ptr<void> watch;
157 if (
auto anchor = watch.lock())
159 std::move(callable)();
165 template<
typename CompletionHandler>
168 asio::dispatch(adaptor_.get_io_service(),
170 std::forward<CompletionHandler>(handler), anchor_});
174 template<
typename CompletionHandler>
175 void post(CompletionHandler&& handler)
177 asio::post(adaptor_.get_io_service(),
179 std::forward<CompletionHandler>(handler), anchor_});
188 send_data(0x9, std::move(msg));
197 send_data(0xA, std::move(msg));
203 send_data(0x2, std::move(msg));
209 send_data(0x1, std::move(msg));
216 void close(std::string
const& msg)
override
219 has_sent_close_ =
true;
220 if (has_recv_close_ && !is_close_handler_called_)
222 is_close_handler_called_ =
true;
224 close_handler_(*
this, msg);
227 write_buffers_.emplace_back(std::move(header));
228 write_buffers_.emplace_back(msg);
233 std::string get_remote_ip()
override
235 return adaptor_.remote_endpoint().address().to_string();
238 void set_max_payload_size(uint64_t payload)
240 max_payload_bytes_ = payload;
247 char buf[2 + 8] =
"\x80\x00";
251 buf[1] +=
static_cast<char>(size);
252 return {buf, buf + 2};
254 else if (size < 0x10000)
257 *(uint16_t*)(buf + 2) = htons(
static_cast<uint16_t
>(size));
258 return {buf, buf + 4};
263 *
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));
264 return {buf, buf + 10};
274 static const std::string header =
275 "HTTP/1.1 101 Switching Protocols\r\n"
276 "Upgrade: websocket\r\n"
277 "Connection: Upgrade\r\n"
278 "Sec-WebSocket-Accept: ";
279 write_buffers_.emplace_back(header);
280 write_buffers_.emplace_back(std::move(hello));
281 write_buffers_.emplace_back(crlf);
282 write_buffers_.emplace_back(crlf);
285 open_handler_(*
this);
298 if (has_sent_close_ && has_recv_close_)
300 close_connection_ =
true;
301 adaptor_.shutdown_readwrite();
310 case WebSocketReadState::MiniHeader:
314 adaptor_.socket().async_read_some(
315 asio::buffer(&mini_header_, 2),
316 [
this](
const error_code& ec, std::size_t
317 #ifdef CROW_ENABLE_DEBUG
324 mini_header_ = ntohs(mini_header_);
325 #ifdef CROW_ENABLE_DEBUG
327 if (!ec && bytes_transferred != 2)
329 throw std::runtime_error(
"WebSocket:MiniHeader:async_read fail:asio bug?");
335 if ((mini_header_ & 0x80) == 0x80)
339 #ifndef CROW_ENFORCE_WS_SPEC
342 close_connection_ =
true;
343 adaptor_.shutdown_readwrite();
346 error_handler_(*
this,
"Client connection not masked.");
351 if ((mini_header_ & 0x7f) == 127)
353 state_ = WebSocketReadState::Len64;
355 else if ((mini_header_ & 0x7f) == 126)
357 state_ = WebSocketReadState::Len16;
361 remaining_length_ = mini_header_ & 0x7f;
362 state_ = WebSocketReadState::Mask;
368 close_connection_ =
true;
369 adaptor_.shutdown_readwrite();
372 error_handler_(*
this, ec.message());
378 case WebSocketReadState::Len16:
380 remaining_length_ = 0;
381 remaining_length16_ = 0;
383 adaptor_.socket(), asio::buffer(&remaining_length16_, 2),
384 [
this](
const error_code& ec, std::size_t
385 #ifdef CROW_ENABLE_DEBUG
390 remaining_length16_ = ntohs(remaining_length16_);
391 remaining_length_ = remaining_length16_;
392 #ifdef CROW_ENABLE_DEBUG
393 if (!ec && bytes_transferred != 2)
395 throw std::runtime_error(
"WebSocket:Len16:async_read fail:asio bug?");
401 state_ = WebSocketReadState::Mask;
406 close_connection_ =
true;
407 adaptor_.shutdown_readwrite();
410 error_handler_(*
this, ec.message());
416 case WebSocketReadState::Len64:
419 adaptor_.socket(), asio::buffer(&remaining_length_, 8),
420 [
this](
const error_code& ec, std::size_t
421 #ifdef CROW_ENABLE_DEBUG
426 remaining_length_ = ((1 == ntohl(1)) ? (remaining_length_) : (static_cast<uint64_t>(ntohl((remaining_length_)&0xFFFFFFFF)) << 32) | ntohl((remaining_length_) >> 32));
427 #ifdef CROW_ENABLE_DEBUG
428 if (!ec && bytes_transferred != 8)
430 throw std::runtime_error(
"WebSocket:Len16:async_read fail:asio bug?");
436 state_ = WebSocketReadState::Mask;
441 close_connection_ =
true;
442 adaptor_.shutdown_readwrite();
445 error_handler_(*
this, ec.message());
451 case WebSocketReadState::Mask:
452 if (remaining_length_ > max_payload_bytes_)
454 close_connection_ =
true;
457 error_handler_(*
this,
"Message length exceeds maximum payload.");
463 adaptor_.socket(), asio::buffer((
char*)&mask_, 4),
464 [
this](
const error_code& ec, std::size_t
465 #ifdef CROW_ENABLE_DEBUG
470 #ifdef CROW_ENABLE_DEBUG
471 if (!ec && bytes_transferred != 4)
473 throw std::runtime_error(
"WebSocket:Mask:async_read fail:asio bug?");
479 state_ = WebSocketReadState::Payload;
484 close_connection_ =
true;
486 error_handler_(*
this, ec.message());
487 adaptor_.shutdown_readwrite();
495 state_ = WebSocketReadState::Payload;
499 case WebSocketReadState::Payload:
501 auto to_read =
static_cast<std::uint64_t
>(buffer_.size());
502 if (remaining_length_ < to_read)
503 to_read = remaining_length_;
504 adaptor_.socket().async_read_some(
505 asio::buffer(buffer_,
static_cast<std::size_t
>(to_read)),
506 [
this](
const error_code& ec, std::size_t bytes_transferred) {
511 fragment_.insert(fragment_.end(), buffer_.begin(), buffer_.begin() + bytes_transferred);
512 remaining_length_ -= bytes_transferred;
513 if (remaining_length_ == 0)
515 if (handle_fragment())
517 state_ = WebSocketReadState::MiniHeader;
526 close_connection_ =
true;
528 error_handler_(*
this, ec.message());
529 adaptor_.shutdown_readwrite();
542 return mini_header_ & 0x8000;
548 return (mini_header_ & 0x0f00) >> 8;
559 for (decltype(fragment_.length()) i = 0; i < fragment_.length(); i++)
561 fragment_[i] ^= ((
char*)&mask_)[i % 4];
568 message_ += fragment_;
571 if (message_handler_)
572 message_handler_(*
this, message_, is_binary_);
580 message_ += fragment_;
583 if (message_handler_)
584 message_handler_(*
this, message_, is_binary_);
592 message_ += fragment_;
595 if (message_handler_)
596 message_handler_(*
this, message_, is_binary_);
603 has_recv_close_ =
true;
604 if (!has_sent_close_)
610 adaptor_.shutdown_readwrite();
612 close_connection_ =
true;
613 if (!is_close_handler_called_)
616 close_handler_(*
this, fragment_);
617 is_close_handler_called_ =
true;
626 send_pong(fragment_);
631 pong_received_ =
true;
646 if (sending_buffers_.empty())
648 sending_buffers_.swap(write_buffers_);
649 std::vector<asio::const_buffer> buffers;
650 buffers.reserve(sending_buffers_.size());
651 for (
auto& s : sending_buffers_)
653 buffers.emplace_back(asio::buffer(s));
655 auto watch = std::weak_ptr<void>{anchor_};
657 adaptor_.socket(), buffers,
658 [&, watch](
const error_code& ec, std::size_t ) {
659 if (!ec && !close_connection_)
661 sending_buffers_.clear();
662 if (!write_buffers_.empty())
665 close_connection_ = true;
669 auto anchor = watch.lock();
670 if (anchor == nullptr) { return; }
672 sending_buffers_.clear();
673 close_connection_ =
true;
684 if (!is_close_handler_called_)
686 close_handler_(*
this,
"uncleanly");
687 handler_->remove_websocket(
this);
688 if (sending_buffers_.empty() && !is_reading)
701 self->send_data_impl(
this);
707 auto header = build_header(s->opcode, s->payload.size());
708 write_buffers_.emplace_back(std::move(header));
709 write_buffers_.emplace_back(std::move(s->payload));
713 void send_data(
int opcode, std::string&& msg)
715 SendMessageType event_arg{
720 post(std::move(event_arg));
727 std::vector<std::string> sending_buffers_;
728 std::vector<std::string> write_buffers_;
730 std::array<char, 4096> buffer_;
732 std::string message_;
733 std::string fragment_;
734 WebSocketReadState state_{WebSocketReadState::MiniHeader};
735 uint16_t remaining_length16_{0};
736 uint64_t remaining_length_{0};
737 uint64_t max_payload_bytes_{UINT64_MAX};
738 bool close_connection_{
false};
739 bool is_reading{
false};
740 bool has_mask_{
false};
742 uint16_t mini_header_;
743 bool has_sent_close_{
false};
744 bool has_recv_close_{
false};
745 bool error_occurred_{
false};
746 bool pong_received_{
false};
747 bool is_close_handler_called_{
false};
749 std::shared_ptr<void> anchor_ = std::make_shared<int>();
755 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:81
void dispatch(CompletionHandler &&handler)
Send data through the socket.
Definition: websocket.h:166
void do_read()
Read a websocket message.
Definition: websocket.h:296
void send_pong(std::string msg) override
Send a "Pong" message.
Definition: websocket.h:195
bool handle_fragment()
Process the payload fragment.
Definition: websocket.h:555
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:245
void send_text(std::string msg) override
Send a plaintext message.
Definition: websocket.h:207
void close(std::string const &msg) override
Send a close signal.
Definition: websocket.h:216
int opcode()
Extract the opcode from the header.
Definition: websocket.h:546
void do_write()
Send the buffers' data through the socket.
Definition: websocket.h:644
void check_destroy()
Destroy the Connection.
Definition: websocket.h:681
void start(std::string &&hello)
Send the HTTP upgrade response.
Definition: websocket.h:272
bool is_FIN()
Check if the FIN bit is set.
Definition: websocket.h:540
void send_ping(std::string msg) override
Send a "Ping" message.
Definition: websocket.h:186
void send_binary(std::string msg) override
Send a binary encoded message.
Definition: websocket.h:201
Connection(const crow::request &req, Adaptor &&adaptor, Handler *handler, uint64_t max_payload, 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 &)> close_handler, std::function< void(crow::websocket::connection &, const std::string &)> error_handler, std::function< bool(const crow::request &, void **)> accept_handler)
Constructor for a connection.
Definition: websocket.h:88
void post(CompletionHandler &&handler)
Send data through the socket and return immediately.
Definition: websocket.h:175
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:694
Definition: websocket.h:151
A base class for websocket connection.
Definition: websocket.h:38