62inline std::string sha1(
const std::string& msg) {
63 std::uint32_t h[5] = {0x67452301u, 0xEFCDAB89u, 0x98BADCFEu, 0x10325476u, 0xC3D2E1F0u};
64 std::string data = msg;
65 const std::uint64_t bitlen =
static_cast<std::uint64_t
>(data.size()) * 8ull;
66 data.push_back(
static_cast<char>(0x80));
67 while (data.size() % 64 != 56) data.push_back(
'\0');
68 for (
int i = 7; i >= 0; --i)
69 data.push_back(
static_cast<char>((bitlen >> (i * 8)) & 0xFF));
71 for (std::size_t off = 0; off < data.size(); off += 64) {
73 for (
int i = 0; i < 16; ++i) {
74 const unsigned char* p =
75 reinterpret_cast<const unsigned char*
>(data.data() + off + i * 4);
76 w[i] = (std::uint32_t(p[0]) << 24) | (std::uint32_t(p[1]) << 16) |
77 (std::uint32_t(p[2]) << 8) | std::uint32_t(p[3]);
79 for (
int i = 16; i < 80; ++i) {
80 const std::uint32_t v = w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16];
81 w[i] = (v << 1) | (v >> 31);
83 std::uint32_t a = h[0], b = h[1], c = h[2], d = h[3], e = h[4];
84 for (
int i = 0; i < 80; ++i) {
85 std::uint32_t f = 0, k = 0;
87 f = (b & c) | ((~b) & d);
93 f = (b & c) | (b & d) | (c & d);
99 const std::uint32_t tmp = ((a << 5) | (a >> 27)) + f + e + k + w[i];
102 c = (b << 30) | (b >> 2);
112 std::string out(20,
'\0');
113 for (
int i = 0; i < 5; ++i)
114 for (
int j = 0; j < 4; ++j)
115 out[i * 4 + j] =
static_cast<char>((h[i] >> ((3 - j) * 8)) & 0xFF);
119inline std::string base64(
const std::string& in) {
120 static const char* tbl =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
123 while (i + 2 < in.size()) {
124 const unsigned v = (
static_cast<unsigned char>(in[i]) << 16) |
125 (
static_cast<unsigned char>(in[i + 1]) << 8) |
126 static_cast<unsigned char>(in[i + 2]);
127 out.push_back(tbl[(v >> 18) & 63]);
128 out.push_back(tbl[(v >> 12) & 63]);
129 out.push_back(tbl[(v >> 6) & 63]);
130 out.push_back(tbl[v & 63]);
133 if (i + 1 == in.size()) {
134 const unsigned v =
static_cast<unsigned char>(in[i]) << 16;
135 out.push_back(tbl[(v >> 18) & 63]);
136 out.push_back(tbl[(v >> 12) & 63]);
138 }
else if (i + 2 == in.size()) {
139 const unsigned v = (
static_cast<unsigned char>(in[i]) << 16) |
140 (
static_cast<unsigned char>(in[i + 1]) << 8);
141 out.push_back(tbl[(v >> 18) & 63]);
142 out.push_back(tbl[(v >> 12) & 63]);
143 out.push_back(tbl[(v >> 6) & 63]);
149inline bool send_all(
int fd,
const char* p, std::size_t n) {
151 const ssize_t k = ::send(fd, p, n, MSG_NOSIGNAL);
152 if (k <= 0)
return false;
154 n -=
static_cast<std::size_t
>(k);
159inline bool recv_exact(
int fd,
char* p, std::size_t n) {
161 const ssize_t k = ::recv(fd, p, n, 0);
162 if (k <= 0)
return false;
164 n -=
static_cast<std::size_t
>(k);
170inline std::string header(
const std::string& head,
const std::string& key) {
171 std::string lower = head, lkey = key;
172 for (std::size_t i = 0; i < lower.size(); ++i)
173 lower[i] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(lower[i])));
174 for (std::size_t i = 0; i < lkey.size(); ++i)
175 lkey[i] =
static_cast<char>(std::tolower(
static_cast<unsigned char>(lkey[i])));
176 std::size_t at = lower.find(
"\n" + lkey +
":");
177 if (at == std::string::npos)
return std::string();
178 at += lkey.size() + 2;
179 const std::size_t end = head.find(
'\n', at);
180 std::string v = head.substr(at, end == std::string::npos ? std::string::npos : end - at);
181 while (!v.empty() && (v[0] ==
' ' || v[0] ==
'\t')) v.erase(v.begin());
182 while (!v.empty() && (v.back() ==
'\r' || v.back() ==
' ')) v.pop_back();
192 fd_ = ::socket(AF_INET, SOCK_STREAM, 0);
193 if (fd_ < 0)
throw WsError(
"cannot create a listening socket");
195 ::setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &on,
sizeof(on));
197 std::memset(&a, 0,
sizeof(a));
198 a.sin_family = AF_INET;
199 a.sin_addr.s_addr = htonl(INADDR_ANY);
200 a.sin_port = htons(
static_cast<uint16_t
>(port));
201 if (::bind(fd_,
reinterpret_cast<sockaddr*
>(&a),
sizeof(a)) != 0) {
203 throw WsError(
"cannot bind port " + std::to_string(port) +
204 "; another process is probably listening on it");
206 if (::listen(fd_, 4) != 0) {
208 throw WsError(
"cannot listen on port " + std::to_string(port));
212 if (fd_ >= 0) ::close(fd_);
226 const int c = ::accept(fd_,
nullptr,
nullptr);
227 if (c < 0)
return false;
228 const bool ok = handshake(c) && exchange(c, serve);
236 static bool handshake(
int c) {
239 while (head.find(
"\r\n\r\n") == std::string::npos) {
240 const ssize_t k = ::recv(c, buf,
sizeof(buf), 0);
241 if (k <= 0)
return false;
242 head.append(buf,
static_cast<std::size_t
>(k));
243 if (head.size() > 65536)
return false;
245 const std::string key = detail::header(head,
"Sec-WebSocket-Key");
246 if (key.empty())
return false;
248 const std::string accept = detail::base64(
249 detail::sha1(key +
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"));
250 const std::string resp =
"HTTP/1.1 101 Switching Protocols\r\n"
251 "Upgrade: websocket\r\n"
252 "Connection: Upgrade\r\n"
253 "Sec-WebSocket-Accept: " + accept +
"\r\n\r\n";
254 return detail::send_all(c, resp.data(), resp.size());
258 static bool read_message(
int c, std::string& out) {
262 if (!detail::recv_exact(c,
reinterpret_cast<char*
>(h), 2))
return false;
263 const bool fin = (h[0] & 0x80) != 0;
264 const int opcode = h[0] & 0x0F;
265 const bool masked = (h[1] & 0x80) != 0;
266 std::uint64_t len = h[1] & 0x7F;
269 if (!detail::recv_exact(c,
reinterpret_cast<char*
>(e), 2))
return false;
270 len = (std::uint64_t(e[0]) << 8) | e[1];
271 }
else if (len == 127) {
273 if (!detail::recv_exact(c,
reinterpret_cast<char*
>(e), 8))
return false;
275 for (
int i = 0; i < 8; ++i) len = (len << 8) | e[i];
279 unsigned char mask[4] = {0, 0, 0, 0};
280 if (masked && !detail::recv_exact(c,
reinterpret_cast<char*
>(mask), 4))
return false;
281 if (!masked)
return false;
282 if (len > (1ull << 30))
return false;
283 std::string payload(
static_cast<std::size_t
>(len),
'\0');
284 if (len && !detail::recv_exact(c, &payload[0], payload.size()))
return false;
285 for (std::size_t i = 0; i < payload.size(); ++i)
286 payload[i] =
static_cast<char>(payload[i] ^ mask[i % 4]);
288 if (opcode == 0x8)
return false;
291 pong.push_back(
static_cast<char>(0x8A));
292 pong.push_back(
static_cast<char>(payload.size() & 0x7F));
294 if (!detail::send_all(c, pong.data(), pong.size()))
return false;
297 if (opcode == 0xA)
continue;
299 if (fin)
return true;
303 static bool send_text(
int c,
const std::string& msg) {
305 f.push_back(
static_cast<char>(0x81));
306 if (msg.size() < 126) {
307 f.push_back(
static_cast<char>(msg.size()));
308 }
else if (msg.size() <= 0xFFFF) {
309 f.push_back(
static_cast<char>(126));
310 f.push_back(
static_cast<char>((msg.size() >> 8) & 0xFF));
311 f.push_back(
static_cast<char>(msg.size() & 0xFF));
313 f.push_back(
static_cast<char>(127));
314 for (
int i = 7; i >= 0; --i)
315 f.push_back(
static_cast<char>((
static_cast<std::uint64_t
>(msg.size()) >> (i * 8)) &
319 if (!detail::send_all(c, f.data(), f.size()))
return false;
322 const char close_frame[2] = {
static_cast<char>(0x88), 0};
323 detail::send_all(c, close_frame, 2);
328 static bool exchange(
int c, Fn serve) {
330 if (!read_message(c, msg))
return false;
331 return send_text(c, serve(msg));