Work in progress moving SSB RPC handlers into javascript.

git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3657 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
2021-09-06 17:50:38 +00:00
parent cfd5341a6b
commit cadcb236ee
9 changed files with 676 additions and 58 deletions

172
src/ssb.c
View File

@ -54,7 +54,7 @@ typedef enum {
} tf_ssb_state_t;
enum {
k_connections_changed_callbacks_max = 4,
k_connections_changed_callbacks_max = 8,
k_tf_ssb_rpc_message_body_length_max = 8192,
};
@ -82,10 +82,19 @@ typedef struct _tf_ssb_rpc_callback_node_t tf_ssb_rpc_callback_node_t;
typedef struct _tf_ssb_rpc_callback_node_t {
const char** name;
tf_ssb_rpc_callback_t* callback;
tf_ssb_rpc_cleanup_t* cleanup;
void* user_data;
tf_ssb_rpc_callback_node_t* next;
} tf_ssb_rpc_callback_node_t;
typedef struct _tf_ssb_blob_want_added_callback_node_t tf_ssb_blob_want_added_callback_node_t;
typedef struct _tf_ssb_blob_want_added_callback_node_t {
void (*callback)(tf_ssb_t* ssb, const char* id, void* user_data);
void (*cleanup)(tf_ssb_t* ssb, void* user_data);
void* user_data;
tf_ssb_blob_want_added_callback_node_t* next;
} tf_ssb_blob_want_added_callback_node_t;
typedef struct _tf_ssb_t {
bool own_context;
JSRuntime* runtime;
@ -109,6 +118,7 @@ typedef struct _tf_ssb_t {
uint8_t priv[crypto_sign_SECRETKEYBYTES];
tf_ssb_connections_changed_callback_t* connections_changed[k_connections_changed_callbacks_max];
tf_ssb_rpc_cleanup_t* connections_changed_cleanup[k_connections_changed_callbacks_max];
void* connections_changed_user_data[k_connections_changed_callbacks_max];
int connections_changed_count;
@ -122,6 +132,8 @@ typedef struct _tf_ssb_t {
tf_ssb_broadcast_t* broadcasts;
tf_ssb_rpc_callback_node_t* rpc;
tf_ssb_blob_want_added_callback_node_t* blob_want_added;
} tf_ssb_t;
typedef struct _tf_ssb_connection_t {
@ -129,6 +141,8 @@ typedef struct _tf_ssb_connection_t {
uv_tcp_t tcp;
uv_connect_t connect;
JSValue object;
char host[256];
int port;
@ -164,11 +178,14 @@ typedef struct _tf_ssb_connection_t {
tf_ssb_request_t* requests;
} tf_ssb_connection_t;
static JSClassID _connection_class_id;
static void _tf_ssb_connection_client_send_hello(uv_stream_t* stream);
static void _tf_ssb_connection_on_close(uv_handle_t* handle);
static void _tf_ssb_connection_close(tf_ssb_connection_t* connection, const char* reason);
static void _tf_ssb_nonce_inc(uint8_t* nonce);
static void _tf_ssb_write(tf_ssb_connection_t* connection, void* data, size_t size);
static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value);
static void _tf_ssb_connection_send_close(tf_ssb_connection_t* connection)
{
@ -362,6 +379,9 @@ void tf_ssb_connection_remove_request(tf_ssb_connection_t* connection, int32_t r
for (tf_ssb_request_t** it = &connection->requests; *it; it = &(*it)->next) {
if ((*it)->request_number == request_number) {
tf_ssb_request_t* found = *it;
if (found->user_data) {
JS_FreeValue(tf_ssb_connection_get_context(connection), JS_MKPTR(JS_TAG_OBJECT, found->user_data));
}
*it = found->next;
free(found);
break;
@ -821,7 +841,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
bool found = false;
for (tf_ssb_rpc_callback_node_t* it = connection->ssb->rpc; it; it = it->next) {
if (_tf_ssb_name_equals(context, val, it->name)) {
it->callback(connection, flags, request_number, val, message, size, it->user_data);
it->callback(connection, flags, request_number, JS_DupValue(context, val), NULL, 0, it->user_data);
found = true;
break;
}
@ -831,7 +851,7 @@ static void _tf_ssb_connection_rpc_recv(tf_ssb_connection_t* connection, uint8_t
void* user_data = NULL;
if (_tf_ssb_connection_get_request_callback(connection, -request_number, &callback, &user_data)) {
if (callback) {
callback(connection, flags, request_number, val, NULL, 0, user_data);
callback(connection, flags, request_number, JS_DupValue(context, val), NULL, 0, user_data);
}
} else {
const char* k_unsupported = "{\"message\": \"unsupported message\", \"name\": \"Error\", \"stack\": \"none\", \"args\": []}";
@ -1011,6 +1031,14 @@ void tf_ssb_append_message(tf_ssb_t* ssb, JSValue message)
void tf_ssb_connection_destroy(tf_ssb_connection_t* connection)
{
free(connection);
}
static void _tf_ssb_connection_on_close(uv_handle_t* handle)
{
tf_ssb_connection_t* connection = handle->data;
handle->data = NULL;
tf_ssb_t* ssb = connection->ssb;
for (tf_ssb_connection_t** it = &connection->ssb->connections; *it; it = &(*it)->next) {
if (*it == connection) {
@ -1025,15 +1053,7 @@ void tf_ssb_connection_destroy(tf_ssb_connection_t* connection)
for (int i = 0; i < ssb->connections_changed_count; i++) {
ssb->connections_changed[i](ssb, k_tf_ssb_change_remove, connection, ssb->connections_changed_user_data[i]);
}
free(connection);
}
static void _tf_ssb_connection_on_close(uv_handle_t* handle)
{
printf("destroy connection\n");
tf_ssb_connection_t* connection = handle->data;
handle->data = NULL;
tf_ssb_connection_destroy(connection);
JS_FreeValue(connection->ssb->context, connection->object);
}
static void _tf_ssb_connection_on_tcp_recv(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf)
@ -1286,6 +1306,14 @@ tf_ssb_t* tf_ssb_create(uv_loop_t* loop, JSContext* context, sqlite3* db, const
ssb->context = JS_NewContext(ssb->runtime);
}
JS_NewClassID(&_connection_class_id);
JSClassDef def =
{
.class_name = "connection",
.finalizer = _tf_ssb_connection_finalizer,
};
JS_NewClass(JS_GetRuntime(ssb->context), _connection_class_id, &def);
if (db) {
ssb->db = db;
} else {
@ -1373,6 +1401,12 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
tf_ssb_connections_destroy(ssb->connections_tracker);
ssb->connections_tracker = NULL;
for (int i = 0; i < ssb->connections_changed_count; i++) {
if (ssb->connections_changed_cleanup[i]) {
ssb->connections_changed_cleanup[i](ssb, ssb->connections_changed_user_data[i]);
}
}
tf_ssb_rpc_destroy(ssb->rpc_state);
ssb->rpc_state = NULL;
@ -1402,6 +1436,23 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
if (ssb->loop == &ssb->own_loop) {
uv_loop_close(ssb->loop);
}
while (ssb->rpc) {
tf_ssb_rpc_callback_node_t* node = ssb->rpc;
ssb->rpc = node->next;
if (node->cleanup) {
node->cleanup(ssb, node->user_data);
node->cleanup = NULL;
}
free(node);
}
while (ssb->blob_want_added) {
tf_ssb_blob_want_added_callback_node_t* node = ssb->blob_want_added;
ssb->blob_want_added = node->next;
if (node->cleanup) {
node->cleanup(ssb, node->user_data);
}
free(node);
}
if (ssb->own_context) {
JS_FreeContext(ssb->context);
JS_FreeRuntime(ssb->runtime);
@ -1414,11 +1465,6 @@ void tf_ssb_destroy(tf_ssb_t* ssb)
ssb->broadcasts = broadcast->next;
free(broadcast);
}
while (ssb->rpc) {
tf_ssb_rpc_callback_node_t* node = ssb->rpc;
ssb->rpc = node->next;
free(node);
}
free(ssb);
}
@ -1427,8 +1473,50 @@ void tf_ssb_run(tf_ssb_t* ssb)
uv_run(ssb->loop, UV_RUN_DEFAULT);
}
static void _tf_ssb_connection_finalizer(JSRuntime* runtime, JSValue value)
{
tf_ssb_connection_t* connection = JS_GetOpaque(value, _connection_class_id);
tf_ssb_connection_destroy(connection);
}
static void _tf_ssb_connection_send_json_response(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data)
{
if (!user_data) {
return;
}
void _tf_ssb_on_rpc(tf_ssb_connection_t* connection, uint8_t flags, int32_t request_number, JSValue args, const uint8_t* message, size_t size, void* user_data);
_tf_ssb_on_rpc(connection, flags, request_number, args, message, size, user_data);
}
static JSValue _tf_ssb_connection_send_json(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
{
tf_ssb_connection_t* connection = JS_GetOpaque(this_val, _connection_class_id);
if (!connection) {
return JS_UNDEFINED;
}
uint32_t request_number = tf_ssb_connection_next_request_number(connection);
JSValue message_val = JS_JSONStringify(context, argv[0], JS_NULL, JS_NULL);
size_t size;
const char* message = JS_ToCStringLen(context, &size, message_val);
JS_FreeValue(context, message_val);
tf_ssb_connection_rpc_send(
connection,
k_ssb_rpc_flag_json | k_ssb_rpc_flag_stream,
request_number,
(const uint8_t*)message,
size,
_tf_ssb_connection_send_json_response,
JS_IsFunction(context, argv[1]) ? JS_VALUE_GET_PTR(JS_DupValue(context, argv[1])) : NULL);
return JS_UNDEFINED;
}
tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, const struct sockaddr_in* addr, const uint8_t* public_key)
{
JSContext* context = ssb->context;
tf_ssb_connection_t* connection = malloc(sizeof(tf_ssb_connection_t));
memset(connection, 0, sizeof(*connection));
connection->ssb = ssb;
@ -1438,6 +1526,15 @@ tf_ssb_connection_t* tf_ssb_connection_create(tf_ssb_t* ssb, const char* host, c
snprintf(connection->host, sizeof(connection->host), "%s", host);
connection->port = ntohs(addr->sin_port);
connection->object = JS_NewObjectClass(ssb->context, _connection_class_id);
JS_SetPropertyStr(context, connection->object, "send_json", JS_NewCFunction(context, _tf_ssb_connection_send_json, "send_json", 2));
char public_key_str[k_id_base64_len] = { 0 };
if (tf_ssb_id_bin_to_str(public_key_str, sizeof(public_key_str), public_key))
{
JS_SetPropertyStr(context, connection->object, "id", JS_NewString(context, public_key_str));
}
JS_SetOpaque(connection->object, connection);
memcpy(connection->serverpub, public_key, sizeof(connection->serverpub));
uv_tcp_init(ssb->loop, &connection->tcp);
@ -1514,6 +1611,10 @@ static void _tf_ssb_on_connection(uv_stream_t* stream, int status) {
connection->tcp.data = connection;
connection->send_request_number = 1;
connection->object = JS_NewObjectClass(ssb->context, _connection_class_id);
JS_SetPropertyStr(ssb->context, connection->object, "send_json", JS_NewCFunction(ssb->context, _tf_ssb_connection_send_json, "send_json", 2));
JS_SetOpaque(connection->object, connection);
if (uv_tcp_init(ssb->loop, &connection->tcp) != 0) {
printf("uv_tcp_init failed\n");
free(connection);
@ -1811,15 +1912,16 @@ void tf_ssb_set_broadcasts_changed_callback(tf_ssb_t* ssb, void (*callback)(tf_s
ssb->broadcasts_changed_user_data = user_data;
}
void tf_ssb_add_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t* callback, void* user_data)
void tf_ssb_add_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_connections_changed_callback_t* callback, tf_ssb_rpc_cleanup_t* cleanup, void* user_data)
{
assert(ssb->connections_changed_count < k_connections_changed_callbacks_max);
ssb->connections_changed[ssb->connections_changed_count] = callback;
ssb->connections_changed_cleanup[ssb->connections_changed_count] = cleanup;
ssb->connections_changed_user_data[ssb->connections_changed_count] = user_data;
ssb->connections_changed_count++;
}
void tf_ssb_register_rpc(tf_ssb_t* ssb, const char** name, tf_ssb_rpc_callback_t* callback, void* user_data)
void tf_ssb_register_rpc(tf_ssb_t* ssb, const char** name, tf_ssb_rpc_callback_t* callback, tf_ssb_rpc_cleanup_t* cleanup, void* user_data)
{
size_t name_len = 0;
int name_count = 0;
@ -1831,6 +1933,7 @@ void tf_ssb_register_rpc(tf_ssb_t* ssb, const char** name, tf_ssb_rpc_callback_t
*node = (tf_ssb_rpc_callback_node_t) {
.name = (const char**)(node + 1),
.callback = callback,
.cleanup = cleanup,
.user_data = user_data,
.next = ssb->rpc,
};
@ -1864,3 +1967,34 @@ int32_t tf_ssb_connection_next_request_number(tf_ssb_connection_t* connection)
{
return connection->send_request_number++;
}
JSClassID tf_ssb_get_connection_class_id()
{
return _connection_class_id;
}
JSValue tf_ssb_connection_get_object(tf_ssb_connection_t* connection)
{
return connection ? connection->object : JS_UNDEFINED;
}
void tf_ssb_register_blob_want_added(tf_ssb_t* ssb, void (*callback)(tf_ssb_t* ssb, const char* id, void* user_data), void (*cleanup)(tf_ssb_t* ssb, void* user_data), void* user_data)
{
tf_ssb_blob_want_added_callback_node_t* node = malloc(sizeof(tf_ssb_blob_want_added_callback_node_t));
*node = (tf_ssb_blob_want_added_callback_node_t)
{
.callback = callback,
.cleanup = cleanup,
.user_data = user_data,
.next = ssb->blob_want_added,
};
ssb->blob_want_added = node;
}
void tf_ssb_notify_blob_want_added(tf_ssb_t* ssb, const char* id)
{
for (tf_ssb_blob_want_added_callback_node_t* node = ssb->blob_want_added; node; node = node->next)
{
node->callback(ssb, id, node->user_data);
}
}