Static Webview - RTC - UDP - TCP - WSS

This commit is contained in:
2026-10-03 01:22:41 -07:00
parent 058104d93f
commit 75224df058
19 changed files with 1994 additions and 284 deletions

View File

@ -101,11 +101,19 @@ namespace WebSocketWrapper {
void processEvents() {
try {
std::vector<int> closed_ids;
for (auto& [id, client] : active_websockets) {
if (client) {
client->processEvents();
if (client->getReadyState() == CLOSED
&& !client->hasPendingEvents()) {
closed_ids.push_back(id);
}
}
}
for (int id : closed_ids) {
active_websockets.erase(id);
}
} catch (...) {
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: CRASH in global processEvents");
@ -120,137 +128,9 @@ namespace WebSocketWrapper {
return id;
}
class SimpleWebSocketClient {
private:
SOCKET socket_;
std::thread* thread_;
std::atomic<bool> connected_{false};
std::atomic<bool> should_stop_{false};
std::string host_;
int port_;
std::string path_;
public:
SimpleWebSocketClient(const std::string& url) : socket_(INVALID_SOCKET), thread_(nullptr) {
parseUrl(url);
}
~SimpleWebSocketClient() {
disconnect();
}
bool connect() {
socket_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (socket_ == INVALID_SOCKET) {
return false;
}
SocketOptimization::optimizeWebSocket(static_cast<int>(socket_));
sockaddr_in addr{};
addr.sin_family = AF_INET;
addr.sin_port = htons(port_);
inet_pton(AF_INET, host_.c_str(), &addr.sin_addr);
if (::connect(socket_, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
closesocket(socket_);
socket_ = INVALID_SOCKET;
return false;
}
std::string handshake =
"GET " + path_ + " HTTP/1.1\r\n"
"Host: " + host_ + ":" + std::to_string(port_) + "\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
"Sec-WebSocket-Version: 13\r\n\r\n";
if (send(socket_, handshake.c_str(), static_cast<int>(handshake.length()), 0) == SOCKET_ERROR) {
closesocket(socket_);
socket_ = INVALID_SOCKET;
return false;
}
char buffer[SocketOptimization::SMALL_BUFFER_SIZE];
int bytes = recv(socket_, buffer, sizeof(buffer) - 1, 0);
if (bytes > 0) {
buffer[bytes] = '\0';
if (strstr(buffer, "101 Switching Protocols")) {
connected_ = true;
return true;
}
}
closesocket(socket_);
socket_ = INVALID_SOCKET;
return false;
}
void disconnect() {
should_stop_ = true;
if (socket_ != INVALID_SOCKET) {
closesocket(socket_);
socket_ = INVALID_SOCKET;
}
if (thread_ && thread_->joinable()) {
thread_->join();
delete thread_;
thread_ = nullptr;
}
connected_ = false;
}
bool isConnected() const {
return connected_;
}
void sendText(const std::string& data) {
if (!connected_ || socket_ == INVALID_SOCKET) return;
// text frame format FIN + text frame
std::vector<uint8_t> frame;
frame.push_back(0x81);
if (data.length() < 126) {
frame.push_back(0x80 | static_cast<uint8_t>(data.length())); // MASK + length
} else {
// TODO: support longer messages
return;
}
// adds simplified masking key
uint8_t mask[4] = {0x12, 0x34, 0x56, 0x78};
frame.insert(frame.end(), mask, mask + 4);
// adds the masked payload
for (size_t i = 0; i < data.length(); i++) {
frame.push_back(data[i] ^ mask[i % 4]);
}
send(socket_, (char*)frame.data(), static_cast<int>(frame.size()), 0);
}
private:
void parseUrl(const std::string& url) {
std::regex url_regex(R"(^wss?://([^:/]+)(?::(\d+))?(/.*)?$)");
std::smatch matches;
if (std::regex_match(url, matches, url_regex)) {
host_ = matches[1].str();
port_ = matches[2].matched ? std::stoi(matches[2].str()) : 80;
path_ = matches[3].matched ? matches[3].str() : "/";
} else {
host_ = "localhost";
port_ = 80;
path_ = "/";
}
}
};
WebSocketClient::WebSocketClient(Isolate* isolate, Global<Context>* global_context, const std::string& url)
: isolate_(isolate), global_context_(global_context), url_(url), ready_state_(CONNECTING),
is_ssl_(false), buffered_amount_(0), ws_(nullptr), ssl_ws_(nullptr)
is_ssl_(false), buffered_amount_(0), ws_(-1)
#ifdef WITH_SSL
#ifdef _WIN32
, ssl_cred_handle_(nullptr), ssl_context_handle_(nullptr), ssl_context_initialized_(false), ssl_socket_(-1)
@ -278,21 +158,35 @@ namespace WebSocketWrapper {
ready_state_ = CONNECTING;
// start connection using thread pool
GlobalThreadPool::getInstance().enqueue([this, host, port, path]() {
connect_future_ = GlobalThreadPool::getInstance().enqueue([this, host, port, path]() {
try {
if (ready_state_ == CLOSED) {
return;
}
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket connecting to %s:%d%s (SSL: %s)",
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket connecting to %s:%d%s (SSL: %s)",
host.c_str(), port, path.c_str(), is_ssl_ ? "yes" : "no");
#endif
if (connectToServer(host, port, path)) {
ready_state_ = OPEN;
handleOpen();
messageLoop();
if (ready_state_ == OPEN) {
messageLoop();
} else {
if (is_ssl_) {
cleanupWebSocketSSL();
}
if (ws_ >= 0) {
#ifdef _WIN32
closesocket((SOCKET)ws_);
#else
::close(ws_);
#endif
ws_ = -1;
}
}
} else {
ready_state_ = CLOSED;
handleError("Connection failed");
ready_state_ = CLOSED;
}
} catch (...) {
ready_state_ = CLOSED;
@ -304,9 +198,39 @@ namespace WebSocketWrapper {
if (ready_state_ == OPEN) {
close();
}
ready_state_ = CLOSED;
if (ws_ >= 0) {
#ifdef _WIN32
::shutdown((SOCKET)ws_, SD_BOTH);
#else
::shutdown(ws_, SHUT_RDWR);
#endif
}
if (connect_future_.valid()) {
connect_future_.wait();
}
if (ws_ >= 0) {
#ifdef _WIN32
closesocket((SOCKET)ws_);
#else
::close(ws_);
#endif
ws_ = -1;
}
#ifdef WITH_SSL
cleanupWSL();
#endif
if (isolate_ != nullptr) {
v8::Locker locker{isolate_};
v8::Isolate::Scope isolate_scope(isolate_);
v8::HandleScope handle_scope(isolate_);
on_open_.Reset();
on_message_.Reset();
on_error_.Reset();
on_close_.Reset();
}
WebSocketEvent ev;
while (event_queue_.try_pop(ev)) {}
}
bool WebSocketClient::parseUrl(const std::string& url, std::string& host, int& port, std::string& path) {
@ -338,7 +262,7 @@ namespace WebSocketWrapper {
return;
}
if (ws_ == nullptr) {
if (ws_ < 0) {
handleError("WebSocket: No socket available for sending");
return;
}
@ -353,7 +277,7 @@ namespace WebSocketWrapper {
// create and send WebSocket frame
std::vector<uint8_t> frame = createWebSocketFrame(data);
int sock = static_cast<int>(reinterpret_cast<intptr_t>(ws_));
int sock = ws_;
int sendResult;
if (is_ssl_ && isSSLReady()) {
@ -388,7 +312,7 @@ namespace WebSocketWrapper {
return;
}
if (ws_ == nullptr) {
if (ws_ < 0) {
handleError("WebSocket: No socket available for sending");
return;
}
@ -403,7 +327,7 @@ namespace WebSocketWrapper {
// binary frame with opcode 0x02
std::vector<uint8_t> frame = createWebSocketBinaryFrame(data);
int sock = static_cast<int>(reinterpret_cast<intptr_t>(ws_));
int sock = ws_;
int sendResult;
if (is_ssl_ && isSSLReady()) {
@ -434,8 +358,17 @@ namespace WebSocketWrapper {
return;
}
if (ready_state_ == OPEN && ws_ >= 0) {
std::vector<uint8_t> frame = createCloseFrame(code, reason);
if (is_ssl_ && isSSLReady()) {
webSocketSSLSend(frame.data(), static_cast<int>(frame.size()));
} else {
int sock = ws_;
::send(sock, reinterpret_cast<const char*>(frame.data()), static_cast<int>(frame.size()), 0);
}
}
ready_state_ = CLOSING;
ready_state_ = CLOSED;
handleClose(code, reason);
}
@ -457,16 +390,6 @@ namespace WebSocketWrapper {
on_close_.Reset(isolate_, callback);
}
// event handlers called from worker thread that queue events safely
void WebSocketClient::handleOpen() {
event_queue_.push(WebSocketEvent(EVENT_OPEN));
}
void WebSocketClient::handleMessage(const std::string& message) {
// push directly to lock free queue
event_queue_.push(WebSocketEvent(EVENT_MESSAGE, message));
}
void WebSocketClient::handleError(const std::string& error) {
try {
#ifdef DEBUG_NETWORK
@ -479,8 +402,8 @@ namespace WebSocketWrapper {
}
void WebSocketClient::handleClose(int code, const std::string& reason) {
ready_state_ = CLOSED;
event_queue_.push(WebSocketEvent(EVENT_CLOSE, code, reason));
ready_state_ = CLOSED;
}
// process events on main thread with lock free queue and batching
@ -928,6 +851,8 @@ namespace WebSocketWrapper {
#endif
return false;
}
ws_ = sock;
sockaddr_in addr{};
addr.sin_family = AF_INET;
@ -945,6 +870,7 @@ namespace WebSocketWrapper {
#else
close(sock);
#endif
ws_ = -1;
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: Failed to resolve host %s", host.c_str());
#endif
@ -961,6 +887,7 @@ namespace WebSocketWrapper {
#else
close(sock);
#endif
ws_ = -1;
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: Failed to connect to %s:%d", host.c_str(), port);
#endif
@ -971,7 +898,7 @@ namespace WebSocketWrapper {
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: TCP connected to %s:%d", host.c_str(), port);
#endif
ws_ = reinterpret_cast<void*>(static_cast<intptr_t>(sock));
ws_ = sock;
if (is_ssl_ && port == 443) {
if (!initializeWebSocketSSL(sock, host)) {
@ -980,7 +907,7 @@ namespace WebSocketWrapper {
#else
close(sock);
#endif
ws_ = nullptr;
ws_ = -1;
return false;
}
#ifdef DEBUG_NETWORK
@ -995,7 +922,7 @@ namespace WebSocketWrapper {
#else
close(sock);
#endif
ws_ = nullptr;
ws_ = -1;
return false;
}
@ -1003,9 +930,11 @@ namespace WebSocketWrapper {
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: Handshake completed successfully");
#endif
ready_state_ = OPEN;
fireOpenEvent();
if (ready_state_ == CONNECTING) {
ready_state_ = OPEN;
fireOpenEvent();
}
return true;
} catch (const std::exception& e) {
@ -1060,23 +989,35 @@ namespace WebSocketWrapper {
}
char buffer[SocketOptimization::SMALL_BUFFER_SIZE];
int bytesReceived;
if (is_ssl_ && isSSLReady()) {
bytesReceived = webSocketSSLReceive(buffer, sizeof(buffer) - 1);
} else {
bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0);
std::string response;
for (int attempt = 0; attempt < 64
&& response.find("\r\n\r\n") == std::string::npos; ++attempt) {
int bytesReceived;
if (is_ssl_ && isSSLReady()) {
bytesReceived = webSocketSSLReceive(buffer, sizeof(buffer) - 1);
if (bytesReceived == 0) {
continue;
}
} else {
bytesReceived = recv(sock, buffer, sizeof(buffer) - 1, 0);
}
if (bytesReceived <= 0) {
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: Failed to receive handshake response");
#endif
return false;
}
response.append(buffer, bytesReceived);
}
if (bytesReceived <= 0) {
if (response.find("\r\n\r\n") == std::string::npos) {
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: Failed to receive handshake response");
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: Handshake response never completed");
#endif
return false;
}
buffer[bytesReceived] = '\0';
std::string response(buffer);
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: Received handshake response");
#endif
@ -1155,8 +1096,8 @@ namespace WebSocketWrapper {
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: Connection closed: %d - %s", code, reason.c_str());
#endif
ready_state_ = CLOSED;
event_queue_.push(WebSocketEvent(EVENT_CLOSE, code, reason));
ready_state_ = CLOSED;
}
// complete frame helper for ring buffer messageLoop
@ -1186,14 +1127,23 @@ namespace WebSocketWrapper {
close_reason = std::string(payload.begin() + 2, payload.end());
}
}
fireCloseEvent(close_code, close_reason);
if (ready_state_ == OPEN) {
std::vector<uint8_t> echoFrame = createCloseFrame(close_code, "");
if (is_ssl_ && isSSLReady()) {
webSocketSSLSend(echoFrame.data(), static_cast<int>(echoFrame.size()));
} else {
int sock = ws_;
::send(sock, reinterpret_cast<const char*>(echoFrame.data()), static_cast<int>(echoFrame.size()), 0);
}
fireCloseEvent(close_code, close_reason);
}
}
break;
case 0x9: // ping frame
{
std::vector<uint8_t> pongFrame = createPongFrame(payload);
int sock = static_cast<int>(reinterpret_cast<intptr_t>(ws_));
int sock = ws_;
if (is_ssl_ && isSSLReady()) {
webSocketSSLSend(pongFrame.data(), static_cast<int>(pongFrame.size()));
} else {
@ -1213,14 +1163,14 @@ namespace WebSocketWrapper {
// following uWebSockets
void WebSocketClient::messageLoop() {
if (ws_ == nullptr) {
if (ws_ < 0) {
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_ERROR, "WebSocket: No socket available for message loop");
#endif
return;
}
int sock = static_cast<int>(reinterpret_cast<intptr_t>(ws_));
int sock = ws_;
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: Starting message loop");
#endif
@ -1271,8 +1221,11 @@ namespace WebSocketWrapper {
} else {
bytesReceived = recv(sock, reinterpret_cast<char*>(writePtr), static_cast<int>(contiguousSpace), 0);
}
if (bytesReceived <= 0) {
if (bytesReceived == 0 && is_ssl_ && isSSLReady()) {
continue;
}
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: Connection closed by server");
#endif
@ -1357,6 +1310,10 @@ namespace WebSocketWrapper {
}
}
if (ready_state_ == OPEN) {
fireCloseEvent(1006, "");
}
#ifdef DEBUG_NETWORK
kinc_log(KINC_LOG_LEVEL_INFO, "WebSocket: Message loop completed");
#endif
@ -1464,7 +1421,7 @@ namespace WebSocketWrapper {
case 0x9:
{
std::vector<uint8_t> pongFrame = createPongFrame(payload);
int sock = static_cast<int>(reinterpret_cast<intptr_t>(ws_));
int sock = ws_;
int sendResult;
if (is_ssl_ && isSSLReady()) {
@ -1964,7 +1921,7 @@ namespace WebSocketWrapper {
}
}
if (status == SEC_I_CONTINUE_NEEDED && outbuffers[0].cbBuffer > 0 && outbuffers[0].pvBuffer != nullptr) {
if ((status == SEC_I_CONTINUE_NEEDED || status == SEC_E_OK) && outbuffers[0].cbBuffer > 0 && outbuffers[0].pvBuffer != nullptr) {
// validate buffer before sending
if (outbuffers[0].cbBuffer < 0 || outbuffers[0].cbBuffer > 65536) {
kinc_log(KINC_LOG_LEVEL_ERROR, "Invalid WebSocket SSL handshake buffer size: %d bytes (corrupted data)", outbuffers[0].cbBuffer);
@ -2024,6 +1981,10 @@ namespace WebSocketWrapper {
}
if (status == SEC_E_OK) {
if (!handshake_buffer.empty()) {
ssl_buffer_.insert(ssl_buffer_.end(), handshake_buffer.begin(),
handshake_buffer.end());
}
ssl_context_initialized_ = true;
kinc_log(KINC_LOG_LEVEL_INFO, "SChannel WebSocket SSL handshake completed successfully");
return true;
@ -2649,6 +2610,160 @@ void runt_websocket_set_onclose(const FunctionCallbackInfo<Value>& args) {
}
// Native WebSocket class implementation for V8
static void dispatchWebSocketEvent(const FunctionCallbackInfo<Value>& args, const char* type, const char* handler) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Object> self = args.Data().As<Object>();
Local<Object> event;
if (args.Length() > 0 && args[0]->IsObject()) {
event = args[0].As<Object>();
} else {
event = Object::New(isolate);
}
event->Set(context, String::NewFromUtf8(isolate, "type").ToLocalChecked(), String::NewFromUtf8(isolate, type).ToLocalChecked());
event->Set(context, String::NewFromUtf8(isolate, "target").ToLocalChecked(), self);
event->Set(context, String::NewFromUtf8(isolate, "currentTarget").ToLocalChecked(), self);
Local<Value> argv[1] = { event };
Local<Value> fn = self->Get(context, String::NewFromUtf8(isolate, handler).ToLocalChecked()).ToLocalChecked();
if (fn->IsFunction()) {
TryCatch try_catch(isolate);
fn.As<Function>()->Call(context, self, 1, argv);
if (try_catch.HasCaught()) {
String::Utf8Value err(isolate, try_catch.Exception());
kinc_log(KINC_LOG_LEVEL_WARNING, "WebSocket %s handler threw: %s", type,
*err != nullptr ? *err : "unknown");
}
}
Local<Value> listenersVal = self->Get(context, String::NewFromUtf8(isolate, "_listeners").ToLocalChecked()).ToLocalChecked();
if (listenersVal->IsObject()) {
Local<Value> arr = listenersVal.As<Object>()->Get(context, String::NewFromUtf8(isolate, type).ToLocalChecked()).ToLocalChecked();
if (arr->IsArray()) {
Local<Array> listeners = arr.As<Array>();
uint32_t length = listeners->Length();
std::vector<Local<Function>> snapshot;
snapshot.reserve(length);
for (uint32_t i = 0; i < length; ++i) {
Local<Value> listener = listeners->Get(context, i).ToLocalChecked();
if (listener->IsFunction()) {
snapshot.push_back(listener.As<Function>());
}
}
for (Local<Function>& listener : snapshot) {
TryCatch try_catch(isolate);
listener->Call(context, self, 1, argv);
if (try_catch.HasCaught()) {
String::Utf8Value err(isolate, try_catch.Exception());
kinc_log(KINC_LOG_LEVEL_WARNING, "WebSocket %s listener threw: %s", type,
*err != nullptr ? *err : "unknown");
}
}
}
}
}
static void WebSocketDispatchOpen(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Object> self = args.Data().As<Object>();
Local<Value> idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked();
if (idVal->IsNumber()) {
int wsId = idVal->Int32Value(context).FromJust();
auto it = WebSocketWrapper::active_websockets.find(wsId);
if (it != WebSocketWrapper::active_websockets.end()) {
self->Set(context, String::NewFromUtf8(isolate, "protocol").ToLocalChecked(),
String::NewFromUtf8(isolate, it->second->getProtocol().c_str()).ToLocalChecked());
}
}
dispatchWebSocketEvent(args, "open", "onopen");
}
static void WebSocketDispatchMessage(const FunctionCallbackInfo<Value>& args) {
dispatchWebSocketEvent(args, "message", "onmessage");
}
static void WebSocketDispatchError(const FunctionCallbackInfo<Value>& args) {
dispatchWebSocketEvent(args, "error", "onerror");
}
static void WebSocketDispatchClose(const FunctionCallbackInfo<Value>& args) {
dispatchWebSocketEvent(args, "close", "onclose");
}
static void WebSocketReadyStateGetter(Local<Name> property, const PropertyCallbackInfo<Value>& info) {
Isolate* isolate = info.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
int state = WebSocketWrapper::CLOSED;
Local<Value> idVal = info.Holder()->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked();
if (idVal->IsNumber()) {
auto it = WebSocketWrapper::active_websockets.find(idVal->Int32Value(context).FromJust());
if (it != WebSocketWrapper::active_websockets.end()) {
state = it->second->getReadyState();
}
}
info.GetReturnValue().Set(Integer::New(isolate, state));
}
static void WebSocketReadyStateSetter(Local<Name> property, Local<Value> value, const PropertyCallbackInfo<void>& info) {}
static void WebSocketAddEventListener(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Object> self = args.Holder();
if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsFunction()) return;
Local<Value> listenersVal = self->Get(context, String::NewFromUtf8(isolate, "_listeners").ToLocalChecked()).ToLocalChecked();
if (!listenersVal->IsObject()) return;
Local<Object> listeners = listenersVal.As<Object>();
Local<Value> arr = listeners->Get(context, args[0]).ToLocalChecked();
Local<Array> listenerList;
if (arr->IsArray()) {
listenerList = arr.As<Array>();
for (uint32_t i = 0; i < listenerList->Length(); ++i) {
Local<Value> existing = listenerList->Get(context, i).ToLocalChecked();
if (existing->StrictEquals(args[1])) return;
}
} else {
listenerList = Array::New(isolate);
listeners->Set(context, args[0], listenerList);
}
listenerList->Set(context, listenerList->Length(), args[1]);
}
static void WebSocketRemoveEventListener(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
Local<Context> context = isolate->GetCurrentContext();
Local<Object> self = args.Holder();
if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsFunction()) return;
Local<Value> listenersVal = self->Get(context, String::NewFromUtf8(isolate, "_listeners").ToLocalChecked()).ToLocalChecked();
if (!listenersVal->IsObject()) return;
Local<Value> arr = listenersVal.As<Object>()->Get(context, args[0]).ToLocalChecked();
if (!arr->IsArray()) return;
Local<Array> listenerList = arr.As<Array>();
for (uint32_t i = 0; i < listenerList->Length(); ++i) {
Local<Value> listener = listenerList->Get(context, i).ToLocalChecked();
if (listener->StrictEquals(args[1])) {
Local<Value> splice = listenerList->Get(context, String::NewFromUtf8(isolate, "splice").ToLocalChecked()).ToLocalChecked();
Local<Value> spliceArgs[2] = { Integer::New(isolate, i), Integer::New(isolate, 1) };
splice.As<Function>()->Call(context, listenerList, 2, spliceArgs);
break;
}
}
}
static void WebSocketConstructor(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
HandleScope scope(isolate);
@ -2672,7 +2787,6 @@ static void WebSocketConstructor(const FunctionCallbackInfo<Value>& args) {
Local<Object> instance = args.This();
instance->Set(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked(), Integer::New(isolate, wsId));
instance->Set(context, String::NewFromUtf8(isolate, "url").ToLocalChecked(), args[0]);
instance->Set(context, String::NewFromUtf8(isolate, "readyState").ToLocalChecked(), Integer::New(isolate, 0));
instance->Set(context, String::NewFromUtf8(isolate, "protocol").ToLocalChecked(), String::NewFromUtf8(isolate, "").ToLocalChecked());
instance->Set(context, String::NewFromUtf8(isolate, "extensions").ToLocalChecked(), String::NewFromUtf8(isolate, "").ToLocalChecked());
instance->Set(context, String::NewFromUtf8(isolate, "binaryType").ToLocalChecked(), String::NewFromUtf8(isolate, "blob").ToLocalChecked());
@ -2681,6 +2795,15 @@ static void WebSocketConstructor(const FunctionCallbackInfo<Value>& args) {
instance->Set(context, String::NewFromUtf8(isolate, "onmessage").ToLocalChecked(), Null(isolate));
instance->Set(context, String::NewFromUtf8(isolate, "onerror").ToLocalChecked(), Null(isolate));
instance->Set(context, String::NewFromUtf8(isolate, "onclose").ToLocalChecked(), Null(isolate));
instance->Set(context, String::NewFromUtf8(isolate, "_listeners").ToLocalChecked(), Object::New(isolate));
auto it = WebSocketWrapper::active_websockets.find(wsId);
if (it != WebSocketWrapper::active_websockets.end()) {
it->second->setOnOpen(Function::New(context, WebSocketDispatchOpen, instance).ToLocalChecked());
it->second->setOnMessage(Function::New(context, WebSocketDispatchMessage, instance).ToLocalChecked());
it->second->setOnError(Function::New(context, WebSocketDispatchError, instance).ToLocalChecked());
it->second->setOnClose(Function::New(context, WebSocketDispatchClose, instance).ToLocalChecked());
}
args.GetReturnValue().Set(instance);
}
@ -2745,8 +2868,6 @@ static void WebSocketClose(const FunctionCallbackInfo<Value>& args) {
if (readyState == 2 || readyState == 3) return;
self->Set(context, String::NewFromUtf8(isolate, "readyState").ToLocalChecked(), Integer::New(isolate, 2));
Local<Value> idVal = self->Get(context, String::NewFromUtf8(isolate, "_id").ToLocalChecked()).ToLocalChecked();
int wsId = idVal->Int32Value(context).FromJust();
@ -2883,10 +3004,15 @@ void bind_websocket_class(Isolate* isolate, const Global<Context>& context) {
Local<FunctionTemplate> wsTpl = FunctionTemplate::New(isolate, WebSocketConstructor);
wsTpl->SetClassName(String::NewFromUtf8(isolate, "WebSocket").ToLocalChecked());
wsTpl->InstanceTemplate()->SetInternalFieldCount(1);
wsTpl->InstanceTemplate()->SetNativeDataProperty(
String::NewFromUtf8(isolate, "readyState").ToLocalChecked(),
WebSocketReadyStateGetter, WebSocketReadyStateSetter);
Local<ObjectTemplate> proto = wsTpl->PrototypeTemplate();
proto->Set(isolate, "send", FunctionTemplate::New(isolate, WebSocketSend));
proto->Set(isolate, "close", FunctionTemplate::New(isolate, WebSocketClose));
proto->Set(isolate, "addEventListener", FunctionTemplate::New(isolate, WebSocketAddEventListener));
proto->Set(isolate, "removeEventListener", FunctionTemplate::New(isolate, WebSocketRemoveEventListener));
Local<Function> wsFunc = wsTpl->GetFunction(current_context).ToLocalChecked();
wsFunc->Set(current_context, String::NewFromUtf8(isolate, "CONNECTING").ToLocalChecked(), Integer::New(isolate, 0));
@ -2895,6 +3021,11 @@ void bind_websocket_class(Isolate* isolate, const Global<Context>& context) {
wsFunc->Set(current_context, String::NewFromUtf8(isolate, "CLOSED").ToLocalChecked(), Integer::New(isolate, 3));
global->Set(current_context, String::NewFromUtf8(isolate, "WebSocket").ToLocalChecked(), wsFunc);
Local<Value> windowVal = global->Get(current_context, String::NewFromUtf8(isolate, "window").ToLocalChecked()).ToLocalChecked();
if (windowVal->IsObject()) {
windowVal.As<Object>()->Set(current_context, String::NewFromUtf8(isolate, "WebSocket").ToLocalChecked(), wsFunc);
}
}
#endif