From 9e1b05608eba9b96906ea9fe21a38d91a8bc2238 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 3 Nov 2018 12:35:42 +0100 Subject: [PATCH] Node.js 11, uWS.nextTick, purely V8 --- Makefile | 6 +- dist/uws.js | 5 +- examples/HelloWorld.js | 4 +- src/addon.cpp | 192 ++++++++++++++++++++++++----------------- 4 files changed, 124 insertions(+), 83 deletions(-) diff --git a/Makefile b/Makefile index fef1f45..b33a5b5 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ -C_SHARED := -DLIBUS_USE_LIBUV -flto -O3 -c -fPIC -I ../v0.15/uSockets/src ../v0.15/uSockets/src/*.c ../v0.15/uSockets/src/eventing/*.c +C_SHARED := -DLIBUS_USE_LIBUV -flto -O3 -c -fPIC -I ../uWebSockets/uSockets/src ../uWebSockets/uSockets/src/*.c ../uWebSockets/uSockets/src/eventing/*.c -CPP_SHARED := -DLIBUS_USE_LIBUV -flto -O3 -c -fPIC -std=c++17 -I ../v0.15/uSockets/src -I ../v0.15/src src/addon.cpp +CPP_SHARED := -DLIBUS_USE_LIBUV -flto -O3 -c -fPIC -std=c++17 -I ../uWebSockets/uSockets/src -I ../uWebSockets/src src/addon.cpp CPP_OSX := -stdlib=libc++ -mmacosx-version-min=10.7 -undefined dynamic_lookup @@ -9,12 +9,14 @@ default: NODE=targets/node-v8.1.2 ABI=57 make `(uname -s)` NODE=targets/node-v9.2.0 ABI=59 make `(uname -s)` NODE=targets/node-v10.0.0 ABI=64 make `(uname -s)` + NODE=targets/node-v11.1.0 ABI=67 make `(uname -s)` for f in dist/*.node; do chmod +x $$f; done targets: mkdir targets curl https://nodejs.org/dist/v8.1.2/node-v8.1.2-headers.tar.gz | tar xz -C targets curl https://nodejs.org/dist/v9.2.0/node-v9.2.0-headers.tar.gz | tar xz -C targets curl https://nodejs.org/dist/v10.0.0/node-v10.0.0-headers.tar.gz | tar xz -C targets + curl https://nodejs.org/dist/v11.1.0/node-v11.1.0-headers.tar.gz | tar xz -C targets Linux: gcc $(C_SHARED) g++ $(CPP_SHARED) -I $$NODE/include/node diff --git a/dist/uws.js b/dist/uws.js index 0097a10..d9a46b5 100644 --- a/dist/uws.js +++ b/dist/uws.js @@ -1,6 +1,9 @@ module.exports = (() => { try { - return require(`./uws_${process.platform}_${process.versions.modules}.node`); + const uWS = require(`./uws_${process.platform}_${process.versions.modules}.node`); + /* We are not compatible with Node.js domain */ + process.nextTick = (f, ...args) => uWS.nextTick(f.apply(...args)); + return uWS; } catch (e) { throw new Error('This version of µWS is not compatible with your Node.js build.'); } diff --git a/examples/HelloWorld.js b/examples/HelloWorld.js index 33a089a..982f4f9 100644 --- a/examples/HelloWorld.js +++ b/examples/HelloWorld.js @@ -2,8 +2,10 @@ const uWS = require('../dist/uws.js'); const port = 3000; +const world = new (require('util').TextEncoder)().encode('World!'); + uWS.App().get('/hello', (res, req) => { - res.end('world!'); + res.end(world); }).get('/*', (res, req) => { res.writeHeader('content-type', 'text/html; charset= utf-8').end(req.getHeader('user-agent') + ' är din user agent, biatch!'); }).listen(port, (token) => { diff --git a/src/addon.cpp b/src/addon.cpp index fa57a00..8d9f28d 100644 --- a/src/addon.cpp +++ b/src/addon.cpp @@ -1,6 +1,8 @@ +/* We depend only on raw & vanilla libuv, V8 and OpenSSL. + * There is to be no dependencies on anything Node.js in here. */ #include -#include -#include +#include + #include using namespace v8; @@ -18,9 +20,6 @@ public: 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(); @@ -54,127 +53,162 @@ Persistent reqTemplate; void res_end(const FunctionCallbackInfo &args) { - // you might want to do extra work here to swap to tryEnd if passed a Buffer? - // or always use tryEnd and simply grab the object as persistent? + // you might want to do extra work here to swap to tryEnd if passed a Buffer? + // or always use tryEnd and simply grab the object as persistent? - NativeString data(args[0]); - ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->end(std::string_view(data.getData(), data.getLength())); + NativeString data(args[0]); + ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->end(std::string_view(data.getData(), data.getLength())); - // Return this - args.GetReturnValue().Set(args.Holder()); + // Return this + args.GetReturnValue().Set(args.Holder()); } void res_writeHeader(const FunctionCallbackInfo &args) { - // get string - NativeString header(args[0]); - NativeString value(args[1]); + // get string + NativeString header(args[0]); + NativeString value(args[1]); - ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->writeHeader(std::string_view(header.getData(), header.getLength()), std::string_view(value.getData(), value.getLength())); + ((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()); + args.GetReturnValue().Set(args.Holder()); } void req_getHeader(const FunctionCallbackInfo &args) { - // get string - NativeString data(args[0]); - char *buf = data.getData(); int length = data.getLength(); + // get string + NativeString data(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)); + 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())); + args.GetReturnValue().Set(String::NewFromUtf8(isolate, header.data(), v8::String::kNormalString, header.length())); } void uWS_App_get(const FunctionCallbackInfo &args) { - uWS::App *app = (uWS::App *) args.Holder()->GetAlignedPointerFromInternalField(0); + uWS::App *app = (uWS::App *) args.Holder()->GetAlignedPointerFromInternalField(0); - NativeString nativeString(args[0]); + NativeString nativeString(args[0]); - Persistent *pf = new Persistent(); - pf->Reset(args.GetIsolate(), Local::Cast(args[1])); + Persistent *pf = new Persistent(); + pf->Reset(args.GetIsolate(), Local::Cast(args[1])); - app->get(std::string(nativeString.getData(), nativeString.getLength()), [pf](auto *res, auto *req) { - HandleScope hs(isolate); + //Persistent> p(isolate, Local::Cast(args[1])); - Local resObject = Local::New(isolate, resTemplate)->Clone(); - resObject->SetAlignedPointerInInternalField(0, res); + app->get(std::string(nativeString.getData(), nativeString.getLength()), [pf](auto *res, auto *req) { + HandleScope hs(isolate); - Local reqObject = Local::New(isolate, reqTemplate)->Clone(); - reqObject->SetAlignedPointerInInternalField(0, req); + Local resObject = Local::New(isolate, resTemplate)->Clone(); + resObject->SetAlignedPointerInInternalField(0, res); - // node:: is horrible but - Local argv[] = {resObject, reqObject}; - node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local::New(isolate, *pf), 2, argv); + 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()); + args.GetReturnValue().Set(args.Holder()); } // port, callback? void uWS_App_listen(const FunctionCallbackInfo &args) { - uWS::App *app = (uWS::App *) args.Holder()->GetAlignedPointerFromInternalField(0); + uWS::App *app = (uWS::App *) args.Holder()->GetAlignedPointerFromInternalField(0); - int port = args[0]->Uint32Value(); + int port = args[0]->Uint32Value(); - app->listen(port, [&args](auto *token) { - Local argv[] = {Boolean::New(isolate, token)}; - Local::Cast(args[1])->Call(isolate->GetCurrentContext()->Global(), 1, argv); - }); + 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()); + // Return this + args.GetReturnValue().Set(args.Holder()); +} + +uv_check_t check; +#include +std::vector>> nextTickQueue; + +// we need to override process.nextTick to avoid horrible loss of performance by Node.js +void nextTick(const FunctionCallbackInfo &args) { + nextTickQueue.push_back(Persistent>(isolate, Local::Cast(args[0]))); } // uWS.App() -> object void uWS_App(const FunctionCallbackInfo &args) { - // uWS.App - Local appTemplate = FunctionTemplate::New(isolate); - appTemplate->SetClassName(String::NewFromUtf8(isolate, "uWS.App")); - appTemplate->InstanceTemplate()->SetInternalFieldCount(1); - - // Get - appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "get"), FunctionTemplate::New(isolate, uWS_App_get)); + // uWS.App + Local appTemplate = FunctionTemplate::New(isolate); + appTemplate->SetClassName(String::NewFromUtf8(isolate, "uWS.App")); + appTemplate->InstanceTemplate()->SetInternalFieldCount(1); - // Listen - appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "listen"), FunctionTemplate::New(isolate, uWS_App_listen)); + // Get + appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "get"), FunctionTemplate::New(isolate, uWS_App_get)); - // Instantiate and set intenal pointer - Local localApp = appTemplate->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); + // Listen + appTemplate->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "listen"), FunctionTemplate::New(isolate, uWS_App_listen)); - // Delete this boy - localApp->SetAlignedPointerInInternalField(0, new uWS::App()); + // Instantiate and set intenal pointer + Local localApp = appTemplate->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - // Return an instance of this shit - args.GetReturnValue().Set(localApp); + // Delete this boy + localApp->SetAlignedPointerInInternalField(0, new uWS::App()); + + // Return an instance of this shit + args.GetReturnValue().Set(localApp); } void Main(Local exports) { - isolate = exports->GetIsolate(); - /*reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "url"), Request::url); - reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "method"), Request::method);*/ + isolate = exports->GetIsolate(); - // App is a function returning an object - NODE_SET_METHOD(exports, "App", uWS_App); + /* uWS.nextTick is executed in uv_check_t */ + uv_loop_t *loop = uv_default_loop(); + uv_check_init(loop, &check); + check.data = isolate; + uv_check_start(&check, [](uv_check_t *check) { - // HttpResponse template - Local resTemplateLocal = FunctionTemplate::New(isolate); - 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)); + if (nextTickQueue.size()) { + Isolate *isolate = (Isolate *) check->data; + HandleScope hs(isolate); - Local resObjectLocal = resTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - resTemplate.Reset(isolate, resObjectLocal); + for (Persistent> &f : nextTickQueue) { + Local::New(isolate, f)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); + f.Reset(); + } - // Request template - 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)); + nextTickQueue.clear(); + } + }); + uv_unref((uv_handle_t *) &check); - Local reqObjectLocal = reqTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - reqTemplate.Reset(isolate, reqObjectLocal); + /*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, "nextTick"), FunctionTemplate::New(isolate, nextTick)->GetFunction()); + + + // HttpResponse template + Local resTemplateLocal = FunctionTemplate::New(isolate); + 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.Reset(isolate, resObjectLocal); + + // 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); } +/* This is the only part where we are allowed/forced to add Node.js specific code. + * We do this because we are forced to, and we need a node module version added */ +#include NODE_MODULE(uWS, Main)