Add netease_voice projects

This commit is contained in:
HuangXin 2018-07-16 16:05:17 +08:00
parent 73c1fc0013
commit 0bd780b103
30 changed files with 2717 additions and 30 deletions

Binary file not shown.

BIN
dl/libjansson-2.11.tar.gz Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
include $(TOPDIR)/rules.mk include $(TOPDIR)/rules.mk
PKG_NAME:=jansson PKG_NAME:=libjansson
PKG_VERSION:=2.11 PKG_VERSION:=2.11
PKG_SOURCE_PROTO:=git PKG_SOURCE_PROTO:=git
@ -12,41 +12,39 @@ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
CMAKE_INSTALL:=1 CMAKE_INSTALL:=1
PKG_RELEASE=$(PKG_SOURCE_VERSION) PKG_RELEASE=$(PKG_SOURCE_VERSION)
CMAKE_OPTIONS += -DJANSSON_BUILD_SHARED_LIBS=1 CMAKE_OPTIONS += -DJANSSON_BUILD_SHARED_LIBS=1
PKG_BUILD_DIR:=$(COMPILE_DIR)/$(PKG_SOURCE_SUBDIR)
include $(BUILD_DIR)/package.mk include $(BUILD_DIR)/package.mk
include $(BUILD_DIR)/cmake.mk include $(BUILD_DIR)/cmake.mk
define Package/libjansson define Package/$(PKG_NAME)
SECTION:=libs SECTION:=$(PKG_NAME)
CATEGORY:=Libraries CATEGORY:=Libraries
DEPENDS:=+libc DEPENDS:=+libc
TITLE:=A small library for encoding, decoding and manipulating JSON data TITLE:=Libjansson is Ultralightweight JSON parser in ANSI C
endef endef
define Package/libjansson/description define Package/$(PKG_NAME)/description
Jansson is a C library for encoding, decoding and manipulating JSON data. It features: Jansson is a C library for encoding, decoding and manipulating JSON data. It features:
Simple and intuitive API and data model Simple and intuitive API and data model
Comprehensive documentation Comprehensive documentation
No dependencies on other libraries No dependencies on other libraries
Full Unicode support (UTF-8) Full Unicode support (UTF-8)
Extensive test suite Extensive test suite
endef
define Package/libjansson/install
$(INSTALL_DIR) $(1)/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/lib* $(1)/lib/
endef endef
define Build/InstallDev define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/include/
$(CP) $(PKG_INSTALL_DIR)/usr/include/*.h $(1)/usr/include/
$(INSTALL_DIR) $(1)/lib $(INSTALL_DIR) $(1)/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/lib* $(1)/lib/ $(CP) $(PKG_INSTALL_DIR)/usr/lib/lib* $(1)/lib/
endef endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/lib
$(CP) $(PKG_INSTALL_DIR)/usr/lib/lib* $(1)/lib/
endef
$(eval $(call BuildPackage,libjansson)) $(eval $(call BuildPackage,$(PKG_NAME)))

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,45 @@
include $(TOPDIR)/rules.mk
# Name and release number of this package
PKG_NAME:=libuws
PKG_VERSION:=1.0.0
PKG_BUILD_DIR:=$(COMPILE_DIR)/$(PKG_NAME)
include $(BUILD_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=libs
CATEGORY:=Libraries
TITLE:=uws lib
DEPENDS:=+libstdcpp
endef
define Package/$(PKG_NAME)/description
This is a uws dynamic library by rokid added by wk!
endef
define Build/Prepare
mkdir -p $(PKG_BUILD_DIR)
$(CP) -d ./lib/* $(PKG_BUILD_DIR)/
endef
define Build/Configure
endef
define Build/Compile
endef
define Build/InstallDev
$(INSTALL_DIR) $(1)/usr/lib
$(INSTALL_DIR) $(1)/usr/include/uWS
$(INSTALL_DATA) $(PKG_BUILD_DIR)/*.so $(1)/usr/lib
$(INSTALL_DATA) $(PKG_BUILD_DIR)/include/*.h $(1)/usr/include/uWS
endef
define Package/$(PKG_NAME)/install
$(INSTALL_DIR) $(1)/usr/lib
$(CP) -d $(PKG_BUILD_DIR)/*.so $(1)/usr/lib/
endef
$(eval $(call BuildPackage,$(PKG_NAME)))

View File

@ -0,0 +1,184 @@
#ifndef ASIO_H
#define ASIO_H
#include <boost/asio.hpp>
typedef boost::asio::ip::tcp::socket::native_type uv_os_sock_t;
static const int UV_READABLE = 1;
static const int UV_WRITABLE = 2;
struct Loop : boost::asio::io_service {
static Loop *createLoop(bool defaultLoop = true) {
return new Loop;
}
void destroy() {
delete this;
}
void run() {
boost::asio::io_service::run();
}
};
struct Timer {
boost::asio::deadline_timer asio_timer;
void *data;
Timer(Loop *loop) : asio_timer(*loop) {
}
void start(void (*cb)(Timer *), int first, int repeat) {
asio_timer.expires_from_now(boost::posix_time::milliseconds(first));
asio_timer.async_wait([this, cb, repeat](const boost::system::error_code &ec) {
if (ec != boost::asio::error::operation_aborted) {
if (repeat) {
start(cb, repeat, repeat);
}
cb(this);
}
});
}
void setData(void *data) {
this->data = data;
}
void *getData() {
return data;
}
// bug: cancel does not cancel expired timers!
// it has to guarantee that the timer is not called after
// stop is called! ffs boost!
void stop() {
asio_timer.cancel();
}
void close() {
asio_timer.get_io_service().post([this]() {
delete this;
});
}
};
struct Async {
Loop *loop;
void (*cb)(Async *);
void *data;
boost::asio::io_service::work asio_work;
Async(Loop *loop) : loop(loop), asio_work(*loop) {
}
void start(void (*cb)(Async *)) {
this->cb = cb;
}
void send() {
loop->post([this]() {
cb(this);
});
}
void close() {
loop->post([this]() {
delete this;
});
}
void setData(void *data) {
this->data = data;
}
void *getData() {
return data;
}
};
struct Poll {
boost::asio::posix::stream_descriptor *socket;
void (*cb)(Poll *p, int status, int events);
Poll(Loop *loop, uv_os_sock_t fd) {
socket = new boost::asio::posix::stream_descriptor(*loop, fd);
socket->non_blocking(true);
}
bool isClosed() {
return !socket;
}
boost::asio::ip::tcp::socket::native_type getFd() {
return socket ? socket->native_handle() : -1;
}
void setCb(void (*cb)(Poll *p, int status, int events)) {
this->cb = cb;
}
void (*getCb())(Poll *, int, int) {
return cb;
}
void reInit(Loop *loop, uv_os_sock_t fd) {
delete socket;
socket = new boost::asio::posix::stream_descriptor(*loop, fd);
socket->non_blocking(true);
}
void start(Loop *, Poll *self, int events) {
if (events & UV_READABLE) {
socket->async_read_some(boost::asio::null_buffers(), [self](boost::system::error_code ec, std::size_t) {
if (ec != boost::asio::error::operation_aborted) {
self->start(nullptr, self, UV_READABLE);
self->cb(self, ec ? -1 : 0, UV_READABLE);
}
});
}
if (events & UV_WRITABLE) {
socket->async_write_some(boost::asio::null_buffers(), [self](boost::system::error_code ec, std::size_t) {
if (ec != boost::asio::error::operation_aborted) {
self->start(nullptr, self, UV_WRITABLE);
self->cb(self, ec ? -1 : 0, UV_WRITABLE);
}
});
}
}
void change(Loop *, Poll *self, int events) {
socket->cancel();
start(nullptr, self, events);
}
bool fastTransfer(Loop *loop, Loop *newLoop, int events) {
return false;
}
// todo: asio is thread safe, use it!
bool threadSafeChange(Loop *loop, Poll *self, int events) {
return false;
}
void stop(Loop *) {
socket->cancel();
}
// this is not correct, but it works for now
// think about transfer - should allow one to not delete
// but in this case it doesn't matter at all
void close(Loop *loop, void (*cb)(Poll *)) {
socket->release();
socket->get_io_service().post([cb, this]() {
cb(this);
});
delete socket;
socket = nullptr;
}
};
#endif // ASIO_H

View 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

View File

@ -0,0 +1,257 @@
#ifndef EPOLL_H
#define EPOLL_H
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <fcntl.h>
#include <chrono>
#include <algorithm>
#include <vector>
#include <mutex>
typedef int uv_os_sock_t;
static const int UV_READABLE = EPOLLIN;
static const int UV_WRITABLE = EPOLLOUT;
struct Poll;
struct Timer;
extern std::recursive_mutex cbMutex;
extern void (*callbacks[16])(Poll *, int, int);
extern int cbHead;
struct Timepoint {
void (*cb)(Timer *);
Timer *timer;
std::chrono::system_clock::time_point timepoint;
int nextDelay;
};
struct Loop {
int epfd;
int numPolls = 0;
bool cancelledLastTimer;
int delay = -1;
epoll_event readyEvents[1024];
std::chrono::system_clock::time_point timepoint;
std::vector<Timepoint> timers;
std::vector<std::pair<Poll *, void (*)(Poll *)>> closing;
void (*preCb)(void *) = nullptr;
void (*postCb)(void *) = nullptr;
void *preCbData, *postCbData;
Loop(bool defaultLoop) {
epfd = epoll_create1(EPOLL_CLOEXEC);
timepoint = std::chrono::system_clock::now();
}
static Loop *createLoop(bool defaultLoop = true) {
return new Loop(defaultLoop);
}
void destroy() {
::close(epfd);
delete this;
}
void run();
int getEpollFd() {
return epfd;
}
};
struct Timer {
Loop *loop;
void *data;
Timer(Loop *loop) {
this->loop = loop;
}
void start(void (*cb)(Timer *), int timeout, int repeat) {
loop->timepoint = std::chrono::system_clock::now();
std::chrono::system_clock::time_point timepoint = loop->timepoint + std::chrono::milliseconds(timeout);
Timepoint t = {cb, this, timepoint, repeat};
loop->timers.insert(
std::upper_bound(loop->timers.begin(), loop->timers.end(), t, [](const Timepoint &a, const Timepoint &b) {
return a.timepoint < b.timepoint;
}),
t
);
loop->delay = -1;
if (loop->timers.size()) {
loop->delay = std::max<int>(std::chrono::duration_cast<std::chrono::milliseconds>(loop->timers[0].timepoint - loop->timepoint).count(), 0);
}
}
void setData(void *data) {
this->data = data;
}
void *getData() {
return data;
}
// always called before destructor
void stop() {
auto pos = loop->timers.begin();
for (Timepoint &t : loop->timers) {
if (t.timer == this) {
loop->timers.erase(pos);
break;
}
pos++;
}
loop->cancelledLastTimer = true;
loop->delay = -1;
if (loop->timers.size()) {
loop->delay = std::max<int>(std::chrono::duration_cast<std::chrono::milliseconds>(loop->timers[0].timepoint - loop->timepoint).count(), 0);
}
}
void close() {
delete this;
}
};
// 4 bytes
struct Poll {
protected:
struct {
int fd : 28;
unsigned int cbIndex : 4;
} state = {-1, 0};
Poll(Loop *loop, uv_os_sock_t fd) {
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
state.fd = fd;
loop->numPolls++;
}
// todo: pre-set all of callbacks up front and remove mutex
void setCb(void (*cb)(Poll *p, int status, int events)) {
cbMutex.lock();
state.cbIndex = cbHead;
for (int i = 0; i < cbHead; i++) {
if (callbacks[i] == cb) {
state.cbIndex = i;
break;
}
}
if (state.cbIndex == cbHead) {
callbacks[cbHead++] = cb;
}
cbMutex.unlock();
}
void (*getCb())(Poll *, int, int) {
return callbacks[state.cbIndex];
}
void reInit(Loop *loop, uv_os_sock_t fd) {
state.fd = fd;
loop->numPolls++;
}
void start(Loop *loop, Poll *self, int events) {
epoll_event event;
event.events = events;
event.data.ptr = self;
epoll_ctl(loop->epfd, EPOLL_CTL_ADD, state.fd, &event);
}
void change(Loop *loop, Poll *self, int events) {
epoll_event event;
event.events = events;
event.data.ptr = self;
epoll_ctl(loop->epfd, EPOLL_CTL_MOD, state.fd, &event);
}
void stop(Loop *loop) {
epoll_event event;
epoll_ctl(loop->epfd, EPOLL_CTL_DEL, state.fd, &event);
}
bool fastTransfer(Loop *loop, Loop *newLoop, int events) {
stop(loop);
start(newLoop, this, events);
loop->numPolls--;
// needs to lock the newLoop's numPolls!
newLoop->numPolls++;
return true;
}
bool threadSafeChange(Loop *loop, Poll *self, int events) {
change(loop, self, events);
return true;
}
void close(Loop *loop, void (*cb)(Poll *)) {
state.fd = -1;
loop->closing.push_back({this, cb});
}
public:
bool isClosed() {
return state.fd == -1;
}
uv_os_sock_t getFd() {
return state.fd;
}
friend struct Loop;
};
// this should be put in the Loop as a general "post" function always available
struct Async : Poll {
void (*cb)(Async *);
Loop *loop;
void *data;
Async(Loop *loop) : Poll(loop, ::eventfd(0, EFD_CLOEXEC)) {
this->loop = loop;
}
void start(void (*cb)(Async *)) {
this->cb = cb;
Poll::setCb([](Poll *p, int, int) {
uint64_t val;
if (::read(((Async *) p)->state.fd, &val, 8) == 8) {
((Async *) p)->cb((Async *) p);
}
});
Poll::start(loop, this, UV_READABLE);
}
void send() {
uint64_t one = 1;
if (::write(state.fd, &one, 8) != 8) {
return;
}
}
void close() {
Poll::stop(loop);
::close(state.fd);
Poll::close(loop, [](Poll *p) {
delete p;
});
}
void setData(void *data) {
this->data = data;
}
void *getData() {
return data;
}
};
#endif // EPOLL_H

View 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

View File

@ -0,0 +1,148 @@
#ifndef GROUP_UWS_H
#define GROUP_UWS_H
#include "WebSocket.h"
#include "HTTPSocket.h"
#include "Extensions.h"
#include <functional>
#include <stack>
namespace uWS {
enum ListenOptions {
TRANSFERS
};
struct Hub;
template <bool isServer>
struct WIN32_EXPORT Group : private uS::NodeData {
protected:
friend struct Hub;
friend struct WebSocket<isServer>;
friend struct HttpSocket<false>;
friend struct HttpSocket<true>;
std::function<void(WebSocket<isServer> *, HttpRequest)> connectionHandler;
std::function<void(WebSocket<isServer> *)> transferHandler;
std::function<void(WebSocket<isServer> *, char *message, size_t length, OpCode opCode)> messageHandler;
std::function<void(WebSocket<isServer> *, int code, char *message, size_t length)> disconnectionHandler;
std::function<void(WebSocket<isServer> *, char *, size_t)> pingHandler;
std::function<void(WebSocket<isServer> *, char *, size_t)> pongHandler;
std::function<void(HttpSocket<isServer> *)> httpConnectionHandler;
std::function<void(HttpResponse *, HttpRequest, char *, size_t, size_t)> httpRequestHandler;
std::function<void(HttpResponse *, char *, size_t, size_t)> httpDataHandler;
std::function<void(HttpResponse *)> httpCancelledRequestHandler;
std::function<void(HttpSocket<isServer> *)> httpDisconnectionHandler;
std::function<void(HttpSocket<isServer> *, HttpRequest)> httpUpgradeHandler;
using errorType = typename std::conditional<isServer, int, void *>::type;
std::function<void(errorType)> errorHandler;
unsigned int maxPayload;
Hub *hub;
int extensionOptions;
Timer *timer = nullptr, *httpTimer = nullptr;
std::string userPingMessage;
std::stack<Poll *> iterators;
// todo: cannot be named user, collides with parent!
void *userData = nullptr;
static void timerCallback(Timer *timer);
static void dummyCallback(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 = "");
// workaround for loop not quit when server no response
// if has timer, epoll_wait will awake periodic
void setTimer(int intervalMs);
// same as listen(TRANSFERS), backwards compatible API for now
void addAsync() {
if (!async) {
NodeData::addAsync();
}
}
void listen(ListenOptions listenOptions) {
if (listenOptions == TRANSFERS && !async) {
addAsync();
}
}
template <class F>
void forEach(const F &cb) {
Poll *iterator = webSocketHead;
iterators.push(iterator);
while (iterator) {
Poll *lastIterator = iterator;
cb((WebSocket<isServer> *) iterator);
iterator = iterators.top();
if (lastIterator == iterator) {
iterator = ((uS::Socket *) iterator)->next;
iterators.top() = iterator;
}
}
iterators.pop();
}
// duplicated code for now!
template <class F>
void forEachHttpSocket(const F &cb) {
Poll *iterator = httpSocketHead;
iterators.push(iterator);
while (iterator) {
Poll *lastIterator = iterator;
cb((HttpSocket<isServer> *) iterator);
iterator = iterators.top();
if (lastIterator == iterator) {
iterator = ((uS::Socket *) iterator)->next;
iterators.top() = iterator;
}
}
iterators.pop();
}
static Group<isServer> *from(uS::Socket *s) {
return static_cast<Group<isServer> *>(s->getNodeData());
}
};
}
#endif // GROUP_UWS_H

View 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 : 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

View File

@ -0,0 +1,97 @@
#ifndef HUB_UWS_H
#define HUB_UWS_H
#include "Group.h"
#include "Node.h"
#include <string>
#include <zlib.h>
#include <mutex>
#include <map>
namespace uWS {
struct WIN32_EXPORT Hub : private uS::Node, public Group<SERVER>, public Group<CLIENT> {
protected:
struct ConnectionData {
std::string path;
void *user;
Group<CLIENT> *group;
};
z_stream inflationStream = {};
char *inflationBuffer;
char *inflate(char *data, size_t &length, size_t maxPayload);
std::string dynamicInflationBuffer;
static const int LARGE_BUFFER_SIZE = 300 * 1024;
static void onServerAccept(uS::Socket *s);
static void onClientConnection(uS::Socket *s, bool error);
public:
template <bool isServer>
Group<isServer> *createGroup(int extensionOptions = 0, unsigned int maxPayload = 16777216) {
return new Group<isServer>(extensionOptions, maxPayload, this, nodeData);
}
template <bool isServer>
Group<isServer> &getDefaultGroup() {
return static_cast<Group<isServer> &>(*this);
}
bool listen(int port, uS::TLS::Context sslContext = nullptr, int options = 0, Group<SERVER> *eh = nullptr);
bool listen(const char *host, int port, uS::TLS::Context sslContext = nullptr, int options = 0, Group<SERVER> *eh = nullptr);
void connect(std::string uri, void *user = nullptr, std::map<std::string, std::string> extraHeaders = {}, int timeoutMs = 5000, Group<CLIENT> *eh = nullptr);
void upgrade(uv_os_sock_t fd, const char *secKey, SSL *ssl, const char *extensions, size_t extensionsLength, const char *subprotocol, size_t subprotocolLength, Group<SERVER> *serverGroup = nullptr);
Hub(int extensionOptions = 0, bool useDefaultLoop = false, unsigned int maxPayload = 16777216) : uS::Node(LARGE_BUFFER_SIZE, WebSocketProtocol<SERVER, WebSocket<SERVER>>::CONSUME_PRE_PADDING, WebSocketProtocol<SERVER, WebSocket<SERVER>>::CONSUME_POST_PADDING, useDefaultLoop),
Group<SERVER>(extensionOptions, maxPayload, this, nodeData), Group<CLIENT>(0, maxPayload, this, nodeData) {
inflateInit2(&inflationStream, -15);
inflationBuffer = new char[LARGE_BUFFER_SIZE];
#ifdef UWS_THREADSAFE
getLoop()->preCbData = nodeData;
getLoop()->preCb = [](void *nodeData) {
static_cast<uS::NodeData *>(nodeData)->asyncMutex->lock();
};
getLoop()->postCbData = nodeData;
getLoop()->postCb = [](void *nodeData) {
static_cast<uS::NodeData *>(nodeData)->asyncMutex->unlock();
};
#endif
}
~Hub() {
inflateEnd(&inflationStream);
delete [] inflationBuffer;
}
using uS::Node::run;
using uS::Node::getLoop;
using Group<SERVER>::onConnection;
using Group<CLIENT>::onConnection;
using Group<SERVER>::onTransfer;
using Group<SERVER>::onMessage;
using Group<CLIENT>::onMessage;
using Group<SERVER>::onDisconnection;
using Group<CLIENT>::onDisconnection;
using Group<SERVER>::onPing;
using Group<CLIENT>::onPing;
using Group<SERVER>::onPong;
using Group<CLIENT>::onPong;
using Group<SERVER>::onError;
using Group<CLIENT>::onError;
using Group<SERVER>::onHttpRequest;
using Group<SERVER>::onHttpData;
using Group<SERVER>::onHttpConnection;
using Group<SERVER>::onHttpDisconnection;
using Group<SERVER>::onHttpUpgrade;
using Group<SERVER>::onCancelledHttpRequest;
friend struct WebSocket<SERVER>;
friend struct WebSocket<CLIENT>;
};
}
#endif // HUB_UWS_H

View File

@ -0,0 +1,175 @@
#ifndef LIBUV_H
#define LIBUV_H
#include <uv.h>
static_assert (UV_VERSION_MINOR >= 3, "µWebSockets requires libuv >=1.3.0");
struct Loop : uv_loop_t {
static Loop *createLoop(bool defaultLoop = true) {
if (defaultLoop) {
return (Loop *) uv_default_loop();
} else {
return (Loop *) uv_loop_new();
}
}
void destroy() {
if (this != uv_default_loop()) {
uv_loop_delete(this);
}
}
void run() {
uv_run(this, UV_RUN_DEFAULT);
}
};
struct Async {
uv_async_t uv_async;
Async(Loop *loop) {
uv_async.loop = loop;
}
void start(void (*cb)(Async *)) {
uv_async_init(uv_async.loop, &uv_async, (uv_async_cb) cb);
}
void send() {
uv_async_send(&uv_async);
}
void close() {
uv_close((uv_handle_t *) &uv_async, [](uv_handle_t *a) {
delete (Async *) a;
});
}
void setData(void *data) {
uv_async.data = data;
}
void *getData() {
return uv_async.data;
}
};
struct Timer {
uv_timer_t uv_timer;
Timer(Loop *loop) {
uv_timer_init(loop, &uv_timer);
}
void start(void (*cb)(Timer *), int first, int repeat) {
uv_timer_start(&uv_timer, (uv_timer_cb) cb, first, repeat);
}
void setData(void *data) {
uv_timer.data = data;
}
void *getData() {
return uv_timer.data;
}
void stop() {
uv_timer_stop(&uv_timer);
}
void close() {
uv_close((uv_handle_t *) &uv_timer, [](uv_handle_t *t) {
delete (Timer *) t;
});
}
private:
~Timer() {
}
};
struct Poll {
uv_poll_t *uv_poll;
void (*cb)(Poll *p, int status, int events);
Poll(Loop *loop, uv_os_sock_t fd) {
uv_poll = new uv_poll_t;
uv_poll_init_socket(loop, uv_poll, fd);
}
Poll(Poll &&other) {
uv_poll = other.uv_poll;
cb = other.cb;
other.uv_poll = nullptr;
}
Poll(const Poll &other) = delete;
~Poll() {
delete uv_poll;
}
bool isClosed() {
return uv_is_closing((uv_handle_t *) uv_poll);
}
uv_os_sock_t getFd() {
#ifdef _WIN32
uv_os_sock_t fd;
uv_fileno((uv_handle_t *) uv_poll, (uv_os_fd_t *) &fd);
return fd;
#else
return uv_poll->io_watcher.fd;
#endif
}
void setCb(void (*cb)(Poll *p, int status, int events)) {
this->cb = cb;
}
void (*getCb())(Poll *, int, int) {
return cb;
}
void reInit(Loop *loop, uv_os_sock_t fd) {
delete uv_poll;
uv_poll = new uv_poll_t;
uv_poll_init_socket(loop, uv_poll, fd);
}
void start(Loop *, Poll *self, int events) {
uv_poll->data = self;
uv_poll_start(uv_poll, events, [](uv_poll_t *p, int status, int events) {
Poll *self = (Poll *) p->data;
self->cb(self, status, events);
});
}
void change(Loop *, Poll *self, int events) {
start(nullptr, self, events);
}
void stop(Loop *loop) {
uv_poll_stop(uv_poll);
}
bool fastTransfer(Loop *loop, Loop *newLoop, int events) {
return false;
}
bool threadSafeChange(Loop *, Poll *self, int events) {
return false;
}
void close(Loop *loop, void (*cb)(Poll *)) {
this->cb = (void(*)(Poll *, int, int)) cb;
uv_close((uv_handle_t *) uv_poll, [](uv_handle_t *p) {
Poll *poll = (Poll *) p->data;
void (*cb)(Poll *) = (void(*)(Poll *)) poll->cb;
cb(poll);
});
}
};
#endif // LIBUV_H

View 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

View File

@ -0,0 +1,202 @@
#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;
char portstr[8];
snprintf(portstr, sizeof(portstr), "%d", port);
if (getaddrinfo(hostname, portstr, &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;
char portstr[8];
snprintf(portstr, sizeof(portstr), "%d", port);
if (getaddrinfo(host, portstr, &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

View 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

View 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

View 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

View File

@ -0,0 +1,6 @@
#ifndef UWS_UWS_H
#define UWS_UWS_H
#include "Hub.h"
#endif // UWS_UWS_H

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -21,7 +21,7 @@ define Package/$(PKG_NAME)
CATEGORY:=Netease CATEGORY:=Netease
TITLE:=Netease voice main program TITLE:=Netease voice main program
MAINTAINER:=Wang zijiao <hzwangzijiao@corp.netease.com> MAINTAINER:=Wang zijiao <hzwangzijiao@corp.netease.com>
DEPENDS:= +libjansson +libspeex +alsa-lib +libuws +libjson-c +libyunxin +libcutils $(MAKE_COMMON_DEPEND) DEPENDS:= +libduilite +libjansson +libspeex +alsa-lib +libuws +libjson-c +libyunxin +libcutils $(MAKE_COMMON_DEPEND)
ifeq ('$(CONFIG_XUNFEI_MSC_SDK)', 'y') ifeq ('$(CONFIG_XUNFEI_MSC_SDK)', 'y')
DEPENDS+=+libmsc DEPENDS+=+libmsc
@ -29,7 +29,7 @@ define Package/$(PKG_NAME)
DEPENDS+=+libmsc DEPENDS+=+libmsc
endif endif
ifeq ('$(CONFIG_XUNFEI_CAE_SDK)', 'y') ifeq ('$(CONFIG_XUNFEI_CAE_SDK)', 'y')
DEPENDS+=+libcae DEPENDS+=+libcae
endif endif

View File

@ -2329,7 +2329,7 @@ CONFIG_PACKAGE_bluez-libs=y
# CONFIG_PACKAGE_libbfd is not set # CONFIG_PACKAGE_libbfd is not set
# CONFIG_PACKAGE_libblkid is not set # CONFIG_PACKAGE_libblkid is not set
CONFIG_PACKAGE_libblobmsg-json=y CONFIG_PACKAGE_libblobmsg-json=y
# CONFIG_PACKAGE_libcae is not set CONFIG_PACKAGE_libcae=y
# CONFIG_PACKAGE_libcap is not set # CONFIG_PACKAGE_libcap is not set
# CONFIG_PACKAGE_libcares is not set # CONFIG_PACKAGE_libcares is not set
# CONFIG_PACKAGE_libcharset is not set # CONFIG_PACKAGE_libcharset is not set
@ -2387,7 +2387,7 @@ CONFIG_LIBCURL_PROXY=y
# CONFIG_PACKAGE_libdb47xx is not set # CONFIG_PACKAGE_libdb47xx is not set
# CONFIG_PACKAGE_libdevmapper is not set # CONFIG_PACKAGE_libdevmapper is not set
# CONFIG_PACKAGE_libdrm is not set # CONFIG_PACKAGE_libdrm is not set
# CONFIG_PACKAGE_libduilite is not set CONFIG_PACKAGE_libduilite=y
# CONFIG_PACKAGE_libelf1 is not set # CONFIG_PACKAGE_libelf1 is not set
CONFIG_PACKAGE_libdbus=y CONFIG_PACKAGE_libdbus=y
# CONFIG_PACKAGE_libepoxy is not set # CONFIG_PACKAGE_libepoxy is not set
@ -2427,7 +2427,7 @@ CONFIG_PACKAGE_libiconv-full=y
# CONFIG_PACKAGE_libintl-full is not set # CONFIG_PACKAGE_libintl-full is not set
# CONFIG_PACKAGE_libiw is not set # CONFIG_PACKAGE_libiw is not set
# CONFIG_PACKAGE_libiwinfo is not set # CONFIG_PACKAGE_libiwinfo is not set
# CONFIG_PACKAGE_libjansson is not set CONFIG_PACKAGE_libjansson=y
# CONFIG_PACKAGE_libjpeg is not set # CONFIG_PACKAGE_libjpeg is not set
CONFIG_PACKAGE_libjson-c=y CONFIG_PACKAGE_libjson-c=y
# CONFIG_PACKAGE_libkmod is not set # CONFIG_PACKAGE_libkmod is not set
@ -2451,9 +2451,9 @@ CONFIG_PACKAGE_libjson-c=y
# CONFIG_PACKAGE_libmysqlclient-r is not set # CONFIG_PACKAGE_libmysqlclient-r is not set
CONFIG_PACKAGE_libncurses=y CONFIG_PACKAGE_libncurses=y
CONFIG_PACKAGE_libncursesw=y CONFIG_PACKAGE_libncursesw=y
# CONFIG_PACKAGE_libneteasedc is not set CONFIG_PACKAGE_libneteasedc=y
# CONFIG_PACKAGE_libneteasevoice is not set # CONFIG_PACKAGE_libneteasevoice is not set
# CONFIG_PACKAGE_libneteasevoicews is not set CONFIG_PACKAGE_libneteasevoicews=y
# CONFIG_PACKAGE_libnetfilter-conntrack is not set # CONFIG_PACKAGE_libnetfilter-conntrack is not set
# CONFIG_PACKAGE_libnetfilter-cthelper is not set # CONFIG_PACKAGE_libnetfilter-cthelper is not set
# CONFIG_PACKAGE_libnetfilter-cttimeout is not set # CONFIG_PACKAGE_libnetfilter-cttimeout is not set
@ -2532,6 +2532,7 @@ CONFIG_PACKAGE_libuclient=y
# CONFIG_PACKAGE_libustream-openssl is not set # CONFIG_PACKAGE_libustream-openssl is not set
CONFIG_PACKAGE_libuuid=y CONFIG_PACKAGE_libuuid=y
CONFIG_PACKAGE_libuv=y CONFIG_PACKAGE_libuv=y
CONFIG_PACKAGE_libuws=y
# CONFIG_PACKAGE_libv4l is not set # CONFIG_PACKAGE_libv4l is not set
# CONFIG_PACKAGE_libvorbis is not set # CONFIG_PACKAGE_libvorbis is not set
# CONFIG_PACKAGE_libvorbisidec is not set # CONFIG_PACKAGE_libvorbisidec is not set
@ -2543,7 +2544,7 @@ CONFIG_PACKAGE_libuv=y
# CONFIG_PACKAGE_libxkbcommon is not set # CONFIG_PACKAGE_libxkbcommon is not set
# CONFIG_PACKAGE_libxml2 is not set # CONFIG_PACKAGE_libxml2 is not set
# CONFIG_PACKAGE_libxslt is not set # CONFIG_PACKAGE_libxslt is not set
# CONFIG_PACKAGE_libyunxin is not set CONFIG_PACKAGE_libyunxin=y
# CONFIG_PACKAGE_libzmq-curve is not set # CONFIG_PACKAGE_libzmq-curve is not set
# CONFIG_PACKAGE_libzmq-nc is not set # CONFIG_PACKAGE_libzmq-nc is not set
# CONFIG_PACKAGE_linux-atm is not set # CONFIG_PACKAGE_linux-atm is not set
@ -2785,7 +2786,20 @@ CONFIG_RES_NORMAL_MODE=y
# CONFIG_PACKAGE_mcu_ota is not set # CONFIG_PACKAGE_mcu_ota is not set
CONFIG_PACKAGE_netease_control_center=y CONFIG_PACKAGE_netease_control_center=y
# CONFIG_PACKAGE_netease_test is not set # CONFIG_PACKAGE_netease_test is not set
# CONFIG_PACKAGE_netease_voice is not set CONFIG_PACKAGE_netease_voice=y
#
# SDK Select
#
CONFIG_NETEASE_MSC_SDK=y
# CONFIG_XUNFEI_MSC_SDK is not set
# CONFIG_NETEASE_CAE_SDK is not set
CONFIG_XUNFEI_CAE_SDK=y
CONFIG_NETEASE_DUILITE_SDK=y
CONFIG_NETEASE_TTS_SDK=y
# CONFIG_XUNFEI_TTS_SDK is not set
# CONFIG_USED_NONE is not set
CONFIG_USED_DC_SDK=y
# CONFIG_PACKAGE_ntes_record is not set # CONFIG_PACKAGE_ntes_record is not set
CONFIG_PACKAGE_ota=y CONFIG_PACKAGE_ota=y
CONFIG_PACKAGE_pv1res=y CONFIG_PACKAGE_pv1res=y