#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)); } void v8_runt_udp_create(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); int socket_id = runt_udp_create(); #ifdef DEBUG_NETWORK kinc_log(KINC_LOG_LEVEL_INFO, "[V8] runt_udp_create returned: %d", socket_id); #endif args.GetReturnValue().Set(Integer::New(isolate, socket_id)); } void v8_runt_udp_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_udp_bind(socket_id, *address, port); args.GetReturnValue().Set(Boolean::New(isolate, result)); } void v8_runt_udp_sendto(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); if (args.Length() < 4 || !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 host(isolate, args[1]); int port = args[2]->Int32Value(isolate->GetCurrentContext()) .FromJust(); int sent = -1; if (args[3]->IsArrayBuffer()) { Local buffer = Local::Cast(args[3]); std::shared_ptr backing = buffer->GetBackingStore(); sent = runt_udp_sendto(socket_id, *host, port, static_cast(backing->Data()), static_cast(backing->ByteLength())); } else if (args[3]->IsArrayBufferView()) { Local view = Local::Cast(args[3]); Local buffer = view->Buffer(); std::shared_ptr backing = buffer->GetBackingStore(); const char* data = static_cast(backing->Data()) + view->ByteOffset(); sent = runt_udp_sendto(socket_id, *host, port, data, static_cast(view->ByteLength())); } else if (args[3]->IsString()) { String::Utf8Value str_data(isolate, args[3]); if (*str_data == nullptr) { args.GetReturnValue().Set(Integer::New(isolate, -1)); return; } sent = runt_udp_sendto(socket_id, *host, port, *str_data, str_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_udp_recvfrom(const FunctionCallbackInfo& args) { Isolate* isolate = args.GetIsolate(); Local context = isolate->GetCurrentContext(); 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(context).FromJust(); int max_length = args[1]->Int32Value(context).FromJust(); char* data = nullptr; char host[64] = {0}; int port = 0; int received = runt_udp_recvfrom(socket_id, max_length, &data, host, sizeof(host), &port); if (received > 0 && 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)); Local obj = Object::New(isolate); obj->Set(context, String::NewFromUtf8(isolate, "data").ToLocalChecked(), buffer); obj->Set(context, String::NewFromUtf8(isolate, "host").ToLocalChecked(), String::NewFromUtf8(isolate, host).ToLocalChecked()); obj->Set(context, String::NewFromUtf8(isolate, "port").ToLocalChecked(), Integer::New(isolate, port)); args.GetReturnValue().Set(obj); } else if (received < 0) { args.GetReturnValue().Set(Integer::New(isolate, -1)); } else { args.GetReturnValue().Set(Null(isolate)); } } #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()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_udp_create").ToLocalChecked(), Function::New(current_context, v8_runt_udp_create).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_udp_bind").ToLocalChecked(), Function::New(current_context, v8_runt_udp_bind).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_udp_sendto").ToLocalChecked(), Function::New(current_context, v8_runt_udp_sendto).ToLocalChecked()); global->Set(current_context, String::NewFromUtf8(isolate, "runt_udp_recvfrom").ToLocalChecked(), Function::New(current_context, v8_runt_udp_recvfrom).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 }