UNPKG

node-datachannel

Version:

WebRTC For Node.js and Electron. libdatachannel node bindings.

270 lines (233 loc) 8.47 kB
#include "web-socket-server-wrapper.h" #include "plog/Log.h" Napi::FunctionReference WebSocketServerWrapper::constructor = Napi::FunctionReference(); std::unordered_set<WebSocketServerWrapper *> WebSocketServerWrapper::instances; void WebSocketServerWrapper::StopAll() { PLOG_DEBUG << "StopAll() called"; auto copy(instances); for (auto inst : copy) inst->doStop(); } Napi::Object WebSocketServerWrapper::Init(Napi::Env env, Napi::Object exports) { Napi::HandleScope scope(env); Napi::Function func = DefineClass( env, "WebSocketServer", { InstanceMethod("stop", &WebSocketServerWrapper::stop), InstanceMethod("port", &WebSocketServerWrapper::port), InstanceMethod("onClient", &WebSocketServerWrapper::onClient) }); // If this is not the first call, we don't want to reassign the constructor (hot-reload problem) if(constructor.IsEmpty()) { constructor = Napi::Persistent(func); constructor.SuppressDestruct(); } exports.Set("WebSocketServer", func); return exports; } WebSocketServerWrapper::WebSocketServerWrapper(const Napi::CallbackInfo &info) : Napi::ObjectWrap<WebSocketServerWrapper>(info) { PLOG_DEBUG << "Constructor called"; Napi::Env env = info.Env(); // Create WebSocketServer without config if (info.Length() == 0) { try { PLOG_DEBUG << "Creating a new WebSocketServer without config"; mWebSocketServerPtr = std::make_unique<rtc::WebSocketServer>(); } catch (std::exception &ex) { Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer without config: ") + ex.what()).ThrowAsJavaScriptException(); return; } PLOG_DEBUG << "WebSocketServer created without config"; instances.insert(this); return; } // Create WebSocketServer with config Napi::Object config = info[0].As<Napi::Object>(); rtc::WebSocketServerConfiguration webSocketServerConfig; // Port if (config.Has("port")) { if (!config.Get("port").IsNumber()) { Napi::TypeError::New(info.Env(), "port must be a number").ThrowAsJavaScriptException(); return; } webSocketServerConfig.port = config.Get("port").ToNumber().Uint32Value(); } // Enable TLS if (config.Has("enableTls")) { if (!config.Get("enableTls").IsBoolean()) { Napi::TypeError::New(info.Env(), "enableTls must be boolean").ThrowAsJavaScriptException(); return; } webSocketServerConfig.enableTls = config.Get("enableTls").ToBoolean(); } // Certificate PEM File if (config.Has("certificatePemFile")) { if (!config.Get("certificatePemFile").IsString()) { Napi::TypeError::New(info.Env(), "certificatePemFile must be a string").ThrowAsJavaScriptException(); return; } webSocketServerConfig.certificatePemFile = config.Get("certificatePemFile").ToString(); } // Key PEM File if (config.Has("keyPemFile")) { if (!config.Get("keyPemFile").IsString()) { Napi::TypeError::New(info.Env(), "keyPemFile must be a string").ThrowAsJavaScriptException(); return; } webSocketServerConfig.keyPemFile = config.Get("keyPemFile").ToString(); } // Key PEM Pass if (config.Has("keyPemPass")) { if (!config.Get("keyPemPass").IsString()) { Napi::TypeError::New(info.Env(), "keyPemPass must be a string").ThrowAsJavaScriptException(); return; } webSocketServerConfig.keyPemPass = config.Get("keyPemPass").ToString(); } // Bind Address if (config.Has("bindAddress")) { if (!config.Get("bindAddress").IsString()) { Napi::TypeError::New(info.Env(), "bindAddress must be a string").ThrowAsJavaScriptException(); return; } webSocketServerConfig.bindAddress = config.Get("bindAddress").ToString(); } // Connection Timeout if (config.Has("connectionTimeout")) { if (!config.Get("connectionTimeout").IsNumber()) { Napi::TypeError::New(info.Env(), "connectionTimeout must be a number").ThrowAsJavaScriptException(); return; } webSocketServerConfig.connectionTimeout = std::chrono::milliseconds(config.Get("connectionTimeout").ToNumber().Int64Value()); } // Max Message Size if (config.Has("maxMessageSize")) { if (!config.Get("maxMessageSize").IsNumber()) { Napi::TypeError::New(info.Env(), "maxMessageSize must be a number").ThrowAsJavaScriptException(); return; } webSocketServerConfig.maxMessageSize = config.Get("maxMessageSize").ToNumber().Int32Value(); } // Create WebSocketServer with config try { PLOG_DEBUG << "Creating a new WebSocketServer"; mWebSocketServerPtr = std::make_unique<rtc::WebSocketServer>(webSocketServerConfig); } catch (std::exception &ex) { Napi::Error::New(env, std::string("libdatachannel error while creating WebSocketServer: ") + ex.what()).ThrowAsJavaScriptException(); return; } PLOG_DEBUG << "WebSocketServer created"; instances.insert(this); } WebSocketServerWrapper::~WebSocketServerWrapper() { PLOG_DEBUG << "Destructor called"; doStop(); } void WebSocketServerWrapper::doStop() { PLOG_DEBUG << "doStop() called"; if (mWebSocketServerPtr) { PLOG_DEBUG << "Stopping..."; try { mWebSocketServerPtr->stop(); mWebSocketServerPtr.reset(); } catch (std::exception &ex) { std::cerr << std::string("libdatachannel error while closing WebSocketServer: ") + ex.what() << std::endl; return; } } mOnClientCallback.reset(); instances.erase(this); } void WebSocketServerWrapper::stop(const Napi::CallbackInfo &info) { PLOG_DEBUG << "stop() called"; doStop(); } Napi::Value WebSocketServerWrapper::port(const Napi::CallbackInfo &info) { PLOG_DEBUG << "port() called"; Napi::Env env = info.Env(); if (!mWebSocketServerPtr) { return Napi::Number::New(info.Env(), 0); } try { return Napi::Number::New(info.Env(), mWebSocketServerPtr->port()); } catch (std::exception &ex) { Napi::Error::New(env, std::string("WebSocketServer error: ") + ex.what()).ThrowAsJavaScriptException(); return Napi::Number::New(info.Env(), 0); } } void WebSocketServerWrapper::onClient(const Napi::CallbackInfo &info) { PLOG_DEBUG << "onClient() called"; Napi::Env env = info.Env(); int length = info.Length(); if (!mWebSocketServerPtr) { Napi::Error::New(env, "onClient() called on destroyed WebSocketServer").ThrowAsJavaScriptException(); return; } if (length < 1 || !info[0].IsFunction()) { Napi::TypeError::New(env, "Function expected as onClient callback").ThrowAsJavaScriptException(); return; } // Callback mOnClientCallback = std::make_unique<ThreadSafeCallback>(info[0].As<Napi::Function>()); mWebSocketServerPtr->onClient([&](std::shared_ptr<rtc::WebSocket> ws) { PLOG_DEBUG << "onClient ws received from WebSocketServer"; if (mOnClientCallback) mOnClientCallback->call([this, ws](Napi::Env env, std::vector<napi_value> &args) { PLOG_DEBUG << "mOnClientCallback call(1)"; // Check the WebSocketServer is not stopped if(instances.find(this) == instances.end()) throw ThreadSafeCallback::CancelException(); // This will run in main thread and needs to construct the // arguments for the call std::shared_ptr<rtc::WebSocket> webSocket = ws; // First argument is just a placeholder auto instance = WebSocketWrapper::constructor.New({Napi::Boolean::New(env, false), Napi::External<std::shared_ptr<rtc::WebSocket>>::New(env, &webSocket)}); args = {instance}; PLOG_DEBUG << "mOnClientCallback call(2)"; }); }); }