diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fef1f45 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +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 + +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_OSX := -stdlib=libc++ -mmacosx-version-min=10.7 -undefined dynamic_lookup + +default: + make targets + 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)` + 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 +Linux: + gcc $(C_SHARED) + g++ $(CPP_SHARED) -I $$NODE/include/node + g++ -flto -O3 *.o -std=c++17 -shared -static-libstdc++ -static-libgcc -s -o dist/uws_linux_$$ABI.node +Darwin: + g++ $(CPP_SHARED) $(CPP_OSX) -I $$NODE/include/node -o dist/uws_darwin_$$ABI.node +.PHONY: clean +clean: + rm -f dist/README.md + rm -f dist/LICENSE + rm -f dist/uws_*.node + rm -f dist/uws.js + rm -rf dist/src + rm -rf targets diff --git a/dist/uws.js b/dist/uws.js new file mode 100644 index 0000000..0097a10 --- /dev/null +++ b/dist/uws.js @@ -0,0 +1,7 @@ +module.exports = (() => { + try { + return require(`./uws_${process.platform}_${process.versions.modules}.node`); + } 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 new file mode 100644 index 0000000..33a089a --- /dev/null +++ b/examples/HelloWorld.js @@ -0,0 +1,17 @@ +const uWS = require('../dist/uws.js'); + +const port = 3000; + +uWS.App().get('/hello', (res, req) => { + 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) => { + if (token) { + console.log('Listening to port ' + port); + } else { + console.log('Failed to listen to port ' + port); + } +}); + +console.log('Timers will not work until us_loop_integrate is done'); diff --git a/make.bat b/make.bat new file mode 100644 index 0000000..e940eac --- /dev/null +++ b/make.bat @@ -0,0 +1,27 @@ +call "%VS140COMNTOOLS%..\..\vc\vcvarsall.bat" amd64 + +if not exist 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/v8.1.2/win-x64/node.lib > targets/node-v8.1.2/node.lib +curl https://nodejs.org/dist/v9.2.0/node-v9.2.0-headers.tar.gz | tar xz -C targets +curl https://nodejs.org/dist/v9.2.0/win-x64/node.lib > targets/node-v9.2.0/node.lib +curl https://nodejs.org/dist/v10.0.0/node-v10.0.0-headers.tar.gz | tar xz -C targets +curl https://nodejs.org/dist/v10.0.0/win-x64/node.lib > targets/node-v10.0.0/node.lib +) + +cp README.md dist/README.md +cp ../uWebSockets/LICENSE dist/LICENSE +cp -r ../uWebSockets/src dist/ +cp src/addon.cpp dist/src/addon.cpp +cp src/addon.h dist/src/addon.h +cp src/http.h dist/src/http.h +cp src/uws.js dist/uws.js + +cl /I targets/node-v8.1.2/include/node /EHsc /Ox /LD /Fedist/uws_win32_57.node dist/src/*.cpp targets/node-v8.1.2/node.lib +cl /I targets/node-v9.2.0/include/node /EHsc /Ox /LD /Fedist/uws_win32_59.node dist/src/*.cpp targets/node-v9.2.0/node.lib +cl /I targets/node-v10.0.0/include/node /EHsc /Ox /LD /Fedist/uws_win32_64.node dist/src/*.cpp targets/node-v10.0.0/node.lib + +rm *.obj +rm dist/*.exp +rm dist/*.lib diff --git a/src/addon.cpp b/src/addon.cpp new file mode 100644 index 0000000..fa57a00 --- /dev/null +++ b/src/addon.cpp @@ -0,0 +1,180 @@ +#include +#include +#include +#include +using namespace v8; + +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(); + } + } +}; + +#include "App.h" +Isolate *isolate; +Persistent resTemplate; +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? + + 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()); +} + +void res_writeHeader(const FunctionCallbackInfo &args) { + // 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())); + + 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(); + + 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 uWS_App_get(const FunctionCallbackInfo &args) { + uWS::App *app = (uWS::App *) args.Holder()->GetAlignedPointerFromInternalField(0); + + NativeString nativeString(args[0]); + + 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); + + Local resObject = Local::New(isolate, resTemplate)->Clone(); + resObject->SetAlignedPointerInInternalField(0, res); + + Local reqObject = Local::New(isolate, reqTemplate)->Clone(); + reqObject->SetAlignedPointerInInternalField(0, req); + + // node:: is horrible but + Local argv[] = {resObject, reqObject}; + node::MakeCallback(isolate, isolate->GetCurrentContext()->Global(), Local::New(isolate, *pf), 2, argv); + + }); + + args.GetReturnValue().Set(args.Holder()); +} + +// port, callback? +void uWS_App_listen(const FunctionCallbackInfo &args) { + uWS::App *app = (uWS::App *) args.Holder()->GetAlignedPointerFromInternalField(0); + + 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); + }); + + // Return this + args.GetReturnValue().Set(args.Holder()); +} + +// 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)); + + // 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, 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);*/ + + // App is a function returning an object + NODE_SET_METHOD(exports, "App", uWS_App); + + // 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 + 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); +} + +NODE_MODULE(uWS, Main)