mirror of
https://github.com/owenlejeune/AirHockey.git
synced 2025-12-31 20:01:25 -05:00
first commit
This commit is contained in:
188
node_modules/uws/src/Asio.h
generated
vendored
Normal file
188
node_modules/uws/src/Asio.h
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
#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;
|
||||
|
||||
namespace uS {
|
||||
|
||||
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
|
||||
15
node_modules/uws/src/Backend.h
generated
vendored
Normal file
15
node_modules/uws/src/Backend.h
generated
vendored
Normal file
@@ -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
|
||||
65
node_modules/uws/src/Epoll.cpp
generated
vendored
Normal file
65
node_modules/uws/src/Epoll.cpp
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Backend.h"
|
||||
|
||||
#ifdef USE_EPOLL
|
||||
|
||||
namespace uS {
|
||||
|
||||
// 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
|
||||
261
node_modules/uws/src/Epoll.h
generated
vendored
Normal file
261
node_modules/uws/src/Epoll.h
generated
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
#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;
|
||||
|
||||
namespace uS {
|
||||
|
||||
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
|
||||
131
node_modules/uws/src/Extensions.cpp
generated
vendored
Normal file
131
node_modules/uws/src/Extensions.cpp
generated
vendored
Normal file
@@ -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>;
|
||||
|
||||
}
|
||||
29
node_modules/uws/src/Extensions.h
generated
vendored
Normal file
29
node_modules/uws/src/Extensions.h
generated
vendored
Normal file
@@ -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
|
||||
263
node_modules/uws/src/Group.cpp
generated
vendored
Normal file
263
node_modules/uws/src/Group.cpp
generated
vendored
Normal file
@@ -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(uS::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 uS::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 uS::Timer(hub->getLoop());
|
||||
httpTimer->setData(this);
|
||||
httpTimer->start([](uS::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>;
|
||||
|
||||
}
|
||||
144
node_modules/uws/src/Group.h
generated
vendored
Normal file
144
node_modules/uws/src/Group.h
generated
vendored
Normal file
@@ -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;
|
||||
uS::Timer *timer = nullptr, *httpTimer = nullptr;
|
||||
std::string userPingMessage;
|
||||
std::stack<uS::Poll *> iterators;
|
||||
|
||||
// todo: cannot be named user, collides with parent!
|
||||
void *userData = nullptr;
|
||||
static void timerCallback(uS::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) {
|
||||
uS::Poll *iterator = webSocketHead;
|
||||
iterators.push(iterator);
|
||||
while (iterator) {
|
||||
uS::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) {
|
||||
uS::Poll *iterator = httpSocketHead;
|
||||
iterators.push(iterator);
|
||||
while (iterator) {
|
||||
uS::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
|
||||
310
node_modules/uws/src/HTTPSocket.cpp
generated
vendored
Normal file
310
node_modules/uws/src/HTTPSocket.cpp
generated
vendored
Normal file
@@ -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>;
|
||||
|
||||
}
|
||||
285
node_modules/uws/src/HTTPSocket.h
generated
vendored
Normal file
285
node_modules/uws/src/HTTPSocket.h
generated
vendored
Normal file
@@ -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
|
||||
213
node_modules/uws/src/Hub.cpp
generated
vendored
Normal file
213
node_modules/uws/src/Hub.cpp
generated
vendored
Normal file
@@ -0,0 +1,213 @@
|
||||
#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);
|
||||
}
|
||||
|
||||
bool parseURI(std::string &uri, bool &secure, std::string &hostname, int &port, std::string &path) {
|
||||
port = 80;
|
||||
secure = false;
|
||||
size_t offset = 5;
|
||||
if (!uri.compare(0, 6, "wss://")) {
|
||||
port = 443;
|
||||
secure = true;
|
||||
offset = 6;
|
||||
} else if (uri.compare(0, 5, "ws://")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (offset == uri.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (uri[offset] == '[') {
|
||||
if (++offset == uri.length()) {
|
||||
return false;
|
||||
}
|
||||
size_t endBracket = uri.find(']', offset);
|
||||
if (endBracket == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
hostname = uri.substr(offset, endBracket - offset);
|
||||
offset = endBracket + 1;
|
||||
} else {
|
||||
hostname = uri.substr(offset, uri.find_first_of(":/", offset) - offset);
|
||||
offset += hostname.length();
|
||||
}
|
||||
|
||||
if (offset == uri.length()) {
|
||||
path.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uri[offset] == ':') {
|
||||
offset++;
|
||||
std::string portStr = uri.substr(offset, uri.find('/', offset) - offset);
|
||||
if (portStr.length()) {
|
||||
try {
|
||||
port = stoi(portStr);
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
offset += portStr.length();
|
||||
}
|
||||
|
||||
if (offset == uri.length()) {
|
||||
path.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (uri[offset] == '/') {
|
||||
path = uri.substr(++offset);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
int port;
|
||||
bool secure;
|
||||
std::string hostname, path;
|
||||
|
||||
if (!parseURI(uri, secure, hostname, port, path)) {
|
||||
eh->errorHandler(user);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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, {});
|
||||
}
|
||||
|
||||
}
|
||||
98
node_modules/uws/src/Hub.h
generated
vendored
Normal file
98
node_modules/uws/src/Hub.h
generated
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
#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<CLIENT>::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
|
||||
179
node_modules/uws/src/Libuv.h
generated
vendored
Normal file
179
node_modules/uws/src/Libuv.h
generated
vendored
Normal file
@@ -0,0 +1,179 @@
|
||||
#ifndef LIBUV_H
|
||||
#define LIBUV_H
|
||||
|
||||
#include <uv.h>
|
||||
static_assert (UV_VERSION_MINOR >= 3, "µWebSockets requires libuv >=1.3.0");
|
||||
|
||||
namespace uS {
|
||||
|
||||
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
|
||||
78
node_modules/uws/src/Networking.cpp
generated
vendored
Normal file
78
node_modules/uws/src/Networking.cpp
generated
vendored
Normal file
@@ -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
|
||||
|
||||
}
|
||||
259
node_modules/uws/src/Networking.h
generated
vendored
Normal file
259
node_modules/uws/src/Networking.h
generated
vendored
Normal file
@@ -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
|
||||
83
node_modules/uws/src/Node.cpp
generated
vendored
Normal file
83
node_modules/uws/src/Node.cpp
generated
vendored
Normal file
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
198
node_modules/uws/src/Node.h
generated
vendored
Normal file
198
node_modules/uws/src/Node.h
generated
vendored
Normal file
@@ -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
|
||||
28
node_modules/uws/src/Socket.cpp
generated
vendored
Normal file
28
node_modules/uws/src/Socket.cpp
generated
vendored
Normal file
@@ -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"};
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
507
node_modules/uws/src/Socket.h
generated
vendored
Normal file
507
node_modules/uws/src/Socket.h
generated
vendored
Normal file
@@ -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
|
||||
405
node_modules/uws/src/WebSocket.cpp
generated
vendored
Normal file
405
node_modules/uws/src/WebSocket.cpp
generated
vendored
Normal file
@@ -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>;
|
||||
|
||||
}
|
||||
89
node_modules/uws/src/WebSocket.h
generated
vendored
Normal file
89
node_modules/uws/src/WebSocket.h
generated
vendored
Normal file
@@ -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
|
||||
377
node_modules/uws/src/WebSocketProtocol.h
generated
vendored
Normal file
377
node_modules/uws/src/WebSocketProtocol.h
generated
vendored
Normal file
@@ -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
|
||||
24
node_modules/uws/src/addon.cpp
generated
vendored
Normal file
24
node_modules/uws/src/addon.cpp
generated
vendored
Normal file
@@ -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)
|
||||
464
node_modules/uws/src/addon.h
generated
vendored
Normal file
464
node_modules/uws/src/addon.h
generated
vendored
Normal file
@@ -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);
|
||||
}
|
||||
};
|
||||
357
node_modules/uws/src/http.h
generated
vendored
Normal file
357
node_modules/uws/src/http.h
generated
vendored
Normal file
@@ -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;
|
||||
}
|
||||
};
|
||||
6
node_modules/uws/src/uWS.h
generated
vendored
Normal file
6
node_modules/uws/src/uWS.h
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef UWS_UWS_H
|
||||
#define UWS_UWS_H
|
||||
|
||||
#include "Hub.h"
|
||||
|
||||
#endif // UWS_UWS_H
|
||||
Reference in New Issue
Block a user