#include "App.h" #include "Utilities.h" #include using namespace v8; /* todo: probably isCorked, cork should be exposed? */ struct WebSocketWrapper { template static inline uWS::WebSocket *getWebSocket(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = (uWS::WebSocket *) args.Holder()->GetAlignedPointerFromInternalField(0); if (!ws) { args.GetReturnValue().Set(isolate->ThrowException(String::NewFromUtf8(isolate, "Invalid access of closed uWS.WebSocket/SSLWebSocket.", NewStringType::kNormal).ToLocalChecked())); } return ws; } static inline void invalidateWsObject(const FunctionCallbackInfo &args) { args.Holder()->SetAlignedPointerInInternalField(0, nullptr); } /* Takes string topic */ template static void uWS_WebSocket_subscribe(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { NativeString topic(isolate, args[0]); if (topic.isInvalid(args)) { return; } ws->subscribe(topic.getString()); } } /* Takes string topic, returns boolean success */ template static void uWS_WebSocket_unsubscribe(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { NativeString topic(isolate, args[0]); if (topic.isInvalid(args)) { return; } bool success = ws->unsubscribe(topic.getString()); args.GetReturnValue().Set(Boolean::New(isolate, success)); } } /* Takes string topic, message */ template static void uWS_WebSocket_publish(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { NativeString topic(isolate, args[0]); NativeString message(isolate, args[1]); ws->publish(topic.getString(), message.getString(), BooleanValue(isolate, args[2]) ? uWS::OpCode::BINARY : uWS::OpCode::TEXT, BooleanValue(isolate, args[3])); } } /* It would make sense to call terminate "close" and call close "end" to line up with HTTP */ /* That also makes sense seince close takes message and code -> you can end with a string message */ /* Takes nothing returns nothing */ template static void uWS_WebSocket_close(const FunctionCallbackInfo &args) { auto *ws = getWebSocket(args); if (ws) { invalidateWsObject(args); ws->close(); } } /* Takes code, message, returns undefined */ template static void uWS_WebSocket_end(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { int code = 0; if (args.Length() >= 1) { code = args[0]->Uint32Value(isolate->GetCurrentContext()).ToChecked(); } NativeString message(args.GetIsolate(), args[1]); if (message.isInvalid(args)) { return; } invalidateWsObject(args); ws->end(code, message.getString()); } } /* Takes nothing returns arraybuffer */ template static void uWS_WebSocket_getRemoteAddress(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { std::string_view ip = ws->getRemoteAddress(); /* Todo: we need to pass a copy here */ args.GetReturnValue().Set(ArrayBuffer::New(isolate, (void *) ip.data(), ip.length()/*, ArrayBufferCreationMode::kInternalized*/)); } } /* Takes nothing returns arraybuffer */ template static void uWS_WebSocket_getRemoteAddressAsText(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { std::string_view ip = ws->getRemoteAddressAsText(); /* Todo: we need to pass a copy here */ args.GetReturnValue().Set(ArrayBuffer::New(isolate, (void *) ip.data(), ip.length()/*, ArrayBufferCreationMode::kInternalized*/)); } } /* Takes nothing, returns integer */ template static void uWS_WebSocket_getBufferedAmount(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { unsigned int bufferedAmount = ws->getBufferedAmount(); args.GetReturnValue().Set(Integer::NewFromUnsigned(isolate, bufferedAmount)); } } /* Takes message, isBinary, compressed. Returns true on success, false otherwise */ template static void uWS_WebSocket_send(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { NativeString message(args.GetIsolate(), args[0]); if (message.isInvalid(args)) { return; } bool ok = ws->send(message.getString(), BooleanValue(isolate, args[1]) ? uWS::OpCode::BINARY : uWS::OpCode::TEXT, BooleanValue(isolate, args[2])); args.GetReturnValue().Set(Boolean::New(isolate, ok)); } } /* Takes message. Returns true on success, false otherwise */ template static void uWS_WebSocket_ping(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { NativeString message(args.GetIsolate(), args[0]); if (message.isInvalid(args)) { return; } /* This is a wrapper that does not exist in the C++ project */ bool ok = ws->send(message.getString(), uWS::OpCode::PING); args.GetReturnValue().Set(Boolean::New(isolate, ok)); } } /* Takes nothing, returns this */ template static void uWS_WebSocket_unsubscribeAll(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { ws->unsubscribeAll(); args.GetReturnValue().Set(args.Holder()); } } /* Takes function, returns this */ template static void uWS_WebSocket_cork(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *ws = getWebSocket(args); if (ws) { ws->cork([cb = Local::Cast(args[0]), isolate]() { /* No need for CallJS here */ cb->Call(isolate->GetCurrentContext(), isolate->GetCurrentContext()->Global(), 0, nullptr).IsEmpty(); }); args.GetReturnValue().Set(args.Holder()); } } template static Local init(Isolate *isolate) { Local wsTemplateLocal = FunctionTemplate::New(isolate); if (SSL) { wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLWebSocket", NewStringType::kNormal).ToLocalChecked()); } else { wsTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.WebSocket", NewStringType::kNormal).ToLocalChecked()); } wsTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); /* Register our functions */ wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "send", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_send)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_end)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_close)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getBufferedAmount", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getBufferedAmount)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddress)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "subscribe", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_subscribe)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "unsubscribe", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_unsubscribe)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "publish", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_publish)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "unsubscribeAll", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_unsubscribeAll)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_cork)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "ping", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_ping)); wsTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddressAsText", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, uWS_WebSocket_getRemoteAddressAsText)); /* Create the template */ Local wsObjectLocal = wsTemplateLocal->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); return wsObjectLocal; } };