3#include <coroio/sockutils.hpp>
8#define htonll(x) htobe64(x)
9#define ntohll(x) be64toh(x)
10#elif defined(__APPLE__)
12#elif defined(__FreeBSD__)
14#include <sys/endian.h>
15#define htonll(x) htobe64(x)
16#define ntohll(x) be64toh(x)
19#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0A00)
20inline uint64_t htonll(uint64_t value) {
21#if BYTE_ORDER == LITTLE_ENDIAN || defined(_M_IX86) || defined(_M_X64)
22 return ((uint64_t)htonl(
static_cast<uint32_t
>(value & 0xFFFFFFFF)) << 32) |
23 htonl(
static_cast<uint32_t
>(value >> 32));
29inline uint64_t ntohll(uint64_t value) {
30#if BYTE_ORDER == LITTLE_ENDIAN || defined(_M_IX86) || defined(_M_X64)
31 return ((uint64_t)ntohl(
static_cast<uint32_t
>(value & 0xFFFFFFFF)) << 32) |
32 ntohl(
static_cast<uint32_t
>(value >> 32));
47std::string GenerateWebSocketKey(std::random_device& rd);
48void CheckSecWebSocketAccept(
const std::string& allServerHeaders,
const std::string& clientKeyBase64);
88template<
typename TSocket>
115 auto key = NDetail::GenerateWebSocketKey(Rd);
116 std::string request =
117 "GET " + path +
" HTTP/1.1\r\n"
118 "Host: " + host +
"\r\n"
119 "User-Agent: coroio\r\n"
121 "Connection: Upgrade\r\n"
122 "Upgrade: websocket\r\n"
123 "Sec-WebSocket-Key: " + key +
"\r\n"
124 "Sec-WebSocket-Version: 13\r\n\r\n";
126 co_await Writer.Write(request.data(), request.size());
128 auto response =
co_await Reader.ReadUntil(
"\r\n\r\n");
130 NDetail::CheckSecWebSocketAccept(response, key);
132 if (response.find(
"101 Switching Protocols") == std::string::npos) {
133 throw std::runtime_error(
"Failed to establish WebSocket connection");
146 co_await SendFrame(0x1, message);
158 auto [opcode, payload] =
co_await ReceiveFrame();
160 throw std::runtime_error(
161 "Unexpected opcode: " +
162 std::to_string(opcode) +
163 " , expected text frame, got: '" +
164 std::string(payload) +
"'");
173 std::random_device Rd;
175 std::vector<uint8_t> Frame;
177 TFuture<void> SendFrame(uint8_t opcode, std::string_view payload) {
179 Frame.push_back(0x80 | opcode);
181 uint8_t maskingKey[4];
182 for (
int i = 0; i < 4; ++i) {
183 maskingKey[i] =
static_cast<uint8_t
>(Rd());
186 if (payload.size() <= 125) {
187 Frame.push_back(0x80 |
static_cast<uint8_t
>(payload.size()));
188 }
else if (payload.size() <= 0xFFFF) {
189 Frame.push_back(0x80 | 126);
190 uint16_t length = htons(
static_cast<uint16_t
>(payload.size()));
191 Frame.insert(Frame.end(),
reinterpret_cast<uint8_t*
>(&length),
reinterpret_cast<uint8_t*
>(&length) + 2);
193 Frame.push_back(0x80 | 127);
194 uint64_t length = htonll(payload.size());
195 Frame.insert(Frame.end(),
reinterpret_cast<uint8_t*
>(&length),
reinterpret_cast<uint8_t*
>(&length) + 8);
198 Frame.insert(Frame.end(), std::begin(maskingKey), std::end(maskingKey));
200 for (
size_t i = 0; i < payload.size(); ++i) {
201 Frame.push_back(payload[i] ^ maskingKey[i % 4]);
204 co_await Writer.
Write(Frame.data(), Frame.size());
208 TFuture<std::pair<uint8_t, std::string_view>> ReceiveFrame() {
210 co_await Reader.
Read(header,
sizeof(header));
212 uint8_t opcode = header[0] & 0x0F;
213 bool masked = header[1] & 0x80;
214 uint64_t payloadLength = header[1] & 0x7F;
216 if (payloadLength == 126) {
217 uint16_t extendedLength;
218 co_await Reader.
Read(&extendedLength,
sizeof(extendedLength));
219 payloadLength = ntohs(extendedLength);
220 }
else if (payloadLength == 127) {
221 uint64_t extendedLength;
222 co_await Reader.
Read(&extendedLength,
sizeof(extendedLength));
223 payloadLength = ntohll(extendedLength);
226 uint8_t mask[4] = {0};
228 co_await Reader.
Read(mask,
sizeof(mask));
231 Payload.resize(payloadLength);
232 co_await Reader.
Read(Payload.data(), Payload.size());
235 for (
size_t i = 0; i < Payload.size(); ++i) {
236 Payload[i] ^= mask[i % 4];
240 co_return {opcode, Payload};
High-level asynchronous socket for network communication.
Definition socket.hpp:364
Implements a WebSocket protocol layer on top of a given socket.
Definition ws.hpp:89
TWebSocket(TSocket &socket)
Constructs a WebSocket instance wrapping the provided socket.
Definition ws.hpp:96
TFuture< void > Connect(const std::string &host, const std::string &path)
Initiates the WebSocket handshake.
Definition ws.hpp:114
TFuture< void > SendText(std::string_view message)
Sends a text message as a WebSocket frame.
Definition ws.hpp:145
TFuture< std::string_view > ReceiveText()
Receives a text message from the WebSocket.
Definition ws.hpp:157
A utility for reading data from a socket-like object, either a fixed number of bytes or until a speci...
Definition sockutils.hpp:64
TFuture< void > Read(void *data, size_t size)
Reads exactly size bytes and stores them into data.
Definition sockutils.hpp:89
A utility for writing data to a socket-like object.
Definition sockutils.hpp:199
TFuture< void > Write(const void *data, size_t size)
Writes exactly size bytes from data to the socket.
Definition sockutils.hpp:222
Future type for coroutines returning a value of type T.
Definition corochain.hpp:182