aboutsummaryrefslogtreecommitdiffhomepage
path: root/node_modules/uws/src
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/uws/src')
-rw-r--r--node_modules/uws/src/Asio.h184
-rw-r--r--node_modules/uws/src/Backend.h15
-rw-r--r--node_modules/uws/src/Epoll.cpp60
-rw-r--r--node_modules/uws/src/Epoll.h257
-rw-r--r--node_modules/uws/src/Extensions.cpp131
-rw-r--r--node_modules/uws/src/Extensions.h29
-rw-r--r--node_modules/uws/src/Group.cpp263
-rw-r--r--node_modules/uws/src/Group.h144
-rw-r--r--node_modules/uws/src/HTTPSocket.cpp310
-rw-r--r--node_modules/uws/src/HTTPSocket.h285
-rw-r--r--node_modules/uws/src/Hub.cpp177
-rw-r--r--node_modules/uws/src/Hub.h97
-rw-r--r--node_modules/uws/src/Libuv.h175
-rw-r--r--node_modules/uws/src/Networking.cpp78
-rw-r--r--node_modules/uws/src/Networking.h259
-rw-r--r--node_modules/uws/src/Node.cpp83
-rw-r--r--node_modules/uws/src/Node.h198
-rw-r--r--node_modules/uws/src/Socket.cpp28
-rw-r--r--node_modules/uws/src/Socket.h507
-rw-r--r--node_modules/uws/src/WebSocket.cpp405
-rw-r--r--node_modules/uws/src/WebSocket.h89
-rw-r--r--node_modules/uws/src/WebSocketProtocol.h377
-rw-r--r--node_modules/uws/src/addon.cpp24
-rw-r--r--node_modules/uws/src/addon.h464
-rw-r--r--node_modules/uws/src/http.h357
-rw-r--r--node_modules/uws/src/uWS.h6
26 files changed, 5002 insertions, 0 deletions
diff --git a/node_modules/uws/src/Asio.h b/node_modules/uws/src/Asio.h
new file mode 100644
index 0000000..2792c29
--- /dev/null
+++ b/node_modules/uws/src/Asio.h
@@ -0,0 +1,184 @@
+#ifndef ASIO_H
+#define ASIO_H
+
+#include <boost/asio.hpp>
+
+typedef boost::asio::ip::tcp::socket::native_type uv_os_sock_t;
+static const int UV_READABLE = 1;
+static const int UV_WRITABLE = 2;
+
+struct Loop : boost::asio::io_service {
+
+ static Loop *createLoop(bool defaultLoop = true) {
+ return new Loop;
+ }
+
+ void destroy() {
+ delete this;
+ }
+
+ void run() {
+ boost::asio::io_service::run();
+ }
+};
+
+struct Timer {
+ boost::asio::deadline_timer asio_timer;
+ void *data;
+
+ Timer(Loop *loop) : asio_timer(*loop) {
+
+ }
+
+ void start(void (*cb)(Timer *), int first, int repeat) {
+ asio_timer.expires_from_now(boost::posix_time::milliseconds(first));
+ asio_timer.async_wait([this, cb, repeat](const boost::system::error_code &ec) {
+ if (ec != boost::asio::error::operation_aborted) {
+ if (repeat) {
+ start(cb, repeat, repeat);
+ }
+ cb(this);
+ }
+ });
+ }
+
+ void setData(void *data) {
+ this->data = data;
+ }
+
+ void *getData() {
+ return data;
+ }
+
+ // bug: cancel does not cancel expired timers!
+ // it has to guarantee that the timer is not called after
+ // stop is called! ffs boost!
+ void stop() {
+ asio_timer.cancel();
+ }
+
+ void close() {
+ asio_timer.get_io_service().post([this]() {
+ delete this;
+ });
+ }
+};
+
+struct Async {
+ Loop *loop;
+ void (*cb)(Async *);
+ void *data;
+
+ boost::asio::io_service::work asio_work;
+
+ Async(Loop *loop) : loop(loop), asio_work(*loop) {
+ }
+
+ void start(void (*cb)(Async *)) {
+ this->cb = cb;
+ }
+
+ void send() {
+ loop->post([this]() {
+ cb(this);
+ });
+ }
+
+ void close() {
+ loop->post([this]() {
+ delete this;
+ });
+ }
+
+ void setData(void *data) {
+ this->data = data;
+ }
+
+ void *getData() {
+ return data;
+ }
+};
+
+struct Poll {
+ boost::asio::posix::stream_descriptor *socket;
+ void (*cb)(Poll *p, int status, int events);
+
+ Poll(Loop *loop, uv_os_sock_t fd) {
+ socket = new boost::asio::posix::stream_descriptor(*loop, fd);
+ socket->non_blocking(true);
+ }
+
+ bool isClosed() {
+ return !socket;
+ }
+
+ boost::asio::ip::tcp::socket::native_type getFd() {
+ return socket ? socket->native_handle() : -1;
+ }
+
+ void setCb(void (*cb)(Poll *p, int status, int events)) {
+ this->cb = cb;
+ }
+
+ void (*getCb())(Poll *, int, int) {
+ return cb;
+ }
+
+ void reInit(Loop *loop, uv_os_sock_t fd) {
+ delete socket;
+ socket = new boost::asio::posix::stream_descriptor(*loop, fd);
+ socket->non_blocking(true);
+ }
+
+ void start(Loop *, Poll *self, int events) {
+ if (events & UV_READABLE) {
+ socket->async_read_some(boost::asio::null_buffers(), [self](boost::system::error_code ec, std::size_t) {
+ if (ec != boost::asio::error::operation_aborted) {
+ self->start(nullptr, self, UV_READABLE);
+ self->cb(self, ec ? -1 : 0, UV_READABLE);
+ }
+ });
+ }
+
+ if (events & UV_WRITABLE) {
+ socket->async_write_some(boost::asio::null_buffers(), [self](boost::system::error_code ec, std::size_t) {
+ if (ec != boost::asio::error::operation_aborted) {
+ self->start(nullptr, self, UV_WRITABLE);
+ self->cb(self, ec ? -1 : 0, UV_WRITABLE);
+ }
+ });
+ }
+ }
+
+ void change(Loop *, Poll *self, int events) {
+ socket->cancel();
+ start(nullptr, self, events);
+ }
+
+ bool fastTransfer(Loop *loop, Loop *newLoop, int events) {
+ return false;
+ }
+
+ // todo: asio is thread safe, use it!
+ bool threadSafeChange(Loop *loop, Poll *self, int events) {
+ return false;
+ }
+
+ void stop(Loop *) {
+ socket->cancel();
+ }
+
+ // this is not correct, but it works for now
+ // think about transfer - should allow one to not delete
+ // but in this case it doesn't matter at all
+ void close(Loop *loop, void (*cb)(Poll *)) {
+ socket->release();
+ socket->get_io_service().post([cb, this]() {
+ cb(this);
+ });
+ delete socket;
+ socket = nullptr;
+ }
+};
+
+#endif // ASIO_H
diff --git a/node_modules/uws/src/Backend.h b/node_modules/uws/src/Backend.h
new file mode 100644
index 0000000..4bfed95
--- /dev/null
+++ b/node_modules/uws/src/Backend.h
@@ -0,0 +1,15 @@
+#ifndef BACKEND_H
+#define BACKEND_H
+
+// Default to Epoll if nothing specified and on Linux
+// Default to Libuv if nothing specified and not on Linux
+#ifdef USE_ASIO
+#include "Asio.h"
+#elif !defined(__linux__) || defined(USE_LIBUV)
+#include "Libuv.h"
+#else
+#define USE_EPOLL
+#include "Epoll.h"
+#endif
+
+#endif // BACKEND_H
diff --git a/node_modules/uws/src/Epoll.cpp b/node_modules/uws/src/Epoll.cpp
new file mode 100644
index 0000000..f78d2ba
--- /dev/null
+++ b/node_modules/uws/src/Epoll.cpp
@@ -0,0 +1,60 @@
+#include "Backend.h"
+
+#ifdef USE_EPOLL
+
+// todo: remove this mutex, have callbacks set at program start
+std::recursive_mutex cbMutex;
+void (*callbacks[16])(Poll *, int, int);
+int cbHead = 0;
+
+void Loop::run() {
+ timepoint = std::chrono::system_clock::now();
+ while (numPolls) {
+ for (std::pair<Poll *, void (*)(Poll *)> c : closing) {
+ numPolls--;
+
+ c.second(c.first);
+
+ if (!numPolls) {
+ closing.clear();
+ return;
+ }
+ }
+ closing.clear();
+
+ int numFdReady = epoll_wait(epfd, readyEvents, 1024, delay);
+ timepoint = std::chrono::system_clock::now();
+
+ if (preCb) {
+ preCb(preCbData);
+ }
+
+ for (int i = 0; i < numFdReady; i++) {
+ Poll *poll = (Poll *) readyEvents[i].data.ptr;
+ int status = -bool(readyEvents[i].events & EPOLLERR);
+ callbacks[poll->state.cbIndex](poll, status, readyEvents[i].events);
+ }
+
+ while (timers.size() && timers[0].timepoint < timepoint) {
+ Timer *timer = timers[0].timer;
+ cancelledLastTimer = false;
+ timers[0].cb(timers[0].timer);
+
+ if (cancelledLastTimer) {
+ continue;
+ }
+
+ int repeat = timers[0].nextDelay;
+ auto cb = timers[0].cb;
+ timers.erase(timers.begin());
+ if (repeat) {
+ timer->start(cb, repeat, repeat);
+ }
+ }
+
+ if (postCb) {
+ postCb(postCbData);
+ }
+ }
+}
+#endif
diff --git a/node_modules/uws/src/Epoll.h b/node_modules/uws/src/Epoll.h
new file mode 100644
index 0000000..949791f
--- /dev/null
+++ b/node_modules/uws/src/Epoll.h
@@ -0,0 +1,257 @@
+#ifndef EPOLL_H
+#define EPOLL_H
+
+#include <sys/epoll.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <chrono>
+#include <algorithm>
+#include <vector>
+#include <mutex>
+
+typedef int uv_os_sock_t;
+static const int UV_READABLE = EPOLLIN;
+static const int UV_WRITABLE = EPOLLOUT;
+
+struct Poll;
+struct Timer;
+
+extern std::recursive_mutex cbMutex;
+extern void (*callbacks[16])(Poll *, int, int);
+extern int cbHead;
+
+struct Timepoint {
+ void (*cb)(Timer *);
+ Timer *timer;
+ std::chrono::system_clock::time_point timepoint;
+ int nextDelay;
+};
+
+struct Loop {
+ int epfd;
+ int numPolls = 0;
+ bool cancelledLastTimer;
+ int delay = -1;
+ epoll_event readyEvents[1024];
+ std::chrono::system_clock::time_point timepoint;
+ std::vector<Timepoint> timers;
+ std::vector<std::pair<Poll *, void (*)(Poll *)>> closing;
+
+ void (*preCb)(void *) = nullptr;
+ void (*postCb)(void *) = nullptr;
+ void *preCbData, *postCbData;
+
+ Loop(bool defaultLoop) {
+ epfd = epoll_create1(EPOLL_CLOEXEC);
+ timepoint = std::chrono::system_clock::now();
+ }
+
+ static Loop *createLoop(bool defaultLoop = true) {
+ return new Loop(defaultLoop);
+ }
+
+ void destroy() {
+ ::close(epfd);
+ delete this;
+ }
+
+ void run();
+
+ int getEpollFd() {
+ return epfd;
+ }
+};
+
+struct Timer {
+ Loop *loop;
+ void *data;
+
+ Timer(Loop *loop) {
+ this->loop = loop;
+ }
+
+ void start(void (*cb)(Timer *), int timeout, int repeat) {
+ loop->timepoint = std::chrono::system_clock::now();
+ std::chrono::system_clock::time_point timepoint = loop->timepoint + std::chrono::milliseconds(timeout);
+
+ Timepoint t = {cb, this, timepoint, repeat};
+ loop->timers.insert(
+ std::upper_bound(loop->timers.begin(), loop->timers.end(), t, [](const Timepoint &a, const Timepoint &b) {
+ return a.timepoint < b.timepoint;
+ }),
+ t
+ );
+
+ loop->delay = -1;
+ if (loop->timers.size()) {
+ loop->delay = std::max<int>(std::chrono::duration_cast<std::chrono::milliseconds>(loop->timers[0].timepoint - loop->timepoint).count(), 0);
+ }
+ }
+
+ void setData(void *data) {
+ this->data = data;
+ }
+
+ void *getData() {
+ return data;
+ }
+
+ // always called before destructor
+ void stop() {
+ auto pos = loop->timers.begin();
+ for (Timepoint &t : loop->timers) {
+ if (t.timer == this) {
+ loop->timers.erase(pos);
+ break;
+ }
+ pos++;
+ }
+ loop->cancelledLastTimer = true;
+
+ loop->delay = -1;
+ if (loop->timers.size()) {
+ loop->delay = std::max<int>(std::chrono::duration_cast<std::chrono::milliseconds>(loop->timers[0].timepoint - loop->timepoint).count(), 0);
+ }
+ }
+
+ void close() {
+ delete this;
+ }
+};
+
+// 4 bytes
+struct Poll {
+protected:
+ struct {
+ int fd : 28;
+ unsigned int cbIndex : 4;
+ } state = {-1, 0};
+
+ Poll(Loop *loop, uv_os_sock_t fd) {
+ fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
+ state.fd = fd;
+ loop->numPolls++;
+ }
+
+ // todo: pre-set all of callbacks up front and remove mutex
+ void setCb(void (*cb)(Poll *p, int status, int events)) {
+ cbMutex.lock();
+ state.cbIndex = cbHead;
+ for (int i = 0; i < cbHead; i++) {
+ if (callbacks[i] == cb) {
+ state.cbIndex = i;
+ break;
+ }
+ }
+ if (state.cbIndex == cbHead) {
+ callbacks[cbHead++] = cb;
+ }
+ cbMutex.unlock();
+ }
+
+ void (*getCb())(Poll *, int, int) {
+ return callbacks[state.cbIndex];
+ }
+
+ void reInit(Loop *loop, uv_os_sock_t fd) {
+ state.fd = fd;
+ loop->numPolls++;
+ }
+
+ void start(Loop *loop, Poll *self, int events) {
+ epoll_event event;
+ event.events = events;
+ event.data.ptr = self;
+ epoll_ctl(loop->epfd, EPOLL_CTL_ADD, state.fd, &event);
+ }
+
+ void change(Loop *loop, Poll *self, int events) {
+ epoll_event event;
+ event.events = events;
+ event.data.ptr = self;
+ epoll_ctl(loop->epfd, EPOLL_CTL_MOD, state.fd, &event);
+ }
+
+ void stop(Loop *loop) {
+ epoll_event event;
+ epoll_ctl(loop->epfd, EPOLL_CTL_DEL, state.fd, &event);
+ }
+
+ bool fastTransfer(Loop *loop, Loop *newLoop, int events) {
+ stop(loop);
+ start(newLoop, this, events);
+ loop->numPolls--;
+ // needs to lock the newLoop's numPolls!
+ newLoop->numPolls++;
+ return true;
+ }
+
+ bool threadSafeChange(Loop *loop, Poll *self, int events) {
+ change(loop, self, events);
+ return true;
+ }
+
+ void close(Loop *loop, void (*cb)(Poll *)) {
+ state.fd = -1;
+ loop->closing.push_back({this, cb});
+ }
+
+public:
+ bool isClosed() {
+ return state.fd == -1;
+ }
+
+ uv_os_sock_t getFd() {
+ return state.fd;
+ }
+
+ friend struct Loop;
+};
+
+// this should be put in the Loop as a general "post" function always available
+struct Async : Poll {
+ void (*cb)(Async *);
+ Loop *loop;
+ void *data;
+
+ Async(Loop *loop) : Poll(loop, ::eventfd(0, EFD_CLOEXEC)) {
+ this->loop = loop;
+ }
+
+ void start(void (*cb)(Async *)) {
+ this->cb = cb;
+ Poll::setCb([](Poll *p, int, int) {
+ uint64_t val;
+ if (::read(((Async *) p)->state.fd, &val, 8) == 8) {
+ ((Async *) p)->cb((Async *) p);
+ }
+ });
+ Poll::start(loop, this, UV_READABLE);
+ }
+
+ void send() {
+ uint64_t one = 1;
+ if (::write(state.fd, &one, 8) != 8) {
+ return;
+ }
+ }
+
+ void close() {
+ Poll::stop(loop);
+ ::close(state.fd);
+ Poll::close(loop, [](Poll *p) {
+ delete p;
+ });
+ }
+
+ void setData(void *data) {
+ this->data = data;
+ }
+
+ void *getData() {
+ return data;
+ }
+};
+
+#endif // EPOLL_H
diff --git a/node_modules/uws/src/Extensions.cpp b/node_modules/uws/src/Extensions.cpp
new file mode 100644
index 0000000..ef8f9da
--- /dev/null
+++ b/node_modules/uws/src/Extensions.cpp
@@ -0,0 +1,131 @@
+#include "Extensions.h"
+
+namespace uWS {
+
+enum ExtensionTokens {
+ TOK_PERMESSAGE_DEFLATE = 1838,
+ TOK_SERVER_NO_CONTEXT_TAKEOVER = 2807,
+ TOK_CLIENT_NO_CONTEXT_TAKEOVER = 2783,
+ TOK_SERVER_MAX_WINDOW_BITS = 2372,
+ TOK_CLIENT_MAX_WINDOW_BITS = 2348
+};
+
+class ExtensionsParser {
+private:
+ int *lastInteger = nullptr;
+
+public:
+ bool perMessageDeflate = false;
+ bool serverNoContextTakeover = false;
+ bool clientNoContextTakeover = false;
+ int serverMaxWindowBits = 0;
+ int clientMaxWindowBits = 0;
+
+ int getToken(const char *&in, const char *stop);
+ ExtensionsParser(const char *data, size_t length);
+};
+
+int ExtensionsParser::getToken(const char *&in, const char *stop) {
+ while (!isalnum(*in) && in != stop) {
+ in++;
+ }
+
+ int hashedToken = 0;
+ while (isalnum(*in) || *in == '-' || *in == '_') {
+ if (isdigit(*in)) {
+ hashedToken = hashedToken * 10 - (*in - '0');
+ } else {
+ hashedToken += *in;
+ }
+ in++;
+ }
+ return hashedToken;
+}
+
+ExtensionsParser::ExtensionsParser(const char *data, size_t length) {
+ const char *stop = data + length;
+ int token = 1;
+ for (; token && token != TOK_PERMESSAGE_DEFLATE; token = getToken(data, stop));
+
+ perMessageDeflate = (token == TOK_PERMESSAGE_DEFLATE);
+ while ((token = getToken(data, stop))) {
+ switch (token) {
+ case TOK_PERMESSAGE_DEFLATE:
+ return;
+ case TOK_SERVER_NO_CONTEXT_TAKEOVER:
+ serverNoContextTakeover = true;
+ break;
+ case TOK_CLIENT_NO_CONTEXT_TAKEOVER:
+ clientNoContextTakeover = true;
+ break;
+ case TOK_SERVER_MAX_WINDOW_BITS:
+ serverMaxWindowBits = 1;
+ lastInteger = &serverMaxWindowBits;
+ break;
+ case TOK_CLIENT_MAX_WINDOW_BITS:
+ clientMaxWindowBits = 1;
+ lastInteger = &clientMaxWindowBits;
+ break;
+ default:
+ if (token < 0 && lastInteger) {
+ *lastInteger = -token;
+ }
+ break;
+ }
+ }
+}
+
+template <bool isServer>
+ExtensionsNegotiator<isServer>::ExtensionsNegotiator(int wantedOptions) {
+ options = wantedOptions;
+}
+
+template <bool isServer>
+std::string ExtensionsNegotiator<isServer>::generateOffer() {
+ std::string extensionsOffer;
+ if (options & Options::PERMESSAGE_DEFLATE) {
+ extensionsOffer += "permessage-deflate";
+
+ if (options & Options::CLIENT_NO_CONTEXT_TAKEOVER) {
+ extensionsOffer += "; client_no_context_takeover";
+ }
+
+ if (options & Options::SERVER_NO_CONTEXT_TAKEOVER) {
+ extensionsOffer += "; server_no_context_takeover";
+ }
+ }
+
+ return extensionsOffer;
+}
+
+template <bool isServer>
+void ExtensionsNegotiator<isServer>::readOffer(std::string offer) {
+ if (isServer) {
+ ExtensionsParser extensionsParser(offer.data(), offer.length());
+ if ((options & PERMESSAGE_DEFLATE) && extensionsParser.perMessageDeflate) {
+ if (extensionsParser.clientNoContextTakeover || (options & CLIENT_NO_CONTEXT_TAKEOVER)) {
+ options |= CLIENT_NO_CONTEXT_TAKEOVER;
+ }
+
+ if (extensionsParser.serverNoContextTakeover) {
+ options |= SERVER_NO_CONTEXT_TAKEOVER;
+ } else {
+ options &= ~SERVER_NO_CONTEXT_TAKEOVER;
+ }
+ } else {
+ options &= ~PERMESSAGE_DEFLATE;
+ }
+ } else {
+ // todo!
+ }
+}
+
+template <bool isServer>
+int ExtensionsNegotiator<isServer>::getNegotiatedOptions() {
+ return options;
+}
+
+template class ExtensionsNegotiator<true>;
+template class ExtensionsNegotiator<false>;
+
+}
diff --git a/node_modules/uws/src/Extensions.h b/node_modules/uws/src/Extensions.h
new file mode 100644
index 0000000..763b4d2
--- /dev/null
+++ b/node_modules/uws/src/Extensions.h
@@ -0,0 +1,29 @@
+#ifndef EXTENSIONS_UWS_H
+#define EXTENSIONS_UWS_H
+
+#include <string>
+
+namespace uWS {
+
+enum Options : unsigned int {
+ NO_OPTIONS = 0,
+ PERMESSAGE_DEFLATE = 1,
+ SERVER_NO_CONTEXT_TAKEOVER = 2,
+ CLIENT_NO_CONTEXT_TAKEOVER = 4,
+ NO_DELAY = 8
+};
+
+template <bool isServer>
+class ExtensionsNegotiator {
+private:
+ int options;
+public:
+ ExtensionsNegotiator(int wantedOptions);
+ std::string generateOffer();
+ void readOffer(std::string offer);
+ int getNegotiatedOptions();
+};
+
+}
+
+#endif // EXTENSIONS_UWS_H
diff --git a/node_modules/uws/src/Group.cpp b/node_modules/uws/src/Group.cpp
new file mode 100644
index 0000000..028b1a0
--- /dev/null
+++ b/node_modules/uws/src/Group.cpp
@@ -0,0 +1,263 @@
+#include "Group.h"
+#include "Hub.h"
+
+namespace uWS {
+
+template <bool isServer>
+void Group<isServer>::setUserData(void *user) {
+ this->userData = user;
+}
+
+template <bool isServer>
+void *Group<isServer>::getUserData() {
+ return userData;
+}
+
+template <bool isServer>
+void Group<isServer>::timerCallback(Timer *timer) {
+ Group<isServer> *group = (Group<isServer> *) timer->getData();
+
+ group->forEach([](uWS::WebSocket<isServer> *webSocket) {
+ if (webSocket->hasOutstandingPong) {
+ webSocket->terminate();
+ } else {
+ webSocket->hasOutstandingPong = true;
+ }
+ });
+
+ if (group->userPingMessage.length()) {
+ group->broadcast(group->userPingMessage.data(), group->userPingMessage.length(), OpCode::TEXT);
+ } else {
+ group->broadcast(nullptr, 0, OpCode::PING);
+ }
+}
+
+template <bool isServer>
+void Group<isServer>::startAutoPing(int intervalMs, std::string userMessage) {
+ timer = new Timer(loop);
+ timer->setData(this);
+ timer->start(timerCallback, intervalMs, intervalMs);
+ userPingMessage = userMessage;
+}
+
+template <bool isServer>
+void Group<isServer>::addHttpSocket(HttpSocket<isServer> *httpSocket) {
+ if (httpSocketHead) {
+ httpSocketHead->prev = httpSocket;
+ httpSocket->next = httpSocketHead;
+ } else {
+ httpSocket->next = nullptr;
+ // start timer
+ httpTimer = new Timer(hub->getLoop());
+ httpTimer->setData(this);
+ httpTimer->start([](Timer *httpTimer) {
+ Group<isServer> *group = (Group<isServer> *) httpTimer->getData();
+ group->forEachHttpSocket([](HttpSocket<isServer> *httpSocket) {
+ if (httpSocket->missedDeadline) {
+ httpSocket->terminate();
+ } else if (!httpSocket->outstandingResponsesHead) {
+ httpSocket->missedDeadline = true;
+ }
+ });
+ }, 1000, 1000);
+ }
+ httpSocketHead = httpSocket;
+ httpSocket->prev = nullptr;
+}
+
+template <bool isServer>
+void Group<isServer>::removeHttpSocket(HttpSocket<isServer> *httpSocket) {
+ if (iterators.size()) {
+ iterators.top() = httpSocket->next;
+ }
+ if (httpSocket->prev == httpSocket->next) {
+ httpSocketHead = nullptr;
+ httpTimer->stop();
+ httpTimer->close();
+ } else {
+ if (httpSocket->prev) {
+ ((HttpSocket<isServer> *) httpSocket->prev)->next = httpSocket->next;
+ } else {
+ httpSocketHead = (HttpSocket<isServer> *) httpSocket->next;
+ }
+ if (httpSocket->next) {
+ ((HttpSocket<isServer> *) httpSocket->next)->prev = httpSocket->prev;
+ }
+ }
+}
+
+template <bool isServer>
+void Group<isServer>::addWebSocket(WebSocket<isServer> *webSocket) {
+ if (webSocketHead) {
+ webSocketHead->prev = webSocket;
+ webSocket->next = webSocketHead;
+ } else {
+ webSocket->next = nullptr;
+ }
+ webSocketHead = webSocket;
+ webSocket->prev = nullptr;
+}
+
+template <bool isServer>
+void Group<isServer>::removeWebSocket(WebSocket<isServer> *webSocket) {
+ if (iterators.size()) {
+ iterators.top() = webSocket->next;
+ }
+ if (webSocket->prev == webSocket->next) {
+ webSocketHead = nullptr;
+ } else {
+ if (webSocket->prev) {
+ ((WebSocket<isServer> *) webSocket->prev)->next = webSocket->next;
+ } else {
+ webSocketHead = (WebSocket<isServer> *) webSocket->next;
+ }
+ if (webSocket->next) {
+ ((WebSocket<isServer> *) webSocket->next)->prev = webSocket->prev;
+ }
+ }
+}
+
+template <bool isServer>
+Group<isServer>::Group(int extensionOptions, unsigned int maxPayload, Hub *hub, uS::NodeData *nodeData) : uS::NodeData(*nodeData), maxPayload(maxPayload), hub(hub), extensionOptions(extensionOptions) {
+ connectionHandler = [](WebSocket<isServer> *, HttpRequest) {};
+ transferHandler = [](WebSocket<isServer> *) {};
+ messageHandler = [](WebSocket<isServer> *, char *, size_t, OpCode) {};
+ disconnectionHandler = [](WebSocket<isServer> *, int, char *, size_t) {};
+ pingHandler = pongHandler = [](WebSocket<isServer> *, char *, size_t) {};
+ errorHandler = [](errorType) {};
+ httpRequestHandler = [](HttpResponse *, HttpRequest, char *, size_t, size_t) {};
+ httpConnectionHandler = [](HttpSocket<isServer> *) {};
+ httpDisconnectionHandler = [](HttpSocket<isServer> *) {};
+ httpCancelledRequestHandler = [](HttpResponse *) {};
+ httpDataHandler = [](HttpResponse *, char *, size_t, size_t) {};
+
+ this->extensionOptions |= CLIENT_NO_CONTEXT_TAKEOVER | SERVER_NO_CONTEXT_TAKEOVER;
+}
+
+template <bool isServer>
+void Group<isServer>::stopListening() {
+ if (isServer) {
+ if (user) {
+ // todo: we should allow one group to listen to many ports!
+ uS::ListenSocket *listenSocket = (uS::ListenSocket *) user;
+
+ if (listenSocket->timer) {
+ listenSocket->timer->stop();
+ listenSocket->timer->close();
+ }
+
+ listenSocket->closeSocket<uS::ListenSocket>();
+
+ // mark as stopped listening (extra care?)
+ user = nullptr;
+ }
+ }
+
+ if (async) {
+ async->close();
+ }
+}
+
+template <bool isServer>
+void Group<isServer>::onConnection(std::function<void (WebSocket<isServer> *, HttpRequest)> handler) {
+ connectionHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onTransfer(std::function<void (WebSocket<isServer> *)> handler) {
+ transferHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onMessage(std::function<void (WebSocket<isServer> *, char *, size_t, OpCode)> handler) {
+ messageHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onDisconnection(std::function<void (WebSocket<isServer> *, int, char *, size_t)> handler) {
+ disconnectionHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onPing(std::function<void (WebSocket<isServer> *, char *, size_t)> handler) {
+ pingHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onPong(std::function<void (WebSocket<isServer> *, char *, size_t)> handler) {
+ pongHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onError(std::function<void (typename Group::errorType)> handler) {
+ errorHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onHttpConnection(std::function<void (HttpSocket<isServer> *)> handler) {
+ httpConnectionHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onHttpRequest(std::function<void (HttpResponse *, HttpRequest, char *, size_t, size_t)> handler) {
+ httpRequestHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onHttpData(std::function<void(HttpResponse *, char *, size_t, size_t)> handler) {
+ httpDataHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onHttpDisconnection(std::function<void (HttpSocket<isServer> *)> handler) {
+ httpDisconnectionHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onCancelledHttpRequest(std::function<void (HttpResponse *)> handler) {
+ httpCancelledRequestHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::onHttpUpgrade(std::function<void(HttpSocket<isServer> *, HttpRequest)> handler) {
+ httpUpgradeHandler = handler;
+}
+
+template <bool isServer>
+void Group<isServer>::broadcast(const char *message, size_t length, OpCode opCode) {
+
+#ifdef UWS_THREADSAFE
+ std::lock_guard<std::recursive_mutex> lockGuard(*asyncMutex);
+#endif
+
+ typename WebSocket<isServer>::PreparedMessage *preparedMessage = WebSocket<isServer>::prepareMessage((char *) message, length, opCode, false);
+ forEach([preparedMessage](uWS::WebSocket<isServer> *ws) {
+ ws->sendPrepared(preparedMessage);
+ });
+ WebSocket<isServer>::finalizeMessage(preparedMessage);
+}
+
+template <bool isServer>
+void Group<isServer>::terminate() {
+ forEach([](uWS::WebSocket<isServer> *ws) {
+ ws->terminate();
+ });
+ stopListening();
+}
+
+template <bool isServer>
+void Group<isServer>::close(int code, char *message, size_t length) {
+ forEach([code, message, length](uWS::WebSocket<isServer> *ws) {
+ ws->close(code, message, length);
+ });
+ stopListening();
+ if (timer) {
+ timer->stop();
+ timer->close();
+ }
+}
+
+template struct Group<true>;
+template struct Group<false>;
+
+}
diff --git a/node_modules/uws/src/Group.h b/node_modules/uws/src/Group.h
new file mode 100644
index 0000000..18c8c63
--- /dev/null
+++ b/node_modules/uws/src/Group.h
@@ -0,0 +1,144 @@
+#ifndef GROUP_UWS_H
+#define GROUP_UWS_H
+
+#include "WebSocket.h"
+#include "HTTPSocket.h"
+#include "Extensions.h"
+#include <functional>
+#include <stack>
+
+namespace uWS {
+
+enum ListenOptions {
+ TRANSFERS
+};
+
+struct Hub;
+
+template <bool isServer>
+struct WIN32_EXPORT Group : private uS::NodeData {
+protected:
+ friend struct Hub;
+ friend struct WebSocket<isServer>;
+ friend struct HttpSocket<false>;
+ friend struct HttpSocket<true>;
+
+ std::function<void(WebSocket<isServer> *, HttpRequest)> connectionHandler;
+ std::function<void(WebSocket<isServer> *)> transferHandler;
+ std::function<void(WebSocket<isServer> *, char *message, size_t length, OpCode opCode)> messageHandler;
+ std::function<void(WebSocket<isServer> *, int code, char *message, size_t length)> disconnectionHandler;
+ std::function<void(WebSocket<isServer> *, char *, size_t)> pingHandler;
+ std::function<void(WebSocket<isServer> *, char *, size_t)> pongHandler;
+ std::function<void(HttpSocket<isServer> *)> httpConnectionHandler;
+ std::function<void(HttpResponse *, HttpRequest, char *, size_t, size_t)> httpRequestHandler;
+ std::function<void(HttpResponse *, char *, size_t, size_t)> httpDataHandler;
+ std::function<void(HttpResponse *)> httpCancelledRequestHandler;
+ std::function<void(HttpSocket<isServer> *)> httpDisconnectionHandler;
+ std::function<void(HttpSocket<isServer> *, HttpRequest)> httpUpgradeHandler;
+
+ using errorType = typename std::conditional<isServer, int, void *>::type;
+ std::function<void(errorType)> errorHandler;
+
+ unsigned int maxPayload;
+ Hub *hub;
+ int extensionOptions;
+ Timer *timer = nullptr, *httpTimer = nullptr;
+ std::string userPingMessage;
+ std::stack<Poll *> iterators;
+
+ // todo: cannot be named user, collides with parent!
+ void *userData = nullptr;
+ static void timerCallback(Timer *timer);
+
+ WebSocket<isServer> *webSocketHead = nullptr;
+ HttpSocket<isServer> *httpSocketHead = nullptr;
+
+ void addWebSocket(WebSocket<isServer> *webSocket);
+ void removeWebSocket(WebSocket<isServer> *webSocket);
+
+ // todo: remove these, template
+ void addHttpSocket(HttpSocket<isServer> *httpSocket);
+ void removeHttpSocket(HttpSocket<isServer> *httpSocket);
+
+ Group(int extensionOptions, unsigned int maxPayload, Hub *hub, uS::NodeData *nodeData);
+ void stopListening();
+
+public:
+ void onConnection(std::function<void(WebSocket<isServer> *, HttpRequest)> handler);
+ void onTransfer(std::function<void(WebSocket<isServer> *)> handler);
+ void onMessage(std::function<void(WebSocket<isServer> *, char *, size_t, OpCode)> handler);
+ void onDisconnection(std::function<void(WebSocket<isServer> *, int code, char *message, size_t length)> handler);
+ void onPing(std::function<void(WebSocket<isServer> *, char *, size_t)> handler);
+ void onPong(std::function<void(WebSocket<isServer> *, char *, size_t)> handler);
+ void onError(std::function<void(errorType)> handler);
+ void onHttpConnection(std::function<void(HttpSocket<isServer> *)> handler);
+ void onHttpRequest(std::function<void(HttpResponse *, HttpRequest, char *data, size_t length, size_t remainingBytes)> handler);
+ void onHttpData(std::function<void(HttpResponse *, char *data, size_t length, size_t remainingBytes)> handler);
+ void onHttpDisconnection(std::function<void(HttpSocket<isServer> *)> handler);
+ void onCancelledHttpRequest(std::function<void(HttpResponse *)> handler);
+ void onHttpUpgrade(std::function<void(HttpSocket<isServer> *, HttpRequest)> handler);
+
+ // Thread safe
+ void broadcast(const char *message, size_t length, OpCode opCode);
+ void setUserData(void *user);
+ void *getUserData();
+
+ // Not thread safe
+ void terminate();
+ void close(int code = 1000, char *message = nullptr, size_t length = 0);
+ void startAutoPing(int intervalMs, std::string userMessage = "");
+
+ // same as listen(TRANSFERS), backwards compatible API for now
+ void addAsync() {
+ if (!async) {
+ NodeData::addAsync();
+ }
+ }
+
+ void listen(ListenOptions listenOptions) {
+ if (listenOptions == TRANSFERS && !async) {
+ addAsync();
+ }
+ }
+
+ template <class F>
+ void forEach(const F &cb) {
+ Poll *iterator = webSocketHead;
+ iterators.push(iterator);
+ while (iterator) {
+ Poll *lastIterator = iterator;
+ cb((WebSocket<isServer> *) iterator);
+ iterator = iterators.top();
+ if (lastIterator == iterator) {
+ iterator = ((uS::Socket *) iterator)->next;
+ iterators.top() = iterator;
+ }
+ }
+ iterators.pop();
+ }
+
+ // duplicated code for now!
+ template <class F>
+ void forEachHttpSocket(const F &cb) {
+ Poll *iterator = httpSocketHead;
+ iterators.push(iterator);
+ while (iterator) {
+ Poll *lastIterator = iterator;
+ cb((HttpSocket<isServer> *) iterator);
+ iterator = iterators.top();
+ if (lastIterator == iterator) {
+ iterator = ((uS::Socket *) iterator)->next;
+ iterators.top() = iterator;
+ }
+ }
+ iterators.pop();
+ }
+
+ static Group<isServer> *from(uS::Socket *s) {
+ return static_cast<Group<isServer> *>(s->getNodeData());
+ }
+};
+
+}
+
+#endif // GROUP_UWS_H
diff --git a/node_modules/uws/src/HTTPSocket.cpp b/node_modules/uws/src/HTTPSocket.cpp
new file mode 100644
index 0000000..84e30b2
--- /dev/null
+++ b/node_modules/uws/src/HTTPSocket.cpp
@@ -0,0 +1,310 @@
+#include "HTTPSocket.h"
+#include "Group.h"
+#include "Extensions.h"
+#include <cstdio>
+
+#define MAX_HEADERS 100
+#define MAX_HEADER_BUFFER_SIZE 4096
+#define FORCE_SLOW_PATH false
+
+namespace uWS {
+
+// UNSAFETY NOTE: assumes *end == '\r' (might unref end pointer)
+char *getHeaders(char *buffer, char *end, Header *headers, size_t maxHeaders) {
+ for (unsigned int i = 0; i < maxHeaders; i++) {
+ for (headers->key = buffer; (*buffer != ':') & (*buffer > 32); *(buffer++) |= 32);
+ if (*buffer == '\r') {
+ if ((buffer != end) & (buffer[1] == '\n') & (i > 0)) {
+ headers->key = nullptr;
+ return buffer + 2;
+ } else {
+ return nullptr;
+ }
+ } else {
+ headers->keyLength = buffer - headers->key;
+ for (buffer++; (*buffer == ':' || *buffer < 33) && *buffer != '\r'; buffer++);
+ headers->value = buffer;
+ buffer = (char *) memchr(buffer, '\r', end - buffer); //for (; *buffer != '\r'; buffer++);
+ if (buffer /*!= end*/ && buffer[1] == '\n') {
+ headers->valueLength = buffer - headers->value;
+ buffer += 2;
+ headers++;
+ } else {
+ return nullptr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+// UNSAFETY NOTE: assumes 24 byte input length
+static void base64(unsigned char *src, char *dst) {
+ static const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ for (int i = 0; i < 18; i += 3) {
+ *dst++ = b64[(src[i] >> 2) & 63];
+ *dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];
+ *dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];
+ *dst++ = b64[src[i + 2] & 63];
+ }
+ *dst++ = b64[(src[18] >> 2) & 63];
+ *dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];
+ *dst++ = b64[((src[19] & 15) << 2)];
+ *dst++ = '=';
+}
+
+template <bool isServer>
+uS::Socket *HttpSocket<isServer>::onData(uS::Socket *s, char *data, size_t length) {
+ HttpSocket<isServer> *httpSocket = (HttpSocket<isServer> *) s;
+
+ httpSocket->cork(true);
+
+ if (httpSocket->contentLength) {
+ httpSocket->missedDeadline = false;
+ if (httpSocket->contentLength >= length) {
+ Group<isServer>::from(httpSocket)->httpDataHandler(httpSocket->outstandingResponsesTail, data, length, httpSocket->contentLength -= length);
+ return httpSocket;
+ } else {
+ Group<isServer>::from(httpSocket)->httpDataHandler(httpSocket->outstandingResponsesTail, data, httpSocket->contentLength, 0);
+ data += httpSocket->contentLength;
+ length -= httpSocket->contentLength;
+ httpSocket->contentLength = 0;
+ }
+ }
+
+ if (FORCE_SLOW_PATH || httpSocket->httpBuffer.length()) {
+ if (httpSocket->httpBuffer.length() + length > MAX_HEADER_BUFFER_SIZE) {
+ httpSocket->onEnd(httpSocket);
+ return httpSocket;
+ }
+
+ httpSocket->httpBuffer.reserve(httpSocket->httpBuffer.length() + length + WebSocketProtocol<uWS::CLIENT, WebSocket<uWS::CLIENT>>::CONSUME_POST_PADDING);
+ httpSocket->httpBuffer.append(data, length);
+ data = (char *) httpSocket->httpBuffer.data();
+ length = httpSocket->httpBuffer.length();
+ }
+
+ char *end = data + length;
+ char *cursor = data;
+ *end = '\r';
+ Header headers[MAX_HEADERS];
+ do {
+ char *lastCursor = cursor;
+ if ((cursor = getHeaders(cursor, end, headers, MAX_HEADERS))) {
+ HttpRequest req(headers);
+
+ if (isServer) {
+ headers->valueLength = std::max<int>(0, headers->valueLength - 9);
+ httpSocket->missedDeadline = false;
+ if (req.getHeader("upgrade", 7)) {
+ if (Group<SERVER>::from(httpSocket)->httpUpgradeHandler) {
+ Group<SERVER>::from(httpSocket)->httpUpgradeHandler((HttpSocket<SERVER> *) httpSocket, req);
+ } else {
+ Header secKey = req.getHeader("sec-websocket-key", 17);
+ Header extensions = req.getHeader("sec-websocket-extensions", 24);
+ Header subprotocol = req.getHeader("sec-websocket-protocol", 22);
+ if (secKey.valueLength == 24) {
+ bool perMessageDeflate;
+ httpSocket->upgrade(secKey.value, extensions.value, extensions.valueLength,
+ subprotocol.value, subprotocol.valueLength, &perMessageDeflate);
+ Group<isServer>::from(httpSocket)->removeHttpSocket(httpSocket);
+
+ // Warning: changes socket, needs to inform the stack of Poll address change!
+ WebSocket<isServer> *webSocket = new WebSocket<isServer>(perMessageDeflate, httpSocket);
+ webSocket->template setState<WebSocket<isServer>>();
+ webSocket->change(webSocket->nodeData->loop, webSocket, webSocket->setPoll(UV_READABLE));
+ Group<isServer>::from(webSocket)->addWebSocket(webSocket);
+
+ webSocket->cork(true);
+ Group<isServer>::from(webSocket)->connectionHandler(webSocket, req);
+ // todo: should not uncork if closed!
+ webSocket->cork(false);
+ delete httpSocket;
+
+ return webSocket;
+ } else {
+ httpSocket->onEnd(httpSocket);
+ }
+ }
+ return httpSocket;
+ } else {
+ if (Group<SERVER>::from(httpSocket)->httpRequestHandler) {
+
+ HttpResponse *res = HttpResponse::allocateResponse(httpSocket);
+ if (httpSocket->outstandingResponsesTail) {
+ httpSocket->outstandingResponsesTail->next = res;
+ } else {
+ httpSocket->outstandingResponsesHead = res;
+ }
+ httpSocket->outstandingResponsesTail = res;
+
+ Header contentLength;
+ if (req.getMethod() != HttpMethod::METHOD_GET && (contentLength = req.getHeader("content-length", 14))) {
+ httpSocket->contentLength = atoi(contentLength.value);
+ size_t bytesToRead = std::min<int>(httpSocket->contentLength, end - cursor);
+ Group<SERVER>::from(httpSocket)->httpRequestHandler(res, req, cursor, bytesToRead, httpSocket->contentLength -= bytesToRead);
+ cursor += bytesToRead;
+ } else {
+ Group<SERVER>::from(httpSocket)->httpRequestHandler(res, req, nullptr, 0, 0);
+ }
+
+ if (httpSocket->isClosed() || httpSocket->isShuttingDown()) {
+ return httpSocket;
+ }
+ } else {
+ httpSocket->onEnd(httpSocket);
+ return httpSocket;
+ }
+ }
+ } else {
+ if (req.getHeader("upgrade", 7)) {
+
+ // Warning: changes socket, needs to inform the stack of Poll address change!
+ WebSocket<isServer> *webSocket = new WebSocket<isServer>(false, httpSocket);
+ httpSocket->cancelTimeout();
+ webSocket->setUserData(httpSocket->httpUser);
+ webSocket->template setState<WebSocket<isServer>>();
+ webSocket->change(webSocket->nodeData->loop, webSocket, webSocket->setPoll(UV_READABLE));
+ Group<isServer>::from(webSocket)->addWebSocket(webSocket);
+
+ webSocket->cork(true);
+ Group<isServer>::from(webSocket)->connectionHandler(webSocket, req);
+ if (!(webSocket->isClosed() || webSocket->isShuttingDown())) {
+ WebSocketProtocol<isServer, WebSocket<isServer>>::consume(cursor, end - cursor, webSocket);
+ }
+ webSocket->cork(false);
+ delete httpSocket;
+
+ return webSocket;
+ } else {
+ httpSocket->onEnd(httpSocket);
+ }
+ return httpSocket;
+ }
+ } else {
+ if (!httpSocket->httpBuffer.length()) {
+ if (length > MAX_HEADER_BUFFER_SIZE) {
+ httpSocket->onEnd(httpSocket);
+ } else {
+ httpSocket->httpBuffer.append(lastCursor, end - lastCursor);
+ }
+ }
+ return httpSocket;
+ }
+ } while(cursor != end);
+
+ httpSocket->cork(false);
+ httpSocket->httpBuffer.clear();
+
+ return httpSocket;
+}
+
+// todo: make this into a transformer and make use of sendTransformed
+template <bool isServer>
+void HttpSocket<isServer>::upgrade(const char *secKey, const char *extensions, size_t extensionsLength,
+ const char *subprotocol, size_t subprotocolLength, bool *perMessageDeflate) {
+
+ Queue::Message *messagePtr;
+
+ if (isServer) {
+ *perMessageDeflate = false;
+ std::string extensionsResponse;
+ if (extensionsLength) {
+ Group<isServer> *group = Group<isServer>::from(this);
+ ExtensionsNegotiator<uWS::SERVER> extensionsNegotiator(group->extensionOptions);
+ extensionsNegotiator.readOffer(std::string(extensions, extensionsLength));
+ extensionsResponse = extensionsNegotiator.generateOffer();
+ if (extensionsNegotiator.getNegotiatedOptions() & PERMESSAGE_DEFLATE) {
+ *perMessageDeflate = true;
+ }
+ }
+
+ unsigned char shaInput[] = "XXXXXXXXXXXXXXXXXXXXXXXX258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+ memcpy(shaInput, secKey, 24);
+ unsigned char shaDigest[SHA_DIGEST_LENGTH];
+ SHA1(shaInput, sizeof(shaInput) - 1, shaDigest);
+
+ char upgradeBuffer[1024];
+ memcpy(upgradeBuffer, "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ", 97);
+ base64(shaDigest, upgradeBuffer + 97);
+ memcpy(upgradeBuffer + 125, "\r\n", 2);
+ size_t upgradeResponseLength = 127;
+ if (extensionsResponse.length() && extensionsResponse.length() < 200) {
+ memcpy(upgradeBuffer + upgradeResponseLength, "Sec-WebSocket-Extensions: ", 26);
+ memcpy(upgradeBuffer + upgradeResponseLength + 26, extensionsResponse.data(), extensionsResponse.length());
+ memcpy(upgradeBuffer + upgradeResponseLength + 26 + extensionsResponse.length(), "\r\n", 2);
+ upgradeResponseLength += 26 + extensionsResponse.length() + 2;
+ }
+ if (subprotocolLength && subprotocolLength < 200) {
+ memcpy(upgradeBuffer + upgradeResponseLength, "Sec-WebSocket-Protocol: ", 24);
+ memcpy(upgradeBuffer + upgradeResponseLength + 24, subprotocol, subprotocolLength);
+ memcpy(upgradeBuffer + upgradeResponseLength + 24 + subprotocolLength, "\r\n", 2);
+ upgradeResponseLength += 24 + subprotocolLength + 2;
+ }
+ static char stamp[] = "Sec-WebSocket-Version: 13\r\nWebSocket-Server: uWebSockets\r\n\r\n";
+ memcpy(upgradeBuffer + upgradeResponseLength, stamp, sizeof(stamp) - 1);
+ upgradeResponseLength += sizeof(stamp) - 1;
+
+ messagePtr = allocMessage(upgradeResponseLength, upgradeBuffer);
+ } else {
+ messagePtr = allocMessage(httpBuffer.length(), httpBuffer.data());
+ httpBuffer.clear();
+ }
+
+ bool wasTransferred;
+ if (write(messagePtr, wasTransferred)) {
+ if (!wasTransferred) {
+ freeMessage(messagePtr);
+ } else {
+ messagePtr->callback = nullptr;
+ }
+ } else {
+ freeMessage(messagePtr);
+ }
+}
+
+template <bool isServer>
+void HttpSocket<isServer>::onEnd(uS::Socket *s) {
+ HttpSocket<isServer> *httpSocket = (HttpSocket<isServer> *) s;
+
+ if (!httpSocket->isShuttingDown()) {
+ if (isServer) {
+ Group<isServer>::from(httpSocket)->removeHttpSocket(httpSocket);
+ Group<isServer>::from(httpSocket)->httpDisconnectionHandler(httpSocket);
+ }
+ } else {
+ httpSocket->cancelTimeout();
+ }
+
+ httpSocket->template closeSocket<HttpSocket<isServer>>();
+
+ while (!httpSocket->messageQueue.empty()) {
+ Queue::Message *message = httpSocket->messageQueue.front();
+ if (message->callback) {
+ message->callback(nullptr, message->callbackData, true, nullptr);
+ }
+ httpSocket->messageQueue.pop();
+ }
+
+ while (httpSocket->outstandingResponsesHead) {
+ Group<isServer>::from(httpSocket)->httpCancelledRequestHandler(httpSocket->outstandingResponsesHead);
+ HttpResponse *next = httpSocket->outstandingResponsesHead->next;
+ delete httpSocket->outstandingResponsesHead;
+ httpSocket->outstandingResponsesHead = next;
+ }
+
+ if (httpSocket->preAllocatedResponse) {
+ delete httpSocket->preAllocatedResponse;
+ }
+
+ httpSocket->nodeData->clearPendingPollChanges(httpSocket);
+
+ if (!isServer) {
+ httpSocket->cancelTimeout();
+ Group<CLIENT>::from(httpSocket)->errorHandler(httpSocket->httpUser);
+ }
+}
+
+template struct HttpSocket<SERVER>;
+template struct HttpSocket<CLIENT>;
+
+}
diff --git a/node_modules/uws/src/HTTPSocket.h b/node_modules/uws/src/HTTPSocket.h
new file mode 100644
index 0000000..5cc7a7f
--- /dev/null
+++ b/node_modules/uws/src/HTTPSocket.h
@@ -0,0 +1,285 @@
+#ifndef HTTPSOCKET_UWS_H
+#define HTTPSOCKET_UWS_H
+
+#include "Socket.h"
+#include <string>
+// #include <experimental/string_view>
+
+namespace uWS {
+
+struct Header {
+ char *key, *value;
+ unsigned int keyLength, valueLength;
+
+ operator bool() {
+ return key;
+ }
+
+ // slow without string_view!
+ std::string toString() {
+ return std::string(value, valueLength);
+ }
+};
+
+enum HttpMethod {
+ METHOD_GET,
+ METHOD_POST,
+ METHOD_PUT,
+ METHOD_DELETE,
+ METHOD_PATCH,
+ METHOD_OPTIONS,
+ METHOD_HEAD,
+ METHOD_TRACE,
+ METHOD_CONNECT,
+ METHOD_INVALID
+};
+
+struct HttpRequest {
+ Header *headers;
+ Header getHeader(const char *key) {
+ return getHeader(key, strlen(key));
+ }
+
+ HttpRequest(Header *headers = nullptr) : headers(headers) {}
+
+ Header getHeader(const char *key, size_t length) {
+ if (headers) {
+ for (Header *h = headers; *++h; ) {
+ if (h->keyLength == length && !strncmp(h->key, key, length)) {
+ return *h;
+ }
+ }
+ }
+ return {nullptr, nullptr, 0, 0};
+ }
+
+ Header getUrl() {
+ if (headers->key) {
+ return *headers;
+ }
+ return {nullptr, nullptr, 0, 0};
+ }
+
+ HttpMethod getMethod() {
+ if (!headers->key) {
+ return METHOD_INVALID;
+ }
+ switch (headers->keyLength) {
+ case 3:
+ if (!strncmp(headers->key, "get", 3)) {
+ return METHOD_GET;
+ } else if (!strncmp(headers->key, "put", 3)) {
+ return METHOD_PUT;
+ }
+ break;
+ case 4:
+ if (!strncmp(headers->key, "post", 4)) {
+ return METHOD_POST;
+ } else if (!strncmp(headers->key, "head", 4)) {
+ return METHOD_HEAD;
+ }
+ break;
+ case 5:
+ if (!strncmp(headers->key, "patch", 5)) {
+ return METHOD_PATCH;
+ } else if (!strncmp(headers->key, "trace", 5)) {
+ return METHOD_TRACE;
+ }
+ break;
+ case 6:
+ if (!strncmp(headers->key, "delete", 6)) {
+ return METHOD_DELETE;
+ }
+ break;
+ case 7:
+ if (!strncmp(headers->key, "options", 7)) {
+ return METHOD_OPTIONS;
+ } else if (!strncmp(headers->key, "connect", 7)) {
+ return METHOD_CONNECT;
+ }
+ break;
+ }
+ return METHOD_INVALID;
+ }
+};
+
+struct HttpResponse;
+
+template <const bool isServer>
+struct WIN32_EXPORT HttpSocket : uS::Socket {
+ void *httpUser; // remove this later, setTimeout occupies user for now
+ HttpResponse *outstandingResponsesHead = nullptr;
+ HttpResponse *outstandingResponsesTail = nullptr;
+ HttpResponse *preAllocatedResponse = nullptr;
+
+ std::string httpBuffer;
+ size_t contentLength = 0;
+ bool missedDeadline = false;
+
+ HttpSocket(uS::Socket *socket) : uS::Socket(std::move(*socket)) {}
+
+ void terminate() {
+ onEnd(this);
+ }
+
+ void upgrade(const char *secKey, const char *extensions,
+ size_t extensionsLength, const char *subprotocol,
+ size_t subprotocolLength, bool *perMessageDeflate);
+
+private:
+ friend struct uS::Socket;
+ friend struct HttpResponse;
+ friend struct Hub;
+ static uS::Socket *onData(uS::Socket *s, char *data, size_t length);
+ static void onEnd(uS::Socket *s);
+};
+
+struct HttpResponse {
+ HttpSocket<true> *httpSocket;
+ HttpResponse *next = nullptr;
+ void *userData = nullptr;
+ void *extraUserData = nullptr;
+ HttpSocket<true>::Queue::Message *messageQueue = nullptr;
+ bool hasEnded = false;
+ bool hasHead = false;
+
+ HttpResponse(HttpSocket<true> *httpSocket) : httpSocket(httpSocket) {
+
+ }
+
+ template <bool isServer>
+ static HttpResponse *allocateResponse(HttpSocket<isServer> *httpSocket) {
+ if (httpSocket->preAllocatedResponse) {
+ HttpResponse *ret = httpSocket->preAllocatedResponse;
+ httpSocket->preAllocatedResponse = nullptr;
+ return ret;
+ } else {
+ return new HttpResponse((HttpSocket<true> *) httpSocket);
+ }
+ }
+
+ //template <bool isServer>
+ void freeResponse(HttpSocket<true> *httpData) {
+ if (httpData->preAllocatedResponse) {
+ delete this;
+ } else {
+ httpData->preAllocatedResponse = this;
+ }
+ }
+
+ void write(const char *message, size_t length = 0,
+ void(*callback)(void *httpSocket, void *data, bool cancelled, void *reserved) = nullptr,
+ void *callbackData = nullptr) {
+
+ struct NoopTransformer {
+ static size_t estimate(const char *data, size_t length) {
+ return length;
+ }
+
+ static size_t transform(const char *src, char *dst, size_t length, int transformData) {
+ memcpy(dst, src, length);
+ return length;
+ }
+ };
+
+ httpSocket->sendTransformed<NoopTransformer>(message, length, callback, callbackData, 0);
+ hasHead = true;
+ }
+
+ // todo: maybe this function should have a fast path for 0 length?
+ void end(const char *message = nullptr, size_t length = 0,
+ void(*callback)(void *httpResponse, void *data, bool cancelled, void *reserved) = nullptr,
+ void *callbackData = nullptr) {
+
+ struct TransformData {
+ bool hasHead;
+ } transformData = {hasHead};
+
+ struct HttpTransformer {
+
+ // todo: this should get TransformData!
+ static size_t estimate(const char *data, size_t length) {
+ return length + 128;
+ }
+
+ static size_t transform(const char *src, char *dst, size_t length, TransformData transformData) {
+ // todo: sprintf is extremely slow
+ int offset = transformData.hasHead ? 0 : std::sprintf(dst, "HTTP/1.1 200 OK\r\nContent-Length: %u\r\n\r\n", (unsigned int) length);
+ memcpy(dst + offset, src, length);
+ return length + offset;
+ }
+ };
+
+ if (httpSocket->outstandingResponsesHead != this) {
+ HttpSocket<true>::Queue::Message *messagePtr = httpSocket->allocMessage(HttpTransformer::estimate(message, length));
+ messagePtr->length = HttpTransformer::transform(message, (char *) messagePtr->data, length, transformData);
+ messagePtr->callback = callback;
+ messagePtr->callbackData = callbackData;
+ messagePtr->nextMessage = messageQueue;
+ messageQueue = messagePtr;
+ hasEnded = true;
+ } else {
+ httpSocket->sendTransformed<HttpTransformer>(message, length, callback, callbackData, transformData);
+ // move head as far as possible
+ HttpResponse *head = next;
+ while (head) {
+ // empty message queue
+ HttpSocket<true>::Queue::Message *messagePtr = head->messageQueue;
+ while (messagePtr) {
+ HttpSocket<true>::Queue::Message *nextMessage = messagePtr->nextMessage;
+
+ bool wasTransferred;
+ if (httpSocket->write(messagePtr, wasTransferred)) {
+ if (!wasTransferred) {
+ httpSocket->freeMessage(messagePtr);
+ if (callback) {
+ callback(this, callbackData, false, nullptr);
+ }
+ } else {
+ messagePtr->callback = callback;
+ messagePtr->callbackData = callbackData;
+ }
+ } else {
+ httpSocket->freeMessage(messagePtr);
+ if (callback) {
+ callback(this, callbackData, true, nullptr);
+ }
+ goto updateHead;
+ }
+ messagePtr = nextMessage;
+ }
+ // cannot go beyond unfinished responses
+ if (!head->hasEnded) {
+ break;
+ } else {
+ HttpResponse *next = head->next;
+ head->freeResponse(httpSocket);
+ head = next;
+ }
+ }
+ updateHead:
+ httpSocket->outstandingResponsesHead = head;
+ if (!head) {
+ httpSocket->outstandingResponsesTail = nullptr;
+ }
+
+ freeResponse(httpSocket);
+ }
+ }
+
+ void setUserData(void *userData) {
+ this->userData = userData;
+ }
+
+ void *getUserData() {
+ return userData;
+ }
+
+ HttpSocket<true> *getHttpSocket() {
+ return httpSocket;
+ }
+};
+
+}
+
+#endif // HTTPSOCKET_UWS_H
diff --git a/node_modules/uws/src/Hub.cpp b/node_modules/uws/src/Hub.cpp
new file mode 100644
index 0000000..771c263
--- /dev/null
+++ b/node_modules/uws/src/Hub.cpp
@@ -0,0 +1,177 @@
+#include "Hub.h"
+#include "HTTPSocket.h"
+#include <openssl/sha.h>
+
+namespace uWS {
+
+char *Hub::inflate(char *data, size_t &length, size_t maxPayload) {
+ dynamicInflationBuffer.clear();
+
+ inflationStream.next_in = (Bytef *) data;
+ inflationStream.avail_in = length;
+
+ int err;
+ do {
+ inflationStream.next_out = (Bytef *) inflationBuffer;
+ inflationStream.avail_out = LARGE_BUFFER_SIZE;
+ err = ::inflate(&inflationStream, Z_FINISH);
+ if (!inflationStream.avail_in) {
+ break;
+ }
+
+ dynamicInflationBuffer.append(inflationBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
+ } while (err == Z_BUF_ERROR && dynamicInflationBuffer.length() <= maxPayload);
+
+ inflateReset(&inflationStream);
+
+ if ((err != Z_BUF_ERROR && err != Z_OK) || dynamicInflationBuffer.length() > maxPayload) {
+ length = 0;
+ return nullptr;
+ }
+
+ if (dynamicInflationBuffer.length()) {
+ dynamicInflationBuffer.append(inflationBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
+
+ length = dynamicInflationBuffer.length();
+ return (char *) dynamicInflationBuffer.data();
+ }
+
+ length = LARGE_BUFFER_SIZE - inflationStream.avail_out;
+ return inflationBuffer;
+}
+
+void Hub::onServerAccept(uS::Socket *s) {
+ HttpSocket<SERVER> *httpSocket = new HttpSocket<SERVER>(s);
+ delete s;
+
+ httpSocket->setState<HttpSocket<SERVER>>();
+ httpSocket->start(httpSocket->nodeData->loop, httpSocket, httpSocket->setPoll(UV_READABLE));
+ httpSocket->setNoDelay(true);
+ Group<SERVER>::from(httpSocket)->addHttpSocket(httpSocket);
+ Group<SERVER>::from(httpSocket)->httpConnectionHandler(httpSocket);
+}
+
+void Hub::onClientConnection(uS::Socket *s, bool error) {
+ HttpSocket<CLIENT> *httpSocket = (HttpSocket<CLIENT> *) s;
+
+ if (error) {
+ httpSocket->onEnd(httpSocket);
+ } else {
+ httpSocket->setState<HttpSocket<CLIENT>>();
+ httpSocket->change(httpSocket->nodeData->loop, httpSocket, httpSocket->setPoll(UV_READABLE));
+ httpSocket->setNoDelay(true);
+ httpSocket->upgrade(nullptr, nullptr, 0, nullptr, 0, nullptr);
+ }
+}
+
+bool Hub::listen(const char *host, int port, uS::TLS::Context sslContext, int options, Group<SERVER> *eh) {
+ if (!eh) {
+ eh = (Group<SERVER> *) this;
+ }
+
+ if (uS::Node::listen<onServerAccept>(host, port, sslContext, options, (uS::NodeData *) eh, nullptr)) {
+ eh->errorHandler(port);
+ return false;
+ }
+ return true;
+}
+
+bool Hub::listen(int port, uS::TLS::Context sslContext, int options, Group<SERVER> *eh) {
+ return listen(nullptr, port, sslContext, options, eh);
+}
+
+uS::Socket *allocateHttpSocket(uS::Socket *s) {
+ return (uS::Socket *) new HttpSocket<CLIENT>(s);
+}
+
+void Hub::connect(std::string uri, void *user, std::map<std::string, std::string> extraHeaders, int timeoutMs, Group<CLIENT> *eh) {
+ if (!eh) {
+ eh = (Group<CLIENT> *) this;
+ }
+
+ size_t offset = 0;
+ std::string protocol = uri.substr(offset, uri.find("://")), hostname, portStr, path;
+ if ((offset += protocol.length() + 3) < uri.length()) {
+ hostname = uri.substr(offset, uri.find_first_of(":/", offset) - offset);
+
+ offset += hostname.length();
+ if (uri[offset] == ':') {
+ offset++;
+ portStr = uri.substr(offset, uri.find("/", offset) - offset);
+ }
+
+ offset += portStr.length();
+ if (uri[offset] == '/') {
+ path = uri.substr(++offset);
+ }
+ }
+
+ if (hostname.length()) {
+ int port = 80;
+ bool secure = false;
+ if (protocol == "wss") {
+ port = 443;
+ secure = true;
+ } else if (protocol != "ws") {
+ eh->errorHandler(user);
+ }
+
+ if (portStr.length()) {
+ port = stoi(portStr);
+ }
+
+ HttpSocket<CLIENT> *httpSocket = (HttpSocket<CLIENT> *) uS::Node::connect<allocateHttpSocket, onClientConnection>(hostname.c_str(), port, secure, eh);
+ if (httpSocket) {
+ // startTimeout occupies the user
+ httpSocket->startTimeout<HttpSocket<CLIENT>::onEnd>(timeoutMs);
+ httpSocket->httpUser = user;
+
+ std::string randomKey = "x3JJHMbDL1EzLkh9GBhXDw==";
+// for (int i = 0; i < 22; i++) {
+// randomKey[i] = rand() %
+// }
+
+ httpSocket->httpBuffer = "GET /" + path + " HTTP/1.1\r\n"
+ "Upgrade: websocket\r\n"
+ "Connection: Upgrade\r\n"
+ "Sec-WebSocket-Key: " + randomKey + "\r\n"
+ "Host: " + hostname + "\r\n" +
+ "Sec-WebSocket-Version: 13\r\n";
+
+ for (std::pair<std::string, std::string> header : extraHeaders) {
+ httpSocket->httpBuffer += header.first + ": " + header.second + "\r\n";
+ }
+
+ httpSocket->httpBuffer += "\r\n";
+ } else {
+ eh->errorHandler(user);
+ }
+ } else {
+ eh->errorHandler(user);
+ }
+}
+
+void Hub::upgrade(uv_os_sock_t fd, const char *secKey, SSL *ssl, const char *extensions, size_t extensionsLength, const char *subprotocol, size_t subprotocolLength, Group<SERVER> *serverGroup) {
+ if (!serverGroup) {
+ serverGroup = &getDefaultGroup<SERVER>();
+ }
+
+ uS::Socket s((uS::NodeData *) serverGroup, serverGroup->loop, fd, ssl);
+ s.setNoDelay(true);
+
+ // todo: skip httpSocket -> it cannot fail anyways!
+ HttpSocket<SERVER> *httpSocket = new HttpSocket<SERVER>(&s);
+ httpSocket->setState<HttpSocket<SERVER>>();
+ httpSocket->change(httpSocket->nodeData->loop, httpSocket, httpSocket->setPoll(UV_READABLE));
+ bool perMessageDeflate;
+ httpSocket->upgrade(secKey, extensions, extensionsLength, subprotocol, subprotocolLength, &perMessageDeflate);
+
+ WebSocket<SERVER> *webSocket = new WebSocket<SERVER>(perMessageDeflate, httpSocket);
+ delete httpSocket;
+ webSocket->setState<WebSocket<SERVER>>();
+ webSocket->change(webSocket->nodeData->loop, webSocket, webSocket->setPoll(UV_READABLE));
+ serverGroup->addWebSocket(webSocket);
+ serverGroup->connectionHandler(webSocket, {});
+}
+
+}
diff --git a/node_modules/uws/src/Hub.h b/node_modules/uws/src/Hub.h
new file mode 100644
index 0000000..f879579
--- /dev/null
+++ b/node_modules/uws/src/Hub.h
@@ -0,0 +1,97 @@
+#ifndef HUB_UWS_H
+#define HUB_UWS_H
+
+#include "Group.h"
+#include "Node.h"
+#include <string>
+#include <zlib.h>
+#include <mutex>
+#include <map>
+
+namespace uWS {
+
+struct WIN32_EXPORT Hub : private uS::Node, public Group<SERVER>, public Group<CLIENT> {
+protected:
+ struct ConnectionData {
+ std::string path;
+ void *user;
+ Group<CLIENT> *group;
+ };
+
+ z_stream inflationStream = {};
+ char *inflationBuffer;
+ char *inflate(char *data, size_t &length, size_t maxPayload);
+ std::string dynamicInflationBuffer;
+ static const int LARGE_BUFFER_SIZE = 300 * 1024;
+
+ static void onServerAccept(uS::Socket *s);
+ static void onClientConnection(uS::Socket *s, bool error);
+
+public:
+ template <bool isServer>
+ Group<isServer> *createGroup(int extensionOptions = 0, unsigned int maxPayload = 16777216) {
+ return new Group<isServer>(extensionOptions, maxPayload, this, nodeData);
+ }
+
+ template <bool isServer>
+ Group<isServer> &getDefaultGroup() {
+ return static_cast<Group<isServer> &>(*this);
+ }
+
+ bool listen(int port, uS::TLS::Context sslContext = nullptr, int options = 0, Group<SERVER> *eh = nullptr);
+ bool listen(const char *host, int port, uS::TLS::Context sslContext = nullptr, int options = 0, Group<SERVER> *eh = nullptr);
+ void connect(std::string uri, void *user = nullptr, std::map<std::string, std::string> extraHeaders = {}, int timeoutMs = 5000, Group<CLIENT> *eh = nullptr);
+ void upgrade(uv_os_sock_t fd, const char *secKey, SSL *ssl, const char *extensions, size_t extensionsLength, const char *subprotocol, size_t subprotocolLength, Group<SERVER> *serverGroup = nullptr);
+
+ Hub(int extensionOptions = 0, bool useDefaultLoop = false, unsigned int maxPayload = 16777216) : uS::Node(LARGE_BUFFER_SIZE, WebSocketProtocol<SERVER, WebSocket<SERVER>>::CONSUME_PRE_PADDING, WebSocketProtocol<SERVER, WebSocket<SERVER>>::CONSUME_POST_PADDING, useDefaultLoop),
+ Group<SERVER>(extensionOptions, maxPayload, this, nodeData), Group<CLIENT>(0, maxPayload, this, nodeData) {
+ inflateInit2(&inflationStream, -15);
+ inflationBuffer = new char[LARGE_BUFFER_SIZE];
+
+#ifdef UWS_THREADSAFE
+ getLoop()->preCbData = nodeData;
+ getLoop()->preCb = [](void *nodeData) {
+ static_cast<uS::NodeData *>(nodeData)->asyncMutex->lock();
+ };
+
+ getLoop()->postCbData = nodeData;
+ getLoop()->postCb = [](void *nodeData) {
+ static_cast<uS::NodeData *>(nodeData)->asyncMutex->unlock();
+ };
+#endif
+ }
+
+ ~Hub() {
+ inflateEnd(&inflationStream);
+ delete [] inflationBuffer;
+ }
+
+ using uS::Node::run;
+ using uS::Node::getLoop;
+ using Group<SERVER>::onConnection;
+ using Group<CLIENT>::onConnection;
+ using Group<SERVER>::onTransfer;
+ using Group<SERVER>::onMessage;
+ using Group<CLIENT>::onMessage;
+ using Group<SERVER>::onDisconnection;
+ using Group<CLIENT>::onDisconnection;
+ using Group<SERVER>::onPing;
+ using Group<CLIENT>::onPing;
+ using Group<SERVER>::onPong;
+ using Group<CLIENT>::onPong;
+ using Group<SERVER>::onError;
+ using Group<CLIENT>::onError;
+ using Group<SERVER>::onHttpRequest;
+ using Group<SERVER>::onHttpData;
+ using Group<SERVER>::onHttpConnection;
+ using Group<SERVER>::onHttpDisconnection;
+ using Group<SERVER>::onHttpUpgrade;
+ using Group<SERVER>::onCancelledHttpRequest;
+
+ friend struct WebSocket<SERVER>;
+ friend struct WebSocket<CLIENT>;
+};
+
+}
+
+#endif // HUB_UWS_H
diff --git a/node_modules/uws/src/Libuv.h b/node_modules/uws/src/Libuv.h
new file mode 100644
index 0000000..7a71c53
--- /dev/null
+++ b/node_modules/uws/src/Libuv.h
@@ -0,0 +1,175 @@
+#ifndef LIBUV_H
+#define LIBUV_H
+
+#include <uv.h>
+static_assert (UV_VERSION_MINOR >= 3, "µWebSockets requires libuv >=1.3.0");
+
+struct Loop : uv_loop_t {
+ static Loop *createLoop(bool defaultLoop = true) {
+ if (defaultLoop) {
+ return (Loop *) uv_default_loop();
+ } else {
+ return (Loop *) uv_loop_new();
+ }
+ }
+
+ void destroy() {
+ if (this != uv_default_loop()) {
+ uv_loop_delete(this);
+ }
+ }
+
+ void run() {
+ uv_run(this, UV_RUN_DEFAULT);
+ }
+};
+
+struct Async {
+ uv_async_t uv_async;
+
+ Async(Loop *loop) {
+ uv_async.loop = loop;
+ }
+
+ void start(void (*cb)(Async *)) {
+ uv_async_init(uv_async.loop, &uv_async, (uv_async_cb) cb);
+ }
+
+ void send() {
+ uv_async_send(&uv_async);
+ }
+
+ void close() {
+ uv_close((uv_handle_t *) &uv_async, [](uv_handle_t *a) {
+ delete (Async *) a;
+ });
+ }
+
+ void setData(void *data) {
+ uv_async.data = data;
+ }
+
+ void *getData() {
+ return uv_async.data;
+ }
+};
+
+struct Timer {
+ uv_timer_t uv_timer;
+
+ Timer(Loop *loop) {
+ uv_timer_init(loop, &uv_timer);
+ }
+
+ void start(void (*cb)(Timer *), int first, int repeat) {
+ uv_timer_start(&uv_timer, (uv_timer_cb) cb, first, repeat);
+ }
+
+ void setData(void *data) {
+ uv_timer.data = data;
+ }
+
+ void *getData() {
+ return uv_timer.data;
+ }
+
+ void stop() {
+ uv_timer_stop(&uv_timer);
+ }
+
+ void close() {
+ uv_close((uv_handle_t *) &uv_timer, [](uv_handle_t *t) {
+ delete (Timer *) t;
+ });
+ }
+
+private:
+ ~Timer() {
+
+ }
+};
+
+struct Poll {
+ uv_poll_t *uv_poll;
+ void (*cb)(Poll *p, int status, int events);
+
+ Poll(Loop *loop, uv_os_sock_t fd) {
+ uv_poll = new uv_poll_t;
+ uv_poll_init_socket(loop, uv_poll, fd);
+ }
+
+ Poll(Poll &&other) {
+ uv_poll = other.uv_poll;
+ cb = other.cb;
+ other.uv_poll = nullptr;
+ }
+
+ Poll(const Poll &other) = delete;
+
+ ~Poll() {
+ delete uv_poll;
+ }
+
+ bool isClosed() {
+ return uv_is_closing((uv_handle_t *) uv_poll);
+ }
+
+ uv_os_sock_t getFd() {
+#ifdef _WIN32
+ uv_os_sock_t fd;
+ uv_fileno((uv_handle_t *) uv_poll, (uv_os_fd_t *) &fd);
+ return fd;
+#else
+ return uv_poll->io_watcher.fd;
+#endif
+ }
+
+ void setCb(void (*cb)(Poll *p, int status, int events)) {
+ this->cb = cb;
+ }
+
+ void (*getCb())(Poll *, int, int) {
+ return cb;
+ }
+
+ void reInit(Loop *loop, uv_os_sock_t fd) {
+ delete uv_poll;
+ uv_poll = new uv_poll_t;
+ uv_poll_init_socket(loop, uv_poll, fd);
+ }
+
+ void start(Loop *, Poll *self, int events) {
+ uv_poll->data = self;
+ uv_poll_start(uv_poll, events, [](uv_poll_t *p, int status, int events) {
+ Poll *self = (Poll *) p->data;
+ self->cb(self, status, events);
+ });
+ }
+
+ void change(Loop *, Poll *self, int events) {
+ start(nullptr, self, events);
+ }
+
+ void stop(Loop *loop) {
+ uv_poll_stop(uv_poll);
+ }
+
+ bool fastTransfer(Loop *loop, Loop *newLoop, int events) {
+ return false;
+ }
+
+ bool threadSafeChange(Loop *, Poll *self, int events) {
+ return false;
+ }
+
+ void close(Loop *loop, void (*cb)(Poll *)) {
+ this->cb = (void(*)(Poll *, int, int)) cb;
+ uv_close((uv_handle_t *) uv_poll, [](uv_handle_t *p) {
+ Poll *poll = (Poll *) p->data;
+ void (*cb)(Poll *) = (void(*)(Poll *)) poll->cb;
+ cb(poll);
+ });
+ }
+};
+
+#endif // LIBUV_H
diff --git a/node_modules/uws/src/Networking.cpp b/node_modules/uws/src/Networking.cpp
new file mode 100644
index 0000000..743f83b
--- /dev/null
+++ b/node_modules/uws/src/Networking.cpp
@@ -0,0 +1,78 @@
+#include "Networking.h"
+
+namespace uS {
+
+namespace TLS {
+
+Context::Context(const Context &other)
+{
+ if (other.context) {
+ context = other.context;
+ SSL_CTX_up_ref(context);
+ }
+}
+
+Context &Context::operator=(const Context &other) {
+ if (other.context) {
+ context = other.context;
+ SSL_CTX_up_ref(context);
+ }
+ return *this;
+}
+
+Context::~Context()
+{
+ if (context) {
+ SSL_CTX_free(context);
+ }
+}
+
+struct Init {
+ Init() {SSL_library_init();}
+ ~Init() {/*EVP_cleanup();*/}
+} init;
+
+Context createContext(std::string certChainFileName, std::string keyFileName, std::string keyFilePassword)
+{
+ Context context(SSL_CTX_new(SSLv23_server_method()));
+ if (!context.context) {
+ return nullptr;
+ }
+
+ if (keyFilePassword.length()) {
+ context.password.reset(new std::string(keyFilePassword));
+ SSL_CTX_set_default_passwd_cb_userdata(context.context, context.password.get());
+ SSL_CTX_set_default_passwd_cb(context.context, Context::passwordCallback);
+ }
+
+ SSL_CTX_set_options(context.context, SSL_OP_NO_SSLv3);
+
+ if (SSL_CTX_use_certificate_chain_file(context.context, certChainFileName.c_str()) != 1) {
+ return nullptr;
+ } else if (SSL_CTX_use_PrivateKey_file(context.context, keyFileName.c_str(), SSL_FILETYPE_PEM) != 1) {
+ return nullptr;
+ }
+
+ return context;
+}
+
+}
+
+#ifndef _WIN32
+struct Init {
+ Init() {signal(SIGPIPE, SIG_IGN);}
+} init;
+#endif
+
+#ifdef _WIN32
+#pragma comment(lib, "Ws2_32.lib")
+
+struct WindowsInit {
+ WSADATA wsaData;
+ WindowsInit() {WSAStartup(MAKEWORD(2, 2), &wsaData);}
+ ~WindowsInit() {WSACleanup();}
+} windowsInit;
+
+#endif
+
+}
diff --git a/node_modules/uws/src/Networking.h b/node_modules/uws/src/Networking.h
new file mode 100644
index 0000000..7ae88a2
--- /dev/null
+++ b/node_modules/uws/src/Networking.h
@@ -0,0 +1,259 @@
+// the purpose of this header should be to provide SSL and networking wrapped in a common interface
+// it should allow cross-platform networking and SSL and also easy usage of mTCP and similar tech
+
+#ifndef NETWORKING_UWS_H
+#define NETWORKING_UWS_H
+
+#include <openssl/opensslv.h>
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#define SSL_CTX_up_ref(x) x->references++
+#define SSL_up_ref(x) x->references++
+#endif
+
+#ifndef __linux
+#define MSG_NOSIGNAL 0
+#else
+#include <endian.h>
+#endif
+
+#ifdef __APPLE__
+#include <libkern/OSByteOrder.h>
+#define htobe64(x) OSSwapHostToBigInt64(x)
+#define be64toh(x) OSSwapBigToHostInt64(x)
+#endif
+
+#ifdef _WIN32
+#define NOMINMAX
+#include <WinSock2.h>
+#include <Ws2tcpip.h>
+#pragma comment(lib, "ws2_32.lib")
+#define SHUT_WR SD_SEND
+#ifdef __MINGW32__
+// Windows has always been tied to LE
+#define htobe64(x) __builtin_bswap64(x)
+#define be64toh(x) __builtin_bswap64(x)
+#else
+#define __thread __declspec(thread)
+#define htobe64(x) htonll(x)
+#define be64toh(x) ntohll(x)
+#define pthread_t DWORD
+#define pthread_self GetCurrentThreadId
+#endif
+#define WIN32_EXPORT __declspec(dllexport)
+
+inline void close(SOCKET fd) {closesocket(fd);}
+inline int setsockopt(SOCKET fd, int level, int optname, const void *optval, socklen_t optlen) {
+ return setsockopt(fd, level, optname, (const char *) optval, optlen);
+}
+
+inline SOCKET dup(SOCKET socket) {
+ WSAPROTOCOL_INFOW pi;
+ if (WSADuplicateSocketW(socket, GetCurrentProcessId(), &pi) == SOCKET_ERROR) {
+ return INVALID_SOCKET;
+ }
+ return WSASocketW(pi.iAddressFamily, pi.iSocketType, pi.iProtocol, &pi, 0, WSA_FLAG_OVERLAPPED);
+}
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <cstring>
+#define SOCKET_ERROR -1
+#define INVALID_SOCKET -1
+#define WIN32_EXPORT
+#endif
+
+#include "Backend.h"
+#include <openssl/ssl.h>
+#include <csignal>
+#include <vector>
+#include <string>
+#include <mutex>
+#include <algorithm>
+#include <memory>
+
+namespace uS {
+
+// todo: mark sockets nonblocking in these functions
+// todo: probably merge this Context with the TLS::Context for same interface for SSL and non-SSL!
+struct Context {
+
+#ifdef USE_MTCP
+ mtcp_context *mctx;
+#endif
+
+ Context() {
+ // mtcp_create_context
+#ifdef USE_MTCP
+ mctx = mtcp_create_context(0); // cpu index?
+#endif
+ }
+
+ ~Context() {
+#ifdef USE_MTCP
+ mtcp_destroy_context(mctx);
+#endif
+ }
+
+ // returns INVALID_SOCKET on error
+ uv_os_sock_t acceptSocket(uv_os_sock_t fd) {
+ uv_os_sock_t acceptedFd;
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ // Linux, FreeBSD
+ acceptedFd = accept4(fd, nullptr, nullptr, SOCK_CLOEXEC | SOCK_NONBLOCK);
+#else
+ // Windows, OS X
+ acceptedFd = accept(fd, nullptr, nullptr);
+#endif
+
+#ifdef __APPLE__
+ if (acceptedFd != INVALID_SOCKET) {
+ int noSigpipe = 1;
+ setsockopt(acceptedFd, SOL_SOCKET, SO_NOSIGPIPE, &noSigpipe, sizeof(int));
+ }
+#endif
+ return acceptedFd;
+ }
+
+ // returns INVALID_SOCKET on error
+ uv_os_sock_t createSocket(int domain, int type, int protocol) {
+ int flags = 0;
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ flags = SOCK_CLOEXEC | SOCK_NONBLOCK;
+#endif
+
+ uv_os_sock_t createdFd = socket(domain, type | flags, protocol);
+
+#ifdef __APPLE__
+ if (createdFd != INVALID_SOCKET) {
+ int noSigpipe = 1;
+ setsockopt(createdFd, SOL_SOCKET, SO_NOSIGPIPE, &noSigpipe, sizeof(int));
+ }
+#endif
+
+ return createdFd;
+ }
+
+ void closeSocket(uv_os_sock_t fd) {
+#ifdef _WIN32
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ }
+
+ bool wouldBlock() {
+#ifdef _WIN32
+ return WSAGetLastError() == WSAEWOULDBLOCK;
+#else
+ return errno == EWOULDBLOCK;// || errno == EAGAIN;
+#endif
+ }
+};
+
+namespace TLS {
+
+class WIN32_EXPORT Context {
+private:
+ SSL_CTX *context = nullptr;
+ std::shared_ptr<std::string> password;
+
+ static int passwordCallback(char *buf, int size, int rwflag, void *u)
+ {
+ std::string *password = (std::string *) u;
+ int length = std::min<int>(size, password->length());
+ memcpy(buf, password->data(), length);
+ buf[length] = '\0';
+ return length;
+ }
+
+public:
+ friend Context WIN32_EXPORT createContext(std::string certChainFileName, std::string keyFileName, std::string keyFilePassword);
+ Context(SSL_CTX *context) : context(context) {
+
+ }
+
+ Context() = default;
+ Context(const Context &other);
+ Context &operator=(const Context &other);
+ ~Context();
+ operator bool() {
+ return context;
+ }
+
+ SSL_CTX *getNativeContext() {
+ return context;
+ }
+};
+
+Context WIN32_EXPORT createContext(std::string certChainFileName, std::string keyFileName, std::string keyFilePassword = std::string());
+
+}
+
+struct Socket;
+
+// NodeData is like a Context, maybe merge them?
+struct WIN32_EXPORT NodeData {
+ char *recvBufferMemoryBlock;
+ char *recvBuffer;
+ int recvLength;
+ Loop *loop;
+ uS::Context *netContext;
+ void *user = nullptr;
+ static const int preAllocMaxSize = 1024;
+ char **preAlloc;
+ SSL_CTX *clientContext;
+
+ Async *async = nullptr;
+ pthread_t tid;
+
+ std::recursive_mutex *asyncMutex;
+ std::vector<Poll *> transferQueue;
+ std::vector<Poll *> changePollQueue;
+ static void asyncCallback(Async *async);
+
+ static int getMemoryBlockIndex(size_t length) {
+ return (length >> 4) + bool(length & 15);
+ }
+
+ char *getSmallMemoryBlock(int index) {
+ if (preAlloc[index]) {
+ char *memory = preAlloc[index];
+ preAlloc[index] = nullptr;
+ return memory;
+ } else {
+ return new char[index << 4];
+ }
+ }
+
+ void freeSmallMemoryBlock(char *memory, int index) {
+ if (!preAlloc[index]) {
+ preAlloc[index] = memory;
+ } else {
+ delete [] memory;
+ }
+ }
+
+public:
+ void addAsync() {
+ async = new Async(loop);
+ async->setData(this);
+ async->start(NodeData::asyncCallback);
+ }
+
+ void clearPendingPollChanges(Poll *p) {
+ asyncMutex->lock();
+ changePollQueue.erase(
+ std::remove(changePollQueue.begin(), changePollQueue.end(), p),
+ changePollQueue.end()
+ );
+ asyncMutex->unlock();
+ }
+};
+
+}
+
+#endif // NETWORKING_UWS_H
diff --git a/node_modules/uws/src/Node.cpp b/node_modules/uws/src/Node.cpp
new file mode 100644
index 0000000..cd20e79
--- /dev/null
+++ b/node_modules/uws/src/Node.cpp
@@ -0,0 +1,83 @@
+#include "Node.h"
+
+namespace uS {
+
+// this should be Node
+void NodeData::asyncCallback(Async *async)
+{
+ NodeData *nodeData = (NodeData *) async->getData();
+
+ nodeData->asyncMutex->lock();
+ for (Poll *p : nodeData->transferQueue) {
+ Socket *s = (Socket *) p;
+ TransferData *transferData = (TransferData *) s->getUserData();
+
+ s->reInit(nodeData->loop, transferData->fd);
+ s->setCb(transferData->pollCb);
+ s->start(nodeData->loop, s, s->setPoll(transferData->pollEvents));
+
+ s->nodeData = transferData->destination;
+ s->setUserData(transferData->userData);
+ auto *transferCb = transferData->transferCb;
+
+ delete transferData;
+ transferCb(s);
+ }
+
+ for (Poll *p : nodeData->changePollQueue) {
+ Socket *s = (Socket *) p;
+ s->change(s->nodeData->loop, s, s->getPoll());
+ }
+
+ nodeData->changePollQueue.clear();
+ nodeData->transferQueue.clear();
+ nodeData->asyncMutex->unlock();
+}
+
+Node::Node(int recvLength, int prePadding, int postPadding, bool useDefaultLoop) {
+ nodeData = new NodeData;
+ nodeData->recvBufferMemoryBlock = new char[recvLength];
+ nodeData->recvBuffer = nodeData->recvBufferMemoryBlock + prePadding;
+ nodeData->recvLength = recvLength - prePadding - postPadding;
+
+ nodeData->tid = pthread_self();
+ loop = Loop::createLoop(useDefaultLoop);
+
+ // each node has a context
+ nodeData->netContext = new Context();
+
+ nodeData->loop = loop;
+ nodeData->asyncMutex = &asyncMutex;
+
+ int indices = NodeData::getMemoryBlockIndex(NodeData::preAllocMaxSize) + 1;
+ nodeData->preAlloc = new char*[indices];
+ for (int i = 0; i < indices; i++) {
+ nodeData->preAlloc[i] = nullptr;
+ }
+
+ nodeData->clientContext = SSL_CTX_new(SSLv23_client_method());
+ SSL_CTX_set_options(nodeData->clientContext, SSL_OP_NO_SSLv3);
+}
+
+void Node::run() {
+ nodeData->tid = pthread_self();
+ loop->run();
+}
+
+Node::~Node() {
+ delete [] nodeData->recvBufferMemoryBlock;
+ SSL_CTX_free(nodeData->clientContext);
+
+ int indices = NodeData::getMemoryBlockIndex(NodeData::preAllocMaxSize) + 1;
+ for (int i = 0; i < indices; i++) {
+ if (nodeData->preAlloc[i]) {
+ delete [] nodeData->preAlloc[i];
+ }
+ }
+ delete [] nodeData->preAlloc;
+ delete nodeData->netContext;
+ delete nodeData;
+ loop->destroy();
+}
+
+}
diff --git a/node_modules/uws/src/Node.h b/node_modules/uws/src/Node.h
new file mode 100644
index 0000000..3c4d3be
--- /dev/null
+++ b/node_modules/uws/src/Node.h
@@ -0,0 +1,198 @@
+#ifndef NODE_UWS_H
+#define NODE_UWS_H
+
+#include "Socket.h"
+#include <vector>
+#include <mutex>
+
+namespace uS {
+
+enum ListenOptions : int {
+ REUSE_PORT = 1,
+ ONLY_IPV4 = 2
+};
+
+class WIN32_EXPORT Node {
+private:
+ template <void C(Socket *p, bool error)>
+ static void connect_cb(Poll *p, int status, int events) {
+ C((Socket *) p, status < 0);
+ }
+
+ template <void A(Socket *s)>
+ static void accept_poll_cb(Poll *p, int status, int events) {
+ ListenSocket *listenData = (ListenSocket *) p;
+ accept_cb<A, false>(listenData);
+ }
+
+ template <void A(Socket *s)>
+ static void accept_timer_cb(Timer *p) {
+ ListenSocket *listenData = (ListenSocket *) p->getData();
+ accept_cb<A, true>(listenData);
+ }
+
+ template <void A(Socket *s), bool TIMER>
+ static void accept_cb(ListenSocket *listenSocket) {
+ uv_os_sock_t serverFd = listenSocket->getFd();
+ Context *netContext = listenSocket->nodeData->netContext;
+ uv_os_sock_t clientFd = netContext->acceptSocket(serverFd);
+ if (clientFd == INVALID_SOCKET) {
+ /*
+ * If accept is failing, the pending connection won't be removed and the
+ * polling will cause the server to spin, using 100% cpu. Switch to a timer
+ * event instead to avoid this.
+ */
+ if (!TIMER && !netContext->wouldBlock()) {
+ listenSocket->stop(listenSocket->nodeData->loop);
+
+ listenSocket->timer = new Timer(listenSocket->nodeData->loop);
+ listenSocket->timer->setData(listenSocket);
+ listenSocket->timer->start(accept_timer_cb<A>, 1000, 1000);
+ }
+ return;
+ } else if (TIMER) {
+ listenSocket->timer->stop();
+ listenSocket->timer->close();
+ listenSocket->timer = nullptr;
+
+ listenSocket->setCb(accept_poll_cb<A>);
+ listenSocket->start(listenSocket->nodeData->loop, listenSocket, UV_READABLE);
+ }
+ do {
+ SSL *ssl = nullptr;
+ if (listenSocket->sslContext) {
+ ssl = SSL_new(listenSocket->sslContext.getNativeContext());
+ SSL_set_accept_state(ssl);
+ }
+
+ Socket *socket = new Socket(listenSocket->nodeData, listenSocket->nodeData->loop, clientFd, ssl);
+ socket->setPoll(UV_READABLE);
+ A(socket);
+ } while ((clientFd = netContext->acceptSocket(serverFd)) != INVALID_SOCKET);
+ }
+
+protected:
+ Loop *loop;
+ NodeData *nodeData;
+ std::recursive_mutex asyncMutex;
+
+public:
+ Node(int recvLength = 1024, int prePadding = 0, int postPadding = 0, bool useDefaultLoop = false);
+ ~Node();
+ void run();
+
+ Loop *getLoop() {
+ return loop;
+ }
+
+ template <uS::Socket *I(Socket *s), void C(Socket *p, bool error)>
+ Socket *connect(const char *hostname, int port, bool secure, NodeData *nodeData) {
+ Context *netContext = nodeData->netContext;
+
+ addrinfo hints, *result;
+ memset(&hints, 0, sizeof(addrinfo));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if (getaddrinfo(hostname, std::to_string(port).c_str(), &hints, &result) != 0) {
+ return nullptr;
+ }
+
+ uv_os_sock_t fd = netContext->createSocket(result->ai_family, result->ai_socktype, result->ai_protocol);
+ if (fd == INVALID_SOCKET) {
+ freeaddrinfo(result);
+ return nullptr;
+ }
+
+ ::connect(fd, result->ai_addr, result->ai_addrlen);
+ freeaddrinfo(result);
+
+ SSL *ssl = nullptr;
+ if (secure) {
+ ssl = SSL_new(nodeData->clientContext);
+ SSL_set_connect_state(ssl);
+ SSL_set_tlsext_host_name(ssl, hostname);
+ }
+
+ Socket initialSocket(nodeData, getLoop(), fd, ssl);
+ uS::Socket *socket = I(&initialSocket);
+
+ socket->setCb(connect_cb<C>);
+ socket->start(loop, socket, socket->setPoll(UV_WRITABLE));
+ return socket;
+ }
+
+ // todo: hostname, backlog
+ template <void A(Socket *s)>
+ bool listen(const char *host, int port, uS::TLS::Context sslContext, int options, uS::NodeData *nodeData, void *user) {
+ addrinfo hints, *result;
+ memset(&hints, 0, sizeof(addrinfo));
+
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ Context *netContext = nodeData->netContext;
+
+ if (getaddrinfo(host, std::to_string(port).c_str(), &hints, &result)) {
+ return true;
+ }
+
+ uv_os_sock_t listenFd = SOCKET_ERROR;
+ addrinfo *listenAddr;
+ if ((options & uS::ONLY_IPV4) == 0) {
+ for (addrinfo *a = result; a && listenFd == SOCKET_ERROR; a = a->ai_next) {
+ if (a->ai_family == AF_INET6) {
+ listenFd = netContext->createSocket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ listenAddr = a;
+ }
+ }
+ }
+
+ for (addrinfo *a = result; a && listenFd == SOCKET_ERROR; a = a->ai_next) {
+ if (a->ai_family == AF_INET) {
+ listenFd = netContext->createSocket(a->ai_family, a->ai_socktype, a->ai_protocol);
+ listenAddr = a;
+ }
+ }
+
+ if (listenFd == SOCKET_ERROR) {
+ freeaddrinfo(result);
+ return true;
+ }
+
+#ifdef __linux
+#ifdef SO_REUSEPORT
+ if (options & REUSE_PORT) {
+ int optval = 1;
+ setsockopt(listenFd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
+ }
+#endif
+#endif
+
+ int enabled = true;
+ setsockopt(listenFd, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled));
+
+ if (bind(listenFd, listenAddr->ai_addr, listenAddr->ai_addrlen) || ::listen(listenFd, 512)) {
+ netContext->closeSocket(listenFd);
+ freeaddrinfo(result);
+ return true;
+ }
+
+ ListenSocket *listenSocket = new ListenSocket(nodeData, loop, listenFd, nullptr);
+ listenSocket->sslContext = sslContext;
+ listenSocket->nodeData = nodeData;
+
+ listenSocket->setCb(accept_poll_cb<A>);
+ listenSocket->start(loop, listenSocket, UV_READABLE);
+
+ // should be vector of listen data! one group can have many listeners!
+ nodeData->user = listenSocket;
+
+ freeaddrinfo(result);
+ return false;
+ }
+};
+
+}
+
+#endif // NODE_UWS_H
diff --git a/node_modules/uws/src/Socket.cpp b/node_modules/uws/src/Socket.cpp
new file mode 100644
index 0000000..c35bbf8
--- /dev/null
+++ b/node_modules/uws/src/Socket.cpp
@@ -0,0 +1,28 @@
+#include "Socket.h"
+
+namespace uS {
+
+Socket::Address Socket::getAddress()
+{
+ uv_os_sock_t fd = getFd();
+
+ sockaddr_storage addr;
+ socklen_t addrLength = sizeof(addr);
+ if (getpeername(fd, (sockaddr *) &addr, &addrLength) == -1) {
+ return {0, "", ""};
+ }
+
+ static __thread char buf[INET6_ADDRSTRLEN];
+
+ if (addr.ss_family == AF_INET) {
+ sockaddr_in *ipv4 = (sockaddr_in *) &addr;
+ inet_ntop(AF_INET, &ipv4->sin_addr, buf, sizeof(buf));
+ return {ntohs(ipv4->sin_port), buf, "IPv4"};
+ } else {
+ sockaddr_in6 *ipv6 = (sockaddr_in6 *) &addr;
+ inet_ntop(AF_INET6, &ipv6->sin6_addr, buf, sizeof(buf));
+ return {ntohs(ipv6->sin6_port), buf, "IPv6"};
+ }
+}
+
+}
diff --git a/node_modules/uws/src/Socket.h b/node_modules/uws/src/Socket.h
new file mode 100644
index 0000000..2179ff8
--- /dev/null
+++ b/node_modules/uws/src/Socket.h
@@ -0,0 +1,507 @@
+#ifndef SOCKET_UWS_H
+#define SOCKET_UWS_H
+
+#include "Networking.h"
+
+namespace uS {
+
+struct TransferData {
+ // Connection state
+ uv_os_sock_t fd;
+ SSL *ssl;
+
+ // Poll state
+ void (*pollCb)(Poll *, int, int);
+ int pollEvents;
+
+ // User state
+ void *userData;
+
+ // Destination
+ NodeData *destination;
+ void (*transferCb)(Poll *);
+};
+
+// perfectly 64 bytes (4 + 60)
+struct WIN32_EXPORT Socket : Poll {
+protected:
+ struct {
+ int poll : 4;
+ int shuttingDown : 4;
+ } state = {0, false};
+
+ SSL *ssl;
+ void *user = nullptr;
+ NodeData *nodeData;
+
+ // this is not needed by HttpSocket!
+ struct Queue {
+ struct Message {
+ const char *data;
+ size_t length;
+ Message *nextMessage = nullptr;
+ void (*callback)(void *socket, void *data, bool cancelled, void *reserved) = nullptr;
+ void *callbackData = nullptr, *reserved = nullptr;
+ };
+
+ Message *head = nullptr, *tail = nullptr;
+ void pop()
+ {
+ Message *nextMessage;
+ if ((nextMessage = head->nextMessage)) {
+ delete [] (char *) head;
+ head = nextMessage;
+ } else {
+ delete [] (char *) head;
+ head = tail = nullptr;
+ }
+ }
+
+ bool empty() {return head == nullptr;}
+ Message *front() {return head;}
+
+ void push(Message *message)
+ {
+ message->nextMessage = nullptr;
+ if (tail) {
+ tail->nextMessage = message;
+ tail = message;
+ } else {
+ head = message;
+ tail = message;
+ }
+ }
+ } messageQueue;
+
+ int getPoll() {
+ return state.poll;
+ }
+
+ int setPoll(int poll) {
+ state.poll = poll;
+ return poll;
+ }
+
+ void setShuttingDown(bool shuttingDown) {
+ state.shuttingDown = shuttingDown;
+ }
+
+ void transfer(NodeData *nodeData, void (*cb)(Poll *)) {
+ // userData is invalid from now on till onTransfer
+ setUserData(new TransferData({getFd(), ssl, getCb(), getPoll(), getUserData(), nodeData, cb}));
+ stop(this->nodeData->loop);
+ close(this->nodeData->loop, [](Poll *p) {
+ Socket *s = (Socket *) p;
+ TransferData *transferData = (TransferData *) s->getUserData();
+
+ transferData->destination->asyncMutex->lock();
+ bool wasEmpty = transferData->destination->transferQueue.empty();
+ transferData->destination->transferQueue.push_back(s);
+ transferData->destination->asyncMutex->unlock();
+
+ if (wasEmpty) {
+ transferData->destination->async->send();
+ }
+ });
+ }
+
+ void changePoll(Socket *socket) {
+ if (!threadSafeChange(nodeData->loop, this, socket->getPoll())) {
+ if (socket->nodeData->tid != pthread_self()) {
+ socket->nodeData->asyncMutex->lock();
+ socket->nodeData->changePollQueue.push_back(socket);
+ socket->nodeData->asyncMutex->unlock();
+ socket->nodeData->async->send();
+ } else {
+ change(socket->nodeData->loop, socket, socket->getPoll());
+ }
+ }
+ }
+
+ // clears user data!
+ template <void onTimeout(Socket *)>
+ void startTimeout(int timeoutMs = 15000) {
+ Timer *timer = new Timer(nodeData->loop);
+ timer->setData(this);
+ timer->start([](Timer *timer) {
+ Socket *s = (Socket *) timer->getData();
+ s->cancelTimeout();
+ onTimeout(s);
+ }, timeoutMs, 0);
+
+ user = timer;
+ }
+
+ void cancelTimeout() {
+ Timer *timer = (Timer *) getUserData();
+ if (timer) {
+ timer->stop();
+ timer->close();
+ user = nullptr;
+ }
+ }
+
+ template <class STATE>
+ static void sslIoHandler(Poll *p, int status, int events) {
+ Socket *socket = (Socket *) p;
+
+ if (status < 0) {
+ STATE::onEnd((Socket *) p);
+ return;
+ }
+
+ if (!socket->messageQueue.empty() && ((events & UV_WRITABLE) || SSL_want(socket->ssl) == SSL_READING)) {
+ socket->cork(true);
+ while (true) {
+ Queue::Message *messagePtr = socket->messageQueue.front();
+ int sent = SSL_write(socket->ssl, messagePtr->data, messagePtr->length);
+ if (sent == (ssize_t) messagePtr->length) {
+ if (messagePtr->callback) {
+ messagePtr->callback(p, messagePtr->callbackData, false, messagePtr->reserved);
+ }
+ socket->messageQueue.pop();
+ if (socket->messageQueue.empty()) {
+ if ((socket->state.poll & UV_WRITABLE) && SSL_want(socket->ssl) != SSL_WRITING) {
+ socket->change(socket->nodeData->loop, socket, socket->setPoll(UV_READABLE));
+ }
+ break;
+ }
+ } else if (sent <= 0) {
+ switch (SSL_get_error(socket->ssl, sent)) {
+ case SSL_ERROR_WANT_READ:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if ((socket->getPoll() & UV_WRITABLE) == 0) {
+ socket->change(socket->nodeData->loop, socket, socket->setPoll(socket->getPoll() | UV_WRITABLE));
+ }
+ break;
+ default:
+ STATE::onEnd((Socket *) p);
+ return;
+ }
+ break;
+ }
+ }
+ socket->cork(false);
+ }
+
+ if (events & UV_READABLE) {
+ do {
+ int length = SSL_read(socket->ssl, socket->nodeData->recvBuffer, socket->nodeData->recvLength);
+ if (length <= 0) {
+ switch (SSL_get_error(socket->ssl, length)) {
+ case SSL_ERROR_WANT_READ:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if ((socket->getPoll() & UV_WRITABLE) == 0) {
+ socket->change(socket->nodeData->loop, socket, socket->setPoll(socket->getPoll() | UV_WRITABLE));
+ }
+ break;
+ default:
+ STATE::onEnd((Socket *) p);
+ return;
+ }
+ break;
+ } else {
+ // Warning: onData can delete the socket! Happens when HttpSocket upgrades
+ socket = STATE::onData((Socket *) p, socket->nodeData->recvBuffer, length);
+ if (socket->isClosed() || socket->isShuttingDown()) {
+ return;
+ }
+ }
+ } while (SSL_pending(socket->ssl));
+ }
+ }
+
+ template <class STATE>
+ static void ioHandler(Poll *p, int status, int events) {
+ Socket *socket = (Socket *) p;
+ NodeData *nodeData = socket->nodeData;
+ Context *netContext = nodeData->netContext;
+
+ if (status < 0) {
+ STATE::onEnd((Socket *) p);
+ return;
+ }
+
+ if (events & UV_WRITABLE) {
+ if (!socket->messageQueue.empty() && (events & UV_WRITABLE)) {
+ socket->cork(true);
+ while (true) {
+ Queue::Message *messagePtr = socket->messageQueue.front();
+ ssize_t sent = ::send(socket->getFd(), messagePtr->data, messagePtr->length, MSG_NOSIGNAL);
+ if (sent == (ssize_t) messagePtr->length) {
+ if (messagePtr->callback) {
+ messagePtr->callback(p, messagePtr->callbackData, false, messagePtr->reserved);
+ }
+ socket->messageQueue.pop();
+ if (socket->messageQueue.empty()) {
+ // todo, remove bit, don't set directly
+ socket->change(socket->nodeData->loop, socket, socket->setPoll(UV_READABLE));
+ break;
+ }
+ } else if (sent == SOCKET_ERROR) {
+ if (!netContext->wouldBlock()) {
+ STATE::onEnd((Socket *) p);
+ return;
+ }
+ break;
+ } else {
+ messagePtr->length -= sent;
+ messagePtr->data += sent;
+ break;
+ }
+ }
+ socket->cork(false);
+ }
+ }
+
+ if (events & UV_READABLE) {
+ int length = recv(socket->getFd(), nodeData->recvBuffer, nodeData->recvLength, 0);
+ if (length > 0) {
+ STATE::onData((Socket *) p, nodeData->recvBuffer, length);
+ } else if (length <= 0 || (length == SOCKET_ERROR && !netContext->wouldBlock())) {
+ STATE::onEnd((Socket *) p);
+ }
+ }
+
+ }
+
+ template<class STATE>
+ void setState() {
+ if (ssl) {
+ setCb(sslIoHandler<STATE>);
+ } else {
+ setCb(ioHandler<STATE>);
+ }
+ }
+
+ bool hasEmptyQueue() {
+ return messageQueue.empty();
+ }
+
+ void enqueue(Queue::Message *message) {
+ messageQueue.push(message);
+ }
+
+ Queue::Message *allocMessage(size_t length, const char *data = 0) {
+ Queue::Message *messagePtr = (Queue::Message *) new char[sizeof(Queue::Message) + length];
+ messagePtr->length = length;
+ messagePtr->data = ((char *) messagePtr) + sizeof(Queue::Message);
+ messagePtr->nextMessage = nullptr;
+
+ if (data) {
+ memcpy((char *) messagePtr->data, data, messagePtr->length);
+ }
+
+ return messagePtr;
+ }
+
+ void freeMessage(Queue::Message *message) {
+ delete [] (char *) message;
+ }
+
+ bool write(Queue::Message *message, bool &wasTransferred) {
+ ssize_t sent = 0;
+ if (messageQueue.empty()) {
+
+ if (ssl) {
+ sent = SSL_write(ssl, message->data, message->length);
+ if (sent == (ssize_t) message->length) {
+ wasTransferred = false;
+ return true;
+ } else if (sent < 0) {
+ switch (SSL_get_error(ssl, sent)) {
+ case SSL_ERROR_WANT_READ:
+ break;
+ case SSL_ERROR_WANT_WRITE:
+ if ((getPoll() & UV_WRITABLE) == 0) {
+ setPoll(getPoll() | UV_WRITABLE);
+ changePoll(this);
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ } else {
+ sent = ::send(getFd(), message->data, message->length, MSG_NOSIGNAL);
+ if (sent == (ssize_t) message->length) {
+ wasTransferred = false;
+ return true;
+ } else if (sent == SOCKET_ERROR) {
+ if (!nodeData->netContext->wouldBlock()) {
+ return false;
+ }
+ } else {
+ message->length -= sent;
+ message->data += sent;
+ }
+
+ if ((getPoll() & UV_WRITABLE) == 0) {
+ setPoll(getPoll() | UV_WRITABLE);
+ changePoll(this);
+ }
+ }
+ }
+ messageQueue.push(message);
+ wasTransferred = true;
+ return true;
+ }
+
+ template <class T, class D>
+ void sendTransformed(const char *message, size_t length, void(*callback)(void *socket, void *data, bool cancelled, void *reserved), void *callbackData, D transformData) {
+ size_t estimatedLength = T::estimate(message, length) + sizeof(Queue::Message);
+
+ if (hasEmptyQueue()) {
+ if (estimatedLength <= uS::NodeData::preAllocMaxSize) {
+ int memoryLength = estimatedLength;
+ int memoryIndex = nodeData->getMemoryBlockIndex(memoryLength);
+
+ Queue::Message *messagePtr = (Queue::Message *) nodeData->getSmallMemoryBlock(memoryIndex);
+ messagePtr->data = ((char *) messagePtr) + sizeof(Queue::Message);
+ messagePtr->length = T::transform(message, (char *) messagePtr->data, length, transformData);
+
+ bool wasTransferred;
+ if (write(messagePtr, wasTransferred)) {
+ if (!wasTransferred) {
+ nodeData->freeSmallMemoryBlock((char *) messagePtr, memoryIndex);
+ if (callback) {
+ callback(this, callbackData, false, nullptr);
+ }
+ } else {
+ messagePtr->callback = callback;
+ messagePtr->callbackData = callbackData;
+ }
+ } else {
+ nodeData->freeSmallMemoryBlock((char *) messagePtr, memoryIndex);
+ if (callback) {
+ callback(this, callbackData, true, nullptr);
+ }
+ }
+ } else {
+ Queue::Message *messagePtr = allocMessage(estimatedLength - sizeof(Queue::Message));
+ messagePtr->length = T::transform(message, (char *) messagePtr->data, length, transformData);
+
+ bool wasTransferred;
+ if (write(messagePtr, wasTransferred)) {
+ if (!wasTransferred) {
+ freeMessage(messagePtr);
+ if (callback) {
+ callback(this, callbackData, false, nullptr);
+ }
+ } else {
+ messagePtr->callback = callback;
+ messagePtr->callbackData = callbackData;
+ }
+ } else {
+ freeMessage(messagePtr);
+ if (callback) {
+ callback(this, callbackData, true, nullptr);
+ }
+ }
+ }
+ } else {
+ Queue::Message *messagePtr = allocMessage(estimatedLength - sizeof(Queue::Message));
+ messagePtr->length = T::transform(message, (char *) messagePtr->data, length, transformData);
+ messagePtr->callback = callback;
+ messagePtr->callbackData = callbackData;
+ enqueue(messagePtr);
+ }
+ }
+
+public:
+ Socket(NodeData *nodeData, Loop *loop, uv_os_sock_t fd, SSL *ssl) : Poll(loop, fd), ssl(ssl), nodeData(nodeData) {
+ if (ssl) {
+ // OpenSSL treats SOCKETs as int
+ SSL_set_fd(ssl, (int) fd);
+ SSL_set_mode(ssl, SSL_MODE_RELEASE_BUFFERS);
+ }
+ }
+
+ NodeData *getNodeData() {
+ return nodeData;
+ }
+
+ Poll *next = nullptr, *prev = nullptr;
+
+ void *getUserData() {
+ return user;
+ }
+
+ void setUserData(void *user) {
+ this->user = user;
+ }
+
+ struct Address {
+ unsigned int port;
+ const char *address;
+ const char *family;
+ };
+
+ Address getAddress();
+
+ void setNoDelay(int enable) {
+ setsockopt(getFd(), IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
+ }
+
+ void cork(int enable) {
+#if defined(TCP_CORK)
+ // Linux & SmartOS have proper TCP_CORK
+ setsockopt(getFd(), IPPROTO_TCP, TCP_CORK, &enable, sizeof(int));
+#elif defined(TCP_NOPUSH)
+ // Mac OS X & FreeBSD have TCP_NOPUSH
+ setsockopt(getFd(), IPPROTO_TCP, TCP_NOPUSH, &enable, sizeof(int));
+ if (!enable) {
+ // Tested on OS X, FreeBSD situation is unclear
+ ::send(getFd(), "", 0, MSG_NOSIGNAL);
+ }
+#endif
+ }
+
+ void shutdown() {
+ if (ssl) {
+ //todo: poll in/out - have the io_cb recall shutdown if failed
+ SSL_shutdown(ssl);
+ } else {
+ ::shutdown(getFd(), SHUT_WR);
+ }
+ }
+
+ template <class T>
+ void closeSocket() {
+ uv_os_sock_t fd = getFd();
+ Context *netContext = nodeData->netContext;
+ stop(nodeData->loop);
+ netContext->closeSocket(fd);
+
+ if (ssl) {
+ SSL_free(ssl);
+ }
+
+ Poll::close(nodeData->loop, [](Poll *p) {
+ delete (T *) p;
+ });
+ }
+
+ bool isShuttingDown() {
+ return state.shuttingDown;
+ }
+
+ friend class Node;
+ friend struct NodeData;
+};
+
+struct ListenSocket : Socket {
+
+ ListenSocket(NodeData *nodeData, Loop *loop, uv_os_sock_t fd, SSL *ssl) : Socket(nodeData, loop, fd, ssl) {
+
+ }
+
+ Timer *timer = nullptr;
+ uS::TLS::Context sslContext;
+};
+
+}
+
+#endif // SOCKET_UWS_H
diff --git a/node_modules/uws/src/WebSocket.cpp b/node_modules/uws/src/WebSocket.cpp
new file mode 100644
index 0000000..89ac23a
--- /dev/null
+++ b/node_modules/uws/src/WebSocket.cpp
@@ -0,0 +1,405 @@
+#include "WebSocket.h"
+#include "Group.h"
+#include "Hub.h"
+
+namespace uWS {
+
+/*
+ * Frames and sends a WebSocket message.
+ *
+ * Hints: Consider using any of the prepare function if any of their
+ * use cases match what you are trying to achieve (pub/sub, broadcast)
+ *
+ * Thread safe
+ *
+ */
+template <bool isServer>
+void WebSocket<isServer>::send(const char *message, size_t length, OpCode opCode, void(*callback)(WebSocket<isServer> *webSocket, void *data, bool cancelled, void *reserved), void *callbackData) {
+
+#ifdef UWS_THREADSAFE
+ std::lock_guard<std::recursive_mutex> lockGuard(*nodeData->asyncMutex);
+ if (isClosed()) {
+ if (callback) {
+ callback(this, callbackData, true, nullptr);
+ }
+ return;
+ }
+#endif
+
+ const int HEADER_LENGTH = WebSocketProtocol<!isServer, WebSocket<!isServer>>::LONG_MESSAGE_HEADER;
+
+ struct TransformData {
+ OpCode opCode;
+ } transformData = {opCode};
+
+ struct WebSocketTransformer {
+ static size_t estimate(const char *data, size_t length) {
+ return length + HEADER_LENGTH;
+ }
+
+ static size_t transform(const char *src, char *dst, size_t length, TransformData transformData) {
+ return WebSocketProtocol<isServer, WebSocket<isServer>>::formatMessage(dst, src, length, transformData.opCode, length, false);
+ }
+ };
+
+ sendTransformed<WebSocketTransformer>((char *) message, length, (void(*)(void *, void *, bool, void *)) callback, callbackData, transformData);
+}
+
+/*
+ * Prepares a single message for use with sendPrepared.
+ *
+ * Hints: Useful in cases where you need to send the same message to many
+ * recipients. Do not use when only sending one message.
+ *
+ * Thread safe
+ *
+ */
+template <bool isServer>
+typename WebSocket<isServer>::PreparedMessage *WebSocket<isServer>::prepareMessage(char *data, size_t length, OpCode opCode, bool compressed, void(*callback)(WebSocket<isServer> *webSocket, void *data, bool cancelled, void *reserved)) {
+ PreparedMessage *preparedMessage = new PreparedMessage;
+ preparedMessage->buffer = new char[length + 10];
+ preparedMessage->length = WebSocketProtocol<isServer, WebSocket<isServer>>::formatMessage(preparedMessage->buffer, data, length, opCode, length, compressed);
+ preparedMessage->references = 1;
+ preparedMessage->callback = (void(*)(void *, void *, bool, void *)) callback;
+ return preparedMessage;
+}
+
+/*
+ * Prepares a batch of messages to send as one single TCP packet / syscall.
+ *
+ * Hints: Useful when doing pub/sub-like broadcasts where many recipients should receive many
+ * messages. Do not use if only sending one message.
+ *
+ * Thread safe
+ *
+ */
+template <bool isServer>
+typename WebSocket<isServer>::PreparedMessage *WebSocket<isServer>::prepareMessageBatch(std::vector<std::string> &messages, std::vector<int> &excludedMessages, OpCode opCode, bool compressed, void (*callback)(WebSocket<isServer> *, void *, bool, void *))
+{
+ // should be sent in!
+ size_t batchLength = 0;
+ for (size_t i = 0; i < messages.size(); i++) {
+ batchLength += messages[i].length();
+ }
+
+ PreparedMessage *preparedMessage = new PreparedMessage;
+ preparedMessage->buffer = new char[batchLength + 10 * messages.size()];
+
+ int offset = 0;
+ for (size_t i = 0; i < messages.size(); i++) {
+ offset += WebSocketProtocol<isServer, WebSocket<isServer>>::formatMessage(preparedMessage->buffer + offset, messages[i].data(), messages[i].length(), opCode, messages[i].length(), compressed);
+ }
+ preparedMessage->length = offset;
+ preparedMessage->references = 1;
+ preparedMessage->callback = (void(*)(void *, void *, bool, void *)) callback;
+ return preparedMessage;
+}
+
+/*
+ * Sends a prepared message.
+ *
+ * Hints: Used to improve broadcasting and similar use cases where the same
+ * message is sent to multiple recipients. Do not used if only sending one message
+ * in total.
+ *
+ * Warning: Modifies passed PreparedMessage and is thus not thread safe. Other
+ * data is also modified and it makes sense to not make this function thread-safe
+ * since it is a central part in broadcasting and other high-perf code paths.
+ *
+ */
+template <bool isServer>
+void WebSocket<isServer>::sendPrepared(typename WebSocket<isServer>::PreparedMessage *preparedMessage, void *callbackData) {
+ // todo: see if this can be made a transformer instead
+ preparedMessage->references++;
+ void (*callback)(void *webSocket, void *userData, bool cancelled, void *reserved) = [](void *webSocket, void *userData, bool cancelled, void *reserved) {
+ PreparedMessage *preparedMessage = (PreparedMessage *) userData;
+ bool lastReference = !--preparedMessage->references;
+
+ if (preparedMessage->callback) {
+ preparedMessage->callback(webSocket, reserved, cancelled, (void *) lastReference);
+ }
+
+ if (lastReference) {
+ delete [] preparedMessage->buffer;
+ delete preparedMessage;
+ }
+ };
+
+ // candidate for fixed size pool allocator
+ int memoryLength = sizeof(Queue::Message);
+ int memoryIndex = nodeData->getMemoryBlockIndex(memoryLength);
+
+ Queue::Message *messagePtr = (Queue::Message *) nodeData->getSmallMemoryBlock(memoryIndex);
+ messagePtr->data = preparedMessage->buffer;
+ messagePtr->length = preparedMessage->length;
+
+ bool wasTransferred;
+ if (write(messagePtr, wasTransferred)) {
+ if (!wasTransferred) {
+ nodeData->freeSmallMemoryBlock((char *) messagePtr, memoryIndex);
+ if (callback) {
+ callback(this, preparedMessage, false, callbackData);
+ }
+ } else {
+ messagePtr->callback = callback;
+ messagePtr->callbackData = preparedMessage;
+ messagePtr->reserved = callbackData;
+ }
+ } else {
+ nodeData->freeSmallMemoryBlock((char *) messagePtr, memoryIndex);
+ if (callback) {
+ callback(this, preparedMessage, true, callbackData);
+ }
+ }
+}
+
+/*
+ * Decrements the reference count of passed PreparedMessage. On zero references
+ * the memory will be deleted.
+ *
+ * Hints: Used together with prepareMessage, prepareMessageBatch and similar calls.
+ *
+ * Warning: Will modify passed PrepareMessage and is thus not thread safe by itself.
+ *
+ */
+template <bool isServer>
+void WebSocket<isServer>::finalizeMessage(typename WebSocket<isServer>::PreparedMessage *preparedMessage) {
+ if (!--preparedMessage->references) {
+ delete [] preparedMessage->buffer;
+ delete preparedMessage;
+ }
+}
+
+template <bool isServer>
+uS::Socket *WebSocket<isServer>::onData(uS::Socket *s, char *data, size_t length) {
+ WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(s);
+
+ webSocket->hasOutstandingPong = false;
+ if (!webSocket->isShuttingDown()) {
+ webSocket->cork(true);
+ WebSocketProtocol<isServer, WebSocket<isServer>>::consume(data, length, webSocket);
+ if (!webSocket->isClosed()) {
+ webSocket->cork(false);
+ }
+ }
+
+ return webSocket;
+}
+
+/*
+ * Immediately terminates this WebSocket. Will call onDisconnection of its Group.
+ *
+ * Hints: Close code will be 1006 and message will be empty.
+ *
+ */
+template <bool isServer>
+void WebSocket<isServer>::terminate() {
+
+#ifdef UWS_THREADSAFE
+ std::lock_guard<std::recursive_mutex> lockGuard(*nodeData->asyncMutex);
+ if (isClosed()) {
+ return;
+ }
+#endif
+
+ WebSocket<isServer>::onEnd(this);
+}
+
+/*
+ * Transfers this WebSocket from its current Group to specified Group.
+ *
+ * Receiving Group has to have called listen(uWS::TRANSFERS) prior.
+ *
+ * Hints: Useful to implement subprotocols on the same thread and Loop
+ * or to transfer WebSockets between threads at any point (dynamic load balancing).
+ *
+ * Warning: From the point of call to the point of onTransfer, this WebSocket
+ * is invalid and cannot be used. What you put in is not guaranteed to be what you
+ * get in onTransfer, the only guaranteed consistency is passed userData is the userData
+ * of given WebSocket in onTransfer. Use setUserData and getUserData to identify the WebSocket.
+ */
+template <bool isServer>
+void WebSocket<isServer>::transfer(Group<isServer> *group) {
+ Group<isServer>::from(this)->removeWebSocket(this);
+ if (group->loop == Group<isServer>::from(this)->loop) {
+ // fast path
+ this->nodeData = group;
+ Group<isServer>::from(this)->addWebSocket(this);
+ Group<isServer>::from(this)->transferHandler(this);
+ } else {
+ // slow path
+ uS::Socket::transfer((uS::NodeData *) group, [](Poll *p) {
+ WebSocket<isServer> *webSocket = (WebSocket<isServer> *) p;
+ Group<isServer>::from(webSocket)->addWebSocket(webSocket);
+ Group<isServer>::from(webSocket)->transferHandler(webSocket);
+ });
+ }
+}
+
+/*
+ * Immediately calls onDisconnection of its Group and begins a passive
+ * WebSocket closedown handshake in the background (might succeed or not,
+ * we don't care).
+ *
+ * Hints: Close code and message will be what you pass yourself.
+ *
+ */
+template <bool isServer>
+void WebSocket<isServer>::close(int code, const char *message, size_t length) {
+
+ // startTimeout is not thread safe
+
+ static const int MAX_CLOSE_PAYLOAD = 123;
+ length = std::min<size_t>(MAX_CLOSE_PAYLOAD, length);
+ Group<isServer>::from(this)->removeWebSocket(this);
+ Group<isServer>::from(this)->disconnectionHandler(this, code, (char *) message, length);
+ setShuttingDown(true);
+
+ // todo: using the shared timer in the group, we can skip creating a new timer per socket
+ // only this line and the one in Hub::connect uses the timeout feature
+ startTimeout<WebSocket<isServer>::onEnd>();
+
+ char closePayload[MAX_CLOSE_PAYLOAD + 2];
+ int closePayloadLength = WebSocketProtocol<isServer, WebSocket<isServer>>::formatClosePayload(closePayload, code, message, length);
+ send(closePayload, closePayloadLength, OpCode::CLOSE, [](WebSocket<isServer> *p, void *data, bool cancelled, void *reserved) {
+ if (!cancelled) {
+ p->shutdown();
+ }
+ });
+}
+
+template <bool isServer>
+void WebSocket<isServer>::onEnd(uS::Socket *s) {
+ WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(s);
+
+ if (!webSocket->isShuttingDown()) {
+ Group<isServer>::from(webSocket)->removeWebSocket(webSocket);
+ Group<isServer>::from(webSocket)->disconnectionHandler(webSocket, 1006, nullptr, 0);
+ } else {
+ webSocket->cancelTimeout();
+ }
+
+ webSocket->template closeSocket<WebSocket<isServer>>();
+
+ while (!webSocket->messageQueue.empty()) {
+ Queue::Message *message = webSocket->messageQueue.front();
+ if (message->callback) {
+ message->callback(nullptr, message->callbackData, true, nullptr);
+ }
+ webSocket->messageQueue.pop();
+ }
+
+ webSocket->nodeData->clearPendingPollChanges(webSocket);
+}
+
+template <bool isServer>
+bool WebSocket<isServer>::handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState) {
+ WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(webSocketState);
+ Group<isServer> *group = Group<isServer>::from(webSocket);
+
+ if (opCode < 3) {
+ if (!remainingBytes && fin && !webSocket->fragmentBuffer.length()) {
+ if (webSocket->compressionStatus == WebSocket<isServer>::CompressionStatus::COMPRESSED_FRAME) {
+ webSocket->compressionStatus = WebSocket<isServer>::CompressionStatus::ENABLED;
+ data = group->hub->inflate(data, length, group->maxPayload);
+ if (!data) {
+ forceClose(webSocketState);
+ return true;
+ }
+ }
+
+ if (opCode == 1 && !WebSocketProtocol<isServer, WebSocket<isServer>>::isValidUtf8((unsigned char *) data, length)) {
+ forceClose(webSocketState);
+ return true;
+ }
+
+ group->messageHandler(webSocket, data, length, (OpCode) opCode);
+ if (webSocket->isClosed() || webSocket->isShuttingDown()) {
+ return true;
+ }
+ } else {
+ webSocket->fragmentBuffer.append(data, length);
+ if (!remainingBytes && fin) {
+ length = webSocket->fragmentBuffer.length();
+ if (webSocket->compressionStatus == WebSocket<isServer>::CompressionStatus::COMPRESSED_FRAME) {
+ webSocket->compressionStatus = WebSocket<isServer>::CompressionStatus::ENABLED;
+ webSocket->fragmentBuffer.append("....");
+ data = group->hub->inflate((char *) webSocket->fragmentBuffer.data(), length, group->maxPayload);
+ if (!data) {
+ forceClose(webSocketState);
+ return true;
+ }
+ } else {
+ data = (char *) webSocket->fragmentBuffer.data();
+ }
+
+ if (opCode == 1 && !WebSocketProtocol<isServer, WebSocket<isServer>>::isValidUtf8((unsigned char *) data, length)) {
+ forceClose(webSocketState);
+ return true;
+ }
+
+ group->messageHandler(webSocket, data, length, (OpCode) opCode);
+ if (webSocket->isClosed() || webSocket->isShuttingDown()) {
+ return true;
+ }
+ webSocket->fragmentBuffer.clear();
+ }
+ }
+ } else {
+ if (!remainingBytes && fin && !webSocket->controlTipLength) {
+ if (opCode == CLOSE) {
+ typename WebSocketProtocol<isServer, WebSocket<isServer>>::CloseFrame closeFrame = WebSocketProtocol<isServer, WebSocket<isServer>>::parseClosePayload(data, length);
+ webSocket->close(closeFrame.code, closeFrame.message, closeFrame.length);
+ return true;
+ } else {
+ if (opCode == PING) {
+ webSocket->send(data, length, (OpCode) OpCode::PONG);
+ group->pingHandler(webSocket, data, length);
+ if (webSocket->isClosed() || webSocket->isShuttingDown()) {
+ return true;
+ }
+ } else if (opCode == PONG) {
+ group->pongHandler(webSocket, data, length);
+ if (webSocket->isClosed() || webSocket->isShuttingDown()) {
+ return true;
+ }
+ }
+ }
+ } else {
+ webSocket->fragmentBuffer.append(data, length);
+ webSocket->controlTipLength += length;
+
+ if (!remainingBytes && fin) {
+ char *controlBuffer = (char *) webSocket->fragmentBuffer.data() + webSocket->fragmentBuffer.length() - webSocket->controlTipLength;
+ if (opCode == CLOSE) {
+ typename WebSocketProtocol<isServer, WebSocket<isServer>>::CloseFrame closeFrame = WebSocketProtocol<isServer, WebSocket<isServer>>::parseClosePayload(controlBuffer, webSocket->controlTipLength);
+ webSocket->close(closeFrame.code, closeFrame.message, closeFrame.length);
+ return true;
+ } else {
+ if (opCode == PING) {
+ webSocket->send(controlBuffer, webSocket->controlTipLength, (OpCode) OpCode::PONG);
+ group->pingHandler(webSocket, controlBuffer, webSocket->controlTipLength);
+ if (webSocket->isClosed() || webSocket->isShuttingDown()) {
+ return true;
+ }
+ } else if (opCode == PONG) {
+ group->pongHandler(webSocket, controlBuffer, webSocket->controlTipLength);
+ if (webSocket->isClosed() || webSocket->isShuttingDown()) {
+ return true;
+ }
+ }
+ }
+
+ webSocket->fragmentBuffer.resize(webSocket->fragmentBuffer.length() - webSocket->controlTipLength);
+ webSocket->controlTipLength = 0;
+ }
+ }
+ }
+
+ return false;
+}
+
+template struct WebSocket<SERVER>;
+template struct WebSocket<CLIENT>;
+
+}
diff --git a/node_modules/uws/src/WebSocket.h b/node_modules/uws/src/WebSocket.h
new file mode 100644
index 0000000..9e7f547
--- /dev/null
+++ b/node_modules/uws/src/WebSocket.h
@@ -0,0 +1,89 @@
+#ifndef WEBSOCKET_UWS_H
+#define WEBSOCKET_UWS_H
+
+#include "WebSocketProtocol.h"
+#include "Socket.h"
+
+namespace uWS {
+
+template <bool isServer>
+struct Group;
+
+template <bool isServer>
+struct HttpSocket;
+
+template <const bool isServer>
+struct WIN32_EXPORT WebSocket : uS::Socket, WebSocketState<isServer> {
+protected:
+ std::string fragmentBuffer;
+ enum CompressionStatus : char {
+ DISABLED,
+ ENABLED,
+ COMPRESSED_FRAME
+ } compressionStatus;
+ unsigned char controlTipLength = 0, hasOutstandingPong = false;
+
+ WebSocket(bool perMessageDeflate, uS::Socket *socket) : uS::Socket(std::move(*socket)) {
+ compressionStatus = perMessageDeflate ? CompressionStatus::ENABLED : CompressionStatus::DISABLED;
+ }
+
+ static uS::Socket *onData(uS::Socket *s, char *data, size_t length);
+ static void onEnd(uS::Socket *s);
+ using uS::Socket::closeSocket;
+
+ static bool refusePayloadLength(uint64_t length, WebSocketState<isServer> *webSocketState) {
+ WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(webSocketState);
+ return length > Group<isServer>::from(webSocket)->maxPayload;
+ }
+
+ static bool setCompressed(WebSocketState<isServer> *webSocketState) {
+ WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(webSocketState);
+
+ if (webSocket->compressionStatus == WebSocket<isServer>::CompressionStatus::ENABLED) {
+ webSocket->compressionStatus = WebSocket<isServer>::CompressionStatus::COMPRESSED_FRAME;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ static void forceClose(WebSocketState<isServer> *webSocketState) {
+ WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(webSocketState);
+ webSocket->terminate();
+ }
+
+ static bool handleFragment(char *data, size_t length, unsigned int remainingBytes, int opCode, bool fin, WebSocketState<isServer> *webSocketState);
+
+public:
+ struct PreparedMessage {
+ char *buffer;
+ size_t length;
+ int references;
+ void(*callback)(void *webSocket, void *data, bool cancelled, void *reserved);
+ };
+
+ // Not thread safe
+ void sendPrepared(PreparedMessage *preparedMessage, void *callbackData = nullptr);
+ static void finalizeMessage(PreparedMessage *preparedMessage);
+ void close(int code = 1000, const char *message = nullptr, size_t length = 0);
+ void transfer(Group<isServer> *group);
+
+ // Thread safe
+ void terminate();
+ void ping(const char *message) {send(message, OpCode::PING);}
+ void send(const char *message, OpCode opCode = OpCode::TEXT) {send(message, strlen(message), opCode);}
+ void send(const char *message, size_t length, OpCode opCode, void(*callback)(WebSocket<isServer> *webSocket, void *data, bool cancelled, void *reserved) = nullptr, void *callbackData = nullptr);
+ static PreparedMessage *prepareMessage(char *data, size_t length, OpCode opCode, bool compressed, void(*callback)(WebSocket<isServer> *webSocket, void *data, bool cancelled, void *reserved) = nullptr);
+ static PreparedMessage *prepareMessageBatch(std::vector<std::string> &messages, std::vector<int> &excludedMessages,
+ OpCode opCode, bool compressed, void(*callback)(WebSocket<isServer> *webSocket, void *data, bool cancelled, void *reserved) = nullptr);
+
+ friend struct Hub;
+ friend struct Group<isServer>;
+ friend struct HttpSocket<isServer>;
+ friend struct uS::Socket;
+ friend class WebSocketProtocol<isServer, WebSocket<isServer>>;
+};
+
+}
+
+#endif // WEBSOCKET_UWS_H
diff --git a/node_modules/uws/src/WebSocketProtocol.h b/node_modules/uws/src/WebSocketProtocol.h
new file mode 100644
index 0000000..b7b4695
--- /dev/null
+++ b/node_modules/uws/src/WebSocketProtocol.h
@@ -0,0 +1,377 @@
+#ifndef WEBSOCKETPROTOCOL_UWS_H
+#define WEBSOCKETPROTOCOL_UWS_H
+
+// we do need to include this for htobe64, should be moved from networking!
+#include "Networking.h"
+
+#include <cstring>
+#include <cstdlib>
+
+namespace uWS {
+
+enum OpCode : unsigned char {
+ TEXT = 1,
+ BINARY = 2,
+ CLOSE = 8,
+ PING = 9,
+ PONG = 10
+};
+
+enum {
+ CLIENT,
+ SERVER
+};
+
+// 24 bytes perfectly
+template <bool isServer>
+struct WebSocketState {
+public:
+ static const unsigned int SHORT_MESSAGE_HEADER = isServer ? 6 : 2;
+ static const unsigned int MEDIUM_MESSAGE_HEADER = isServer ? 8 : 4;
+ static const unsigned int LONG_MESSAGE_HEADER = isServer ? 14 : 10;
+
+ // 16 bytes
+ struct State {
+ unsigned int wantsHead : 1;
+ unsigned int spillLength : 4;
+ int opStack : 2; // -1, 0, 1
+ unsigned int lastFin : 1;
+
+ // 15 bytes
+ unsigned char spill[LONG_MESSAGE_HEADER - 1];
+ OpCode opCode[2];
+
+ State() {
+ wantsHead = true;
+ spillLength = 0;
+ opStack = -1;
+ lastFin = true;
+ }
+
+ } state;
+
+ // 8 bytes
+ unsigned int remainingBytes = 0;
+ char mask[isServer ? 4 : 1];
+};
+
+template <const bool isServer, class Impl>
+class WIN32_EXPORT WebSocketProtocol {
+public:
+ static const unsigned int SHORT_MESSAGE_HEADER = isServer ? 6 : 2;
+ static const unsigned int MEDIUM_MESSAGE_HEADER = isServer ? 8 : 4;
+ static const unsigned int LONG_MESSAGE_HEADER = isServer ? 14 : 10;
+
+private:
+ static inline bool isFin(char *frame) {return *((unsigned char *) frame) & 128;}
+ static inline unsigned char getOpCode(char *frame) {return *((unsigned char *) frame) & 15;}
+ static inline unsigned char payloadLength(char *frame) {return ((unsigned char *) frame)[1] & 127;}
+ static inline bool rsv23(char *frame) {return *((unsigned char *) frame) & 48;}
+ static inline bool rsv1(char *frame) {return *((unsigned char *) frame) & 64;}
+
+ static inline void unmaskImprecise(char *dst, char *src, char *mask, unsigned int length) {
+ for (unsigned int n = (length >> 2) + 1; n; n--) {
+ *(dst++) = *(src++) ^ mask[0];
+ *(dst++) = *(src++) ^ mask[1];
+ *(dst++) = *(src++) ^ mask[2];
+ *(dst++) = *(src++) ^ mask[3];
+ }
+ }
+
+ static inline void unmaskImpreciseCopyMask(char *dst, char *src, char *maskPtr, unsigned int length) {
+ char mask[4] = {maskPtr[0], maskPtr[1], maskPtr[2], maskPtr[3]};
+ unmaskImprecise(dst, src, mask, length);
+ }
+
+ static inline void rotateMask(unsigned int offset, char *mask) {
+ char originalMask[4] = {mask[0], mask[1], mask[2], mask[3]};
+ mask[(0 + offset) % 4] = originalMask[0];
+ mask[(1 + offset) % 4] = originalMask[1];
+ mask[(2 + offset) % 4] = originalMask[2];
+ mask[(3 + offset) % 4] = originalMask[3];
+ }
+
+ static inline void unmaskInplace(char *data, char *stop, char *mask) {
+ while (data < stop) {
+ *(data++) ^= mask[0];
+ *(data++) ^= mask[1];
+ *(data++) ^= mask[2];
+ *(data++) ^= mask[3];
+ }
+ }
+
+ enum {
+ SND_CONTINUATION = 1,
+ SND_NO_FIN = 2,
+ SND_COMPRESSED = 64
+ };
+
+ template <unsigned int MESSAGE_HEADER, typename T>
+ static inline bool consumeMessage(T payLength, char *&src, unsigned int &length, WebSocketState<isServer> *wState) {
+ if (getOpCode(src)) {
+ if (wState->state.opStack == 1 || (!wState->state.lastFin && getOpCode(src) < 2)) {
+ Impl::forceClose(wState);
+ return true;
+ }
+ wState->state.opCode[++wState->state.opStack] = (OpCode) getOpCode(src);
+ } else if (wState->state.opStack == -1) {
+ Impl::forceClose(wState);
+ return true;
+ }
+ wState->state.lastFin = isFin(src);
+
+ if (Impl::refusePayloadLength(payLength, wState)) {
+ Impl::forceClose(wState);
+ return true;
+ }
+
+ if (payLength + MESSAGE_HEADER <= length) {
+ if (isServer) {
+ unmaskImpreciseCopyMask(src + MESSAGE_HEADER - 4, src + MESSAGE_HEADER, src + MESSAGE_HEADER - 4, payLength);
+ if (Impl::handleFragment(src + MESSAGE_HEADER - 4, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState)) {
+ return true;
+ }
+ } else {
+ if (Impl::handleFragment(src + MESSAGE_HEADER, payLength, 0, wState->state.opCode[wState->state.opStack], isFin(src), wState)) {
+ return true;
+ }
+ }
+
+ if (isFin(src)) {
+ wState->state.opStack--;
+ }
+
+ src += payLength + MESSAGE_HEADER;
+ length -= payLength + MESSAGE_HEADER;
+ wState->state.spillLength = 0;
+ return false;
+ } else {
+ wState->state.spillLength = 0;
+ wState->state.wantsHead = false;
+ wState->remainingBytes = payLength - length + MESSAGE_HEADER;
+ bool fin = isFin(src);
+ if (isServer) {
+ memcpy(wState->mask, src + MESSAGE_HEADER - 4, 4);
+ unmaskImprecise(src, src + MESSAGE_HEADER, wState->mask, length - MESSAGE_HEADER);
+ rotateMask(4 - (length - MESSAGE_HEADER) % 4, wState->mask);
+ } else {
+ src += MESSAGE_HEADER;
+ }
+ Impl::handleFragment(src, length - MESSAGE_HEADER, wState->remainingBytes, wState->state.opCode[wState->state.opStack], fin, wState);
+ return true;
+ }
+ }
+
+ static inline bool consumeContinuation(char *&src, unsigned int &length, WebSocketState<isServer> *wState) {
+ if (wState->remainingBytes <= length) {
+ if (isServer) {
+ int n = wState->remainingBytes >> 2;
+ unmaskInplace(src, src + n * 4, wState->mask);
+ for (int i = 0, s = wState->remainingBytes % 4; i < s; i++) {
+ src[n * 4 + i] ^= wState->mask[i];
+ }
+ }
+
+ if (Impl::handleFragment(src, wState->remainingBytes, 0, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState)) {
+ return false;
+ }
+
+ if (wState->state.lastFin) {
+ wState->state.opStack--;
+ }
+
+ src += wState->remainingBytes;
+ length -= wState->remainingBytes;
+ wState->state.wantsHead = true;
+ return true;
+ } else {
+ if (isServer) {
+ unmaskInplace(src, src + ((length >> 2) + 1) * 4, wState->mask);
+ }
+
+ wState->remainingBytes -= length;
+ if (Impl::handleFragment(src, length, wState->remainingBytes, wState->state.opCode[wState->state.opStack], wState->state.lastFin, wState)) {
+ return false;
+ }
+
+ if (isServer && length % 4) {
+ rotateMask(4 - (length % 4), wState->mask);
+ }
+ return false;
+ }
+ }
+
+public:
+ WebSocketProtocol() {
+
+ }
+
+ // Based on utf8_check.c by Markus Kuhn, 2005
+ // https://www.cl.cam.ac.uk/~mgk25/ucs/utf8_check.c
+ // Optimized for predominantly 7-bit content by Alex Hultman, 2016
+ // Licensed as Zlib, like the rest of this project
+ static bool isValidUtf8(unsigned char *s, size_t length)
+ {
+ for (unsigned char *e = s + length; s != e; ) {
+ if (s + 4 <= e && ((*(uint32_t *) s) & 0x80808080) == 0) {
+ s += 4;
+ } else {
+ while (!(*s & 0x80)) {
+ if (++s == e) {
+ return true;
+ }
+ }
+
+ if ((s[0] & 0x60) == 0x40) {
+ if (s + 1 >= e || (s[1] & 0xc0) != 0x80 || (s[0] & 0xfe) == 0xc0) {
+ return false;
+ }
+ s += 2;
+ } else if ((s[0] & 0xf0) == 0xe0) {
+ if (s + 2 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 ||
+ (s[0] == 0xe0 && (s[1] & 0xe0) == 0x80) || (s[0] == 0xed && (s[1] & 0xe0) == 0xa0)) {
+ return false;
+ }
+ s += 3;
+ } else if ((s[0] & 0xf8) == 0xf0) {
+ if (s + 3 >= e || (s[1] & 0xc0) != 0x80 || (s[2] & 0xc0) != 0x80 || (s[3] & 0xc0) != 0x80 ||
+ (s[0] == 0xf0 && (s[1] & 0xf0) == 0x80) || (s[0] == 0xf4 && s[1] > 0x8f) || s[0] > 0xf4) {
+ return false;
+ }
+ s += 4;
+ } else {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ struct CloseFrame {
+ uint16_t code;
+ char *message;
+ size_t length;
+ };
+
+ static inline CloseFrame parseClosePayload(char *src, size_t length) {
+ CloseFrame cf = {};
+ if (length >= 2) {
+ memcpy(&cf.code, src, 2);
+ cf = {ntohs(cf.code), src + 2, length - 2};
+ if (cf.code < 1000 || cf.code > 4999 || (cf.code > 1011 && cf.code < 4000) ||
+ (cf.code >= 1004 && cf.code <= 1006) || !isValidUtf8((unsigned char *) cf.message, cf.length)) {
+ return {};
+ }
+ }
+ return cf;
+ }
+
+ static inline size_t formatClosePayload(char *dst, uint16_t code, const char *message, size_t length) {
+ if (code) {
+ code = htons(code);
+ memcpy(dst, &code, 2);
+ memcpy(dst + 2, message, length);
+ return length + 2;
+ }
+ return 0;
+ }
+
+ static inline size_t formatMessage(char *dst, const char *src, size_t length, OpCode opCode, size_t reportedLength, bool compressed) {
+ size_t messageLength;
+ size_t headerLength;
+ if (reportedLength < 126) {
+ headerLength = 2;
+ dst[1] = reportedLength;
+ } else if (reportedLength <= UINT16_MAX) {
+ headerLength = 4;
+ dst[1] = 126;
+ *((uint16_t *) &dst[2]) = htons(reportedLength);
+ } else {
+ headerLength = 10;
+ dst[1] = 127;
+ *((uint64_t *) &dst[2]) = htobe64(reportedLength);
+ }
+
+ int flags = 0;
+ dst[0] = (flags & SND_NO_FIN ? 0 : 128) | (compressed ? SND_COMPRESSED : 0);
+ if (!(flags & SND_CONTINUATION)) {
+ dst[0] |= opCode;
+ }
+
+ char mask[4];
+ if (!isServer) {
+ dst[1] |= 0x80;
+ uint32_t random = rand();
+ memcpy(mask, &random, 4);
+ memcpy(dst + headerLength, &random, 4);
+ headerLength += 4;
+ }
+
+ messageLength = headerLength + length;
+ memcpy(dst + headerLength, src, length);
+
+ if (!isServer) {
+
+ // overwrites up to 3 bytes outside of the given buffer!
+ //WebSocketProtocol<isServer>::unmaskInplace(dst + headerLength, dst + headerLength + length, mask);
+
+ // this is not optimal
+ char *start = dst + headerLength;
+ char *stop = start + length;
+ int i = 0;
+ while (start != stop) {
+ (*start++) ^= mask[i++ % 4];
+ }
+ }
+ return messageLength;
+ }
+
+ static inline void consume(char *src, unsigned int length, WebSocketState<isServer> *wState) {
+ if (wState->state.spillLength) {
+ src -= wState->state.spillLength;
+ length += wState->state.spillLength;
+ memcpy(src, wState->state.spill, wState->state.spillLength);
+ }
+ if (wState->state.wantsHead) {
+ parseNext:
+ while (length >= SHORT_MESSAGE_HEADER) {
+
+ // invalid reserved bits / invalid opcodes / invalid control frames / set compressed frame
+ if ((rsv1(src) && !Impl::setCompressed(wState)) || rsv23(src) || (getOpCode(src) > 2 && getOpCode(src) < 8) ||
+ getOpCode(src) > 10 || (getOpCode(src) > 2 && (!isFin(src) || payloadLength(src) > 125))) {
+ Impl::forceClose(wState);
+ return;
+ }
+
+ if (payloadLength(src) < 126) {
+ if (consumeMessage<SHORT_MESSAGE_HEADER, uint8_t>(payloadLength(src), src, length, wState)) {
+ return;
+ }
+ } else if (payloadLength(src) == 126) {
+ if (length < MEDIUM_MESSAGE_HEADER) {
+ break;
+ } else if(consumeMessage<MEDIUM_MESSAGE_HEADER, uint16_t>(ntohs(*(uint16_t *) &src[2]), src, length, wState)) {
+ return;
+ }
+ } else if (length < LONG_MESSAGE_HEADER) {
+ break;
+ } else if (consumeMessage<LONG_MESSAGE_HEADER, uint64_t>(be64toh(*(uint64_t *) &src[2]), src, length, wState)) {
+ return;
+ }
+ }
+ if (length) {
+ memcpy(wState->state.spill, src, length);
+ wState->state.spillLength = length;
+ }
+ } else if (consumeContinuation(src, length, wState)) {
+ goto parseNext;
+ }
+ }
+
+ static const int CONSUME_POST_PADDING = 4;
+ static const int CONSUME_PRE_PADDING = LONG_MESSAGE_HEADER - 1;
+};
+
+}
+
+#endif // WEBSOCKETPROTOCOL_UWS_H
diff --git a/node_modules/uws/src/addon.cpp b/node_modules/uws/src/addon.cpp
new file mode 100644
index 0000000..15e6905
--- /dev/null
+++ b/node_modules/uws/src/addon.cpp
@@ -0,0 +1,24 @@
+#include "../src/uWS.h"
+#include "addon.h"
+#include "http.h"
+
+void Main(Local<Object> exports) {
+ Isolate *isolate = exports->GetIsolate();
+
+ exports->Set(String::NewFromUtf8(isolate, "server"), Namespace<uWS::SERVER>(isolate).object);
+ exports->Set(String::NewFromUtf8(isolate, "client"), Namespace<uWS::CLIENT>(isolate).object);
+ exports->Set(String::NewFromUtf8(isolate, "httpServer"), HttpServer::getHttpServer(isolate));
+
+ NODE_SET_METHOD(exports, "setUserData", setUserData<uWS::SERVER>);
+ NODE_SET_METHOD(exports, "getUserData", getUserData<uWS::SERVER>);
+ NODE_SET_METHOD(exports, "clearUserData", clearUserData<uWS::SERVER>);
+ NODE_SET_METHOD(exports, "getAddress", getAddress<uWS::SERVER>);
+
+ NODE_SET_METHOD(exports, "transfer", transfer);
+ NODE_SET_METHOD(exports, "upgrade", upgrade);
+ NODE_SET_METHOD(exports, "connect", connect);
+ NODE_SET_METHOD(exports, "setNoop", setNoop);
+ registerCheck(isolate);
+}
+
+NODE_MODULE(uws, Main)
diff --git a/node_modules/uws/src/addon.h b/node_modules/uws/src/addon.h
new file mode 100644
index 0000000..93a41e5
--- /dev/null
+++ b/node_modules/uws/src/addon.h
@@ -0,0 +1,464 @@
+#include <node.h>
+#include <node_buffer.h>
+#include <cstring>
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <uv.h>
+
+using namespace std;
+using namespace v8;
+
+uWS::Hub hub(0, true);
+uv_check_t check;
+Persistent<Function> noop;
+
+void registerCheck(Isolate *isolate) {
+ uv_check_init((uv_loop_t *) hub.getLoop(), &check);
+ check.data = isolate;
+ uv_check_start(&check, [](uv_check_t *check) {
+ Isolate *isolate = (Isolate *) check->data;
+ HandleScope hs(isolate);
+ node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local<Function>::New(isolate, noop), 0, nullptr);
+ });
+ uv_unref((uv_handle_t *) &check);
+}
+
+class NativeString {
+ char *data;
+ size_t length;
+ char utf8ValueMemory[sizeof(String::Utf8Value)];
+ String::Utf8Value *utf8Value = nullptr;
+public:
+ NativeString(const Local<Value> &value) {
+ if (value->IsUndefined()) {
+ data = nullptr;
+ length = 0;
+ } else if (value->IsString()) {
+ utf8Value = new (utf8ValueMemory) String::Utf8Value(value);
+ data = (**utf8Value);
+ length = utf8Value->length();
+ } else if (node::Buffer::HasInstance(value)) {
+ data = node::Buffer::Data(value);
+ length = node::Buffer::Length(value);
+ } else if (value->IsTypedArray()) {
+ Local<ArrayBufferView> arrayBufferView = Local<ArrayBufferView>::Cast(value);
+ ArrayBuffer::Contents contents = arrayBufferView->Buffer()->GetContents();
+ length = contents.ByteLength();
+ data = (char *) contents.Data();
+ } else if (value->IsArrayBuffer()) {
+ Local<ArrayBuffer> arrayBuffer = Local<ArrayBuffer>::Cast(value);
+ ArrayBuffer::Contents contents = arrayBuffer->GetContents();
+ length = contents.ByteLength();
+ data = (char *) contents.Data();
+ } else {
+ static char empty[] = "";
+ data = empty;
+ length = 0;
+ }
+ }
+
+ char *getData() {return data;}
+ size_t getLength() {return length;}
+ ~NativeString() {
+ if (utf8Value) {
+ utf8Value->~Utf8Value();
+ }
+ }
+};
+
+struct GroupData {
+ Persistent<Function> connectionHandler, messageHandler,
+ disconnectionHandler, pingHandler,
+ pongHandler, errorHandler, httpRequestHandler,
+ httpUpgradeHandler, httpCancelledRequestCallback;
+ int size = 0;
+};
+
+template <bool isServer>
+void createGroup(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = hub.createGroup<isServer>(args[0]->IntegerValue(), args[1]->IntegerValue());
+ group->setUserData(new GroupData);
+ args.GetReturnValue().Set(External::New(args.GetIsolate(), group));
+}
+
+template <bool isServer>
+void deleteGroup(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ delete (GroupData *) group->getUserData();
+ delete group;
+}
+
+template <bool isServer>
+inline Local<External> wrapSocket(uWS::WebSocket<isServer> *webSocket, Isolate *isolate) {
+ return External::New(isolate, webSocket);
+}
+
+template <bool isServer>
+inline uWS::WebSocket<isServer> *unwrapSocket(Local<External> external) {
+ return (uWS::WebSocket<isServer> *) external->Value();
+}
+
+inline Local<Value> wrapMessage(const char *message, size_t length, uWS::OpCode opCode, Isolate *isolate) {
+ return opCode == uWS::OpCode::BINARY ? (Local<Value>) ArrayBuffer::New(isolate, (char *) message, length) : (Local<Value>) String::NewFromUtf8(isolate, message, String::kNormalString, length);
+}
+
+template <bool isServer>
+inline Local<Value> getDataV8(uWS::WebSocket<isServer> *webSocket, Isolate *isolate) {
+ return webSocket->getUserData() ? Local<Value>::New(isolate, *(Persistent<Value> *) webSocket->getUserData()) : Local<Value>::Cast(Undefined(isolate));
+}
+
+template <bool isServer>
+void getUserData(const FunctionCallbackInfo<Value> &args) {
+ args.GetReturnValue().Set(getDataV8(unwrapSocket<isServer>(args[0].As<External>()), args.GetIsolate()));
+}
+
+template <bool isServer>
+void clearUserData(const FunctionCallbackInfo<Value> &args) {
+ uWS::WebSocket<isServer> *webSocket = unwrapSocket<isServer>(args[0].As<External>());
+ ((Persistent<Value> *) webSocket->getUserData())->Reset();
+ delete (Persistent<Value> *) webSocket->getUserData();
+}
+
+template <bool isServer>
+void setUserData(const FunctionCallbackInfo<Value> &args) {
+ uWS::WebSocket<isServer> *webSocket = unwrapSocket<isServer>(args[0].As<External>());
+ if (webSocket->getUserData()) {
+ ((Persistent<Value> *) webSocket->getUserData())->Reset(args.GetIsolate(), args[1]);
+ } else {
+ webSocket->setUserData(new Persistent<Value>(args.GetIsolate(), args[1]));
+ }
+}
+
+template <bool isServer>
+void getAddress(const FunctionCallbackInfo<Value> &args)
+{
+ typename uWS::WebSocket<isServer>::Address address = unwrapSocket<isServer>(args[0].As<External>())->getAddress();
+ Local<Array> array = Array::New(args.GetIsolate(), 3);
+ array->Set(0, Integer::New(args.GetIsolate(), address.port));
+ array->Set(1, String::NewFromUtf8(args.GetIsolate(), address.address));
+ array->Set(2, String::NewFromUtf8(args.GetIsolate(), address.family));
+ args.GetReturnValue().Set(array);
+}
+
+uv_handle_t *getTcpHandle(void *handleWrap) {
+ volatile char *memory = (volatile char *) handleWrap;
+ for (volatile uv_handle_t *tcpHandle = (volatile uv_handle_t *) memory; tcpHandle->type != UV_TCP
+ || tcpHandle->data != handleWrap || tcpHandle->loop != uv_default_loop(); tcpHandle = (volatile uv_handle_t *) memory) {
+ memory++;
+ }
+ return (uv_handle_t *) memory;
+}
+
+struct SendCallbackData {
+ Persistent<Function> jsCallback;
+ Isolate *isolate;
+};
+
+template <bool isServer>
+void sendCallback(uWS::WebSocket<isServer> *webSocket, void *data, bool cancelled, void *reserved)
+{
+ SendCallbackData *sc = (SendCallbackData *) data;
+ if (!cancelled) {
+ HandleScope hs(sc->isolate);
+ node::MakeCallback(sc->isolate, sc->isolate->GetCurrentContext()->Global(), Local<Function>::New(sc->isolate, sc->jsCallback), 0, nullptr);
+ }
+ sc->jsCallback.Reset();
+ delete sc;
+}
+
+template <bool isServer>
+void send(const FunctionCallbackInfo<Value> &args)
+{
+ uWS::OpCode opCode = (uWS::OpCode) args[2]->IntegerValue();
+ NativeString nativeString(args[1]);
+
+ SendCallbackData *sc = nullptr;
+ void (*callback)(uWS::WebSocket<isServer> *, void *, bool, void *) = nullptr;
+
+ if (args[3]->IsFunction()) {
+ callback = sendCallback;
+ sc = new SendCallbackData;
+ sc->jsCallback.Reset(args.GetIsolate(), Local<Function>::Cast(args[3]));
+ sc->isolate = args.GetIsolate();
+ }
+
+ unwrapSocket<isServer>(args[0].As<External>())->send(nativeString.getData(),
+ nativeString.getLength(), opCode, callback, sc);
+}
+
+void connect(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::CLIENT> *clientGroup = (uWS::Group<uWS::CLIENT> *) args[0].As<External>()->Value();
+ NativeString uri(args[1]);
+ hub.connect(std::string(uri.getData(), uri.getLength()), new Persistent<Value>(args.GetIsolate(), args[2]), {}, 5000, clientGroup);
+}
+
+struct Ticket {
+ uv_os_sock_t fd;
+ SSL *ssl;
+};
+
+void upgrade(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::SERVER> *serverGroup = (uWS::Group<uWS::SERVER> *) args[0].As<External>()->Value();
+ Ticket *ticket = (Ticket *) args[1].As<External>()->Value();
+ NativeString secKey(args[2]);
+ NativeString extensions(args[3]);
+ NativeString subprotocol(args[4]);
+
+ // todo: move this check into core!
+ if (ticket->fd != INVALID_SOCKET) {
+ hub.upgrade(ticket->fd, secKey.getData(), ticket->ssl, extensions.getData(), extensions.getLength(), subprotocol.getData(), subprotocol.getLength(), serverGroup);
+ } else {
+ if (ticket->ssl) {
+ SSL_free(ticket->ssl);
+ }
+ }
+ delete ticket;
+}
+
+void transfer(const FunctionCallbackInfo<Value> &args) {
+ // (_handle.fd OR _handle), SSL
+ uv_handle_t *handle = nullptr;
+ Ticket *ticket = new Ticket;
+ if (args[0]->IsObject()) {
+ uv_fileno((handle = getTcpHandle(args[0]->ToObject()->GetAlignedPointerFromInternalField(0))), (uv_os_fd_t *) &ticket->fd);
+ } else {
+ ticket->fd = args[0]->IntegerValue();
+ }
+
+ ticket->fd = dup(ticket->fd);
+ ticket->ssl = nullptr;
+ if (args[1]->IsExternal()) {
+ ticket->ssl = (SSL *) args[1].As<External>()->Value();
+ SSL_up_ref(ticket->ssl);
+ }
+
+ // uv_close calls shutdown if not set on Windows
+ if (handle) {
+ // UV_HANDLE_SHARED_TCP_SOCKET
+ handle->flags |= 0x40000000;
+ }
+
+ args.GetReturnValue().Set(External::New(args.GetIsolate(), ticket));
+}
+
+template <bool isServer>
+void onConnection(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *connectionCallback = &groupData->connectionHandler;
+ connectionCallback->Reset(isolate, Local<Function>::Cast(args[1]));
+ group->onConnection([isolate, connectionCallback, groupData](uWS::WebSocket<isServer> *webSocket, uWS::HttpRequest req) {
+ groupData->size++;
+ HandleScope hs(isolate);
+ Local<Value> argv[] = {wrapSocket(webSocket, isolate)};
+ node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local<Function>::New(isolate, *connectionCallback), 1, argv);
+ });
+}
+
+template <bool isServer>
+void onMessage(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *messageCallback = &groupData->messageHandler;
+ messageCallback->Reset(isolate, Local<Function>::Cast(args[1]));
+ group->onMessage([isolate, messageCallback](uWS::WebSocket<isServer> *webSocket, const char *message, size_t length, uWS::OpCode opCode) {
+ HandleScope hs(isolate);
+ Local<Value> argv[] = {wrapMessage(message, length, opCode, isolate),
+ getDataV8(webSocket, isolate)};
+ Local<Function>::New(isolate, *messageCallback)->Call(isolate->GetCurrentContext()->Global(), 2, argv);
+ });
+}
+
+template <bool isServer>
+void onPing(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *pingCallback = &groupData->pingHandler;
+ pingCallback->Reset(isolate, Local<Function>::Cast(args[1]));
+ group->onPing([isolate, pingCallback](uWS::WebSocket<isServer> *webSocket, const char *message, size_t length) {
+ HandleScope hs(isolate);
+ Local<Value> argv[] = {wrapMessage(message, length, uWS::OpCode::PING, isolate),
+ getDataV8(webSocket, isolate)};
+ node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local<Function>::New(isolate, *pingCallback), 2, argv);
+ });
+}
+
+template <bool isServer>
+void onPong(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *pongCallback = &groupData->pongHandler;
+ pongCallback->Reset(isolate, Local<Function>::Cast(args[1]));
+ group->onPong([isolate, pongCallback](uWS::WebSocket<isServer> *webSocket, const char *message, size_t length) {
+ HandleScope hs(isolate);
+ Local<Value> argv[] = {wrapMessage(message, length, uWS::OpCode::PONG, isolate),
+ getDataV8(webSocket, isolate)};
+ node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local<Function>::New(isolate, *pongCallback), 2, argv);
+ });
+}
+
+template <bool isServer>
+void onDisconnection(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *disconnectionCallback = &groupData->disconnectionHandler;
+ disconnectionCallback->Reset(isolate, Local<Function>::Cast(args[1]));
+
+ group->onDisconnection([isolate, disconnectionCallback, groupData](uWS::WebSocket<isServer> *webSocket, int code, char *message, size_t length) {
+ groupData->size--;
+ HandleScope hs(isolate);
+ Local<Value> argv[] = {wrapSocket(webSocket, isolate),
+ Integer::New(isolate, code),
+ wrapMessage(message, length, uWS::OpCode::CLOSE, isolate),
+ getDataV8(webSocket, isolate)};
+ node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local<Function>::New(isolate, *disconnectionCallback), 4, argv);
+ });
+}
+
+void onError(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::CLIENT> *group = (uWS::Group<uWS::CLIENT> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *errorCallback = &groupData->errorHandler;
+ errorCallback->Reset(isolate, Local<Function>::Cast(args[1]));
+
+ group->onError([isolate, errorCallback](void *user) {
+ HandleScope hs(isolate);
+ Local<Value> argv[] = {Local<Value>::New(isolate, *(Persistent<Value> *) user)};
+ node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local<Function>::New(isolate, *errorCallback), 1, argv);
+
+ ((Persistent<Value> *) user)->Reset();
+ delete (Persistent<Value> *) user;
+ });
+}
+
+template <bool isServer>
+void closeSocket(const FunctionCallbackInfo<Value> &args) {
+ NativeString nativeString(args[2]);
+ unwrapSocket<isServer>(args[0].As<External>())->close(args[1]->IntegerValue(), nativeString.getData(), nativeString.getLength());
+}
+
+template <bool isServer>
+void terminateSocket(const FunctionCallbackInfo<Value> &args) {
+ unwrapSocket<isServer>(args[0].As<External>())->terminate();
+}
+
+template <bool isServer>
+void closeGroup(const FunctionCallbackInfo<Value> &args) {
+ NativeString nativeString(args[2]);
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ group->close(args[1]->IntegerValue(), nativeString.getData(), nativeString.getLength());
+}
+
+template <bool isServer>
+void terminateGroup(const FunctionCallbackInfo<Value> &args) {
+ ((uWS::Group<isServer> *) args[0].As<External>()->Value())->terminate();
+}
+
+template <bool isServer>
+void broadcast(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<isServer> *group = (uWS::Group<isServer> *) args[0].As<External>()->Value();
+ uWS::OpCode opCode = args[2]->BooleanValue() ? uWS::OpCode::BINARY : uWS::OpCode::TEXT;
+ NativeString nativeString(args[1]);
+ group->broadcast(nativeString.getData(), nativeString.getLength(), opCode);
+}
+
+template <bool isServer>
+void prepareMessage(const FunctionCallbackInfo<Value> &args) {
+ uWS::OpCode opCode = (uWS::OpCode) args[1]->IntegerValue();
+ NativeString nativeString(args[0]);
+ args.GetReturnValue().Set(External::New(args.GetIsolate(), uWS::WebSocket<isServer>::prepareMessage(nativeString.getData(), nativeString.getLength(), opCode, false)));
+}
+
+template <bool isServer>
+void sendPrepared(const FunctionCallbackInfo<Value> &args) {
+ unwrapSocket<isServer>(args[0].As<External>())
+ ->sendPrepared((typename uWS::WebSocket<isServer>::PreparedMessage *) args[1].As<External>()->Value());
+}
+
+template <bool isServer>
+void finalizeMessage(const FunctionCallbackInfo<Value> &args) {
+ uWS::WebSocket<isServer>::finalizeMessage((typename uWS::WebSocket<isServer>::PreparedMessage *) args[0].As<External>()->Value());
+}
+
+void forEach(const FunctionCallbackInfo<Value> &args) {
+ Isolate *isolate = args.GetIsolate();
+ uWS::Group<uWS::SERVER> *group = (uWS::Group<uWS::SERVER> *) args[0].As<External>()->Value();
+ Local<Function> cb = Local<Function>::Cast(args[1]);
+ group->forEach([isolate, &cb](uWS::WebSocket<uWS::SERVER> *webSocket) {
+ Local<Value> argv[] = {
+ getDataV8(webSocket, isolate)
+ };
+ cb->Call(Null(isolate), 1, argv);
+ });
+}
+
+void getSize(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::SERVER> *group = (uWS::Group<uWS::SERVER> *) args[0].As<External>()->Value();
+ GroupData *groupData = (GroupData *) group->getUserData();
+ args.GetReturnValue().Set(Integer::New(args.GetIsolate(), groupData->size));
+}
+
+void startAutoPing(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::SERVER> *group = (uWS::Group<uWS::SERVER> *) args[0].As<External>()->Value();
+ NativeString nativeString(args[2]);
+ group->startAutoPing(args[1]->IntegerValue(), std::string(nativeString.getData(), nativeString.getLength()));
+}
+
+void setNoop(const FunctionCallbackInfo<Value> &args) {
+ noop.Reset(args.GetIsolate(), Local<Function>::Cast(args[0]));
+}
+
+void listen(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::SERVER> *group = (uWS::Group<uWS::SERVER> *) args[0].As<External>()->Value();
+ hub.listen(args[1]->IntegerValue(), nullptr, 0, group);
+}
+
+template <bool isServer>
+struct Namespace {
+ Local<Object> object;
+ Namespace (Isolate *isolate) {
+ object = Object::New(isolate);
+ NODE_SET_METHOD(object, "send", send<isServer>);
+ NODE_SET_METHOD(object, "close", closeSocket<isServer>);
+ NODE_SET_METHOD(object, "terminate", terminateSocket<isServer>);
+ NODE_SET_METHOD(object, "prepareMessage", prepareMessage<isServer>);
+ NODE_SET_METHOD(object, "sendPrepared", sendPrepared<isServer>);
+ NODE_SET_METHOD(object, "finalizeMessage", finalizeMessage<isServer>);
+
+ Local<Object> group = Object::New(isolate);
+ NODE_SET_METHOD(group, "onConnection", onConnection<isServer>);
+ NODE_SET_METHOD(group, "onMessage", onMessage<isServer>);
+ NODE_SET_METHOD(group, "onDisconnection", onDisconnection<isServer>);
+
+ if (!isServer) {
+ NODE_SET_METHOD(group, "onError", onError);
+ } else {
+ NODE_SET_METHOD(group, "forEach", forEach);
+ NODE_SET_METHOD(group, "getSize", getSize);
+ NODE_SET_METHOD(group, "startAutoPing", startAutoPing);
+ NODE_SET_METHOD(group, "listen", listen);
+ }
+
+ NODE_SET_METHOD(group, "onPing", onPing<isServer>);
+ NODE_SET_METHOD(group, "onPong", onPong<isServer>);
+ NODE_SET_METHOD(group, "create", createGroup<isServer>);
+ NODE_SET_METHOD(group, "delete", deleteGroup<isServer>);
+ NODE_SET_METHOD(group, "close", closeGroup<isServer>);
+ NODE_SET_METHOD(group, "terminate", terminateGroup<isServer>);
+ NODE_SET_METHOD(group, "broadcast", broadcast<isServer>);
+
+ object->Set(String::NewFromUtf8(isolate, "group"), group);
+ }
+};
diff --git a/node_modules/uws/src/http.h b/node_modules/uws/src/http.h
new file mode 100644
index 0000000..61c9d2e
--- /dev/null
+++ b/node_modules/uws/src/http.h
@@ -0,0 +1,357 @@
+#include <iostream>
+
+Persistent<Object> reqTemplate, resTemplate;
+Persistent<Function> httpPersistent;
+
+uWS::HttpRequest *currentReq = nullptr;
+
+struct HttpServer {
+
+ struct Request {
+ static void on(const FunctionCallbackInfo<Value> &args) {
+ NativeString eventName(args[0]);
+ if (std::string(eventName.getData(), eventName.getLength()) == "data") {
+ args.Holder()->SetInternalField(1, args[1]);
+ } else if (std::string(eventName.getData(), eventName.getLength()) == "end") {
+ args.Holder()->SetInternalField(2, args[1]);
+ } else {
+ std::cout << "Warning: req.on(" << std::string(eventName.getData(), eventName.getLength()) << ") is not implemented!" << std::endl;
+ }
+ args.GetReturnValue().Set(args.Holder());
+ }
+
+ static void headers(Local<String> property, const PropertyCallbackInfo<Value> &args) {
+ uWS::HttpRequest *req = currentReq;
+ if (!req) {
+ std::cerr << "Warning: req.headers usage past request handler is not supported!" << std::endl;
+ } else {
+ NativeString nativeString(property);
+ uWS::Header header = req->getHeader(nativeString.getData(), nativeString.getLength());
+ if (header) {
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) header.value, String::kNormalString, header.valueLength));
+ }
+ }
+ }
+
+ static void url(Local<String> property, const PropertyCallbackInfo<Value> &args) {
+ args.GetReturnValue().Set(args.This()->GetInternalField(4));
+ }
+
+ static void method(Local<String> property, const PropertyCallbackInfo<Value> &args) {
+ //std::cout << "method" << std::endl;
+ long methodId = ((long) args.This()->GetAlignedPointerFromInternalField(3)) >> 1;
+ switch (methodId) {
+ case uWS::HttpMethod::METHOD_GET:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "GET", String::kNormalString, 3));
+ break;
+ case uWS::HttpMethod::METHOD_PUT:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "PUT", String::kNormalString, 3));
+ break;
+ case uWS::HttpMethod::METHOD_POST:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "POST", String::kNormalString, 4));
+ break;
+ case uWS::HttpMethod::METHOD_HEAD:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "HEAD", String::kNormalString, 4));
+ break;
+ case uWS::HttpMethod::METHOD_PATCH:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "PATCH", String::kNormalString, 5));
+ break;
+ case uWS::HttpMethod::METHOD_TRACE:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "TRACE", String::kNormalString, 5));
+ break;
+ case uWS::HttpMethod::METHOD_DELETE:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "DELETE", String::kNormalString, 6));
+ break;
+ case uWS::HttpMethod::METHOD_OPTIONS:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "OPTIONS", String::kNormalString, 7));
+ break;
+ case uWS::HttpMethod::METHOD_CONNECT:
+ args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "CONNECT", String::kNormalString, 7));
+ break;
+ }
+ }
+
+ // placeholders
+ static void unpipe(const FunctionCallbackInfo<Value> &args) {
+ //std::cout << "req.unpipe called" << std::endl;
+ }
+
+ static void resume(const FunctionCallbackInfo<Value> &args) {
+ //std::cout << "req.resume called" << std::endl;
+ }
+
+ static void socket(const FunctionCallbackInfo<Value> &args) {
+ // return new empty object
+ args.GetReturnValue().Set(Object::New(args.GetIsolate()));
+ }
+
+ static Local<Object> getTemplateObject(Isolate *isolate) {
+ Local<FunctionTemplate> reqTemplateLocal = FunctionTemplate::New(isolate);
+ reqTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uws.Request"));
+ reqTemplateLocal->InstanceTemplate()->SetInternalFieldCount(5);
+ reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "url"), Request::url);
+ reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "method"), Request::method);
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "on"), FunctionTemplate::New(isolate, Request::on));
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "unpipe"), FunctionTemplate::New(isolate, Request::unpipe));
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "resume"), FunctionTemplate::New(isolate, Request::resume));
+ reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "socket"), FunctionTemplate::New(isolate, Request::socket));
+
+ Local<Object> reqObjectLocal = reqTemplateLocal->GetFunction()->NewInstance();
+
+ Local<ObjectTemplate> headersTemplate = ObjectTemplate::New(isolate);
+ headersTemplate->SetNamedPropertyHandler(Request::headers);
+
+ reqObjectLocal->Set(String::NewFromUtf8(isolate, "headers"), headersTemplate->NewInstance());
+ return reqObjectLocal;
+ }
+ };
+
+ struct Response {
+ static void on(const FunctionCallbackInfo<Value> &args) {
+ NativeString eventName(args[0]);
+ if (std::string(eventName.getData(), eventName.getLength()) == "close") {
+ args.Holder()->SetInternalField(1, args[1]);
+ } else {
+ std::cout << "Warning: res.on(" << std::string(eventName.getData(), eventName.getLength()) << ") is not implemented!" << std::endl;
+ }
+ args.GetReturnValue().Set(args.Holder());
+ }
+
+ static void end(const FunctionCallbackInfo<Value> &args) {
+ uWS::HttpResponse *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0);
+ if (res) {
+ NativeString nativeString(args[0]);
+
+ ((Persistent<Object> *) &res->userData)->Reset();
+ ((Persistent<Object> *) &res->userData)->~Persistent<Object>();
+ ((Persistent<Object> *) &res->extraUserData)->Reset();
+ ((Persistent<Object> *) &res->extraUserData)->~Persistent<Object>();
+ res->end(nativeString.getData(), nativeString.getLength());
+ }
+ }
+
+ // todo: this is slow
+ static void writeHead(const FunctionCallbackInfo<Value> &args) {
+ uWS::HttpResponse *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0);
+ if (res) {
+ std::string head = "HTTP/1.1 " + std::to_string(args[0]->IntegerValue()) + " ";
+
+ if (args.Length() > 1 && args[1]->IsString()) {
+ NativeString statusMessage(args[1]);
+ head.append(statusMessage.getData(), statusMessage.getLength());
+ } else {
+ head += "OK";
+ }
+
+ if (args[args.Length() - 1]->IsObject()) {
+ Local<Object> headersObject = args[args.Length() - 1]->ToObject();
+ Local<Array> headers = headersObject->GetOwnPropertyNames();
+ for (int i = 0; i < headers->Length(); i++) {
+ Local<Value> key = headers->Get(i);
+ Local<Value> value = headersObject->Get(key);
+
+ NativeString nativeKey(key);
+ NativeString nativeValue(value);
+
+ head += "\r\n";
+ head.append(nativeKey.getData(), nativeKey.getLength());
+ head += ": ";
+ head.append(nativeValue.getData(), nativeValue.getLength());
+ }
+ }
+
+ head += "\r\n\r\n";
+ res->write(head.data(), head.length());
+ }
+ }
+
+ // todo: if not writeHead called before then should write implicit headers
+ static void write(const FunctionCallbackInfo<Value> &args) {
+ uWS::HttpResponse *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0);
+
+ if (res) {
+ NativeString nativeString(args[0]);
+ res->write(nativeString.getData(), nativeString.getLength());
+ }
+ }
+
+ static void setHeader(const FunctionCallbackInfo<Value> &args) {
+ //std::cout << "res.setHeader called" << std::endl;
+ }
+
+ static void getHeader(const FunctionCallbackInfo<Value> &args) {
+ //std::cout << "res.getHeader called" << std::endl;
+ }
+
+ static Local<Object> getTemplateObject(Isolate *isolate) {
+ Local<FunctionTemplate> resTemplateLocal = FunctionTemplate::New(isolate);
+ resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uws.Response"));
+ resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(5);
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end"), FunctionTemplate::New(isolate, Response::end));
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHead"), FunctionTemplate::New(isolate, Response::writeHead));
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write"), FunctionTemplate::New(isolate, Response::write));
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "on"), FunctionTemplate::New(isolate, Response::on));
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "setHeader"), FunctionTemplate::New(isolate, Response::setHeader));
+ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getHeader"), FunctionTemplate::New(isolate, Response::getHeader));
+ return resTemplateLocal->GetFunction()->NewInstance();
+ }
+ };
+
+ // todo: wrap everything up - most important function to get correct
+ static void createServer(const FunctionCallbackInfo<Value> &args) {
+
+ // todo: delete this on destructor
+ uWS::Group<uWS::SERVER> *group = hub.createGroup<uWS::SERVER>();
+ group->setUserData(new GroupData);
+ GroupData *groupData = (GroupData *) group->getUserData();
+
+ Isolate *isolate = args.GetIsolate();
+ Persistent<Function> *httpRequestCallback = &groupData->httpRequestHandler;
+ httpRequestCallback->Reset(isolate, Local<Function>::Cast(args[0]));
+ group->onHttpRequest([isolate, httpRequestCallback](uWS::HttpResponse *res, uWS::HttpRequest req, char *data, size_t length, size_t remainingBytes) {
+ HandleScope hs(isolate);
+
+ currentReq = &req;
+
+ Local<Object> reqObject = Local<Object>::New(isolate, reqTemplate)->Clone();
+ reqObject->SetAlignedPointerInInternalField(0, &req);
+ new (&res->extraUserData) Persistent<Object>(isolate, reqObject);
+
+ Local<Object> resObject = Local<Object>::New(isolate, resTemplate)->Clone();
+ resObject->SetAlignedPointerInInternalField(0, res);
+ new (&res->userData) Persistent<Object>(isolate, resObject);
+
+ // store url & method (needed by Koa and Express)
+ long methodId = req.getMethod() << 1;
+ reqObject->SetAlignedPointerInInternalField(3, (void *) methodId);
+ reqObject->SetInternalField(4, String::NewFromOneByte(isolate, (uint8_t *) req.getUrl().value, String::kNormalString, req.getUrl().valueLength));
+
+ Local<Value> argv[] = {reqObject, resObject};
+ Local<Function>::New(isolate, *httpRequestCallback)->Call(isolate->GetCurrentContext()->Global(), 2, argv);
+
+ if (length) {
+ Local<Value> dataCallback = reqObject->GetInternalField(1);
+ if (!dataCallback->IsUndefined()) {
+ Local<Value> argv[] = {ArrayBuffer::New(isolate, data, length)};
+ Local<Function>::Cast(dataCallback)->Call(isolate->GetCurrentContext()->Global(), 1, argv);
+ }
+
+ if (!remainingBytes) {
+ Local<Value> endCallback = reqObject->GetInternalField(2);
+ if (!endCallback->IsUndefined()) {
+ Local<Function>::Cast(endCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr);
+ }
+ }
+ }
+
+ currentReq = nullptr;
+ reqObject->SetAlignedPointerInInternalField(0, nullptr);
+ });
+
+ group->onCancelledHttpRequest([isolate](uWS::HttpResponse *res) {
+ HandleScope hs(isolate);
+
+ // mark res as invalid
+ Local<Object> resObject = Local<Object>::New(isolate, *(Persistent<Object> *) &res->userData);
+ resObject->SetAlignedPointerInInternalField(0, nullptr);
+
+ // mark req as invalid
+ Local<Object> reqObject = Local<Object>::New(isolate, *(Persistent<Object> *) &res->extraUserData);
+ reqObject->SetAlignedPointerInInternalField(0, nullptr);
+
+ // emit res 'close' on aborted response
+ Local<Value> closeCallback = resObject->GetInternalField(1);
+ if (!closeCallback->IsUndefined()) {
+ Local<Function>::Cast(closeCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr);
+ }
+
+ ((Persistent<Object> *) &res->userData)->Reset();
+ ((Persistent<Object> *) &res->userData)->~Persistent<Object>();
+ ((Persistent<Object> *) &res->extraUserData)->Reset();
+ ((Persistent<Object> *) &res->extraUserData)->~Persistent<Object>();
+ });
+
+ group->onHttpData([isolate](uWS::HttpResponse *res, char *data, size_t length, size_t remainingBytes) {
+ Local<Object> reqObject = Local<Object>::New(isolate, *(Persistent<Object> *) res->extraUserData);
+
+ Local<Value> dataCallback = reqObject->GetInternalField(1);
+ if (!dataCallback->IsUndefined()) {
+ Local<Value> argv[] = {ArrayBuffer::New(isolate, data, length)};
+ Local<Function>::Cast(dataCallback)->Call(isolate->GetCurrentContext()->Global(), 1, argv);
+ }
+
+ if (!remainingBytes) {
+ Local<Value> endCallback = reqObject->GetInternalField(2);
+ if (!endCallback->IsUndefined()) {
+ Local<Function>::Cast(endCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr);
+ }
+ }
+ });
+
+ Local<Object> newInstance;
+ if (!args.IsConstructCall()) {
+ args.GetReturnValue().Set(newInstance = Local<Function>::New(args.GetIsolate(), httpPersistent)->NewInstance());
+ } else {
+ args.GetReturnValue().Set(newInstance = args.This());
+ }
+
+ newInstance->SetAlignedPointerInInternalField(0, group);
+ }
+
+ static void on(const FunctionCallbackInfo<Value> &args) {
+ NativeString eventName(args[0]);
+ std::cout << "Warning: server.on(" << std::string(eventName.getData(), eventName.getLength()) << ") is not implemented!" << std::endl;
+ }
+
+ static void listen(const FunctionCallbackInfo<Value> &args) {
+ uWS::Group<uWS::SERVER> *group = (uWS::Group<uWS::SERVER> *) args.Holder()->GetAlignedPointerFromInternalField(0);
+ std::cout << "listen: " << hub.listen(args[0]->IntegerValue(), nullptr, 0, group) << std::endl;
+
+ if (args[args.Length() - 1]->IsFunction()) {
+ Local<Function>::Cast(args[args.Length() - 1])->Call(args.GetIsolate()->GetCurrentContext()->Global(), 0, nullptr);
+ }
+ }
+
+ // var app = getExpressApp(express)
+ static void getExpressApp(const FunctionCallbackInfo<Value> &args) {
+ Isolate *isolate = args.GetIsolate();
+ if (args[0]->IsFunction()) {
+ Local<Function> express = Local<Function>::Cast(args[0]);
+ express->Get(String::NewFromUtf8(isolate, "request"))->ToObject()->SetPrototype(Local<Object>::New(args.GetIsolate(), reqTemplate)->GetPrototype());
+ express->Get(String::NewFromUtf8(isolate, "response"))->ToObject()->SetPrototype(Local<Object>::New(args.GetIsolate(), resTemplate)->GetPrototype());
+
+ // also change app.listen?
+
+ // change prototypes back?
+
+ args.GetReturnValue().Set(express->NewInstance());
+ }
+ }
+
+ static void getResponsePrototype(const FunctionCallbackInfo<Value> &args) {
+ args.GetReturnValue().Set(Local<Object>::New(args.GetIsolate(), resTemplate)->GetPrototype());
+ }
+
+ static void getRequestPrototype(const FunctionCallbackInfo<Value> &args) {
+ args.GetReturnValue().Set(Local<Object>::New(args.GetIsolate(), reqTemplate)->GetPrototype());
+ }
+
+ static Local<Function> getHttpServer(Isolate *isolate) {
+ Local<FunctionTemplate> httpServer = FunctionTemplate::New(isolate, HttpServer::createServer);
+ httpServer->InstanceTemplate()->SetInternalFieldCount(1);
+
+ httpServer->Set(String::NewFromUtf8(isolate, "createServer"), FunctionTemplate::New(isolate, HttpServer::createServer));
+ httpServer->Set(String::NewFromUtf8(isolate, "getExpressApp"), FunctionTemplate::New(isolate, HttpServer::getExpressApp));
+ httpServer->Set(String::NewFromUtf8(isolate, "getResponsePrototype"), FunctionTemplate::New(isolate, HttpServer::getResponsePrototype));
+ httpServer->Set(String::NewFromUtf8(isolate, "getRequestPrototype"), FunctionTemplate::New(isolate, HttpServer::getRequestPrototype));
+ httpServer->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "listen"), FunctionTemplate::New(isolate, HttpServer::listen));
+ httpServer->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "on"), FunctionTemplate::New(isolate, HttpServer::on));
+
+ reqTemplate.Reset(isolate, Request::getTemplateObject(isolate));
+ resTemplate.Reset(isolate, Response::getTemplateObject(isolate));
+
+ Local<Function> httpServerLocal = httpServer->GetFunction();
+ httpPersistent.Reset(isolate, httpServerLocal);
+ return httpServerLocal;
+ }
+};
diff --git a/node_modules/uws/src/uWS.h b/node_modules/uws/src/uWS.h
new file mode 100644
index 0000000..40a0e40
--- /dev/null
+++ b/node_modules/uws/src/uWS.h
@@ -0,0 +1,6 @@
+#ifndef UWS_UWS_H
+#define UWS_UWS_H
+
+#include "Hub.h"
+
+#endif // UWS_UWS_H