#include #include #include #include #include #include using namespace std; using namespace v8; uWS::Hub hub(0, true); uv_check_t check; Persistent 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::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) { 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 = Local::Cast(value); ArrayBuffer::Contents contents = arrayBufferView->Buffer()->GetContents(); length = contents.ByteLength(); data = (char *) contents.Data(); } else if (value->IsArrayBuffer()) { Local arrayBuffer = Local::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 connectionHandler, messageHandler, disconnectionHandler, pingHandler, pongHandler, errorHandler, httpRequestHandler, httpUpgradeHandler, httpCancelledRequestCallback; int size = 0; }; template void createGroup(const FunctionCallbackInfo &args) { uWS::Group *group = hub.createGroup(args[0]->IntegerValue(), args[1]->IntegerValue()); group->setUserData(new GroupData); args.GetReturnValue().Set(External::New(args.GetIsolate(), group)); } template void deleteGroup(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); delete (GroupData *) group->getUserData(); delete group; } template inline Local wrapSocket(uWS::WebSocket *webSocket, Isolate *isolate) { return External::New(isolate, webSocket); } template inline uWS::WebSocket *unwrapSocket(Local external) { return (uWS::WebSocket *) external->Value(); } inline Local wrapMessage(const char *message, size_t length, uWS::OpCode opCode, Isolate *isolate) { return opCode == uWS::OpCode::BINARY ? (Local) ArrayBuffer::New(isolate, (char *) message, length) : (Local) String::NewFromUtf8(isolate, message, String::kNormalString, length); } template inline Local getDataV8(uWS::WebSocket *webSocket, Isolate *isolate) { return webSocket->getUserData() ? Local::New(isolate, *(Persistent *) webSocket->getUserData()) : Local::Cast(Undefined(isolate)); } template void getUserData(const FunctionCallbackInfo &args) { args.GetReturnValue().Set(getDataV8(unwrapSocket(args[0].As()), args.GetIsolate())); } template void clearUserData(const FunctionCallbackInfo &args) { uWS::WebSocket *webSocket = unwrapSocket(args[0].As()); ((Persistent *) webSocket->getUserData())->Reset(); delete (Persistent *) webSocket->getUserData(); } template void setUserData(const FunctionCallbackInfo &args) { uWS::WebSocket *webSocket = unwrapSocket(args[0].As()); if (webSocket->getUserData()) { ((Persistent *) webSocket->getUserData())->Reset(args.GetIsolate(), args[1]); } else { webSocket->setUserData(new Persistent(args.GetIsolate(), args[1])); } } template void getAddress(const FunctionCallbackInfo &args) { typename uWS::WebSocket::Address address = unwrapSocket(args[0].As())->getAddress(); Local 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 jsCallback; Isolate *isolate; }; template void sendCallback(uWS::WebSocket *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::New(sc->isolate, sc->jsCallback), 0, nullptr); } sc->jsCallback.Reset(); delete sc; } template void send(const FunctionCallbackInfo &args) { uWS::OpCode opCode = (uWS::OpCode) args[2]->IntegerValue(); NativeString nativeString(args[1]); SendCallbackData *sc = nullptr; void (*callback)(uWS::WebSocket *, void *, bool, void *) = nullptr; if (args[3]->IsFunction()) { callback = sendCallback; sc = new SendCallbackData; sc->jsCallback.Reset(args.GetIsolate(), Local::Cast(args[3])); sc->isolate = args.GetIsolate(); } bool compress = args[4]->BooleanValue(); unwrapSocket(args[0].As())->send(nativeString.getData(), nativeString.getLength(), opCode, callback, sc, compress); } void connect(const FunctionCallbackInfo &args) { uWS::Group *clientGroup = (uWS::Group *) args[0].As()->Value(); NativeString uri(args[1]); hub.connect(std::string(uri.getData(), uri.getLength()), new Persistent(args.GetIsolate(), args[2]), {}, 5000, clientGroup); } struct Ticket { uv_os_sock_t fd; SSL *ssl; }; void upgrade(const FunctionCallbackInfo &args) { uWS::Group *serverGroup = (uWS::Group *) args[0].As()->Value(); Ticket *ticket = (Ticket *) args[1].As()->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 &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()->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 void onConnection(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *connectionCallback = &groupData->connectionHandler; connectionCallback->Reset(isolate, Local::Cast(args[1])); group->onConnection([isolate, connectionCallback, groupData](uWS::WebSocket *webSocket, uWS::HttpRequest req) { groupData->size++; HandleScope hs(isolate); Local argv[] = {wrapSocket(webSocket, isolate)}; node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local::New(isolate, *connectionCallback), 1, argv); }); } template void onMessage(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *messageCallback = &groupData->messageHandler; messageCallback->Reset(isolate, Local::Cast(args[1])); group->onMessage([isolate, messageCallback](uWS::WebSocket *webSocket, const char *message, size_t length, uWS::OpCode opCode) { HandleScope hs(isolate); Local argv[] = {wrapMessage(message, length, opCode, isolate), getDataV8(webSocket, isolate)}; Local::New(isolate, *messageCallback)->Call(isolate->GetCurrentContext()->Global(), 2, argv); }); } template void onPing(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *pingCallback = &groupData->pingHandler; pingCallback->Reset(isolate, Local::Cast(args[1])); group->onPing([isolate, pingCallback](uWS::WebSocket *webSocket, const char *message, size_t length) { HandleScope hs(isolate); Local argv[] = {wrapMessage(message, length, uWS::OpCode::PING, isolate), getDataV8(webSocket, isolate)}; node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local::New(isolate, *pingCallback), 2, argv); }); } template void onPong(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *pongCallback = &groupData->pongHandler; pongCallback->Reset(isolate, Local::Cast(args[1])); group->onPong([isolate, pongCallback](uWS::WebSocket *webSocket, const char *message, size_t length) { HandleScope hs(isolate); Local argv[] = {wrapMessage(message, length, uWS::OpCode::PONG, isolate), getDataV8(webSocket, isolate)}; node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local::New(isolate, *pongCallback), 2, argv); }); } template void onDisconnection(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *disconnectionCallback = &groupData->disconnectionHandler; disconnectionCallback->Reset(isolate, Local::Cast(args[1])); group->onDisconnection([isolate, disconnectionCallback, groupData](uWS::WebSocket *webSocket, int code, char *message, size_t length) { groupData->size--; HandleScope hs(isolate); Local 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::New(isolate, *disconnectionCallback), 4, argv); }); } void onError(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *errorCallback = &groupData->errorHandler; errorCallback->Reset(isolate, Local::Cast(args[1])); group->onError([isolate, errorCallback](void *user) { HandleScope hs(isolate); Local argv[] = {Local::New(isolate, *(Persistent *) user)}; node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local::New(isolate, *errorCallback), 1, argv); ((Persistent *) user)->Reset(); delete (Persistent *) user; }); } template void closeSocket(const FunctionCallbackInfo &args) { NativeString nativeString(args[2]); unwrapSocket(args[0].As())->close(args[1]->IntegerValue(), nativeString.getData(), nativeString.getLength()); } template void terminateSocket(const FunctionCallbackInfo &args) { unwrapSocket(args[0].As())->terminate(); } template void closeGroup(const FunctionCallbackInfo &args) { NativeString nativeString(args[2]); uWS::Group *group = (uWS::Group *) args[0].As()->Value(); group->close(args[1]->IntegerValue(), nativeString.getData(), nativeString.getLength()); } template void terminateGroup(const FunctionCallbackInfo &args) { ((uWS::Group *) args[0].As()->Value())->terminate(); } template void broadcast(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->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 void prepareMessage(const FunctionCallbackInfo &args) { uWS::OpCode opCode = (uWS::OpCode) args[1]->IntegerValue(); NativeString nativeString(args[0]); args.GetReturnValue().Set(External::New(args.GetIsolate(), uWS::WebSocket::prepareMessage(nativeString.getData(), nativeString.getLength(), opCode, false))); } template void sendPrepared(const FunctionCallbackInfo &args) { unwrapSocket(args[0].As()) ->sendPrepared((typename uWS::WebSocket::PreparedMessage *) args[1].As()->Value()); } template void finalizeMessage(const FunctionCallbackInfo &args) { uWS::WebSocket::finalizeMessage((typename uWS::WebSocket::PreparedMessage *) args[0].As()->Value()); } void forEach(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); uWS::Group *group = (uWS::Group *) args[0].As()->Value(); Local cb = Local::Cast(args[1]); group->forEach([isolate, &cb](uWS::WebSocket *webSocket) { Local argv[] = { getDataV8(webSocket, isolate) }; cb->Call(Null(isolate), 1, argv); }); } void getSize(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); GroupData *groupData = (GroupData *) group->getUserData(); args.GetReturnValue().Set(Integer::New(args.GetIsolate(), groupData->size)); } void startAutoPing(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); NativeString nativeString(args[2]); group->startAutoPing(args[1]->IntegerValue(), std::string(nativeString.getData(), nativeString.getLength())); } void setNoop(const FunctionCallbackInfo &args) { noop.Reset(args.GetIsolate(), Local::Cast(args[0])); } void listen(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args[0].As()->Value(); hub.listen(args[1]->IntegerValue(), nullptr, 0, group); } template struct Namespace { Local object; Namespace (Isolate *isolate) { object = Object::New(isolate); NODE_SET_METHOD(object, "send", send); NODE_SET_METHOD(object, "close", closeSocket); NODE_SET_METHOD(object, "terminate", terminateSocket); NODE_SET_METHOD(object, "prepareMessage", prepareMessage); NODE_SET_METHOD(object, "sendPrepared", sendPrepared); NODE_SET_METHOD(object, "finalizeMessage", finalizeMessage); Local group = Object::New(isolate); NODE_SET_METHOD(group, "onConnection", onConnection); NODE_SET_METHOD(group, "onMessage", onMessage); NODE_SET_METHOD(group, "onDisconnection", onDisconnection); 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); NODE_SET_METHOD(group, "onPong", onPong); NODE_SET_METHOD(group, "create", createGroup); NODE_SET_METHOD(group, "delete", deleteGroup); NODE_SET_METHOD(group, "close", closeGroup); NODE_SET_METHOD(group, "terminate", terminateGroup); NODE_SET_METHOD(group, "broadcast", broadcast); object->Set(String::NewFromUtf8(isolate, "group"), group); } };