#include "socket_v8_bindings.h" #include "socket_bridge.h" #include #include using namespace v8; // V8 wrapper functions for socket bridge void v8_runt_socket_create(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); int socket_id = runt_socket_create(); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_create returned: %d", socket_id); #endif args.GetReturnValue().Set(Integer::New(isolate, socket_id)); } void v8_runt_socket_bind(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 3 || !args[0]->IsInt32() || !args[1]->IsString() || !args[2]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); String::Utf8Value address(isolate, args[1]); int port = args[2]->Int32Value(isolate->GetCurrentContext()).FromJust(); bool result = runt_socket_bind(socket_id, *address, port); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_bind(%d, %s, %d) returned: %s", socket_id, *address, port, result ? "true" : "false"); #endif args.GetReturnValue().Set(Boolean::New(isolate, result)); } void v8_runt_socket_listen(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); int backlog = args[1]->Int32Value(isolate->GetCurrentContext()).FromJust(); bool result = runt_socket_listen(socket_id, backlog); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_listen(%d, %d) returned: %s", socket_id, backlog, result ? "true" : "false"); #endif args.GetReturnValue().Set(Boolean::New(isolate, result)); } void v8_runt_socket_accept(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 1 || !args[0]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); int client_id = runt_socket_accept(socket_id); #ifdef DEBUG_NETWORK if (client_id >= 0) { kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_accept(%d) returned: %d", socket_id, client_id); } #endif args.GetReturnValue().Set(Integer::New(isolate, client_id)); } void v8_runt_socket_connect(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 3 || !args[0]->IsInt32() || !args[1]->IsString() || !args[2]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); String::Utf8Value hostname(isolate, args[1]); int port = args[2]->Int32Value(isolate->GetCurrentContext()).FromJust(); bool result = runt_socket_connect(socket_id, *hostname, port); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_connect(%d, %s, %d) returned: %s", socket_id, *hostname, port, result ? "true" : "false"); #endif args.GetReturnValue().Set(Boolean::New(isolate, result)); } void v8_runt_socket_send(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 2 || !args[0]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); int sent = 0; // look back over how we need to handle buffers and strings as string::Utf8Value must remain if (args[1]->IsString()) { String::Utf8Value str_data(isolate, args[1]); if (*str_data == nullptr) { kinc_log(KINC_LOG_LEVEL_ERROR, "[V8] runt_socket_send: string conversion failed"); args.GetReturnValue().Set(Integer::New(isolate, -1)); return; } int length = str_data.length(); // send while str_data fixes dangling pointer bug sent = runt_socket_send(socket_id, *str_data, length); } else if (args[1]->IsArrayBuffer()) { Local buffer = Local::Cast(args[1]); std::shared_ptr backing = buffer->GetBackingStore(); const char* data = static_cast(backing->Data()); int length = static_cast(backing->ByteLength()); sent = runt_socket_send(socket_id, data, length); } else if (args[1]->IsArrayBufferView()) { Local view = Local::Cast(args[1]); Local buffer = view->Buffer(); std::shared_ptr backing = buffer->GetBackingStore(); size_t offset = view->ByteOffset(); const char* data = static_cast(backing->Data()) + offset; int length = static_cast(view->ByteLength()); sent = runt_socket_send(socket_id, data, length); } else { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Data must be string, ArrayBuffer, or TypedArray").ToLocalChecked())); return; } args.GetReturnValue().Set(Integer::New(isolate, sent)); } void v8_runt_socket_recv(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); int max_length = args[1]->Int32Value(isolate->GetCurrentContext()).FromJust(); char* data = nullptr; int received = runt_socket_recv(socket_id, max_length, &data); if (received > 0 && data) { // ArrayBuffer with received data std::unique_ptr backing = ArrayBuffer::NewBackingStore( data, received, [](void* data, size_t length, void* deleter_data) { delete[] static_cast(data); }, nullptr); Local buffer = ArrayBuffer::New(isolate, std::move(backing)); args.GetReturnValue().Set(buffer); } else if (received == -2) { // closed by peer -> return -2 so Haxe can detect args.GetReturnValue().Set(Integer::New(isolate, -2)); } else { // return null for would-block (0) or error (-1) args.GetReturnValue().Set(Null(isolate)); } } void v8_runt_socket_close(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 1 || !args[0]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); runt_socket_close(socket_id); args.GetReturnValue().Set(Undefined(isolate)); } void v8_runt_socket_set_blocking(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 2 || !args[0]->IsInt32() || !args[1]->IsBoolean()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); bool blocking = args[1]->BooleanValue(isolate); runt_socket_set_blocking(socket_id, blocking); args.GetReturnValue().Set(Undefined(isolate)); } void v8_runt_socket_select(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 1 || !args[0]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); double timeout = 0.0; if (args.Length() >= 2 && args[1]->IsNumber()) { timeout = args[1]->NumberValue(isolate->GetCurrentContext()).FromJust(); } bool hasData = runt_socket_select(socket_id, timeout); args.GetReturnValue().Set(Boolean::New(isolate, hasData)); } void v8_runt_socket_is_connected(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 1 || !args[0]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); bool connected = runt_socket_is_connected(socket_id); args.GetReturnValue().Set(Boolean::New(isolate, connected)); } #ifdef WITH_SSL void v8_runt_socket_enable_ssl(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 1 || !args[0]->IsInt32()) { isolate->ThrowException(Exception::TypeError(String::NewFromUtf8(isolate, "Wrong arguments").ToLocalChecked())); return; } int socket_id = args[0]->Int32Value(isolate->GetCurrentContext()).FromJust(); bool result = runt_socket_enable_ssl(socket_id); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_socket_enable_ssl(%d) returned: %s", socket_id, result ? "true" : "false"); #endif args.GetReturnValue().Set(Boolean::New(isolate, result)); } #endif void bind_socket_bridge(Isolate* isolate, const v8::Global& context) { // V8 locking Locker locker{isolate}; Isolate::Scope isolate_scope(isolate); HandleScope handle_scope(isolate); Local current_context = Local::New(isolate, context); Context::Scope context_scope(current_context); Local global = current_context->Global(); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] Binding socket bridge functions to global context"); #endif global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_create").ToLocalChecked(), Function::New(current_context, v8_runt_socket_create).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_bind").ToLocalChecked(), Function::New(current_context, v8_runt_socket_bind).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_listen").ToLocalChecked(), Function::New(current_context, v8_runt_socket_listen).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_accept").ToLocalChecked(), Function::New(current_context, v8_runt_socket_accept).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_connect").ToLocalChecked(), Function::New(current_context, v8_runt_socket_connect).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_send").ToLocalChecked(), Function::New(current_context, v8_runt_socket_send).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_recv").ToLocalChecked(), Function::New(current_context, v8_runt_socket_recv).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_close").ToLocalChecked(), Function::New(current_context, v8_runt_socket_close).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_set_blocking").ToLocalChecked(), Function::New(current_context, v8_runt_socket_set_blocking).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_select").ToLocalChecked(), Function::New(current_context, v8_runt_socket_select).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_is_connected").ToLocalChecked(), Function::New(current_context, v8_runt_socket_is_connected).ToLocalChecked()); #ifdef WITH_SSL global->Set(current_context, String::NewFromUtf8(isolate, "runt_socket_enable_ssl").ToLocalChecked(), Function::New(current_context, v8_runt_socket_enable_ssl).ToLocalChecked()); #endif }