From 31278c13f3bc8bdc4354c625f43757edad6bcb19 Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Tue, 8 Jan 2019 22:04:56 +0100 Subject: [PATCH] Properly support SSL and non-SSL --- examples/HelloWorld.js | 7 +- src/addon.cpp | 145 ++++++++++++++++++++++++++++------------- uWebSockets | 2 +- 3 files changed, 104 insertions(+), 50 deletions(-) diff --git a/examples/HelloWorld.js b/examples/HelloWorld.js index 5867b82..00c1161 100644 --- a/examples/HelloWorld.js +++ b/examples/HelloWorld.js @@ -5,18 +5,19 @@ const port = 9001; // nice milestone to pass autobahn on both ssl and non-ssl with an without compression -const app = uWS./*SSL*/App({ +const app = uWS.SSLApp({ key_file_name: '/home/alexhultman/key.pem', cert_file_name: '/home/alexhultman/cert.pem', passphrase: '1234' }).get('/hello', (res, req) => { res.end('Hejdå!'); }).ws('/*', { + maxPayloadLength: 16 * 1024 * 1024, open: (ws, req) => { console.log(ws); }, - message: (ws, message, opCode) => { - ws.send(message, opCode); // need op to pass autobahn (guess binary on off is enough?) + message: (ws, message, isBinary) => { + ws.send(message, isBinary); } }).listen(port, (token) => { if (token) { diff --git a/src/addon.cpp b/src/addon.cpp index c668ff9..94b8cbf 100644 --- a/src/addon.cpp +++ b/src/addon.cpp @@ -19,9 +19,6 @@ #include using namespace v8; Isolate *isolate; -Persistent resTemplate; -Persistent reqTemplate; -Persistent wsTemplate; #include #include @@ -68,29 +65,100 @@ public: } }; +// 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) { - - // 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.GetIsolate(), args[0]); - ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->end(std::string_view(data.getData(), data.getLength())); + ((uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0))->end(std::string_view(data.getData(), data.getLength())); - // Return this args.GetReturnValue().Set(args.Holder()); } +template void res_writeHeader(const FunctionCallbackInfo &args) { - // get string 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()), + ((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(); +} + +Persistent reqTemplate; + void req_getHeader(const FunctionCallbackInfo &args) { // get string NativeString data(args.GetIsolate(), args[0]); @@ -101,18 +169,6 @@ void req_getHeader(const FunctionCallbackInfo &args) { args.GetReturnValue().Set(String::NewFromUtf8(isolate, header.data(), v8::String::kNormalString, header.length())); } -/* WebSocket send */ -// not properly templated, just like the httpsockets are not! -void uWS_WebSocket_send(const FunctionCallbackInfo &args) { - uWS::WebSocket *ws = (uWS::WebSocket *) args.Holder()->GetAlignedPointerFromInternalField(0); - - NativeString nativeString(args.GetIsolate(), args[0]); - - //std::cout << std::string_view(nativeString.getData(), nativeString.getLength()) << std::endl; - - ws->send(std::string_view(nativeString.getData(), nativeString.getLength()), uWS::OpCode::TEXT); -} - /* uWS.App.ws('/pattern', options) */ template void uWS_App_ws(const FunctionCallbackInfo &args) { @@ -123,6 +179,8 @@ void uWS_App_ws(const FunctionCallbackInfo &args) { Persistent *openPf = new Persistent(); Persistent *messagePf = new Persistent(); + int maxPayloadLength = 0; + struct PerSocketData { Persistent *socketPf; }; @@ -131,6 +189,9 @@ void uWS_App_ws(const FunctionCallbackInfo &args) { if (args.Length() == 2) { Local behaviorObject = Local::Cast(args[1]); + /* maxPayloadLength */ + maxPayloadLength = behaviorObject->Get(String::NewFromUtf8(isolate, "maxPayloadLength"))->Int32Value(); + /* Open */ openPf->Reset(args.GetIsolate(), Local::Cast(behaviorObject->Get(String::NewFromUtf8(isolate, "open")))); /* Message */ @@ -139,13 +200,13 @@ void uWS_App_ws(const FunctionCallbackInfo &args) { app->template ws(std::string(nativeString.getData(), nativeString.getLength()), { - /*.compression = uWS::SHARED_COMPRESSOR, - .maxPayloadLength = 16 * 1024,*/ + /*.compression = uWS::SHARED_COMPRESSOR,*/ + .maxPayloadLength = maxPayloadLength, .open = [openPf](auto *ws, auto *req) { HandleScope hs(isolate); /* Create a new websocket object */ - Local wsObject = Local::New(isolate, wsTemplate)->Clone(); + Local wsObject = getWsInstance(); wsObject->SetAlignedPointerInInternalField(0, ws); /* Attach a new V8 object with pointer to us, to us */ @@ -160,8 +221,11 @@ void uWS_App_ws(const FunctionCallbackInfo &args) { HandleScope hs(isolate); PerSocketData *perSocketData = (PerSocketData *) ws->getUserData(); - Local argv[2] = {Local::New(isolate, *(perSocketData->socketPf)), ArrayBuffer::New(isolate, (void *) message.data(), message.length())}; // ws, message, opCode - Local::New(isolate, *messagePf)->Call(isolate->GetCurrentContext()->Global(), 2, argv); + Local argv[3] = {Local::New(isolate, *(perSocketData->socketPf)), + ArrayBuffer::New(isolate, (void *) message.data(), message.length()), + Boolean::New(isolate, opCode == uWS::OpCode::BINARY) + }; + Local::New(isolate, *messagePf)->Call(isolate->GetCurrentContext()->Global(), 3, argv); }/* .drain = []() {}, .ping = []() {}, @@ -188,7 +252,7 @@ void uWS_App_get(const FunctionCallbackInfo &args) { app->get(std::string(nativeString.getData(), nativeString.getLength()), [pf](auto *res, auto *req) { HandleScope hs(isolate); - Local resObject = Local::New(isolate, resTemplate)->Clone(); + Local resObject = getResInstance(); resObject->SetAlignedPointerInInternalField(0, res); Local reqObject = Local::New(isolate, reqTemplate)->Clone(); @@ -324,23 +388,12 @@ void Main(Local exports) { exports->Set(String::NewFromUtf8(isolate, "nextTick"), FunctionTemplate::New(isolate, nextTick)->GetFunction()); /* The template for websockets */ - Local wsTemplateLocal = FunctionTemplate::New(isolate); - wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.WebSocket")); - wsTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); - wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "send"), FunctionTemplate::New(isolate, uWS_WebSocket_send)); + initWsTemplate<0>(); + initWsTemplate<1>(); - Local wsObjectLocal = wsTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); - wsTemplate.Reset(isolate, wsObjectLocal); - - // HttpResponse template (not templated) - 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); + /* Initialize SSL and non-SSL templates */ + initResTemplate<0>(); + initResTemplate<1>(); // Request template (do we need to clone this?) Local reqTemplateLocal = FunctionTemplate::New(isolate); diff --git a/uWebSockets b/uWebSockets index 46537f6..8efea34 160000 --- a/uWebSockets +++ b/uWebSockets @@ -1 +1 @@ -Subproject commit 46537f6f2d500bc611e5dc6bb78a7a5da3baea01 +Subproject commit 8efea340e66bc91efbe31b618a446f821c214bbf