#include <bare.h>
#include <js.h>
#include <jstl.h>
#include <stdlib.h>
#include <string.h>
#include <udx.h>
#define UDX_NAPI_INTERACTIVE 0
#define UDX_NAPI_NON_INTERACTIVE 1
#define UDX_NAPI_FRAMED 2
namespace {
// socket
using cb_socket_send_t = js_function_t<void, js_receiver_t, uint64_t, int>;
using cb_socket_message_t = js_function_t<js_typedarray_span_t<>, js_receiver_t, int64_t, int, std::string, int>;
using cb_socket_close_t = js_function_t<void, js_receiver_t>;
using cb_socket_realloc_message_t = js_function_t<js_typedarray_span_t<>, js_receiver_t>;
// stream
using cb_stream_end_t = js_function_t<void, js_receiver_t, uint32_t>;
using cb_stream_data_t = js_function_t<js_typedarray_span_t<>, js_receiver_t, uint32_t>;
using cb_stream_drain_t = js_function_t<void, js_receiver_t>;
using cb_stream_ack_t = js_function_t<void, js_receiver_t, uint32_t>;
using cb_stream_send_t = js_function_t<void, js_receiver_t, int32_t, int32_t>;
using cb_stream_message_t = js_function_t<js_typedarray_span_t<>, js_receiver_t, uint32_t>;
using cb_stream_realloc_data_t = js_function_t<js_typedarray_span_t<>, js_receiver_t>;
using cb_stream_realloc_message_t = js_function_t<js_typedarray_span_t<>, js_receiver_t>;
using cb_stream_close_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>>;
using cb_stream_firewall_t = js_function_t<uint32_t, js_receiver_t, js_receiver_t, uint32_t, std::string, uint32_t>;
using cb_stream_remote_changed_t = js_function_t<void, js_receiver_t>;
// udx
using cb_udx_lookup_t = js_function_t<void, js_receiver_t, std::optional<js_object_t>, std::optional<std::string>, int32_t>;
// interface
using cb_interface_event_t = js_function_t<void, js_receiver_t>;
using cb_interface_close_t = js_function_t<void, js_receiver_t>;
}; // namespace
struct udx_napi_t {
udx_t udx;
uint8_t *read_buf;
size_t read_buf_free;
js_deferred_teardown_t *teardown;
bool exiting;
};
struct udx_napi_socket_t {
udx_socket_t socket;
udx_napi_t *udx;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<cb_socket_send_t> on_send;
js_persistent_t<cb_socket_message_t> on_message;
js_persistent_t<cb_socket_close_t> on_close;
js_persistent_t<cb_socket_realloc_message_t> realloc_message; // TODO: deprecate
};
struct udx_napi_stream_t {
udx_stream_t stream;
udx_napi_t *udx;
int mode;
uint8_t *read_buf;
uint8_t *read_buf_head;
size_t read_buf_free;
ssize_t frame_len;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<cb_stream_data_t> on_data;
js_persistent_t<cb_stream_end_t> on_end;
js_persistent_t<cb_stream_drain_t> on_drain;
js_persistent_t<cb_stream_ack_t> on_ack;
js_persistent_t<cb_stream_send_t> on_send;
js_persistent_t<cb_stream_message_t> on_message;
js_persistent_t<cb_stream_close_t> on_close;
js_persistent_t<cb_stream_firewall_t> on_firewall;
js_persistent_t<cb_stream_remote_changed_t> on_remote_changed;
js_persistent_t<cb_stream_realloc_data_t> realloc_data;
js_persistent_t<cb_stream_realloc_message_t> realloc_message; // TODO: deprecate
};
struct udx_napi_lookup_t {
udx_lookup_t handle;
udx_napi_t *udx;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<cb_udx_lookup_t> on_lookup;
};
struct udx_napi_interface_event_t {
udx_interface_event_t handle;
udx_napi_t *udx;
js_env_t *env;
js_persistent_t<js_receiver_t> ctx;
js_persistent_t<cb_interface_event_t> on_event;
js_persistent_t<cb_interface_close_t> on_close;
};
inline static void
parse_address (const struct sockaddr *name, char *ip, size_t size, int *port, int *family) {
if (name->sa_family == AF_INET) {
auto name_v4 = reinterpret_cast<const struct sockaddr_in *>(name);
if (port) *port = ntohs(name_v4->sin_port);
if (family) *family = 4;
uv_ip4_name(name_v4, ip, size);
} else if (name->sa_family == AF_INET6) {
auto name_v6 = reinterpret_cast<const struct sockaddr_in6 *>(name);
if (port) *port = ntohs(name_v6->sin6_port);
if (family) *family = 6;
uv_ip6_name(name_v6, ip, size);
}
}
inline static int
load_address (js_env_t *env, char *addr, std::string str) {
memset(addr, 0, INET6_ADDRSTRLEN);
const char *val = str.c_str();
size_t len = INET6_ADDRSTRLEN;
if (str.length() < INET6_ADDRSTRLEN) len = str.length();
strncpy(addr, val, len);
return len;
}
static void
on_udx_send (udx_socket_send_t *req, int status) {
int err;
auto *n = reinterpret_cast<udx_napi_socket_t *>(req->socket);
if (n->udx->exiting) return;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_socket_send_t callback;
err = js_get_reference_value(env, n->on_send, callback);
assert(err == 0);
auto id = reinterpret_cast<uintptr_t>(req->data);
err = js_call_function_with_checkpoint(env, callback, ctx, uint64_t(id), status);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_message (udx_socket_t *self, ssize_t read_len, const uv_buf_t *buf, const struct sockaddr *from) {
auto *n = reinterpret_cast<udx_napi_socket_t *>(self);
if (n->udx->exiting) return;
int err;
int port = 0;
char ip[INET6_ADDRSTRLEN];
int family = 0;
parse_address(from, ip, INET6_ADDRSTRLEN, &port, &family);
if (buf->len > n->udx->read_buf_free) return;
memcpy(n->udx->read_buf, buf->base, buf->len);
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_socket_message_t callback;
err = js_get_reference_value(env, n->on_message, callback);
assert(err == 0);
std::string ip_str;
ip_str = ip;
js_typedarray_span_t<> res;
err = js_call_function_with_checkpoint<js_type_options_t{}, js_typedarray_span_t<>, js_receiver_t, int64_t, int, std::string, int>(
env,
callback,
ctx,
int64_t(read_len),
port,
ip_str,
family,
res
);
if (err == 0) {
n->udx->read_buf = res.data();
n->udx->read_buf_free = res.size();
} else {
// avoid reentry
if (!(n->udx->exiting)) {
int err;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_socket_realloc_message_t callback;
err = js_get_reference_value(env, n->realloc_message, callback);
assert(err == 0);
err = js_call_function_with_checkpoint<js_type_options_t{}, js_typedarray_span_t<>, js_receiver_t>(env, callback, ctx, res);
assert(err == 0);
n->udx->read_buf = res.data();
n->udx->read_buf_free = res.size();
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_close (udx_socket_t *self) {
auto n = reinterpret_cast<udx_napi_socket_t *>(self);
int err;
js_env_t *env = n->env;
if (!(n->udx->exiting)) {
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_socket_close_t callback;
err = js_get_reference_value(env, n->on_close, callback);
assert(err == 0);
err = js_call_function_with_checkpoint(env, callback, ctx);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
n->on_send.reset();
n->on_message.reset();
n->on_close.reset();
n->realloc_message.reset();
n->ctx.reset();
}
static void
on_udx_teardown (js_deferred_teardown_t *handle, void *data) {
auto self = reinterpret_cast<udx_napi_t *>(data);
self->exiting = true;
udx_teardown(&self->udx);
}
static void
ensure_teardown (js_env_t *env, udx_napi_t *udx) {
if (udx->teardown) return;
int err = js_add_deferred_teardown_callback(
env,
on_udx_teardown,
reinterpret_cast<void *>(udx),
&udx->teardown
);
if (err != 0) abort();
}
static void
on_udx_stream_end (udx_stream_t *stream) {
int err;
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
if (n->udx->exiting) return;
size_t read = n->read_buf_head - n->read_buf;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_end_t callback;
err = js_get_reference_value(env, n->on_end, callback);
assert(err == 0);
err = js_call_function_with_checkpoint(env, callback, ctx, uint32_t(read));
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_stream_read (udx_stream_t *stream, ssize_t read_len, const uv_buf_t *buf) {
if (read_len == UV_EOF) return on_udx_stream_end(stream);
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
if (n->udx->exiting) return;
// ignore the message if it doesn't fit in the read buffer
if (buf->len > n->read_buf_free) return;
if (n->mode == UDX_NAPI_FRAMED && n->frame_len == -1) {
if (buf->len < 3) {
n->mode = UDX_NAPI_INTERACTIVE;
} else {
auto b = reinterpret_cast<uint8_t *>(buf->base);
n->frame_len = 3 + (b[0] | (b[1] << 8) | (b[2] << 16));
}
}
memcpy(n->read_buf_head, buf->base, buf->len);
n->read_buf_head += buf->len;
n->read_buf_free -= buf->len;
if (n->mode == UDX_NAPI_NON_INTERACTIVE && n->read_buf_free >= 2 * stream->mtu) {
return;
}
ssize_t read = n->read_buf_head - n->read_buf;
if (n->mode == UDX_NAPI_FRAMED) {
if (n->frame_len < read) {
n->mode = UDX_NAPI_INTERACTIVE;
} else if (n->frame_len == read) {
n->frame_len = -1;
} else if (n->read_buf_free < 2 * stream->mtu) {
n->frame_len -= read;
} else {
return; // wait for more data
}
}
js_env_t *env = n->env;
js_handle_scope_t *scope;
int err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_data_t callback;
err = js_get_reference_value(env, n->on_data, callback);
assert(err == 0);
js_typedarray_span_t<> res;
err = js_call_function_with_checkpoint<js_type_options_t{}, js_typedarray_span_t<>, js_receiver_t, uint32_t>(env, callback, ctx, uint32_t(read), res);
if (err == 0) {
n->read_buf = res.data();
n->read_buf_free = res.size();
n->read_buf_head = n->read_buf;
} else {
// avoid re-entry
if (!(n->udx->exiting)) {
js_handle_scope_t *inner_scope;
err = js_open_handle_scope(env, &inner_scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_realloc_data_t callback;
err = js_get_reference_value(env, n->realloc_data, callback);
assert(err == 0);
err = js_call_function_with_checkpoint<js_type_options_t{}, js_typedarray_span_t<>, js_receiver_t>(env, callback, ctx, res);
assert(err == 0);
n->read_buf = res.data();
n->read_buf_free = res.size();
n->read_buf_head = n->read_buf;
err = js_close_handle_scope(env, inner_scope);
assert(err == 0);
}
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_stream_drain (udx_stream_t *stream) {
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
if (n->udx->exiting) return;
js_env_t *env = n->env;
js_handle_scope_t *scope;
int err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_drain_t callback;
err = js_get_reference_value(env, n->on_drain, callback);
assert(err == 0);
err = js_call_function_with_checkpoint(env, callback, ctx);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_stream_ack (udx_stream_write_t *req, int status, int unordered) {
int err = 0;
auto n = reinterpret_cast<udx_napi_stream_t *>(req->stream);
if (n->udx->exiting) return;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_ack_t callback;
err = js_get_reference_value(env, n->on_ack, callback);
assert(err == 0);
auto offset = reinterpret_cast<uintptr_t>(req->data);
err = js_call_function_with_checkpoint(env, callback, ctx, uint32_t(offset));
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_stream_send (udx_stream_send_t *req, int status) {
int err;
auto n = reinterpret_cast<udx_napi_stream_t *>(req->stream);
if (n->udx->exiting) return;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_send_t callback;
err = js_get_reference_value(env, n->on_send, callback);
assert(err == 0);
auto id = reinterpret_cast<uintptr_t>(req->data);
err = js_call_function_with_checkpoint(env, callback, ctx, int32_t(id), status);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_stream_recv (udx_stream_t *stream, ssize_t read_len, const uv_buf_t *buf) {
int err;
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
if (n->udx->exiting) return;
if (buf->len > n->udx->read_buf_free) return;
memcpy(n->udx->read_buf, buf->base, buf->len);
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_message_t callback;
err = js_get_reference_value(env, n->on_message, callback);
assert(err == 0);
js_typedarray_span_t<> res;
err = js_call_function_with_checkpoint<js_type_options_t{}, js_typedarray_span_t<>, js_receiver_t, uint32_t>(env, callback, ctx, uint32_t(read_len), res);
if (err == 0) {
n->udx->read_buf = res.data();
n->udx->read_buf_free = res.size();
} else {
// avoid re-entry
if (!(n->udx->exiting)) {
js_handle_scope_t *inner_scope;
err = js_open_handle_scope(env, &inner_scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_realloc_message_t callback;
err = js_get_reference_value(env, n->realloc_message, callback);
assert(err == 0);
err = js_call_function_with_checkpoint<js_type_options_t{}, js_typedarray_span_t<>, js_receiver_t>(env, callback, ctx, res);
assert(err == 0);
n->udx->read_buf = res.data();
n->udx->read_buf_free = res.size();
err = js_close_handle_scope(env, inner_scope);
assert(err == 0);
}
}
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_stream_finalize (udx_stream_t *stream) {
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
n->on_data.reset();
n->on_end.reset();
n->on_drain.reset();
n->on_ack.reset();
n->on_send.reset();
n->on_message.reset();
n->on_close.reset();
n->on_firewall.reset();
n->on_remote_changed.reset();
n->realloc_data.reset();
n->realloc_message.reset();
n->ctx.reset();
}
static void
on_udx_stream_close (udx_stream_t *stream, int status) {
int err;
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
if (n->udx->exiting) return;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_close_t callback;
err = js_get_reference_value(env, n->on_close, callback);
assert(err == 0);
std::optional<js_object_t> res;
if (status < 0) {
js_string_t code;
err = js_create_string(env, uv_err_name(status), code);
assert(err == 0);
res.emplace(); // init
err = js_create_error(env, code, uv_strerror(status), *res);
assert(err == 0);
}
err = js_call_function_with_checkpoint(env, callback, ctx, res);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static int
on_udx_stream_firewall (udx_stream_t *stream, udx_socket_t *socket, const struct sockaddr *from) {
int err;
auto *n = reinterpret_cast<udx_napi_stream_t *>(stream);
auto *s = reinterpret_cast<udx_napi_socket_t *>(socket);
uint32_t fw = 1; // assume error means firewall it, whilst reporting the uncaught
if (n->udx->exiting) return fw;
int port = 0;
char ip[INET6_ADDRSTRLEN];
int family = 0;
parse_address(from, ip, INET6_ADDRSTRLEN, &port, &family);
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_firewall_t callback;
err = js_get_reference_value(env, n->on_firewall, callback);
assert(err == 0);
js_receiver_t socket_ctx;
err = js_get_reference_value(env, s->ctx, socket_ctx);
assert(err == 0);
err = js_call_function_with_checkpoint<js_type_options_t{}, uint32_t, js_receiver_t, js_receiver_t, uint32_t, std::string, uint32_t>(
env,
callback,
ctx,
socket_ctx,
uint32_t(port),
std::string(ip),
uint32_t(family),
fw
);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
return fw;
}
static void
on_udx_stream_remote_changed (udx_stream_t *stream) {
int err;
auto n = reinterpret_cast<udx_napi_stream_t *>(stream);
if (n->udx->exiting) return;
js_env_t *env = n->env;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_stream_remote_changed_t callback;
err = js_get_reference_value(env, n->on_remote_changed, callback);
assert(err == 0);
err = js_call_function_with_checkpoint(env, callback, ctx);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_lookup (udx_lookup_t *lookup, int status, const struct sockaddr *addr, int addr_len) {
int err;
auto n = reinterpret_cast<udx_napi_lookup_t *>(lookup);
if (n->udx->exiting) return;
js_env_t *env = n->env;
char ip[INET6_ADDRSTRLEN] = "";
int family = 0;
js_handle_scope_t *scope;
err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, n->ctx, ctx);
assert(err == 0);
cb_udx_lookup_t callback;
err = js_get_reference_value(env, n->on_lookup, callback);
assert(err == 0);
std::optional<js_object_t> error;
std::optional<std::string> ip_str;
if (status >= 0) {
parse_address(addr, ip, addr_len, nullptr, &family);
ip_str = ip;
} else {
js_string_t code;
err = js_create_string(env, uv_err_name(status), code);
assert(err == 0);
error.emplace(); // init
err = js_create_error(env, code, uv_strerror(status), *error);
assert(err == 0);
}
err = js_call_function_with_checkpoint(env, callback, ctx, error, ip_str, family);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
n->on_lookup.reset();
n->ctx.reset();
}
static void
on_udx_interface_event (udx_interface_event_t *handle, int status) {
auto e = reinterpret_cast<udx_napi_interface_event_t *>(handle);
if (e->udx->exiting) return;
js_env_t *env = e->env;
js_handle_scope_t *scope;
int err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, e->ctx, ctx);
assert(err == 0);
cb_interface_event_t callback;
err = js_get_reference_value(env, e->on_event, callback);
assert(err == 0);
err = js_call_function_with_checkpoint(env, callback, ctx);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
}
static void
on_udx_interface_event_close (udx_interface_event_t *handle) {
auto e = reinterpret_cast<udx_napi_interface_event_t *>(handle);
if (e->udx->exiting) return;
js_env_t *env = e->env;
js_handle_scope_t *scope;
int err = js_open_handle_scope(env, &scope);
assert(err == 0);
js_receiver_t ctx;
err = js_get_reference_value(env, e->ctx, ctx);
assert(err == 0);
cb_interface_close_t callback;
err = js_get_reference_value(env, e->on_close, callback);
assert(err == 0);
err = js_call_function_with_checkpoint(env, callback, ctx);
assert(err == 0);
err = js_close_handle_scope(env, scope);
assert(err == 0);
e->on_event.reset();
e->on_close.reset();
e->ctx.reset();
}
static void
on_udx_idle (udx_t *u) {
auto self = reinterpret_cast<udx_napi_t *>(u);
if (!self->teardown) return;
int err = js_finish_deferred_teardown_callback(self->teardown);
self->teardown = nullptr;
if (err != 0) abort();
}
static inline void
udx_napi_init (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_t, 1> self,
js_typedarray_span_t<> read_buf
) {
int err;
*self = {};
uv_loop_t *loop;
err = js_get_env_loop(env, &loop);
assert(err == 0);
err = udx_init(loop, &self->udx, on_udx_idle);
assert(err == 0);
self->read_buf = read_buf.data();
self->read_buf_free = read_buf.size();
self->exiting = false;
self->teardown = nullptr;
}
static inline void
udx_napi_socket_init (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_t, 1> udx,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self,
js_receiver_t ctx,
cb_socket_send_t on_send,
cb_socket_message_t on_message,
cb_socket_close_t on_close,
cb_socket_realloc_message_t realloc_message
) {
int err;
*self = {};
udx_socket_t *socket = &self->socket;
self->udx = &*udx;
self->env = env;
err = udx_socket_init(&udx->udx, socket, on_udx_close);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
err = js_create_reference(env, ctx, self->ctx);
assert(err == 0);
err = js_create_reference(env, on_send, self->on_send);
assert(err == 0);
err = js_create_reference(env, on_message, self->on_message);
assert(err == 0);
err = js_create_reference(env, on_close, self->on_close);
assert(err == 0);
err = js_create_reference(env, realloc_message, self->realloc_message);
assert(err == 0);
ensure_teardown(env, udx);
}
static inline int32_t
udx_napi_socket_bind (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self,
uint32_t port,
std::string ip_str,
uint32_t family,
uint32_t flags
) {
int err;
auto socket = &self->socket;
char ip[INET6_ADDRSTRLEN];
load_address(env, ip, ip_str);
struct sockaddr_storage addr;
int addr_len;
if (family == 4) {
addr_len = sizeof(struct sockaddr_in);
err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
} else {
addr_len = sizeof(struct sockaddr_in6);
err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
}
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return -1;
}
err = udx_socket_bind(socket, (struct sockaddr *) &addr, flags);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return -1;
}
// TODO: move the bottom stuff into another function, start, so error handling is easier
struct sockaddr_storage name;
// wont error in practice
err = udx_socket_getsockname(socket, (struct sockaddr *) &name, &addr_len);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return -1;
}
int local_port;
if (family == 4) {
local_port = ntohs(((struct sockaddr_in *) &name)->sin_port);
} else {
local_port = ntohs(((struct sockaddr_in6 *) &name)->sin6_port);
}
// wont error in practice
err = udx_socket_recv_start(socket, on_udx_message);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return -1;
}
return local_port;
}
static inline void
udx_napi_socket_set_ttl (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self,
uint32_t ttl
) {
int err;
err = udx_socket_set_ttl(&self->socket, ttl);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_socket_set_membership (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> socket,
std::string mcast_addr_str,
std::string iface_addr_str,
bool join
) {
int err;
char mcast_addr[INET6_ADDRSTRLEN];
load_address(env, mcast_addr, mcast_addr_str);
char iface_addr[INET6_ADDRSTRLEN];
size_t iface_addr_len = load_address(env, iface_addr, iface_addr_str);
char *iface_param = iface_addr_len > 0 ? iface_addr : NULL;
err = udx_socket_set_membership(&socket->socket, mcast_addr, iface_param, join ? UV_JOIN_GROUP : UV_LEAVE_GROUP);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline uint32_t
udx_napi_socket_get_recv_buffer_size (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self
) {
int err;
int size = 0;
err = udx_socket_get_recv_buffer_size(&self->socket, &size);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
return size;
}
static inline void
udx_napi_socket_set_recv_buffer_size (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self,
uint32_t size
) {
int err = udx_socket_set_recv_buffer_size(&self->socket, size);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline uint32_t
udx_napi_socket_get_send_buffer_size (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self
) {
int size = 0;
int err = udx_socket_get_send_buffer_size(&self->socket, &size);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
return size;
}
static inline uint32_t
udx_napi_socket_set_send_buffer_size (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self,
uint32_t size
) {
int err = udx_socket_set_send_buffer_size(&self->socket, size);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
return size;
}
static inline void
udx_napi_socket_send_ttl (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self,
js_typedarray_span_of_t<udx_socket_send_t, 1> req,
uint32_t rid,
js_typedarray_span_of_t<char> buf,
uint32_t port,
std::string ip_str,
uint32_t family,
uint32_t ttl
) {
int err;
char ip[INET6_ADDRSTRLEN];
load_address(env, ip, ip_str);
req->data = reinterpret_cast<void *>(static_cast<uintptr_t>(rid));
struct sockaddr_storage addr;
if (family == 4) {
err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
} else {
err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
}
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
uv_buf_t b = uv_buf_init(buf.data(), buf.size());
err = udx_socket_send_ttl(req, &self->socket, &b, 1, (const struct sockaddr *) &addr, ttl, on_udx_send);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_socket_close (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_socket_t, 1> self
) {
int err;
err = udx_socket_close(&self->socket);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_stream_init (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_t, 1> udx,
js_typedarray_span_of_t<udx_napi_stream_t, 1> self,
uint32_t id,
uint32_t framed,
js_receiver_t ctx,
cb_stream_data_t on_data,
cb_stream_end_t on_end,
cb_stream_drain_t on_drain,
cb_stream_ack_t on_ack,
cb_stream_send_t on_send,
cb_stream_message_t on_message,
cb_stream_close_t on_close,
cb_stream_firewall_t on_firewall,
cb_stream_remote_changed_t on_remote_changed,
cb_stream_realloc_data_t realloc_data,
cb_stream_realloc_message_t realloc_message
) {
int err;
*self = {};
self->mode = framed ? UDX_NAPI_FRAMED : UDX_NAPI_INTERACTIVE;
self->frame_len = -1;
self->read_buf = NULL;
self->read_buf_head = NULL;
self->read_buf_free = 0;
self->udx = udx;
self->env = env;
auto stream = &self->stream;
err = udx_stream_init(&udx->udx, stream, id, on_udx_stream_close, on_udx_stream_finalize);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
err = js_create_reference(env, ctx, self->ctx);
assert(err == 0);
err = js_create_reference(env, on_data, self->on_data);
assert(err == 0);
err = js_create_reference(env, on_end, self->on_end);
assert(err == 0);
err = js_create_reference(env, on_drain, self->on_drain);
assert(err == 0);
err = js_create_reference(env, on_ack, self->on_ack);
assert(err == 0);
err = js_create_reference(env, on_send, self->on_send);
assert(err == 0);
err = js_create_reference(env, on_message, self->on_message);
assert(err == 0);
err = js_create_reference(env, on_close, self->on_close);
assert(err == 0);
err = js_create_reference(env, on_firewall, self->on_firewall);
assert(err == 0);
err = js_create_reference(env, on_remote_changed, self->on_remote_changed);
assert(err == 0);
err = js_create_reference(env, realloc_data, self->realloc_data);
assert(err == 0);
err = js_create_reference(env, realloc_message, self->realloc_message);
assert(err == 0);
udx_stream_firewall(stream, on_udx_stream_firewall);
udx_stream_write_resume(stream, on_udx_stream_drain);
ensure_teardown(env, udx);
}
static inline void
udx_napi_stream_set_seq (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
uint32_t seq
) {
int err = udx_stream_set_seq(&stream->stream, seq);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_stream_set_ack (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
uint32_t ack
) {
int err = udx_stream_set_ack(&stream->stream, ack);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_stream_set_mode (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
uint32_t mode
) {
stream->mode = mode;
}
static inline void
udx_napi_stream_recv_start (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_t<> read_buf
) {
int err;
stream->read_buf = read_buf.data();
stream->read_buf_head = read_buf.data();
stream->read_buf_free = read_buf.size();
err = udx_stream_read_start(&stream->stream, on_udx_stream_read);
assert(err == 0);
err = udx_stream_recv_start(&stream->stream, on_udx_stream_recv);
assert(err == 0);
}
static inline void
udx_napi_stream_connect (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_of_t<udx_napi_socket_t, 1> socket,
uint32_t remote_id,
uint32_t port,
std::string ip_str,
uint32_t family
) {
int err;
char ip[INET6_ADDRSTRLEN];
load_address(env, ip, ip_str);
struct sockaddr_storage addr;
if (family == 4) {
err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
} else {
err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
}
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
err = udx_stream_connect(&stream->stream, &socket->socket, remote_id, (const struct sockaddr *) &addr);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_stream_change_remote (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_of_t<udx_napi_socket_t, 1> socket,
uint32_t remote_id,
uint32_t port,
std::string ip_str,
uint32_t family
) {
int err;
char ip[INET6_ADDRSTRLEN];
load_address(env, ip, ip_str);
struct sockaddr_storage addr;
if (family == 4) {
err = uv_ip4_addr(ip, port, (struct sockaddr_in *) &addr);
} else {
err = uv_ip6_addr(ip, port, (struct sockaddr_in6 *) &addr);
}
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
int immediate = err = udx_stream_change_remote(&stream->stream, &socket->socket, remote_id, (const struct sockaddr *) &addr, on_udx_stream_remote_changed);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
if (immediate == 1) {
js_receiver_t ctx;
err = js_get_reference_value(env, stream->ctx, ctx);
assert(err == 0);
cb_stream_remote_changed_t callback;
err = js_get_reference_value(env, stream->on_remote_changed, callback);
assert(err == 0);
err = js_call_function(env, callback, ctx);
assert(err == 0);
}
}
static inline void
udx_napi_stream_relay_to (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_of_t<udx_napi_stream_t, 1> destination
) {
int err = udx_stream_relay_to(&stream->stream, &destination->stream);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline uint32_t
udx_napi_stream_send (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_of_t<udx_stream_send_t, 1> req,
uint32_t rid,
js_typedarray_span_of_t<char> buf
) {
req->data = reinterpret_cast<void *>(static_cast<uintptr_t>(rid));
uv_buf_t b = uv_buf_init(buf.data(), buf.size());
int res = udx_stream_send(req, &stream->stream, &b, 1, on_udx_stream_send);
if (res < 0) {
int err = js_throw_error(env, uv_err_name(res), uv_strerror(res));
assert(err == 0);
}
return res;
}
static inline int64_t
udx_napi_stream_write (
js_env_t *env,
js_receiver_t,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_t<> req_handle,
uint32_t rid,
js_typedarray_span_of_t<char> buf
) {
int err;
auto req = reinterpret_cast<udx_stream_write_t *>(req_handle.data());
req->data = reinterpret_cast<void *>(static_cast<uintptr_t>(rid));
auto b = uv_buf_init(buf.data(), buf.size());
int res = udx_stream_write(&*req, &stream->stream, &b, 1, on_udx_stream_ack);
if (res < 0) {
err = js_throw_error(env, uv_err_name(res), uv_strerror(res));
assert(err == 0);
}
return res;
}
// TODO: use only on platforms without fastcall support
static inline int64_t
udx_napi_stream_writev (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_t<> req_handle,
uint32_t rid,
std::vector<js_typedarray_span_of_t<char>> buffers
) {
int err;
auto req = reinterpret_cast<udx_stream_write_t *>(req_handle.data());
req->data = reinterpret_cast<void *>(static_cast<uintptr_t>(rid));
const auto len = buffers.size();
std::vector<uv_buf_t> batch(len);
for (int i = 0; i < len; i++) {
auto buf = buffers[i];
batch[i] = uv_buf_init(buf.data(), buf.size());
}
int res = udx_stream_write(req, &stream->stream, batch.data(), len, on_udx_stream_ack);
if (res < 0) {
err = js_throw_error(env, uv_err_name(res), uv_strerror(res));
assert(err == 0);
}
return res;
}
static inline uint32_t
udx_napi_stream_write_sizeof (uint32_t bufs) {
return udx_stream_write_sizeof(bufs);
}
static inline uint32_t
udx_napi_stream_write_end (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream,
js_typedarray_span_t<> req_handle,
uint32_t rid,
js_typedarray_span_of_t<char> buf
) {
auto req = reinterpret_cast<udx_stream_write_t *>(req_handle.data());
req->data = reinterpret_cast<void *>(static_cast<uintptr_t>(rid));
uv_buf_t b = uv_buf_init(buf.data(), buf.size());
int res = udx_stream_write_end(req, &stream->stream, &b, 1, on_udx_stream_ack);
if (res < 0) {
int err = js_throw_error(env, uv_err_name(res), uv_strerror(res));
assert(err == 0);
}
return res;
}
static inline uint32_t
udx_napi_stream_destroy (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_stream_t, 1> stream
) {
int res = udx_stream_destroy(&stream->stream);
if (res < 0) {
int err = js_throw_error(env, uv_err_name(res), uv_strerror(res));
assert(err == 0);
}
return res;
}
static inline void
udx_napi_lookup (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_t, 1> udx,
js_typedarray_span_of_t<udx_napi_lookup_t, 1> self,
js_string_t host_str,
uint32_t family,
js_receiver_t ctx, // TODO: use auto receiver
cb_udx_lookup_t on_lookup
) {
int err;
*self = {};
self->udx = udx;
std::string host;
err = js_get_value_string(env, host_str, host);
assert(err == 0);
udx_lookup_t *lookup = &self->handle;
self->env = env;
int flags = 0;
if (family == 4) flags |= UDX_LOOKUP_FAMILY_IPV4;
if (family == 6) flags |= UDX_LOOKUP_FAMILY_IPV6;
err = udx_lookup(&udx->udx, lookup, host.c_str(), flags, on_udx_lookup);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
err = js_create_reference(env, ctx, self->ctx);
assert(err == 0);
err = js_create_reference(env, on_lookup, self->on_lookup);
assert(err == 0);
ensure_teardown(env, udx);
}
static inline void
udx_napi_interface_event_init (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_t, 1> udx,
js_typedarray_span_of_t<udx_napi_interface_event_t, 1> self,
js_receiver_t ctx,
cb_interface_event_t on_event,
cb_interface_close_t on_close
) {
int err;
*self = {};
self->udx = udx;
self->env = env;
err = udx_interface_event_init(&udx->udx, &self->handle, on_udx_interface_event_close);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
err = udx_interface_event_start(&self->handle, on_udx_interface_event, 5000);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
return;
}
err = js_create_reference(env, ctx, self->ctx);
assert(err == 0);
err = js_create_reference(env, on_event, self->on_event);
assert(err == 0);
err = js_create_reference(env, on_close, self->on_close);
assert(err == 0);
ensure_teardown(env, udx);
}
static inline void
udx_napi_interface_event_start (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_interface_event_t, 1> self
) {
int err = udx_interface_event_start(&self->handle, on_udx_interface_event, 5000);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_interface_event_stop (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_interface_event_t, 1> self
) {
int err;
err = udx_interface_event_stop(&self->handle);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline void
udx_napi_interface_event_close (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_interface_event_t, 1> self
) {
int err;
err = udx_interface_event_close(&self->handle);
if (err < 0) {
err = js_throw_error(env, uv_err_name(err), uv_strerror(err));
assert(err == 0);
}
}
static inline std::vector<js_object_t>
udx_napi_interface_event_get_addrs (
js_env_t *env,
js_typedarray_span_of_t<udx_napi_interface_event_t, 1> self
) {
int err;
auto event = &self->handle;
char ip[INET6_ADDRSTRLEN];
int family = 0;
std::vector<js_object_t> result;
for (int i = 0; i < event->addrs_len; i++) {
uv_interface_address_t addr = event->addrs[i];
if (addr.address.address4.sin_family == AF_INET) {
uv_ip4_name(&addr.address.address4, ip, sizeof(ip));
family = 4;
} else if (addr.address.address4.sin_family == AF_INET6) {
uv_ip6_name(&addr.address.address6, ip, sizeof(ip));
family = 6;
} else {
continue;
}
js_object_t item;
err = js_create_object(env, item);
assert(err == 0);
err = js_set_property(env, item, "name", addr.name);
assert(err == 0);
err = js_set_property(env, item, "host", ip);
assert(err == 0);
err = js_set_property(env, item, "family", family);
assert(err == 0);
err = js_set_property(env, item, "internal", bool(addr.is_internal));
assert(err == 0);
result.push_back(item);
}
return result;
}
static js_value_t *
udx_native_exports (js_env_t *env, js_value_t *exports) {
int err;
// uint32
#define V(name, value) \
err = js_set_property(env, exports, name, uint32_t(value)); \
assert(err == 0);
V("UV_UDP_IPV6ONLY", UV_UDP_IPV6ONLY);
V("UV_UDP_REUSEADDR", UV_UDP_REUSEADDR);
V("offsetof_udx_stream_t_inflight", offsetof(udx_stream_t, inflight));
V("offsetof_udx_stream_t_mtu", offsetof(udx_stream_t, mtu));
V("offsetof_udx_stream_t_cwnd", offsetof(udx_stream_t, cwnd));
V("offsetof_udx_stream_t_srtt", offsetof(udx_stream_t, srtt));
V("offsetof_udx_stream_t_bytes_rx", offsetof(udx_stream_t, bytes_rx));
V("offsetof_udx_stream_t_packets_rx", offsetof(udx_stream_t, packets_rx));
V("offsetof_udx_stream_t_bytes_tx", offsetof(udx_stream_t, bytes_tx));
V("offsetof_udx_stream_t_packets_tx", offsetof(udx_stream_t, packets_tx));
V("offsetof_udx_stream_t_rto_count", offsetof(udx_stream_t, rto_count));
V("offsetof_udx_stream_t_retransmit_count", offsetof(udx_stream_t, retransmit_count));
V("offsetof_udx_stream_t_fast_recovery_count", offsetof(udx_stream_t, fast_recovery_count));
V("offsetof_udx_socket_t_bytes_rx", offsetof(udx_socket_t, bytes_rx));
V("offsetof_udx_socket_t_packets_rx", offsetof(udx_socket_t, packets_rx));
V("offsetof_udx_socket_t_bytes_tx", offsetof(udx_socket_t, bytes_tx));
V("offsetof_udx_socket_t_packets_tx", offsetof(udx_socket_t, packets_tx));
V("offsetof_udx_socket_t_packets_dropped_by_kernel", offsetof(udx_socket_t, packets_dropped_by_kernel));
V("offsetof_udx_t_bytes_rx", offsetof(udx_t, bytes_rx));
V("offsetof_udx_t_packets_rx", offsetof(udx_t, packets_rx));
V("offsetof_udx_t_bytes_tx", offsetof(udx_t, bytes_tx));
V("offsetof_udx_t_packets_tx", offsetof(udx_t, packets_tx));
V("offsetof_udx_t_packets_dropped_by_kernel", offsetof(udx_t, packets_dropped_by_kernel));
V("sizeof_udx_napi_t", sizeof(udx_napi_t));
V("sizeof_udx_napi_socket_t", sizeof(udx_napi_socket_t));
V("sizeof_udx_napi_stream_t", sizeof(udx_napi_stream_t));
V("sizeof_udx_napi_lookup_t", sizeof(udx_napi_lookup_t));
V("sizeof_udx_napi_interface_event_t", sizeof(udx_napi_interface_event_t));
V("sizeof_udx_socket_send_t", sizeof(udx_socket_send_t));
V("sizeof_udx_stream_send_t", sizeof(udx_stream_send_t));
#undef V
// functions
#define V(name, fn) \
err = js_set_property<fn>(env, exports, name); \
assert(err == 0);
V("udx_napi_init", udx_napi_init);
V("udx_napi_socket_init", udx_napi_socket_init);
V("udx_napi_socket_bind", udx_napi_socket_bind);
V("udx_napi_socket_set_ttl", udx_napi_socket_set_ttl);
V("udx_napi_socket_get_recv_buffer_size", udx_napi_socket_get_recv_buffer_size);
V("udx_napi_socket_set_recv_buffer_size", udx_napi_socket_set_recv_buffer_size);
V("udx_napi_socket_get_send_buffer_size", udx_napi_socket_get_send_buffer_size);
V("udx_napi_socket_set_send_buffer_size", udx_napi_socket_set_send_buffer_size);
V("udx_napi_socket_set_membership", udx_napi_socket_set_membership);
V("udx_napi_socket_send_ttl", udx_napi_socket_send_ttl);
V("udx_napi_socket_close", udx_napi_socket_close);
V("udx_napi_stream_init", udx_napi_stream_init);
V("udx_napi_stream_set_seq", udx_napi_stream_set_seq);
V("udx_napi_stream_set_ack", udx_napi_stream_set_ack);
V("udx_napi_stream_set_mode", udx_napi_stream_set_mode);
V("udx_napi_stream_connect", udx_napi_stream_connect);
V("udx_napi_stream_change_remote", udx_napi_stream_change_remote);
V("udx_napi_stream_relay_to", udx_napi_stream_relay_to);
V("udx_napi_stream_send", udx_napi_stream_send);
V("udx_napi_stream_recv_start", udx_napi_stream_recv_start);
V("udx_napi_stream_write", udx_napi_stream_write);
V("udx_napi_stream_writev", udx_napi_stream_writev);
V("udx_napi_stream_write_sizeof", udx_napi_stream_write_sizeof);
V("udx_napi_stream_write_end", udx_napi_stream_write_end);
V("udx_napi_stream_destroy", udx_napi_stream_destroy);
V("udx_napi_lookup", udx_napi_lookup);
V("udx_napi_interface_event_init", udx_napi_interface_event_init);
V("udx_napi_interface_event_start", udx_napi_interface_event_start);
V("udx_napi_interface_event_stop", udx_napi_interface_event_stop);
V("udx_napi_interface_event_close", udx_napi_interface_event_close);
V("udx_napi_interface_event_get_addrs", udx_napi_interface_event_get_addrs);
#undef V
return exports;
}
BARE_MODULE(udx_native, udx_native_exports)