From 0636cfb0881fde845d4857bd54709efb09f54bed Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Mon, 14 Jan 2019 11:34:20 +0100 Subject: [PATCH] Refactor things into separate files --- src/AppWrapper.h | 238 +++++++++++++++++++++ src/HttpRequestWrapper.h | 29 +++ src/HttpResponseWrapper.h | 45 ++++ src/Utilities.h | 39 ++++ src/WebSocketWrapper.h | 41 ++++ src/addon.cpp | 429 ++------------------------------------ 6 files changed, 412 insertions(+), 409 deletions(-) create mode 100644 src/AppWrapper.h create mode 100644 src/HttpRequestWrapper.h create mode 100644 src/HttpResponseWrapper.h create mode 100644 src/Utilities.h create mode 100644 src/WebSocketWrapper.h diff --git a/src/AppWrapper.h b/src/AppWrapper.h new file mode 100644 index 0000000..5c2d210 --- /dev/null +++ b/src/AppWrapper.h @@ -0,0 +1,238 @@ +// App.post and all other methods - requires good templating? +// test so that we pass Autobahn with compression/without compression with SSL/without SSL + + +/* uWS.App.ws('/pattern', options) */ +template +void uWS_App_ws(const FunctionCallbackInfo &args) { + APP *app = (APP *) args.Holder()->GetAlignedPointerFromInternalField(0); + + // pattern + NativeString nativeString(args.GetIsolate(), args[0]); + + // todo: small leak here, should be unique_ptrs moved in + Persistent *openPf = new Persistent(); + Persistent *messagePf = new Persistent(); + Persistent *drainPf = new Persistent(); + Persistent *closePf = new Persistent(); + + int maxPayloadLength = 0; + + /* For now, let's have 0, 1, 2 be from nothing to shared, to dedicated */ + int compression = 0; + uWS::CompressOptions mappedCompression = uWS::CompressOptions::DISABLED; + + struct PerSocketData { + Persistent *socketPf; + }; + + /* Get the behavior object */ + if (args.Length() == 2) { + Local behaviorObject = Local::Cast(args[1]); + + /* maxPayloadLength */ + maxPayloadLength = behaviorObject->Get(String::NewFromUtf8(isolate, "maxPayloadLength"))->Int32Value(); + + /* Compression */ + compression = behaviorObject->Get(String::NewFromUtf8(isolate, "compression"))->Int32Value(); + + /* Open */ + openPf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "open")))); + /* Message */ + messagePf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "message")))); + /* Drain */ + drainPf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "drain")))); + /* Close */ + closePf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "close")))); + } + + /* Map compression options from integer values */ + if (compression == 1) { + mappedCompression = uWS::CompressOptions::SHARED_COMPRESSOR; + } else if (compression == 2) { + mappedCompression = uWS::CompressOptions::DEDICATED_COMPRESSOR; + } + + app->template ws(std::string(nativeString.getData(), nativeString.getLength()), { + /* idleTimeout */ + .compression = mappedCompression, + .maxPayloadLength = maxPayloadLength, + /* Handlers */ + .open = [openPf](auto *ws, auto *req) { + HandleScope hs(isolate); + + /* Create a new websocket object */ + Local wsObject = getWsInstance(); + wsObject->SetAlignedPointerInInternalField(0, ws); + + /* Attach a new V8 object with pointer to us, to us */ + PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); + perSocketData->socketPf = new Persistent; + perSocketData->socketPf->Reset(isolate, wsObject); + + Local argv[] = {wsObject}; + Local::New(isolate, *openPf)->Call(isolate->GetCurrentContext()->Global(), 1, argv); + }, + .message = [messagePf](auto *ws, std::string_view message, uWS::OpCode opCode) { + HandleScope hs(isolate); + + Local messageArrayBuffer = ArrayBuffer::New(isolate, (void *) message.data(), message.length()); + + PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); + Local argv[3] = {Local::New(isolate, *(perSocketData->socketPf)), + /*ArrayBuffer::New(isolate, (void *) message.data(), message.length())*/ messageArrayBuffer, + Boolean::New(isolate, opCode == uWS::OpCode::BINARY) + }; + Local::New(isolate, *messagePf)->Call(isolate->GetCurrentContext()->Global(), 3, argv); + + /* Important: we clear the ArrayBuffer to make sure it is not invalidly used after return */ + messageArrayBuffer->Neuter(); + }, + .drain = [drainPf](auto *ws) { + HandleScope hs(isolate); + + PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); + Local argv[1] = {Local::New(isolate, *(perSocketData->socketPf)) + }; + Local::New(isolate, *drainPf)->Call(isolate->GetCurrentContext()->Global(), 1, argv); + }, + .ping = [](auto *ws) { + + }, + .pong = [](auto *ws) { + + }, + .close = [closePf](auto *ws, int code, std::string_view message) { + HandleScope hs(isolate); + + Local messageArrayBuffer = ArrayBuffer::New(isolate, (void *) message.data(), message.length()); + + PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); + Local argv[3] = {Local::New(isolate, *(perSocketData->socketPf)), + Integer::New(isolate, code), + messageArrayBuffer + }; + Local::New(isolate, *closePf)->Call(isolate->GetCurrentContext()->Global(), 3, argv); + + /* Again, here we clear the buffer to avoid strange bugs */ + messageArrayBuffer->Neuter(); + } + }); + + /* Return this */ + args.GetReturnValue().Set(args.Holder()); +} + +// todo: all other methods, in particular post! +template +void uWS_App_get(const FunctionCallbackInfo &args) { + APP *app = (APP *) args.Holder()->GetAlignedPointerFromInternalField(0); + + NativeString nativeString(args.GetIsolate(), args[0]); + + Persistent *pf = new Persistent(); + pf->Reset(args.GetIsolate(), Local::Cast(args[1])); + + //Persistent> p(isolate, Local::Cast(args[1])); + + app->get(std::string(nativeString.getData(), nativeString.getLength()), [pf](auto *res, auto *req) { + HandleScope hs(isolate); + + Local resObject = getResInstance(); + resObject->SetAlignedPointerInInternalField(0, res); + + Local reqObject = Local::New(isolate, reqTemplate)->Clone(); + reqObject->SetAlignedPointerInInternalField(0, req); + + Local argv[] = {resObject, reqObject}; + Local::New(isolate, *pf)->Call(isolate->GetCurrentContext()->Global(), 2, argv); + }); + + args.GetReturnValue().Set(args.Holder()); +} + +template +void uWS_App_listen(const FunctionCallbackInfo &args) { + APP *app = (APP *) args.Holder()->GetAlignedPointerFromInternalField(0); + + int port = args[0]->Uint32Value(args.GetIsolate()->GetCurrentContext()).ToChecked(); + + app->listen(port, [&args](auto *token) { + Local argv[] = {Boolean::New(isolate, token)}; + Local::Cast(args[1])->Call(isolate->GetCurrentContext()->Global(), 1, argv); + }); + + // Return this + args.GetReturnValue().Set(args.Holder()); +} + +template +void uWS_App(const FunctionCallbackInfo &args) { + Local appTemplate = FunctionTemplate::New(isolate); + + APP *app; + + /* Name differs based on type */ + if (std::is_same::value) { + appTemplate->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLApp")); + + /* We fill these below */ + us_ssl_socket_context_options ssl_options = {}; + + static std::string keyFileName; + static std::string certFileName; + static std::string passphrase; + + /* Read the options object (SSL options) */ + if (args.Length() == 1) { + /* Key file name */ + NativeString keyFileNameValue(isolate, Local::Cast(args[0])->Get(String::NewFromUtf8(isolate, "key_file_name"))); + if (keyFileNameValue.getLength()) { + keyFileName.append(keyFileNameValue.getData(), keyFileNameValue.getLength()); + ssl_options.key_file_name = keyFileName.c_str(); + } + + /* Cert file name */ + NativeString certFileNameValue(isolate, Local::Cast(args[0])->Get(String::NewFromUtf8(isolate, "cert_file_name"))); + if (certFileNameValue.getLength()) { + certFileName.append(certFileNameValue.getData(), certFileNameValue.getLength()); + ssl_options.cert_file_name = certFileName.c_str(); + } + + /* Passphrase */ + NativeString passphraseValue(isolate, Local::Cast(args[0])->Get(String::NewFromUtf8(isolate, "passphrase"))); + if (passphraseValue.getLength()) { + passphrase.append(passphraseValue.getData(), passphraseValue.getLength()); + ssl_options.passphrase = passphrase.c_str(); + } + } + + app = new APP(ssl_options); + } else { + appTemplate->SetClassName(String::NewFromUtf8(isolate, "uWS.App")); + app = new APP; + } + + appTemplate->InstanceTemplate()->SetInternalFieldCount(1); + + + /* Most used functions will be get, post, ws, listen */ + + // Get and all the Http methods + appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "get"), FunctionTemplate::New(isolate, uWS_App_get)); + + // Ws + appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ws"), FunctionTemplate::New(isolate, uWS_App_ws)); + + // Listen + appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "listen"), FunctionTemplate::New(isolate, uWS_App_listen)); + + // Instantiate and set intenal pointer + Local localApp = appTemplate->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); + + // Delete this boy + localApp->SetAlignedPointerInInternalField(0, app); + + // Return an instance of this shit + args.GetReturnValue().Set(localApp); +} diff --git a/src/HttpRequestWrapper.h b/src/HttpRequestWrapper.h new file mode 100644 index 0000000..8b63d6f --- /dev/null +++ b/src/HttpRequestWrapper.h @@ -0,0 +1,29 @@ +// req.getParam(int) +// req.getUrl() +// req.onAbort ? + +/* The only thing this req needs is getHeader and similar, getParameter, getUrl and so on */ +Persistent reqTemplate; + +void req_getHeader(const FunctionCallbackInfo &args) { + NativeString data(args.GetIsolate(), args[0]); + char *buf = data.getData(); int length = data.getLength(); + + std::string_view header = ((uWS::HttpRequest *) args.Holder()->GetAlignedPointerFromInternalField(0))->getHeader(std::string_view(buf, length)); + + args.GetReturnValue().Set(String::NewFromUtf8(isolate, header.data(), v8::String::kNormalString, header.length())); +} + +void initReqTemplate() { + /*reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "url"), Request::url); + reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "method"), Request::method);*/ + + // Request template (do we need to clone this?) + Local reqTemplateLocal = FunctionTemplate::New(isolate); + reqTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpRequest")); + reqTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); + reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getHeader"), FunctionTemplate::New(isolate, req_getHeader)); + + Local reqObjectLocal = reqTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); + reqTemplate.Reset(isolate, reqObjectLocal); +} diff --git a/src/HttpResponseWrapper.h b/src/HttpResponseWrapper.h new file mode 100644 index 0000000..f9ab951 --- /dev/null +++ b/src/HttpResponseWrapper.h @@ -0,0 +1,45 @@ +// res.onData(JS function) +// res.write +// res.tryEnd + +// this whole template thing could be one struct with members to order tihngs better +Persistent resTemplate[2]; + +template +void res_end(const FunctionCallbackInfo &args) { + NativeString data(args.GetIsolate(), args[0]); + ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->end(std::string_view(data.getData(), data.getLength())); + + args.GetReturnValue().Set(args.Holder()); +} + +template +void res_writeHeader(const FunctionCallbackInfo &args) { + NativeString header(args.GetIsolate(), args[0]); + NativeString value(args.GetIsolate(), args[1]); + ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->writeHeader(std::string_view(header.getData(), header.getLength()), + std::string_view(value.getData(), value.getLength())); + + args.GetReturnValue().Set(args.Holder()); +} + +template +void initResTemplate() { + Local resTemplateLocal = FunctionTemplate::New(isolate); + if (SSL) { + resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLHttpResponse")); + } else { + resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpResponse")); + } + resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); + resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end"), FunctionTemplate::New(isolate, res_end)); + resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader"), FunctionTemplate::New(isolate, res_writeHeader)); + + Local resObjectLocal = resTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); + resTemplate[SSL].Reset(isolate, resObjectLocal); +} + +template +Local getResInstance() { + return Local::New(isolate, resTemplate[std::is_same::value])->Clone(); +} diff --git a/src/Utilities.h b/src/Utilities.h new file mode 100644 index 0000000..42f4abc --- /dev/null +++ b/src/Utilities.h @@ -0,0 +1,39 @@ +class NativeString { + char *data; + size_t length; + char utf8ValueMemory[sizeof(String::Utf8Value)]; + String::Utf8Value *utf8Value = nullptr; +public: + NativeString(Isolate *isolate, const Local &value) { + if (value->IsUndefined()) { + data = nullptr; + length = 0; + } else if (value->IsString()) { + utf8Value = new (utf8ValueMemory) String::Utf8Value(isolate, value); + data = (**utf8Value); + length = utf8Value->length(); + } 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(); + } + } +}; diff --git a/src/WebSocketWrapper.h b/src/WebSocketWrapper.h new file mode 100644 index 0000000..d124e37 --- /dev/null +++ b/src/WebSocketWrapper.h @@ -0,0 +1,41 @@ +// ws.getBufferedAmount() +// ws.close(lalala) +// ws.? + +// also wrap all of this in some common struct +Persistent wsTemplate[2]; + +/* WebSocket send */ + +template +void uWS_WebSocket_send(const FunctionCallbackInfo &args) { + NativeString nativeString(args.GetIsolate(), args[0]); + + bool isBinary = args[1]->Int32Value(); + + bool ok = ((uWS::WebSocket *) args.Holder()->GetAlignedPointerFromInternalField(0))->send( + std::string_view(nativeString.getData(), nativeString.getLength()), isBinary ? uWS::OpCode::BINARY : uWS::OpCode::TEXT + ); + + args.GetReturnValue().Set(Boolean::New(isolate, ok)); +} + +template +void initWsTemplate() { + Local wsTemplateLocal = FunctionTemplate::New(isolate); + if (SSL) { + wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLWebSocket")); + } else { + wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.WebSocket")); + } + wsTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); + wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "send"), FunctionTemplate::New(isolate, uWS_WebSocket_send)); + + Local wsObjectLocal = wsTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); + wsTemplate[SSL].Reset(isolate, wsObjectLocal); +} + +template +Local getWsInstance() { + return Local::New(isolate, wsTemplate[std::is_same::value])->Clone(); +} diff --git a/src/addon.cpp b/src/addon.cpp index 867cdf2..724012d 100644 --- a/src/addon.cpp +++ b/src/addon.cpp @@ -15,411 +15,25 @@ * limitations under the License. */ -// missing features to wrap: -// App.post and all other methods - requires good templating? -// req.getParam(int) -// req.getUrl() -// res.onData(JS function) -// res.write -// res.tryEnd -// req.onAbort ? -// ws.getBufferedAmount() -// ws.close(lalala) -// ws.? -// test so that we pass Autobahn with compression/without compression with SSL/without SSL -// split addon in different separate headers - /* We are only allowed to depend on µWS and V8 in this layer. */ #include "App.h" #include -using namespace v8; -Isolate *isolate; - #include #include #include +using namespace v8; + +/* These two are definitely static */ std::vector>> nextTickQueue; +Isolate *isolate; -class NativeString { - char *data; - size_t length; - char utf8ValueMemory[sizeof(String::Utf8Value)]; - String::Utf8Value *utf8Value = nullptr; -public: - NativeString(Isolate *isolate, const Local &value) { - if (value->IsUndefined()) { - data = nullptr; - length = 0; - } else if (value->IsString()) { - utf8Value = new (utf8ValueMemory) String::Utf8Value(isolate, value); - data = (**utf8Value); - length = utf8Value->length(); - } 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; - } - } +#include "Utilities.h" +#include "WebSocketWrapper.h" +#include "HttpResponseWrapper.h" +#include "HttpRequestWrapper.h" +#include "AppWrapper.h" - char *getData() {return data;} - size_t getLength() {return length;} - ~NativeString() { - if (utf8Value) { - utf8Value->~Utf8Value(); - } - } -}; - -// also wrap all of this in some common struct -Persistent wsTemplate[2]; - -/* WebSocket send */ - -template -void uWS_WebSocket_send(const FunctionCallbackInfo &args) { - NativeString nativeString(args.GetIsolate(), args[0]); - - bool isBinary = args[1]->Int32Value(); - - bool ok = ((uWS::WebSocket *) args.Holder()->GetAlignedPointerFromInternalField(0))->send( - std::string_view(nativeString.getData(), nativeString.getLength()), isBinary ? uWS::OpCode::BINARY : uWS::OpCode::TEXT - ); - - args.GetReturnValue().Set(Boolean::New(isolate, ok)); -} - -template -void initWsTemplate() { - Local wsTemplateLocal = FunctionTemplate::New(isolate); - if (SSL) { - wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLWebSocket")); - } else { - wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.WebSocket")); - } - wsTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "send"), FunctionTemplate::New(isolate, uWS_WebSocket_send)); - - Local wsObjectLocal = wsTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - wsTemplate[SSL].Reset(isolate, wsObjectLocal); -} - -template -Local getWsInstance() { - return Local::New(isolate, wsTemplate[std::is_same::value])->Clone(); -} - -/* -template -struct HttpResponseWrapper { - - HttpResponseWrapper() { - - } - - - -};*/ - -// this whole template thing could be one struct with members to order tihngs better -Persistent resTemplate[2]; - -template -void res_end(const FunctionCallbackInfo &args) { - NativeString data(args.GetIsolate(), args[0]); - ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->end(std::string_view(data.getData(), data.getLength())); - - args.GetReturnValue().Set(args.Holder()); -} - -template -void res_writeHeader(const FunctionCallbackInfo &args) { - NativeString header(args.GetIsolate(), args[0]); - NativeString value(args.GetIsolate(), args[1]); - ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->writeHeader(std::string_view(header.getData(), header.getLength()), - std::string_view(value.getData(), value.getLength())); - - args.GetReturnValue().Set(args.Holder()); -} - -template -void initResTemplate() { - Local resTemplateLocal = FunctionTemplate::New(isolate); - if (SSL) { - resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLHttpResponse")); - } else { - resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpResponse")); - } - resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); - resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end"), FunctionTemplate::New(isolate, res_end)); - resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader"), FunctionTemplate::New(isolate, res_writeHeader)); - - Local resObjectLocal = resTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - resTemplate[SSL].Reset(isolate, resObjectLocal); -} - -template -Local getResInstance() { - return Local::New(isolate, resTemplate[std::is_same::value])->Clone(); -} - -/* The only thing this req needs is getHeader and similar, getParameter, getUrl and so on */ -Persistent reqTemplate; - -void req_getHeader(const FunctionCallbackInfo &args) { - NativeString data(args.GetIsolate(), args[0]); - char *buf = data.getData(); int length = data.getLength(); - - std::string_view header = ((uWS::HttpRequest *) args.Holder()->GetAlignedPointerFromInternalField(0))->getHeader(std::string_view(buf, length)); - - args.GetReturnValue().Set(String::NewFromUtf8(isolate, header.data(), v8::String::kNormalString, header.length())); -} - -/* uWS.App.ws('/pattern', options) */ -template -void uWS_App_ws(const FunctionCallbackInfo &args) { - APP *app = (APP *) args.Holder()->GetAlignedPointerFromInternalField(0); - - // pattern - NativeString nativeString(args.GetIsolate(), args[0]); - - // todo: small leak here, should be unique_ptrs moved in - Persistent *openPf = new Persistent(); - Persistent *messagePf = new Persistent(); - Persistent *drainPf = new Persistent(); - Persistent *closePf = new Persistent(); - - int maxPayloadLength = 0; - - /* For now, let's have 0, 1, 2 be from nothing to shared, to dedicated */ - int compression = 0; - uWS::CompressOptions mappedCompression = uWS::CompressOptions::DISABLED; - - struct PerSocketData { - Persistent *socketPf; - }; - - /* Get the behavior object */ - if (args.Length() == 2) { - Local behaviorObject = Local::Cast(args[1]); - - /* maxPayloadLength */ - maxPayloadLength = behaviorObject->Get(String::NewFromUtf8(isolate, "maxPayloadLength"))->Int32Value(); - - /* Compression */ - compression = behaviorObject->Get(String::NewFromUtf8(isolate, "compression"))->Int32Value(); - - /* Open */ - openPf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "open")))); - /* Message */ - messagePf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "message")))); - /* Drain */ - drainPf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "drain")))); - /* Close */ - closePf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "close")))); - } - - /* Map compression options from integer values */ - if (compression == 1) { - mappedCompression = uWS::CompressOptions::SHARED_COMPRESSOR; - } else if (compression == 2) { - mappedCompression = uWS::CompressOptions::DEDICATED_COMPRESSOR; - } - - app->template ws(std::string(nativeString.getData(), nativeString.getLength()), { - /* idleTimeout */ - .compression = mappedCompression, - .maxPayloadLength = maxPayloadLength, - /* Handlers */ - .open = [openPf](auto *ws, auto *req) { - HandleScope hs(isolate); - - /* Create a new websocket object */ - Local wsObject = getWsInstance(); - wsObject->SetAlignedPointerInInternalField(0, ws); - - /* Attach a new V8 object with pointer to us, to us */ - PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); - perSocketData->socketPf = new Persistent; - perSocketData->socketPf->Reset(isolate, wsObject); - - Local argv[] = {wsObject}; - Local::New(isolate, *openPf)->Call(isolate->GetCurrentContext()->Global(), 1, argv); - }, - .message = [messagePf](auto *ws, std::string_view message, uWS::OpCode opCode) { - HandleScope hs(isolate); - - Local messageArrayBuffer = ArrayBuffer::New(isolate, (void *) message.data(), message.length()); - - PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); - Local argv[3] = {Local::New(isolate, *(perSocketData->socketPf)), - /*ArrayBuffer::New(isolate, (void *) message.data(), message.length())*/ messageArrayBuffer, - Boolean::New(isolate, opCode == uWS::OpCode::BINARY) - }; - Local::New(isolate, *messagePf)->Call(isolate->GetCurrentContext()->Global(), 3, argv); - - /* Important: we clear the ArrayBuffer to make sure it is not invalidly used after return */ - messageArrayBuffer->Neuter(); - }, - .drain = [drainPf](auto *ws) { - HandleScope hs(isolate); - - PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); - Local argv[1] = {Local::New(isolate, *(perSocketData->socketPf)) - }; - Local::New(isolate, *drainPf)->Call(isolate->GetCurrentContext()->Global(), 1, argv); - }, - .ping = [](auto *ws) { - - }, - .pong = [](auto *ws) { - - }, - .close = [closePf](auto *ws, int code, std::string_view message) { - HandleScope hs(isolate); - - Local messageArrayBuffer = ArrayBuffer::New(isolate, (void *) message.data(), message.length()); - - PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); - Local argv[3] = {Local::New(isolate, *(perSocketData->socketPf)), - Integer::New(isolate, code), - messageArrayBuffer - }; - Local::New(isolate, *closePf)->Call(isolate->GetCurrentContext()->Global(), 3, argv); - - /* Again, here we clear the buffer to avoid strange bugs */ - messageArrayBuffer->Neuter(); - } - }); - - /* Return this */ - args.GetReturnValue().Set(args.Holder()); -} - -// todo: all other methods, in particular post! -template -void uWS_App_get(const FunctionCallbackInfo &args) { - APP *app = (APP *) args.Holder()->GetAlignedPointerFromInternalField(0); - - NativeString nativeString(args.GetIsolate(), args[0]); - - Persistent *pf = new Persistent(); - pf->Reset(args.GetIsolate(), Local::Cast(args[1])); - - //Persistent> p(isolate, Local::Cast(args[1])); - - app->get(std::string(nativeString.getData(), nativeString.getLength()), [pf](auto *res, auto *req) { - HandleScope hs(isolate); - - Local resObject = getResInstance(); - resObject->SetAlignedPointerInInternalField(0, res); - - Local reqObject = Local::New(isolate, reqTemplate)->Clone(); - reqObject->SetAlignedPointerInInternalField(0, req); - - Local argv[] = {resObject, reqObject}; - Local::New(isolate, *pf)->Call(isolate->GetCurrentContext()->Global(), 2, argv); - }); - - args.GetReturnValue().Set(args.Holder()); -} - -template -void uWS_App_listen(const FunctionCallbackInfo &args) { - APP *app = (APP *) args.Holder()->GetAlignedPointerFromInternalField(0); - - int port = args[0]->Uint32Value(args.GetIsolate()->GetCurrentContext()).ToChecked(); - - app->listen(port, [&args](auto *token) { - Local argv[] = {Boolean::New(isolate, token)}; - Local::Cast(args[1])->Call(isolate->GetCurrentContext()->Global(), 1, argv); - }); - - // Return this - args.GetReturnValue().Set(args.Holder()); -} - -template -void uWS_App(const FunctionCallbackInfo &args) { - Local appTemplate = FunctionTemplate::New(isolate); - - APP *app; - - /* Name differs based on type */ - if (std::is_same::value) { - appTemplate->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLApp")); - - /* We fill these below */ - us_ssl_socket_context_options ssl_options = {}; - - static std::string keyFileName; - static std::string certFileName; - static std::string passphrase; - - /* Read the options object (SSL options) */ - if (args.Length() == 1) { - /* Key file name */ - NativeString keyFileNameValue(isolate, Local::Cast(args[0])->Get(String::NewFromUtf8(isolate, "key_file_name"))); - if (keyFileNameValue.getLength()) { - keyFileName.append(keyFileNameValue.getData(), keyFileNameValue.getLength()); - ssl_options.key_file_name = keyFileName.c_str(); - } - - /* Cert file name */ - NativeString certFileNameValue(isolate, Local::Cast(args[0])->Get(String::NewFromUtf8(isolate, "cert_file_name"))); - if (certFileNameValue.getLength()) { - certFileName.append(certFileNameValue.getData(), certFileNameValue.getLength()); - ssl_options.cert_file_name = certFileName.c_str(); - } - - /* Passphrase */ - NativeString passphraseValue(isolate, Local::Cast(args[0])->Get(String::NewFromUtf8(isolate, "passphrase"))); - if (passphraseValue.getLength()) { - passphrase.append(passphraseValue.getData(), passphraseValue.getLength()); - ssl_options.passphrase = passphrase.c_str(); - } - } - - app = new APP(ssl_options); - } else { - appTemplate->SetClassName(String::NewFromUtf8(isolate, "uWS.App")); - app = new APP; - } - - appTemplate->InstanceTemplate()->SetInternalFieldCount(1); - - - /* Most used functions will be get, post, ws, listen */ - - // Get and all the Http methods - appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "get"), FunctionTemplate::New(isolate, uWS_App_get)); - - // Ws - appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ws"), FunctionTemplate::New(isolate, uWS_App_ws)); - - // Listen - appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "listen"), FunctionTemplate::New(isolate, uWS_App_listen)); - - // Instantiate and set intenal pointer - Local localApp = appTemplate->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - - // Delete this boy - localApp->SetAlignedPointerInInternalField(0, app); - - // Return an instance of this shit - args.GetReturnValue().Set(localApp); -} - -// we need to override process.nextTick to avoid horrible loss of performance by Node.js +/* We are not compatible with Node.js nextTick for performance (and standalone) reasons */ void nextTick(const FunctionCallbackInfo &args) { nextTickQueue.push_back(Persistent>(isolate, Local::Cast(args[0]))); } @@ -438,26 +52,29 @@ void emptyNextTickQueue(Isolate *isolate) { } void Main(Local exports) { - + /* I guess we store this statically */ isolate = exports->GetIsolate(); /* Register our own nextTick handler */ - /* We should probably also do this in pre, just to be sure */ uWS::Loop::defaultLoop()->setPostHandler([](uWS::Loop *) { emptyNextTickQueue(isolate); }); + /* Also empty in pre, it doesn't matter */ + /*uWS::Loop::defaultLoop()->setPreHandler([]() { + emptyNextTickQueue(isolate); + });*/ + /* Hook up our timers */ us_loop_integrate((us_loop *) uWS::Loop::defaultLoop()); - /*reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "url"), Request::url); - reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "method"), Request::method);*/ - /* uWS namespace */ exports->Set(String::NewFromUtf8(isolate, "App"), FunctionTemplate::New(isolate, uWS_App)->GetFunction()); exports->Set(String::NewFromUtf8(isolate, "SSLApp"), FunctionTemplate::New(isolate, uWS_App)->GetFunction()); exports->Set(String::NewFromUtf8(isolate, "nextTick"), FunctionTemplate::New(isolate, nextTick)->GetFunction()); + // these inits should probably happen elsewhere (in their respective struct's constructor)? + /* The template for websockets */ initWsTemplate<0>(); initWsTemplate<1>(); @@ -466,14 +83,8 @@ void Main(Local exports) { initResTemplate<0>(); initResTemplate<1>(); - // Request template (do we need to clone this?) - Local reqTemplateLocal = FunctionTemplate::New(isolate); - reqTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpRequest")); - reqTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); - reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getHeader"), FunctionTemplate::New(isolate, req_getHeader)); - - Local reqObjectLocal = reqTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - reqTemplate.Reset(isolate, reqObjectLocal); + /* Init a shared request object */ + initReqTemplate(); } /* This is required when building as a Node.js addon */