312 lines
13 KiB
C++
312 lines
13 KiB
C++
#include "socket_v8_bindings.h"
|
|
#include "socket_bridge.h"
|
|
#include <v8.h>
|
|
#include <kinc/log.h>
|
|
|
|
using namespace v8;
|
|
|
|
// V8 wrapper functions for socket bridge
|
|
|
|
void v8_runt_socket_create(const FunctionCallbackInfo<Value>& 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<Value>& 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<Value>& 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<Value>& 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<Value>& 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<Value>& 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<ArrayBuffer> buffer = Local<ArrayBuffer>::Cast(args[1]);
|
|
std::shared_ptr<BackingStore> backing = buffer->GetBackingStore();
|
|
const char* data = static_cast<const char*>(backing->Data());
|
|
int length = static_cast<int>(backing->ByteLength());
|
|
sent = runt_socket_send(socket_id, data, length);
|
|
} else if (args[1]->IsArrayBufferView()) {
|
|
Local<ArrayBufferView> view = Local<ArrayBufferView>::Cast(args[1]);
|
|
Local<ArrayBuffer> buffer = view->Buffer();
|
|
std::shared_ptr<BackingStore> backing = buffer->GetBackingStore();
|
|
size_t offset = view->ByteOffset();
|
|
const char* data = static_cast<const char*>(backing->Data()) + offset;
|
|
int length = static_cast<int>(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<Value>& 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<BackingStore> backing = ArrayBuffer::NewBackingStore(
|
|
data, received, [](void* data, size_t length, void* deleter_data) {
|
|
delete[] static_cast<char*>(data);
|
|
}, nullptr);
|
|
|
|
Local<ArrayBuffer> 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<Value>& 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<Value>& 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<Value>& 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<Value>& 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<Value>& 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<v8::Context>& context) {
|
|
// V8 locking
|
|
Locker locker{isolate};
|
|
Isolate::Scope isolate_scope(isolate);
|
|
HandleScope handle_scope(isolate);
|
|
|
|
Local<Context> current_context = Local<Context>::New(isolate, context);
|
|
Context::Scope context_scope(current_context);
|
|
Local<Object> 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
|
|
|
|
|
|
}
|