11 #include "string/encoding.h"
12 #include "string/format.h"
13 #include "string/string_utils.h"
16 #include <openssl/sha.h>
23 std::generate(
_ws_nonce.begin(),
_ws_nonce.end(), []() { return (uint8_t)std::rand(); });
28 if (response.
status() != 101)
33 bool connection =
false;
37 for (
size_t i = 0; i < response.
headers(); ++i)
39 auto header = response.
header(i);
40 auto key = std::get<0>(header);
41 auto value = std::get<1>(header);
43 if (CppCommon::StringUtils::CompareNoCase(key,
"Connection"))
45 if (!CppCommon::StringUtils::CompareNoCase(value,
"Upgrade"))
48 onWSError(
"Invalid WebSocket handshaked response: 'Connection' header value must be 'Upgrade'");
54 else if (CppCommon::StringUtils::CompareNoCase(key,
"Upgrade"))
56 if (!CppCommon::StringUtils::CompareNoCase(value,
"websocket"))
59 onWSError(
"Invalid WebSocket handshaked response: 'Upgrade' header value must be 'websocket'");
65 else if (CppCommon::StringUtils::CompareNoCase(key,
"Sec-WebSocket-Accept"))
68 std::string wskey = CppCommon::Encoding::Base64Encode(
ws_nonce()) +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
69 char wshash[SHA_DIGEST_LENGTH];
70 SHA1((
const unsigned char*)wskey.data(), wskey.size(), (
unsigned char*)wshash);
73 wskey = CppCommon::Encoding::Base64Decode(value);
76 if (std::strncmp(wskey.data(), wshash, std::min(wskey.size(),
sizeof(wshash))) != 0)
79 onWSError(
"Invalid WebSocket handshaked response: 'Sec-WebSocket-Accept' value validation failed");
88 if (!accept || !connection || !upgrade)
105 if (request.
method() !=
"GET")
109 bool connection =
false;
110 bool upgrade =
false;
112 bool ws_version =
false;
117 for (
size_t i = 0; i < request.
headers(); ++i)
119 auto header = request.
header(i);
120 auto key = std::get<0>(header);
121 auto value = std::get<1>(header);
123 if (CppCommon::StringUtils::CompareNoCase(key,
"Connection"))
125 if (!CppCommon::StringUtils::CompareNoCase(value,
"Upgrade") && !CppCommon::StringUtils::CompareNoCase(CppCommon::StringUtils::RemoveBlank(value),
"keep-alive,Upgrade"))
128 response.
MakeErrorResponse(400,
"Invalid WebSocket handshaked request: 'Connection' header value must be 'Upgrade' or 'keep-alive, Upgrade'");
134 else if (CppCommon::StringUtils::CompareNoCase(key,
"Upgrade"))
136 if (!CppCommon::StringUtils::CompareNoCase(value,
"websocket"))
139 response.
MakeErrorResponse(400,
"Invalid WebSocket handshaked request: 'Upgrade' header value must be 'websocket'");
145 else if (CppCommon::StringUtils::CompareNoCase(key,
"Sec-WebSocket-Key"))
150 response.
MakeErrorResponse(400,
"Invalid WebSocket handshaked request: 'Sec-WebSocket-Key' header value must be non empty");
155 std::string wskey = std::string(value) +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
156 char wshash[SHA_DIGEST_LENGTH];
157 SHA1((
const unsigned char*)wskey.data(), wskey.size(), (
unsigned char*)wshash);
159 accept = CppCommon::Encoding::Base64Encode(std::string(wshash,
sizeof(wshash)));
163 else if (CppCommon::StringUtils::CompareNoCase(key,
"Sec-WebSocket-Version"))
165 if (!CppCommon::StringUtils::CompareNoCase(value,
"13"))
168 response.
MakeErrorResponse(400,
"Invalid WebSocket handshaked request: 'Sec-WebSocket-Version' header value must be '13'");
177 if (!connection && !upgrade && !ws_key && !ws_version)
181 if (!connection || !upgrade || !ws_key || !ws_version)
192 response.
SetHeader(
"Connection",
"Upgrade");
193 response.
SetHeader(
"Upgrade",
"websocket");
194 response.
SetHeader(
"Sec-WebSocket-Accept", accept);
215 bool store_status = ((opcode &
WS_CLOSE) ==
WS_CLOSE) && ((size > 0) || (status != 0));
228 else if (size <= 65535)
237 for (
int i = 7; i >= 0; --i)
255 const uint8_t* data = (
const uint8_t*)buffer;
269 for (
size_t i = index; i < size; ++i)
275 const uint8_t* data = (
const uint8_t*)buffer;
312 for (
size_t i = 0; i < 2; ++i, ++data, --size)
336 else if (payload == 126)
340 for (
size_t i = 0; i < 2; ++i, ++data, --size)
354 else if (payload == 127)
358 for (
size_t i = 0; i < 8; ++i, ++data, --size)
378 for (
size_t i = 0; i < 4; ++i, ++data, --size)
size_t headers() const noexcept
Get the HTTP request headers count.
std::string_view method() const noexcept
Get the HTTP request method.
std::tuple< std::string_view, std::string_view > header(size_t i) const noexcept
Get the HTTP request header by index.
HTTPResponse & SetBody(std::string_view body="")
Set the HTTP response body.
HTTPResponse & SetHeader(std::string_view key, std::string_view value)
Set the HTTP response header.
HTTPResponse & Clear()
Clear the HTTP response cache.
size_t headers() const noexcept
Get the HTTP response headers count.
int status() const noexcept
Get the HTTP response status.
HTTPResponse & MakeErrorResponse(std::string_view content="", std::string_view content_type="text/plain; charset=UTF-8")
Make ERROR response.
HTTPResponse & SetBegin(int status, std::string_view protocol="HTTP/1.1")
Set the HTTP response begin with a given status and protocol.
std::tuple< std::string_view, std::string_view > header(size_t i) const noexcept
Get the HTTP response header by index.
size_t _ws_payload_size
Received frame payload size.
static const uint8_t WS_CLOSE
Close frame.
void PrepareSendFrame(uint8_t opcode, bool mask, const void *buffer, size_t size, int status=0)
Prepare WebSocket send frame.
static const uint8_t WS_TEXT
Text frame.
std::vector< uint8_t > _ws_receive_frame_buffer
Receive frame buffer.
virtual void onWSClose(const void *buffer, size_t size, int status=1000)
Handle WebSocket client close notification.
bool _ws_frame_received
Received frame flag.
static const uint8_t WS_BINARY
Binary frame.
std::string_view ws_nonce() const noexcept
Get the WebSocket random nonce.
static const uint8_t WS_PING
Ping frame.
void InitWSNonce()
Initialize WebSocket random nonce.
void PrepareReceiveFrame(const void *buffer, size_t size)
Prepare WebSocket receive frame.
virtual void onWSConnecting(HTTP::HTTPRequest &request)
Handle WebSocket client connecting notification.
virtual void onWSReceived(const void *buffer, size_t size)
Handle WebSocket received notification.
virtual void onWSPing(const void *buffer, size_t size)
Handle WebSocket ping notification.
std::vector< uint8_t > _ws_receive_final_buffer
Receive final buffer.
uint8_t _ws_receive_mask[4]
Receive mask.
std::array< uint8_t, 16 > _ws_nonce
WebSocket random nonce of 16 bytes.
bool PerformClientUpgrade(const HTTP::HTTPResponse &response, const CppCommon::UUID &id)
Perform WebSocket client upgrade.
std::mutex _ws_send_lock
Send buffer lock.
virtual void onWSConnected(const HTTP::HTTPResponse &response)
Handle WebSocket client connected notification.
uint8_t _ws_opcode
Received frame opcode.
void ClearWSBuffers()
Clear WebSocket send/receive buffers.
virtual void onWSPong(const void *buffer, size_t size)
Handle WebSocket pong notification.
static const uint8_t WS_PONG
Pong frame.
virtual void onWSError(const std::string &message)
Handle WebSocket error notification.
size_t RequiredReceiveFrameSize()
Required WebSocket receive frame size.
std::vector< uint8_t > _ws_send_buffer
Send buffer.
uint8_t _ws_send_mask[4]
Send mask.
virtual void SendResponse(const HTTP::HTTPResponse &response)
Send WebSocket server upgrade response.
bool _ws_final_received
Received final flag.
size_t _ws_header_size
Received frame header size.
bool PerformServerUpgrade(const HTTP::HTTPRequest &request, HTTP::HTTPResponse &response)
Perform WebSocket server upgrade.
bool _ws_handshaked
Handshaked flag.
C++ Server project definitions.
WebSocket C++ Library definition.