forked from cory/tildefriends
I just decided. Braces on their own lines.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3668 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
470814f147
commit
843c53e15e
@ -7,7 +7,8 @@
|
||||
JSValue _crypt_hashpw(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||
JSValue _crypt_gensalt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||
|
||||
void tf_bcrypt_init(JSContext* context) {
|
||||
void tf_bcrypt_init(JSContext* context)
|
||||
{
|
||||
JSValue global = JS_GetGlobalObject(context);
|
||||
JSValue bcrypt = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, global, "bCrypt", bcrypt);
|
||||
@ -16,7 +17,8 @@ void tf_bcrypt_init(JSContext* context) {
|
||||
JS_FreeValue(context, global);
|
||||
}
|
||||
|
||||
JSValue _crypt_hashpw(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _crypt_hashpw(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
const char* key = JS_ToCString(context, argv[0]);
|
||||
const char* salt = JS_ToCString(context, argv[1]);
|
||||
char output[7 + 22 + 31 + 1];
|
||||
@ -27,7 +29,8 @@ JSValue _crypt_hashpw(JSContext* context, JSValueConst this_val, int argc, JSVal
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue _crypt_gensalt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _crypt_gensalt(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
int length;
|
||||
JS_ToInt32(context, &length, argv[0]);
|
||||
char buffer[16];
|
||||
|
@ -9,7 +9,8 @@
|
||||
static JSClassID _database_class_id;
|
||||
static int _database_count;
|
||||
|
||||
typedef struct _database_t {
|
||||
typedef struct _database_t
|
||||
{
|
||||
JSContext* context;
|
||||
JSValue object;
|
||||
void* task;
|
||||
@ -26,13 +27,15 @@ static JSValue _database_remove(JSContext* context, JSValueConst this_val, int a
|
||||
static JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||
static JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||
|
||||
void tf_database_init(JSContext* context, sqlite3* sqlite) {
|
||||
void tf_database_init(JSContext* context, sqlite3* sqlite)
|
||||
{
|
||||
JS_NewClassID(&_database_class_id);
|
||||
JSClassDef def = {
|
||||
.class_name = "Database",
|
||||
.finalizer = &_database_finalizer,
|
||||
};
|
||||
if (JS_NewClass(JS_GetRuntime(context), _database_class_id, &def) != 0) {
|
||||
if (JS_NewClass(JS_GetRuntime(context), _database_class_id, &def) != 0)
|
||||
{
|
||||
printf("Failed to register database.\n");
|
||||
}
|
||||
|
||||
@ -44,14 +47,16 @@ void tf_database_init(JSContext* context, sqlite3* sqlite) {
|
||||
JS_FreeValue(context, global);
|
||||
}
|
||||
|
||||
static JSValue _database_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data) {
|
||||
static JSValue _database_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv, int magic, JSValue* data)
|
||||
{
|
||||
++_database_count;
|
||||
JSValue object = JS_NewObjectClass(context, _database_class_id);
|
||||
sqlite3* db = NULL;
|
||||
JS_ToInt64(context, (int64_t*)&db, data[0]);
|
||||
|
||||
database_t* database = malloc(sizeof(database_t));
|
||||
*database = (database_t) {
|
||||
*database = (database_t)
|
||||
{
|
||||
.task = JS_GetContextOpaque(context),
|
||||
.context = context,
|
||||
.object = object,
|
||||
@ -71,26 +76,32 @@ static JSValue _database_create(JSContext* context, JSValueConst this_val, int a
|
||||
return object;
|
||||
}
|
||||
|
||||
static void _database_finalizer(JSRuntime *runtime, JSValue value) {
|
||||
static void _database_finalizer(JSRuntime *runtime, JSValue value)
|
||||
{
|
||||
database_t* database = JS_GetOpaque(value, _database_class_id);
|
||||
if (database) {
|
||||
if (database)
|
||||
{
|
||||
free((void*)database->id);
|
||||
free(database);
|
||||
}
|
||||
--_database_count;
|
||||
}
|
||||
|
||||
static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue entry = JS_UNDEFINED;
|
||||
database_t* database = JS_GetOpaque(this_val, _database_class_id);
|
||||
if (database) {
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "SELECT value FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(database->db, "SELECT value FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t length;
|
||||
const char* keyString = JS_ToCStringLen(context, &length, argv[0]);
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, keyString, length, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
entry = JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0));
|
||||
}
|
||||
JS_FreeCString(context, keyString);
|
||||
@ -100,11 +111,14 @@ static JSValue _database_get(JSContext* context, JSValueConst this_val, int argc
|
||||
return entry;
|
||||
}
|
||||
|
||||
JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
database_t* database = JS_GetOpaque(this_val, _database_class_id);
|
||||
if (database) {
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, $2, $3)", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(database->db, "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t keyLength;
|
||||
const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
|
||||
size_t valueLength;
|
||||
@ -112,7 +126,8 @@ JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSVal
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, keyString, keyLength, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, valueString, valueLength, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_OK) {
|
||||
sqlite3_step(statement) == SQLITE_OK)
|
||||
{
|
||||
}
|
||||
JS_FreeCString(context, keyString);
|
||||
JS_FreeCString(context, valueString);
|
||||
@ -122,16 +137,20 @@ JSValue _database_set(JSContext* context, JSValueConst this_val, int argc, JSVal
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
database_t* database = JS_GetOpaque(this_val, _database_class_id);
|
||||
if (database) {
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "DELETE FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(database->db, "DELETE FROM properties WHERE id = $1 AND key = $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
size_t keyLength;
|
||||
const char* keyString = JS_ToCStringLen(context, &keyLength, argv[0]);
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, keyString, keyLength, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_OK) {
|
||||
sqlite3_step(statement) == SQLITE_OK)
|
||||
{
|
||||
}
|
||||
JS_FreeCString(context, keyString);
|
||||
sqlite3_finalize(statement);
|
||||
@ -140,16 +159,21 @@ JSValue _database_remove(JSContext* context, JSValueConst this_val, int argc, JS
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue array = JS_UNDEFINED;
|
||||
database_t* database = JS_GetOpaque(this_val, _database_class_id);
|
||||
if (database) {
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = $1", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = $1", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
array = JS_NewArray(context);
|
||||
uint32_t index = 0;
|
||||
while (sqlite3_step(statement) == SQLITE_ROW) {
|
||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
JS_SetPropertyUint32(context, array, index++, JS_NewStringLen(context, (const char*)sqlite3_column_text(statement, 0), sqlite3_column_bytes(statement, 0)));
|
||||
}
|
||||
}
|
||||
@ -159,17 +183,22 @@ JSValue _database_get_all(JSContext* context, JSValueConst this_val, int argc, J
|
||||
return array;
|
||||
}
|
||||
|
||||
JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _database_get_like(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue result = JS_UNDEFINED;
|
||||
database_t* database = JS_GetOpaque(this_val, _database_class_id);
|
||||
if (database) {
|
||||
if (database)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(database->db, "SELECT key, value FROM properties WHERE id = ? AND KEY LIKE ?", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
const char* pattern = JS_ToCString(context, argv[0]);
|
||||
if (sqlite3_bind_text(statement, 1, database->id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, pattern, -1, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_text(statement, 2, pattern, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
result = JS_NewObject(context);
|
||||
while (sqlite3_step(statement) == SQLITE_ROW) {
|
||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
JS_SetPropertyStr(
|
||||
context,
|
||||
result,
|
||||
|
59
src/file.c
59
src/file.c
@ -31,7 +31,8 @@ typedef struct file_stat_t {
|
||||
uv_fs_t _request;
|
||||
} file_stat_t;
|
||||
|
||||
void tf_file_init(JSContext* context) {
|
||||
void tf_file_init(JSContext* context)
|
||||
{
|
||||
JSValue global = JS_GetGlobalObject(context);
|
||||
JSValue file = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, global, "File", file);
|
||||
@ -106,7 +107,8 @@ static void _file_read_open_callback(uv_fs_t* req)
|
||||
}
|
||||
}
|
||||
|
||||
static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
void* task = JS_GetContextOpaque(context);
|
||||
const char* file_name = JS_ToCString(context, argv[0]);
|
||||
|
||||
@ -125,19 +127,24 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
|
||||
return tf_task_get_promise(task, promise);
|
||||
}
|
||||
|
||||
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
const char* fileName = JS_ToCString(context, argv[0]);
|
||||
FILE* file = fopen(fileName, "wb");
|
||||
JS_FreeCString(context, fileName);
|
||||
|
||||
if (file) {
|
||||
if (file)
|
||||
{
|
||||
size_t size;
|
||||
uint8_t* buffer = tf_try_get_array_buffer(context, &size, argv[1]);
|
||||
if (buffer) {
|
||||
if (buffer)
|
||||
{
|
||||
int written = fwrite((const char*)buffer, 1, size, file);
|
||||
result = JS_NewInt32(context, (size_t)written == size ? 0 : written);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* data = JS_ToCStringLen(context, &size, argv[1]);
|
||||
int written = fwrite((const char*)data, 1, size, file);
|
||||
result = JS_NewInt32(context, (size_t)written == size ? 0 : written);
|
||||
@ -148,7 +155,8 @@ static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int a
|
||||
return result;
|
||||
}
|
||||
|
||||
static JSValue _file_rename_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _file_rename_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
void* task = JS_GetContextOpaque(context);
|
||||
const char* oldName = JS_ToCString(context, argv[0]);
|
||||
const char* newName = JS_ToCString(context, argv[1]);
|
||||
@ -159,7 +167,8 @@ static JSValue _file_rename_file(JSContext* context, JSValueConst this_val, int
|
||||
return JS_NewInt32(context, result);
|
||||
}
|
||||
|
||||
static JSValue _file_unlink_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _file_unlink_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
void* task = JS_GetContextOpaque(context);
|
||||
const char* fileName = JS_ToCString(context, argv[0]);
|
||||
uv_fs_t req;
|
||||
@ -168,7 +177,8 @@ static JSValue _file_unlink_file(JSContext* context, JSValueConst this_val, int
|
||||
return JS_NewInt32(context, result);
|
||||
}
|
||||
|
||||
static JSValue _file_read_directory(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _file_read_directory(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
const char* directory = JS_ToCString(context, argv[0]);
|
||||
JSValue array = JS_NewArray(context);
|
||||
|
||||
@ -177,7 +187,8 @@ static JSValue _file_read_directory(JSContext* context, JSValueConst this_val, i
|
||||
std::string pattern = directory;
|
||||
pattern += "\\*";
|
||||
HANDLE handle = FindFirstFile(pattern.c_str(), &find);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
if (handle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
int index = 0;
|
||||
do {
|
||||
JS_SetPropertyUint32(context, array, index++, JS_NewString(context, find.cFileName));
|
||||
@ -186,10 +197,12 @@ static JSValue _file_read_directory(JSContext* context, JSValueConst this_val, i
|
||||
}
|
||||
#else
|
||||
DIR* dir = opendir(directory);
|
||||
if (dir) {
|
||||
if (dir)
|
||||
{
|
||||
uint32_t index = 0;
|
||||
struct dirent* entry = readdir(dir);
|
||||
while (entry) {
|
||||
while (entry)
|
||||
{
|
||||
JS_SetPropertyUint32(context, array, index++, JS_NewString(context, entry->d_name));
|
||||
entry = readdir(dir);
|
||||
}
|
||||
@ -201,7 +214,8 @@ static JSValue _file_read_directory(JSContext* context, JSValueConst this_val, i
|
||||
return array;
|
||||
}
|
||||
|
||||
JSValue _file_make_directory(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _file_make_directory(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
void* task = JS_GetContextOpaque(context);
|
||||
const char* directory = JS_ToCString(context, argv[0]);
|
||||
|
||||
@ -211,7 +225,8 @@ JSValue _file_make_directory(JSContext* context, JSValueConst this_val, int argc
|
||||
return JS_NewInt32(context, result);
|
||||
}
|
||||
|
||||
JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
void* task = JS_GetContextOpaque(context);
|
||||
const char* path = JS_ToCString(context, argv[0]);
|
||||
promiseid_t promise = tf_task_allocate_promise(task);
|
||||
@ -223,7 +238,8 @@ JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueC
|
||||
data->_context = context;
|
||||
|
||||
int result = uv_fs_stat(tf_task_get_loop(task), &data->_request, path, _file_on_stat_complete);
|
||||
if (result) {
|
||||
if (result)
|
||||
{
|
||||
tf_task_reject_promise(task, promise, JS_NewInt32(context, result));
|
||||
free(data);
|
||||
}
|
||||
@ -231,17 +247,22 @@ JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueC
|
||||
return tf_task_get_promise(task, promise);
|
||||
}
|
||||
|
||||
static double _time_spec_to_double(const uv_timespec_t* time_spec) {
|
||||
static double _time_spec_to_double(const uv_timespec_t* time_spec)
|
||||
{
|
||||
return time_spec->tv_sec + (double)(time_spec->tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
static void _file_on_stat_complete(uv_fs_t* request) {
|
||||
static void _file_on_stat_complete(uv_fs_t* request)
|
||||
{
|
||||
file_stat_t* data = (file_stat_t*)(request->data);
|
||||
JSContext* context = data->_context;
|
||||
|
||||
if (request->result) {
|
||||
if (request->result)
|
||||
{
|
||||
tf_task_reject_promise(data->_task, data->_promise, JS_NewInt32(context, request->result));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
JSValue result = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, result, "mtime", JS_NewFloat64(context, _time_spec_to_double(&request->statbuf.st_mtim)));
|
||||
JS_SetPropertyStr(context, result, "ctime", JS_NewFloat64(context, _time_spec_to_double(&request->statbuf.st_ctim)));
|
||||
|
128
src/main.c
128
src/main.c
@ -29,7 +29,8 @@
|
||||
_xopt_ctx = xopt_context((name), (options), ((flags) ^ XOPT_CTX_POSIXMEHARDER), (err_ptr)); \
|
||||
if (*(err_ptr)) break; \
|
||||
*extrac_ptr = xopt_parse(_xopt_ctx, (argc), (argv), (config_ptr), (extrav_ptr), (err_ptr)); \
|
||||
if ((config_ptr)->help) { \
|
||||
if ((config_ptr)->help) \
|
||||
{ \
|
||||
xoptAutohelpOptions __xopt_autohelp_opts; \
|
||||
__xopt_autohelp_opts.usage = (autohelp_usage); \
|
||||
__xopt_autohelp_opts.prefix = (autohelp_prefix); \
|
||||
@ -91,24 +92,29 @@ void shedPrivileges()
|
||||
// RLIMIT_SIGPENDING
|
||||
// RLIMIT_STACK
|
||||
|
||||
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0) {
|
||||
if (setrlimit(RLIMIT_FSIZE, &zeroLimit) != 0)
|
||||
{
|
||||
perror("setrlimit(RLIMIT_FSIZE, {0, 0})");
|
||||
exit(-1);
|
||||
}
|
||||
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0) {
|
||||
if (setrlimit(RLIMIT_NOFILE, &zeroLimit) != 0)
|
||||
{
|
||||
perror("setrlimit(RLIMIT_NOFILE, {0, 0})");
|
||||
exit(-1);
|
||||
}
|
||||
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0) {
|
||||
if (setrlimit(RLIMIT_NPROC, &zeroLimit) != 0)
|
||||
{
|
||||
perror("setrlimit(RLIMIT_NPROC, {0, 0})");
|
||||
exit(-1);
|
||||
}
|
||||
#if !defined (__MACH__)
|
||||
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0) {
|
||||
if (setrlimit(RLIMIT_LOCKS, &zeroLimit) != 0)
|
||||
{
|
||||
perror("setrlimit(RLIMIT_LOCKS, {0, 0})");
|
||||
exit(-1);
|
||||
}
|
||||
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0) {
|
||||
if (setrlimit(RLIMIT_MSGQUEUE, &zeroLimit) != 0)
|
||||
{
|
||||
perror("setrlimit(RLIMIT_MSGQUEUE, {0, 0})");
|
||||
exit(-1);
|
||||
}
|
||||
@ -134,10 +140,12 @@ static int _tf_command_test(const char* file, int argc, char* argv[])
|
||||
int extra_count = 0;
|
||||
const char *err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "test [options]", "options:", NULL, 15);
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
}
|
||||
@ -150,7 +158,8 @@ static int _tf_command_test(const char* file, int argc, char* argv[])
|
||||
tf_tests(&test_options);
|
||||
return 0;
|
||||
xopt_help:
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
@ -177,40 +186,50 @@ static int _tf_command_import(const char* file, int argc, char* argv[])
|
||||
const char *err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "import [options] [paths] ...", "options:", NULL, 15);
|
||||
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
sqlite3* db = NULL;
|
||||
if (args.db_path) {
|
||||
if (args.db_path)
|
||||
{
|
||||
sqlite3_open(args.db_path, &db);
|
||||
}
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, db, NULL);
|
||||
if (extra_count) {
|
||||
for (int i = 0; i < extra_count; i++) {
|
||||
if (extra_count)
|
||||
{
|
||||
for (int i = 0; i < extra_count; i++)
|
||||
{
|
||||
printf("Importing %s...\n", extras[i]);
|
||||
tf_ssb_import(ssb, args.user, extras[i]);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Importing %s...\n", "apps");
|
||||
tf_ssb_import(ssb, args.user, "apps");
|
||||
}
|
||||
tf_ssb_destroy(ssb);
|
||||
if (db) {
|
||||
if (db)
|
||||
{
|
||||
sqlite3_close(db);
|
||||
}
|
||||
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
@ -233,38 +252,47 @@ static int _tf_command_export(const char* file, int argc, char* argv[])
|
||||
const char *err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "export [options] [paths] ...", "options:", NULL, 15);
|
||||
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
tf_ssb_t* ssb = tf_ssb_create(NULL, NULL, NULL, NULL);
|
||||
if (extra_count) {
|
||||
for (int i = 0; i < extra_count; i++) {
|
||||
if (extra_count)
|
||||
{
|
||||
for (int i = 0; i < extra_count; i++)
|
||||
{
|
||||
printf("Exporting %s...\n", extras[i]);
|
||||
tf_ssb_export(ssb, extras[i]);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* k_export[] = {
|
||||
"/~cory/index",
|
||||
"/~cory/docs",
|
||||
};
|
||||
for (int i = 0; i < _countof(k_export); i++) {
|
||||
for (int i = 0; i < _countof(k_export); i++)
|
||||
{
|
||||
printf("Exporting %s...\n", k_export[i]);
|
||||
tf_ssb_export(ssb, k_export[i]);
|
||||
}
|
||||
}
|
||||
tf_ssb_destroy(ssb);
|
||||
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
@ -304,14 +332,17 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
const char *err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "run [options] [paths] ...", "options:", NULL, 15);
|
||||
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
|
||||
@ -340,7 +371,8 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
return result;
|
||||
|
||||
xopt_help:
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
@ -364,14 +396,17 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
const char *err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST | XOPT_CTX_POSIXMEHARDER, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "sandbox [options]", "options:", NULL, 15);
|
||||
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
|
||||
@ -387,7 +422,8 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
@ -411,10 +447,12 @@ static int _tf_command_post(const char* file, int argc, char* argv[])
|
||||
int extra_count = 0;
|
||||
const char *err = NULL;
|
||||
XOPT_PARSE(file, XOPT_CTX_KEEPFIRST, options, &args, argc, (const char**)argv, &extra_count, &extras, &err, stderr, "post [options]", "options:", NULL, 15);
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr, "Error: %s\n", err);
|
||||
return 2;
|
||||
}
|
||||
@ -426,7 +464,8 @@ static int _tf_command_post(const char* file, int argc, char* argv[])
|
||||
return 0;
|
||||
|
||||
xopt_help:
|
||||
if (extras) {
|
||||
if (extras)
|
||||
{
|
||||
free((void*)extras);
|
||||
}
|
||||
return 1;
|
||||
@ -436,7 +475,8 @@ static int _tf_command_usage(const char* file, int argc, char* argv[])
|
||||
{
|
||||
printf("Usage: %s command [command-options]\n", file);
|
||||
printf("commands:\n");
|
||||
for (int i = 0; i < _countof(k_commands); i++) {
|
||||
for (int i = 0; i < _countof(k_commands); i++)
|
||||
{
|
||||
printf(" %s - %s\n", k_commands[i].name, k_commands[i].description);
|
||||
}
|
||||
return 0;
|
||||
@ -449,15 +489,19 @@ int main(int argc, char* argv[])
|
||||
tf_taskstub_startup();
|
||||
|
||||
#if !defined (_WIN32)
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
|
||||
{
|
||||
perror("signal");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (argc >= 2) {
|
||||
for (int i = 0; i < _countof(k_commands); i++) {
|
||||
if (argc >= 2)
|
||||
{
|
||||
for (int i = 0; i < _countof(k_commands); i++)
|
||||
{
|
||||
const command_t* command = &k_commands[i];
|
||||
if (strcmp(argv[1], command->name) == 0) {
|
||||
if (strcmp(argv[1], command->name) == 0)
|
||||
{
|
||||
return command->callback(argv[0], argc - 2, argv + 2);
|
||||
}
|
||||
}
|
||||
|
@ -15,84 +15,106 @@ typedef struct _tf_packetstream_t {
|
||||
bool destroyed;
|
||||
} tf_packetstream_t;
|
||||
|
||||
tf_packetstream_t* tf_packetstream_create() {
|
||||
tf_packetstream_t* tf_packetstream_create()
|
||||
{
|
||||
tf_packetstream_t* impl = malloc(sizeof(tf_packetstream_t));
|
||||
*impl = (tf_packetstream_t) { 0 };
|
||||
return impl;
|
||||
}
|
||||
|
||||
void tf_packetstream_destroy(tf_packetstream_t* stream) {
|
||||
void tf_packetstream_destroy(tf_packetstream_t* stream)
|
||||
{
|
||||
stream->onreceive = NULL;
|
||||
stream->onreceive_user_data = NULL;
|
||||
stream->destroyed = true;
|
||||
if (stream->stream.data) {
|
||||
if (stream->stream.data)
|
||||
{
|
||||
tf_packetstream_close(stream);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
free(stream);
|
||||
}
|
||||
}
|
||||
|
||||
static void _packetstream_allocate(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buffer) {
|
||||
static void _packetstream_allocate(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buffer)
|
||||
{
|
||||
buffer->base = malloc(suggested_size);
|
||||
buffer->len = suggested_size;
|
||||
}
|
||||
|
||||
static void _packetstream_process_messages(tf_packetstream_t* stream) {
|
||||
static void _packetstream_process_messages(tf_packetstream_t* stream)
|
||||
{
|
||||
int packet_type = 0;
|
||||
size_t length = 0;
|
||||
while (stream->buffer_size >= sizeof(packet_type) + sizeof(length)) {
|
||||
while (stream->buffer_size >= sizeof(packet_type) + sizeof(length))
|
||||
{
|
||||
memcpy(&packet_type, stream->buffer, sizeof(packet_type));
|
||||
memcpy(&length, stream->buffer + sizeof(packet_type), sizeof(length));
|
||||
|
||||
if (stream->buffer_size >= sizeof(packet_type) + sizeof(length) + length) {
|
||||
if (stream->onreceive) {
|
||||
if (stream->buffer_size >= sizeof(packet_type) + sizeof(length) + length)
|
||||
{
|
||||
if (stream->onreceive)
|
||||
{
|
||||
stream->onreceive(packet_type, stream->buffer + sizeof(length) + sizeof(packet_type), length, stream->onreceive_user_data);
|
||||
}
|
||||
size_t consumed = sizeof(length) + sizeof(packet_type) + length;
|
||||
memmove(stream->buffer, stream->buffer + consumed, stream->buffer_size - consumed);
|
||||
stream->buffer_size -= consumed;
|
||||
stream->buffer = realloc(stream->buffer, stream->buffer_size);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _packetstream_on_read(uv_stream_t* handle, ssize_t count, const uv_buf_t* buffer) {
|
||||
static void _packetstream_on_read(uv_stream_t* handle, ssize_t count, const uv_buf_t* buffer)
|
||||
{
|
||||
tf_packetstream_t* stream = handle->data;
|
||||
if (count >= 0) {
|
||||
if (count > 0) {
|
||||
if (count >= 0)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
char* new_buffer = realloc(stream->buffer, stream->buffer_size + count);
|
||||
if (new_buffer) {
|
||||
if (new_buffer)
|
||||
{
|
||||
memcpy(new_buffer + stream->buffer_size, buffer->base, count);
|
||||
stream->buffer = new_buffer;
|
||||
stream->buffer_size += count;
|
||||
}
|
||||
_packetstream_process_messages(stream);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_packetstream_close(stream);
|
||||
}
|
||||
free(buffer->base);
|
||||
}
|
||||
|
||||
void tf_packetstream_start(tf_packetstream_t* stream) {
|
||||
void tf_packetstream_start(tf_packetstream_t* stream)
|
||||
{
|
||||
stream->stream.data = stream;
|
||||
uv_read_start((uv_stream_t*)&stream->stream, _packetstream_allocate, _packetstream_on_read);
|
||||
}
|
||||
|
||||
static void _packetstream_on_write(uv_write_t* request, int status) {
|
||||
static void _packetstream_on_write(uv_write_t* request, int status)
|
||||
{
|
||||
free(request);
|
||||
}
|
||||
|
||||
void tf_packetstream_send(tf_packetstream_t* stream, int packet_type, char* begin, size_t length) {
|
||||
void tf_packetstream_send(tf_packetstream_t* stream, int packet_type, char* begin, size_t length)
|
||||
{
|
||||
size_t buffer_length = sizeof(uv_write_t) + sizeof(packet_type) + sizeof(length) + length;
|
||||
uv_write_t* request = malloc(buffer_length);
|
||||
memset(request, 0, sizeof(uv_write_t));
|
||||
char* buffer = (char*)(request + 1);
|
||||
memcpy(buffer, &packet_type, sizeof(packet_type));
|
||||
memcpy(buffer + sizeof(packet_type), &length, sizeof(length));
|
||||
if (length) {
|
||||
if (length)
|
||||
{
|
||||
memcpy(buffer + sizeof(packet_type) + sizeof(length), begin, length);
|
||||
}
|
||||
uv_buf_t write_buffer;
|
||||
@ -101,7 +123,8 @@ void tf_packetstream_send(tf_packetstream_t* stream, int packet_type, char* begi
|
||||
uv_write(request, (uv_stream_t*)&stream->stream, &write_buffer, 1, _packetstream_on_write);
|
||||
}
|
||||
|
||||
void tf_packetstream_set_on_receive(tf_packetstream_t* stream, tf_packetstream_onreceive_t* callback, void* user_data) {
|
||||
void tf_packetstream_set_on_receive(tf_packetstream_t* stream, tf_packetstream_onreceive_t* callback, void* user_data)
|
||||
{
|
||||
stream->onreceive = callback;
|
||||
stream->onreceive_user_data = user_data;
|
||||
}
|
||||
@ -110,17 +133,21 @@ static void _tf_packetstream_handle_closed(uv_handle_t* handle)
|
||||
{
|
||||
tf_packetstream_t* packetstream = handle->data;
|
||||
handle->data = NULL;
|
||||
if (packetstream->destroyed) {
|
||||
if (packetstream->destroyed)
|
||||
{
|
||||
free(packetstream);
|
||||
}
|
||||
}
|
||||
|
||||
void tf_packetstream_close(tf_packetstream_t* stream) {
|
||||
if (stream->stream.data && !uv_is_closing((uv_handle_t*)&stream->stream)) {
|
||||
void tf_packetstream_close(tf_packetstream_t* stream)
|
||||
{
|
||||
if (stream->stream.data && !uv_is_closing((uv_handle_t*)&stream->stream))
|
||||
{
|
||||
uv_close((uv_handle_t*)&stream->stream, _tf_packetstream_handle_closed);
|
||||
}
|
||||
}
|
||||
|
||||
uv_pipe_t* tf_packetstream_get_pipe(tf_packetstream_t* stream) {
|
||||
uv_pipe_t* tf_packetstream_get_pipe(tf_packetstream_t* stream)
|
||||
{
|
||||
return &stream->stream;
|
||||
}
|
||||
|
167
src/serialize.c
167
src/serialize.c
@ -48,7 +48,8 @@ static int32_t _serialize_readInt32(const char** buffer, size_t* size);
|
||||
static int64_t _serialize_readInt64(const char** buffer, size_t* size);
|
||||
static double _serialize_readDouble(const char** buffer, size_t* size);
|
||||
|
||||
void tf_serialize_store(tf_task_t* task, tf_taskstub_t* to, void** out_buffer, size_t* out_size, JSValue value) {
|
||||
void tf_serialize_store(tf_task_t* task, tf_taskstub_t* to, void** out_buffer, size_t* out_size, JSValue value)
|
||||
{
|
||||
buffer_t tmp = { 0 };
|
||||
_serialize_store(task, to, &tmp, value);
|
||||
tmp.data = realloc(tmp.data, tmp.size);
|
||||
@ -56,12 +57,15 @@ void tf_serialize_store(tf_task_t* task, tf_taskstub_t* to, void** out_buffer, s
|
||||
*out_size = tmp.size;
|
||||
}
|
||||
|
||||
JSValue tf_serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size) {
|
||||
JSValue tf_serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size)
|
||||
{
|
||||
return _serialize_load(task, from, buffer, size);
|
||||
}
|
||||
|
||||
static void _buffer_append(buffer_t* buffer, const void* data, size_t size) {
|
||||
if (buffer->capacity < buffer->size + size) {
|
||||
static void _buffer_append(buffer_t* buffer, const void* data, size_t size)
|
||||
{
|
||||
if (buffer->capacity < buffer->size + size)
|
||||
{
|
||||
size_t new_capacity = (size + buffer->capacity) * 2;
|
||||
buffer->data = realloc(buffer->data, new_capacity);
|
||||
buffer->capacity = new_capacity;
|
||||
@ -70,54 +74,64 @@ static void _buffer_append(buffer_t* buffer, const void* data, size_t size) {
|
||||
buffer->size += size;
|
||||
}
|
||||
|
||||
void _serialize_writeInt8(buffer_t* buffer, int8_t value) {
|
||||
void _serialize_writeInt8(buffer_t* buffer, int8_t value)
|
||||
{
|
||||
_buffer_append(buffer, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void _serialize_writeInt32(buffer_t* buffer, int32_t value) {
|
||||
void _serialize_writeInt32(buffer_t* buffer, int32_t value)
|
||||
{
|
||||
_buffer_append(buffer, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void _serialize_writeInt64(buffer_t* buffer, int64_t value) {
|
||||
void _serialize_writeInt64(buffer_t* buffer, int64_t value)
|
||||
{
|
||||
_buffer_append(buffer, &value, sizeof(value));
|
||||
}
|
||||
|
||||
void _serialize_writeDouble(buffer_t* buffer, double value) {
|
||||
void _serialize_writeDouble(buffer_t* buffer, double value)
|
||||
{
|
||||
_buffer_append(buffer, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static void _serialize_read(const char** buffer, size_t* size, void* target, size_t target_size) {
|
||||
static void _serialize_read(const char** buffer, size_t* size, void* target, size_t target_size)
|
||||
{
|
||||
assert(*size >= target_size);
|
||||
memcpy(target, *buffer, target_size);
|
||||
*buffer += target_size;
|
||||
*size -= target_size;
|
||||
}
|
||||
|
||||
static int8_t _serialize_readInt8(const char** buffer, size_t* size) {
|
||||
static int8_t _serialize_readInt8(const char** buffer, size_t* size)
|
||||
{
|
||||
int8_t result;
|
||||
_serialize_read(buffer, size, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
int32_t _serialize_readInt32(const char** buffer, size_t* size) {
|
||||
int32_t _serialize_readInt32(const char** buffer, size_t* size)
|
||||
{
|
||||
int32_t result;
|
||||
_serialize_read(buffer, size, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
int64_t _serialize_readInt64(const char** buffer, size_t* size) {
|
||||
int64_t _serialize_readInt64(const char** buffer, size_t* size)
|
||||
{
|
||||
int64_t result;
|
||||
_serialize_read(buffer, size, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
double _serialize_readDouble(const char** buffer, size_t* size) {
|
||||
double _serialize_readDouble(const char** buffer, size_t* size)
|
||||
{
|
||||
double result;
|
||||
_serialize_read(buffer, size, &result, sizeof(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool _serialize_store(tf_task_t* task, tf_taskstub_t* to, buffer_t* buffer, JSValue value) {
|
||||
static bool _serialize_store(tf_task_t* task, tf_taskstub_t* to, buffer_t* buffer, JSValue value)
|
||||
{
|
||||
return _serialize_storeInternal(task, to, buffer, value, 0);
|
||||
}
|
||||
|
||||
@ -129,16 +143,25 @@ static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_
|
||||
size_t element_size;
|
||||
JSValue typed;
|
||||
uint8_t* bytes;
|
||||
if (JS_IsUndefined(value)) {
|
||||
if (JS_IsUndefined(value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kUndefined);
|
||||
} else if (JS_IsUninitialized(value)) {
|
||||
}
|
||||
else if (JS_IsUninitialized(value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kUninitialized);
|
||||
} else if (JS_IsNull(value)) {
|
||||
}
|
||||
else if (JS_IsNull(value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kNull);
|
||||
} else if (JS_IsBool(value)) {
|
||||
}
|
||||
else if (JS_IsBool(value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kBoolean);
|
||||
_serialize_writeInt8(buffer, JS_ToBool(tf_task_get_context(task), value) ? 1 : 0);
|
||||
} else if (JS_IsNumber(value)) {
|
||||
}
|
||||
else if (JS_IsNumber(value))
|
||||
{
|
||||
int64_t result = 0;
|
||||
if (JS_ToInt64(tf_task_get_context(task), &result, value) == 0)
|
||||
{
|
||||
@ -149,7 +172,9 @@ static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_
|
||||
{
|
||||
fprintf(stderr, "Unable to store integer.\n");
|
||||
}
|
||||
} else if (JS_IsNumber(value)) {
|
||||
}
|
||||
else if (JS_IsNumber(value))
|
||||
{
|
||||
double result = 0.0;
|
||||
if (JS_ToFloat64(tf_task_get_context(task), &result, value) == 0)
|
||||
{
|
||||
@ -160,68 +185,93 @@ static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_
|
||||
{
|
||||
fprintf(stderr, "Unable to store number.\n");
|
||||
}
|
||||
} else if (JS_IsString(value)) {
|
||||
}
|
||||
else if (JS_IsString(value))
|
||||
{
|
||||
size_t len = 0;
|
||||
const char* result = JS_ToCStringLen(tf_task_get_context(task), &len, value);
|
||||
_serialize_writeInt32(buffer, kString);
|
||||
_serialize_writeInt32(buffer, (int32_t)len);
|
||||
_buffer_append(buffer, result, len);
|
||||
JS_FreeCString(tf_task_get_context(task), result);
|
||||
} else if ((bytes = tf_try_get_array_buffer(tf_task_get_context(task), &size, value)) != 0) {
|
||||
}
|
||||
else if ((bytes = tf_try_get_array_buffer(tf_task_get_context(task), &size, value)) != 0)
|
||||
{
|
||||
_serialize_writeInt32(buffer, kArrayBuffer);
|
||||
_serialize_writeInt32(buffer, (int32_t)size);
|
||||
_buffer_append(buffer, bytes, size);
|
||||
} else if (!JS_IsException((typed = tf_try_get_typed_array_buffer(tf_task_get_context(task), value, &offset, &size, &element_size)))) {
|
||||
}
|
||||
else if (!JS_IsException((typed = tf_try_get_typed_array_buffer(tf_task_get_context(task), value, &offset, &size, &element_size))))
|
||||
{
|
||||
size_t total_size;
|
||||
uint8_t* bytes = tf_try_get_array_buffer(tf_task_get_context(task), &total_size, typed);
|
||||
_serialize_writeInt32(buffer, kArrayBuffer);
|
||||
_serialize_writeInt32(buffer, (int32_t)size);
|
||||
_buffer_append(buffer, bytes, size);
|
||||
} else if (JS_IsArray(tf_task_get_context(task), value)) {
|
||||
}
|
||||
else if (JS_IsArray(tf_task_get_context(task), value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kArray);
|
||||
JSValue length_val = JS_GetPropertyStr(tf_task_get_context(task), value, "length");
|
||||
int length;
|
||||
if (JS_ToInt32(tf_task_get_context(task), &length, length_val) == 0) {
|
||||
if (JS_ToInt32(tf_task_get_context(task), &length, length_val) == 0)
|
||||
{
|
||||
_serialize_writeInt32(buffer, length);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
_serialize_storeInternal(task, to, buffer, JS_GetPropertyUint32(tf_task_get_context(task), value, i), depth + 1);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_serialize_writeInt32(buffer, 0);
|
||||
}
|
||||
} else if (JS_IsFunction(tf_task_get_context(task), value)) {
|
||||
}
|
||||
else if (JS_IsFunction(tf_task_get_context(task), value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kFunction);
|
||||
exportid_t exportId = tf_task_export_function(task, to, value);
|
||||
_serialize_writeInt32(buffer, exportId);
|
||||
} else if (JS_IsException(value)) {
|
||||
}
|
||||
else if (JS_IsException(value))
|
||||
{
|
||||
JSValue exception = JS_GetException(context);
|
||||
JSValue error = JS_NewObject(context);
|
||||
JSValue message = JS_GetPropertyStr(context, exception, "message");
|
||||
if (!JS_IsException(message)) {
|
||||
if (!JS_IsException(message))
|
||||
{
|
||||
JS_SetPropertyStr(context, error, "message", message);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_FreeValue(context, message);
|
||||
}
|
||||
if (JS_IsError(context, exception)) {
|
||||
if (JS_IsError(context, exception))
|
||||
{
|
||||
JSValue stack = JS_GetPropertyStr(context, exception, "stack");
|
||||
if (!JS_IsUndefined(stack)) {
|
||||
if (!JS_IsUndefined(stack))
|
||||
{
|
||||
JS_SetPropertyStr(context, error, "stack", JS_DupValue(context, stack));
|
||||
}
|
||||
}
|
||||
_serialize_writeInt32(buffer, kException);
|
||||
_serialize_storeInternal(task, to, buffer, error, depth + 1);
|
||||
JS_FreeValue(context, error);
|
||||
} else if (JS_IsError(tf_task_get_context(task), value)) {
|
||||
}
|
||||
else if (JS_IsError(tf_task_get_context(task), value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kError);
|
||||
JSPropertyEnum* ptab;
|
||||
uint32_t plen;
|
||||
JS_GetOwnPropertyNames(tf_task_get_context(task), &ptab, &plen, value, JS_GPN_STRING_MASK);
|
||||
_serialize_writeInt32(buffer, plen);
|
||||
for (uint32_t i = 0; i < plen; ++i) {
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSValue key = JS_AtomToString(tf_task_get_context(task), ptab[i].atom);
|
||||
JSPropertyDescriptor desc;
|
||||
JSValue key_value = JS_NULL;
|
||||
if (JS_GetOwnProperty(tf_task_get_context(task), &desc, value, ptab[i].atom) == 1) {
|
||||
if (JS_GetOwnProperty(tf_task_get_context(task), &desc, value, ptab[i].atom) == 1)
|
||||
{
|
||||
key_value = desc.value;
|
||||
}
|
||||
_serialize_storeInternal(task, to, buffer, key, depth + 1);
|
||||
@ -229,21 +279,26 @@ static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_
|
||||
JS_FreeValue(tf_task_get_context(task), key);
|
||||
JS_FreeValue(tf_task_get_context(task), key_value);
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i) {
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JS_FreeAtom(tf_task_get_context(task), ptab[i].atom);
|
||||
}
|
||||
js_free(tf_task_get_context(task), ptab);
|
||||
} else if (JS_IsObject(value)) {
|
||||
}
|
||||
else if (JS_IsObject(value))
|
||||
{
|
||||
_serialize_writeInt32(buffer, kObject);
|
||||
JSPropertyEnum* ptab;
|
||||
uint32_t plen;
|
||||
JS_GetOwnPropertyNames(tf_task_get_context(task), &ptab, &plen, value, JS_GPN_STRING_MASK);
|
||||
_serialize_writeInt32(buffer, plen);
|
||||
for (uint32_t i = 0; i < plen; ++i) {
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSValue key = JS_AtomToString(tf_task_get_context(task), ptab[i].atom);
|
||||
JSPropertyDescriptor desc;
|
||||
JSValue key_value = JS_NULL;
|
||||
if (JS_GetOwnProperty(tf_task_get_context(task), &desc, value, ptab[i].atom) == 1) {
|
||||
if (JS_GetOwnProperty(tf_task_get_context(task), &desc, value, ptab[i].atom) == 1)
|
||||
{
|
||||
key_value = desc.value;
|
||||
}
|
||||
_serialize_storeInternal(task, to, buffer, key, depth + 1);
|
||||
@ -251,11 +306,14 @@ static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_
|
||||
JS_FreeValue(tf_task_get_context(task), key);
|
||||
JS_FreeValue(tf_task_get_context(task), key_value);
|
||||
}
|
||||
for (uint32_t i = 0; i < plen; ++i) {
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JS_FreeAtom(tf_task_get_context(task), ptab[i].atom);
|
||||
}
|
||||
js_free(tf_task_get_context(task), ptab);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Unknown JSValue type: %d.\n", JS_VALUE_GET_TAG(value));
|
||||
abort();
|
||||
}
|
||||
@ -263,18 +321,24 @@ static bool _serialize_storeInternal(tf_task_t* task, tf_taskstub_t* to, buffer_
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSValue _serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size) {
|
||||
static JSValue _serialize_load(tf_task_t* task, tf_taskstub_t* from, const char* buffer, size_t size)
|
||||
{
|
||||
return _serialize_loadInternal(task, from, &buffer, &size, 0);
|
||||
}
|
||||
|
||||
static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, const char** buffer, size_t* size, int depth) {
|
||||
if (*size < sizeof(size)) {
|
||||
static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, const char** buffer, size_t* size, int depth)
|
||||
{
|
||||
if (*size < sizeof(size))
|
||||
{
|
||||
return JS_UNDEFINED;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
int32_t type = _serialize_readInt32(buffer, size);
|
||||
JSValue result = JS_UNDEFINED;
|
||||
|
||||
switch (type) {
|
||||
switch (type)
|
||||
{
|
||||
case kUndefined:
|
||||
result = JS_UNDEFINED;
|
||||
break;
|
||||
@ -316,7 +380,8 @@ static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, con
|
||||
{
|
||||
int32_t length = _serialize_readInt32(buffer, size);
|
||||
result = JS_NewArray(tf_task_get_context(task));
|
||||
for (int i = 0; i < length; ++i) {
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
JS_SetPropertyUint32(tf_task_get_context(task), result, i, _serialize_loadInternal(task, from, buffer, size, depth + 1));
|
||||
}
|
||||
}
|
||||
@ -332,7 +397,8 @@ static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, con
|
||||
_serialize_readInt32(buffer, size);
|
||||
JSValue error = JS_NewError(tf_task_get_context(task));
|
||||
int32_t length = _serialize_readInt32(buffer, size);
|
||||
for (int i = 0; i < length; ++i) {
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
JSValue key = _serialize_loadInternal(task, from, buffer, size, depth + 1);
|
||||
JSValue value = _serialize_loadInternal(task, from, buffer, size, depth + 1);
|
||||
const char* key_str = JS_ToCString(tf_task_get_context(task), key);
|
||||
@ -348,7 +414,8 @@ static JSValue _serialize_loadInternal(tf_task_t* task, tf_taskstub_t* from, con
|
||||
{
|
||||
int32_t length = _serialize_readInt32(buffer, size);
|
||||
result = JS_NewObject(tf_task_get_context(task));
|
||||
for (int i = 0; i < length; ++i) {
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
JSValue key = _serialize_loadInternal(task, from, buffer, size, depth + 1);
|
||||
JSValue value = _serialize_loadInternal(task, from, buffer, size, depth + 1);
|
||||
const char* key_str = JS_ToCString(tf_task_get_context(task), key);
|
||||
|
458
src/socket.c
458
src/socket.c
@ -86,23 +86,27 @@ private:
|
||||
};
|
||||
*/
|
||||
|
||||
JSValue tf_socket_init(JSContext* context) {
|
||||
JSValue tf_socket_init(JSContext* context)
|
||||
{
|
||||
JS_NewClassID(&_classId);
|
||||
JSClassDef def = {
|
||||
.class_name = "Socket",
|
||||
.finalizer = &_socket_finalizer,
|
||||
};
|
||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0) {
|
||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to register Socket.\n");
|
||||
}
|
||||
return JS_NewCFunction2(context, _socket_create, "Socket", 0, JS_CFUNC_constructor, 0);
|
||||
}
|
||||
|
||||
int tf_socket_get_count() {
|
||||
int tf_socket_get_count()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
int tf_socket_get_open_count() {
|
||||
int tf_socket_get_open_count()
|
||||
{
|
||||
return _open_count;
|
||||
}
|
||||
|
||||
@ -112,7 +116,8 @@ typedef struct _socket_resolve_data_t {
|
||||
promiseid_t promise;
|
||||
} socket_resolve_data_t;
|
||||
|
||||
socket_t* _socket_create_internal(JSContext* context) {
|
||||
socket_t* _socket_create_internal(JSContext* context)
|
||||
{
|
||||
socket_t* socket = malloc(sizeof(socket_t));
|
||||
memset(socket, 0, sizeof(*socket));
|
||||
|
||||
@ -165,23 +170,28 @@ socket_t* _socket_create_internal(JSContext* context) {
|
||||
return socket;
|
||||
}
|
||||
|
||||
JSValue _socket_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
return _socket_create_internal(context)->_object;
|
||||
}
|
||||
|
||||
void _socket_finalizer(JSRuntime *runtime, JSValue value) {
|
||||
void _socket_finalizer(JSRuntime *runtime, JSValue value)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(value, _classId);
|
||||
--_count;
|
||||
free(socket);
|
||||
}
|
||||
|
||||
void _socket_close_internal(socket_t* socket) {
|
||||
if (!uv_is_closing((uv_handle_t*)&socket->_socket)) {
|
||||
void _socket_close_internal(socket_t* socket)
|
||||
{
|
||||
if (!uv_is_closing((uv_handle_t*)&socket->_socket))
|
||||
{
|
||||
JS_FreeValue(tf_task_get_context(socket->_task), socket->_onRead);
|
||||
socket->_onRead = JS_UNDEFINED;
|
||||
JS_FreeValue(tf_task_get_context(socket->_task), socket->_onError);
|
||||
socket->_onError = JS_UNDEFINED;
|
||||
if (socket->_tls) {
|
||||
if (socket->_tls)
|
||||
{
|
||||
tf_tls_session_destroy(socket->_tls);
|
||||
socket->_tls = NULL;
|
||||
}
|
||||
@ -189,82 +199,110 @@ void _socket_close_internal(socket_t* socket) {
|
||||
}
|
||||
}
|
||||
|
||||
void _socket_reportError(socket_t* socket, const char* error) {
|
||||
if (JS_IsFunction(tf_task_get_context(socket->_task),socket-> _onError)) {
|
||||
void _socket_reportError(socket_t* socket, const char* error)
|
||||
{
|
||||
if (JS_IsFunction(tf_task_get_context(socket->_task),socket-> _onError))
|
||||
{
|
||||
JSValue exception = JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error);
|
||||
JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onError, socket->_object, 1, &exception);
|
||||
if (JS_IsException(result)) {
|
||||
if (JS_IsException(result))
|
||||
{
|
||||
printf("Socket error.\n");
|
||||
js_std_dump_error(tf_task_get_context(socket->_task));
|
||||
}
|
||||
tf_task_run_jobs(socket->_task);
|
||||
JS_FreeValue(tf_task_get_context(socket->_task), exception);
|
||||
JS_FreeValue(tf_task_get_context(socket->_task), result);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Socket::reportError: %s\n", error);
|
||||
}
|
||||
}
|
||||
|
||||
void _socket_reportTlsErrors(socket_t* socket) {
|
||||
void _socket_reportTlsErrors(socket_t* socket)
|
||||
{
|
||||
char buffer[4096];
|
||||
while (socket->_tls && tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) {
|
||||
while (socket->_tls && tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer)))
|
||||
{
|
||||
_socket_reportError(socket, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
JSValue _socket_startTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_startTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
if (!socket->_tls) {
|
||||
if (!socket->_tls)
|
||||
{
|
||||
tf_tls_context_t* context = 0;
|
||||
|
||||
if (argc > 0 && JS_IsObject(argv[0])) {
|
||||
if (argc > 0 && JS_IsObject(argv[0]))
|
||||
{
|
||||
context = tf_tls_context_wrapper_get(argv[0]);
|
||||
} else {
|
||||
if (!_defaultTlsContext) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_defaultTlsContext)
|
||||
{
|
||||
_defaultTlsContext = tf_tls_context_create();
|
||||
}
|
||||
context = _defaultTlsContext;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
if (context)
|
||||
{
|
||||
socket->_tls = tf_tls_context_create_session(context);
|
||||
}
|
||||
|
||||
if (socket->_tls) {
|
||||
if (socket->_tls)
|
||||
{
|
||||
tf_tls_session_set_hostname(socket->_tls, socket->_peerName);
|
||||
if (socket->_direction == kAccept) {
|
||||
if (socket->_direction == kAccept)
|
||||
{
|
||||
tf_tls_session_start_accept(socket->_tls);
|
||||
} else if (socket->_direction == kConnect) {
|
||||
}
|
||||
else if (socket->_direction == kConnect)
|
||||
{
|
||||
tf_tls_session_start_connect(socket->_tls);
|
||||
}
|
||||
socket->_startTlsPromise = tf_task_allocate_promise(socket->_task);
|
||||
JSValue result = tf_task_get_promise(socket->_task, socket->_startTlsPromise);
|
||||
_socket_processOutgoingTls(socket);
|
||||
return result;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return JS_ThrowInternalError(tf_task_get_context(socket->_task), "Failed to get TLS context");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return JS_ThrowInternalError(tf_task_get_context(socket->_task), "startTls with TLS already started");
|
||||
}
|
||||
}
|
||||
|
||||
JSValue _socket_stopTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_stopTls(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
if (socket->_tls) {
|
||||
if (socket->_tls)
|
||||
{
|
||||
_socket_processOutgoingTls(socket);
|
||||
tf_tls_session_destroy(socket->_tls);
|
||||
socket->_tls = NULL;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
JS_ThrowInternalError(tf_task_get_context(socket->_task), "stopTls with TLS already stopped");
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
bool _socket_processSomeOutgoingTls(socket_t* socket, promiseid_t promise, uv_write_cb callback) {
|
||||
bool _socket_processSomeOutgoingTls(socket_t* socket, promiseid_t promise, uv_write_cb callback)
|
||||
{
|
||||
char buffer[65536];
|
||||
int result = tf_tls_session_read_encrypted(socket->_tls, buffer, sizeof(buffer));
|
||||
if (result > 0) {
|
||||
if (result > 0)
|
||||
{
|
||||
char* request_buffer = malloc(sizeof(uv_write_t) + result);
|
||||
uv_write_t* request = (uv_write_t*)request_buffer;
|
||||
memset(request, 0, sizeof(*request));
|
||||
@ -279,23 +317,30 @@ bool _socket_processSomeOutgoingTls(socket_t* socket, promiseid_t promise, uv_wr
|
||||
};
|
||||
|
||||
int writeResult = uv_write(request, (uv_stream_t*)&socket->_socket, &writeBuffer, 1, callback);
|
||||
if (writeResult != 0) {
|
||||
if (writeResult != 0)
|
||||
{
|
||||
free(request_buffer);
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_write: %s", uv_strerror(writeResult));
|
||||
_socket_reportError(socket, error);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket_reportTlsErrors(socket);
|
||||
}
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
void _socket_processOutgoingTls(socket_t* socket) {
|
||||
while (_socket_processSomeOutgoingTls(socket, -1, _socket_onWrite)) {}
|
||||
void _socket_processOutgoingTls(socket_t* socket)
|
||||
{
|
||||
while (_socket_processSomeOutgoingTls(socket, -1, _socket_onWrite))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
JSValue _socket_bind(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_bind(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
const char* node = JS_ToCString(tf_task_get_context(socket->_task), argv[0]);
|
||||
const char* port = JS_ToCString(tf_task_get_context(socket->_task), argv[1]);
|
||||
@ -313,7 +358,8 @@ JSValue _socket_bind(JSContext* context, JSValueConst this_val, int argc, JSValu
|
||||
data->promise = tf_task_allocate_promise(socket->_task);
|
||||
|
||||
int result = uv_getaddrinfo(tf_task_get_loop(socket->_task), &data->resolver, _socket_onResolvedForBind, node, port, &hints);
|
||||
if (result != 0) {
|
||||
if (result != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_getaddrinfo: %s", uv_strerror(result));
|
||||
tf_task_reject_promise(socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), error));
|
||||
@ -322,26 +368,34 @@ JSValue _socket_bind(JSContext* context, JSValueConst this_val, int argc, JSValu
|
||||
return tf_task_get_promise(socket->_task, data->promise);
|
||||
}
|
||||
|
||||
void _socket_onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) {
|
||||
void _socket_onResolvedForBind(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result)
|
||||
{
|
||||
socket_resolve_data_t* data = (socket_resolve_data_t*)resolver->data;
|
||||
if (status != 0) {
|
||||
if (status != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_getaddrinfo: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), error));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
int bindResult = uv_tcp_bind(&data->socket->_socket, result->ai_addr, 0);
|
||||
if (bindResult != 0) {
|
||||
if (bindResult != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_tcp_bind: %s", uv_strerror(bindResult));
|
||||
tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), error));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_task_resolve_promise(data->socket->_task, data->promise, JS_UNDEFINED);
|
||||
}
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
JSValue _socket_connect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_connect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
socket->_direction = kConnect;
|
||||
const char* node = JS_ToCString(context, argv[0]);
|
||||
@ -363,7 +417,8 @@ JSValue _socket_connect(JSContext* context, JSValueConst this_val, int argc, JSV
|
||||
data->promise = promise;
|
||||
|
||||
int result = uv_getaddrinfo(tf_task_get_loop(socket->_task), &data->resolver, _socket_onResolvedForConnect, node, port, &hints);
|
||||
if (result != 0) {
|
||||
if (result != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_getaddrinfo: %s", uv_strerror(result));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, "%s", error));
|
||||
@ -375,18 +430,23 @@ JSValue _socket_connect(JSContext* context, JSValueConst this_val, int argc, JSV
|
||||
return tf_task_get_promise(socket->_task, promise);
|
||||
}
|
||||
|
||||
void _socket_onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result) {
|
||||
void _socket_onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct addrinfo* result)
|
||||
{
|
||||
socket_resolve_data_t* data = resolver->data;
|
||||
if (status != 0) {
|
||||
if (status != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_getaddrinfo: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), "%s", error));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uv_connect_t* request = malloc(sizeof(uv_connect_t));
|
||||
memset(request, 0, sizeof(*request));
|
||||
request->data = (void*)(intptr_t)data->promise;
|
||||
int connectResult = uv_tcp_connect(request, &data->socket->_socket, result->ai_addr, _socket_onConnect);
|
||||
if (connectResult != 0) {
|
||||
if (connectResult != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_tcp_connect: %s", uv_strerror(connectResult));
|
||||
tf_task_reject_promise(data->socket->_task, data->promise, JS_ThrowInternalError(tf_task_get_context(data->socket->_task), "%s", error));
|
||||
@ -396,14 +456,19 @@ void _socket_onResolvedForConnect(uv_getaddrinfo_t* resolver, int status, struct
|
||||
free(data);
|
||||
}
|
||||
|
||||
void _socket_onConnect(uv_connect_t* request, int status) {
|
||||
void _socket_onConnect(uv_connect_t* request, int status)
|
||||
{
|
||||
promiseid_t promise = (intptr_t)request->data;
|
||||
if (promise != -1) {
|
||||
if (promise != -1)
|
||||
{
|
||||
socket_t* socket = request->handle->data;
|
||||
if (status == 0) {
|
||||
if (status == 0)
|
||||
{
|
||||
socket->_connected = true;
|
||||
tf_task_resolve_promise(socket->_task, promise, JS_NewInt32(tf_task_get_context(socket->_task), status));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_tcp_connect: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error));
|
||||
@ -412,29 +477,37 @@ void _socket_onConnect(uv_connect_t* request, int status) {
|
||||
free(request);
|
||||
}
|
||||
|
||||
JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_listen(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
int backlog = 1;
|
||||
JS_ToInt32(context, &backlog, argv[0]);
|
||||
if (JS_IsUndefined(socket->_onConnect)) {
|
||||
if (JS_IsUndefined(socket->_onConnect))
|
||||
{
|
||||
socket->_onConnect = JS_DupValue(context, argv[1]);
|
||||
int result = uv_listen((uv_stream_t*)&socket->_socket, backlog, _socket_onNewConnection);
|
||||
if (result != 0) {
|
||||
if (result != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_listen: %s", uv_strerror(result));
|
||||
return JS_ThrowInternalError(context, error);
|
||||
}
|
||||
return JS_NewInt32(context, result);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return JS_ThrowInternalError(context, "listen: Already listening.");
|
||||
}
|
||||
}
|
||||
|
||||
void _socket_onNewConnection(uv_stream_t* server, int status) {
|
||||
void _socket_onNewConnection(uv_stream_t* server, int status)
|
||||
{
|
||||
socket_t* socket = server->data;
|
||||
if (!JS_IsUndefined(socket->_onConnect)) {
|
||||
if (!JS_IsUndefined(socket->_onConnect))
|
||||
{
|
||||
JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onConnect, socket->_object, 0, NULL);
|
||||
if (JS_IsException(result)) {
|
||||
if (JS_IsException(result))
|
||||
{
|
||||
printf("Socket error on connection.\n");
|
||||
js_std_dump_error(tf_task_get_context(socket->_task));
|
||||
}
|
||||
@ -442,7 +515,8 @@ void _socket_onNewConnection(uv_stream_t* server, int status) {
|
||||
}
|
||||
}
|
||||
|
||||
JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
|
||||
socket_t* client = _socket_create_internal(context);
|
||||
@ -450,10 +524,13 @@ JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSVa
|
||||
promiseid_t promise = tf_task_allocate_promise(socket->_task);
|
||||
JSValue result = tf_task_get_promise(socket->_task, promise);
|
||||
int status = uv_accept((uv_stream_t*)&socket->_socket, (uv_stream_t*)&client->_socket);
|
||||
if (status == 0) {
|
||||
if (status == 0)
|
||||
{
|
||||
client->_connected = true;
|
||||
tf_task_resolve_promise(socket->_task, promise, client->_object);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_accept: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, error));
|
||||
@ -461,9 +538,11 @@ JSValue _socket_accept(JSContext* context, JSValueConst this_val, int argc, JSVa
|
||||
return result;
|
||||
}
|
||||
|
||||
JSValue _socket_close(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_close(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
if (socket->_closePromise == -1) {
|
||||
if (socket->_closePromise == -1)
|
||||
{
|
||||
socket->_closePromise = tf_task_allocate_promise(socket->_task);
|
||||
JSValue result = tf_task_get_promise(socket->_task, socket->_closePromise);
|
||||
_socket_close_internal(socket);
|
||||
@ -472,24 +551,30 @@ JSValue _socket_close(JSContext* context, JSValueConst this_val, int argc, JSVal
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _socket_shutdown(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_shutdown(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
promiseid_t promise = tf_task_allocate_promise(socket->_task);
|
||||
JSValue result = tf_task_get_promise(socket->_task, promise);
|
||||
if (socket->_tls) {
|
||||
if (socket->_tls)
|
||||
{
|
||||
_socket_processTlsShutdown(socket, promise);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket_shutdownInternal(socket, promise);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _socket_shutdownInternal(socket_t* socket, promiseid_t promise) {
|
||||
void _socket_shutdownInternal(socket_t* socket, promiseid_t promise)
|
||||
{
|
||||
uv_shutdown_t* request = malloc(sizeof(uv_shutdown_t));
|
||||
memset(request, 0, sizeof(*request));
|
||||
request->data = (void*)(intptr_t)promise;
|
||||
int result = uv_shutdown(request, (uv_stream_t*)&socket->_socket, _socket_onShutdown);
|
||||
if (result != 0) {
|
||||
if (result != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_shutdown: %s", uv_strerror(result));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error));
|
||||
@ -497,23 +582,28 @@ void _socket_shutdownInternal(socket_t* socket, promiseid_t promise) {
|
||||
}
|
||||
}
|
||||
|
||||
void _socket_processTlsShutdown(socket_t* socket, promiseid_t promise) {
|
||||
void _socket_processTlsShutdown(socket_t* socket, promiseid_t promise)
|
||||
{
|
||||
tf_tls_session_shutdown(socket->_tls);
|
||||
if (!_socket_processSomeOutgoingTls(socket, promise, _socket_onTlsShutdown)) {
|
||||
if (!_socket_processSomeOutgoingTls(socket, promise, _socket_onTlsShutdown))
|
||||
{
|
||||
_socket_shutdownInternal(socket, promise);
|
||||
}
|
||||
}
|
||||
|
||||
void _socket_onTlsShutdown(uv_write_t* request, int status) {
|
||||
void _socket_onTlsShutdown(uv_write_t* request, int status)
|
||||
{
|
||||
socket_t* socket = request->handle->data;
|
||||
promiseid_t promise = (intptr_t)request->data;
|
||||
_socket_processTlsShutdown(socket, promise);
|
||||
free(request);
|
||||
}
|
||||
|
||||
JSValue _socket_onError(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_onError(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
if (!JS_IsUndefined(socket->_onError)) {
|
||||
if (!JS_IsUndefined(socket->_onError))
|
||||
{
|
||||
JS_FreeValue(context, socket->_onError);
|
||||
socket->_onError = JS_UNDEFINED;
|
||||
}
|
||||
@ -521,34 +611,43 @@ JSValue _socket_onError(JSContext* context, JSValueConst this_val, int argc, JSV
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
JSValue _socket_read(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_read(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
socket->_onRead = JS_DupValue(context, argv[0]);
|
||||
int result = uv_read_start((uv_stream_t*)&socket->_socket, _socket_allocateBuffer, _socket_onRead);
|
||||
promiseid_t promise = tf_task_allocate_promise(socket->_task);
|
||||
JSValue read_result = tf_task_get_promise(socket->_task, promise);
|
||||
if (result != 0) {
|
||||
if (result != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_read_start: %s", uv_strerror(result));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, error));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED);
|
||||
}
|
||||
return read_result;
|
||||
}
|
||||
|
||||
void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf) {
|
||||
void _socket_allocateBuffer(uv_handle_t* handle, size_t suggestedSize, uv_buf_t* buf)
|
||||
{
|
||||
*buf = uv_buf_init(malloc(suggestedSize), suggestedSize);
|
||||
}
|
||||
|
||||
void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer) {
|
||||
void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffer)
|
||||
{
|
||||
socket_t* socket = stream->data;
|
||||
if (readSize <= 0) {
|
||||
if (readSize <= 0)
|
||||
{
|
||||
socket->_connected = false;
|
||||
if (!JS_IsUndefined(socket->_onRead)) {
|
||||
if (!JS_IsUndefined(socket->_onRead))
|
||||
{
|
||||
JSValue args[] = { JS_UNDEFINED };
|
||||
JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onRead, socket->_object, 1, args);
|
||||
if (JS_IsException(result)) {
|
||||
if (JS_IsException(result))
|
||||
{
|
||||
printf("Socket error on read.\n");
|
||||
js_std_dump_error(tf_task_get_context(socket->_task));
|
||||
}
|
||||
@ -556,42 +655,60 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe
|
||||
tf_task_run_jobs(socket->_task);
|
||||
}
|
||||
_socket_close_internal(socket);
|
||||
} else {
|
||||
if (socket->_tls) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (socket->_tls)
|
||||
{
|
||||
_socket_reportTlsErrors(socket);
|
||||
tf_tls_session_write_encrypted(socket->_tls, buffer->base, readSize);
|
||||
if (socket->_startTlsPromise != -1) {
|
||||
if (socket->_startTlsPromise != -1)
|
||||
{
|
||||
tf_tls_handshake_t result = tf_tls_session_handshake(socket->_tls);
|
||||
if (result == k_tls_handshake_done) {
|
||||
if (result == k_tls_handshake_done)
|
||||
{
|
||||
promiseid_t promise = socket->_startTlsPromise;
|
||||
socket->_startTlsPromise = -1;
|
||||
tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED);
|
||||
} else if (result == k_tls_handshake_failed) {
|
||||
}
|
||||
else if (result == k_tls_handshake_failed)
|
||||
{
|
||||
promiseid_t promise = socket->_startTlsPromise;
|
||||
socket->_startTlsPromise = -1;
|
||||
char buffer[8192];
|
||||
if (tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) {
|
||||
if (tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer)))
|
||||
{
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), buffer));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_task_reject_promise(socket->_task, promise, JS_UNDEFINED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
while (true)
|
||||
{
|
||||
char plain[8192];
|
||||
int result = tf_tls_session_read_plain(socket->_tls, plain, sizeof(plain));
|
||||
if (result > 0) {
|
||||
if (result > 0)
|
||||
{
|
||||
_socket_notifyDataRead(socket, plain, result);
|
||||
} else if (result == k_tls_read_failed) {
|
||||
}
|
||||
else if (result == k_tls_read_failed)
|
||||
{
|
||||
_socket_reportTlsErrors(socket);
|
||||
_socket_close_internal(socket);
|
||||
break;
|
||||
} else if (result == k_tls_read_zero) {
|
||||
if (!JS_IsUndefined(socket->_onRead)) {
|
||||
}
|
||||
else if (result == k_tls_read_zero)
|
||||
{
|
||||
if (!JS_IsUndefined(socket->_onRead))
|
||||
{
|
||||
JSValue args[] = { JS_UNDEFINED };
|
||||
JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onRead, socket->_object, 1, args);
|
||||
if (JS_IsException(result)) {
|
||||
if (JS_IsException(result))
|
||||
{
|
||||
printf("Socket error on read plain.\n");
|
||||
js_std_dump_error(tf_task_get_context(socket->_task));
|
||||
}
|
||||
@ -599,21 +716,27 @@ void _socket_onRead(uv_stream_t* stream, ssize_t readSize, const uv_buf_t* buffe
|
||||
tf_task_run_jobs(socket->_task);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (socket->_tls) {
|
||||
if (socket->_tls)
|
||||
{
|
||||
_socket_processOutgoingTls(socket);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
_socket_notifyDataRead(socket, buffer->base, readSize);
|
||||
}
|
||||
}
|
||||
free(buffer->base);
|
||||
}
|
||||
|
||||
static JSValue _newUint8Array(JSContext* context, const void* data, size_t length) {
|
||||
static JSValue _newUint8Array(JSContext* context, const void* data, size_t length)
|
||||
{
|
||||
JSValue arrayBuffer = JS_NewArrayBufferCopy(context, (const uint8_t*)data, length);
|
||||
JSValue global = JS_GetGlobalObject(context);
|
||||
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
|
||||
@ -624,13 +747,17 @@ static JSValue _newUint8Array(JSContext* context, const void* data, size_t lengt
|
||||
return typedArray;
|
||||
}
|
||||
|
||||
void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length) {
|
||||
if (!JS_IsUndefined(socket->_onRead)) {
|
||||
if (data && length > 0) {
|
||||
void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length)
|
||||
{
|
||||
if (!JS_IsUndefined(socket->_onRead))
|
||||
{
|
||||
if (data && length > 0)
|
||||
{
|
||||
JSValue typedArray = _newUint8Array(tf_task_get_context(socket->_task), data, length);
|
||||
JSValue args[] = { typedArray };
|
||||
JSValue result = JS_Call(tf_task_get_context(socket->_task), socket->_onRead, socket->_object, 1, args);
|
||||
if (JS_IsException(result)) {
|
||||
if (JS_IsException(result))
|
||||
{
|
||||
printf("Socket error on data read.\n");
|
||||
js_std_dump_error(tf_task_get_context(socket->_task));
|
||||
}
|
||||
@ -641,36 +768,45 @@ void _socket_notifyDataRead(socket_t* socket, const char* data, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
int _socket_writeBytes(socket_t* socket, promiseid_t promise, int (*callback)(socket_t* socket, promiseid_t promise, const char*, size_t), JSValue value, int* outLength) {
|
||||
int _socket_writeBytes(socket_t* socket, promiseid_t promise, int (*callback)(socket_t* socket, promiseid_t promise, const char*, size_t), JSValue value, int* outLength)
|
||||
{
|
||||
int result = -1;
|
||||
size_t length;
|
||||
uint8_t* array = NULL;
|
||||
JSContext* context = tf_task_get_context(socket->_task);
|
||||
if (JS_IsString(value)) {
|
||||
if (JS_IsString(value))
|
||||
{
|
||||
const char* stringValue = JS_ToCStringLen(context, &length, value);
|
||||
result = callback(socket, promise, stringValue, length);
|
||||
JS_FreeCString(context, stringValue);
|
||||
} else if ((array = tf_try_get_array_buffer(context, &length, value)) != 0) {
|
||||
}
|
||||
else if ((array = tf_try_get_array_buffer(context, &length, value)) != 0)
|
||||
{
|
||||
result = callback(socket, promise, (const char*)array, length);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset;
|
||||
size_t element_size;
|
||||
JSValue buffer = tf_try_get_typed_array_buffer(context, value, &offset, &length, &element_size);
|
||||
size_t size;
|
||||
if ((array = tf_try_get_array_buffer(context, &size, buffer)) != 0) {
|
||||
if ((array = tf_try_get_array_buffer(context, &size, buffer)) != 0)
|
||||
{
|
||||
result = callback(socket, promise, (const char*)array, length);
|
||||
}
|
||||
JS_FreeValue(context, buffer);
|
||||
}
|
||||
|
||||
if (outLength) {
|
||||
if (outLength)
|
||||
{
|
||||
*outLength = (int)length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int _socket_writeInternal(socket_t* socket, promiseid_t promise, const char* data, size_t length) {
|
||||
int _socket_writeInternal(socket_t* socket, promiseid_t promise, const char* data, size_t length)
|
||||
{
|
||||
char* rawBuffer = malloc(sizeof(uv_write_t) + length);
|
||||
uv_write_t* request = (uv_write_t*)rawBuffer;
|
||||
memcpy(rawBuffer + sizeof(uv_write_t), data, length);
|
||||
@ -684,51 +820,70 @@ int _socket_writeInternal(socket_t* socket, promiseid_t promise, const char* dat
|
||||
return uv_write(request, (uv_stream_t*)&socket->_socket, &buffer, 1, _socket_onWrite);
|
||||
}
|
||||
|
||||
static int _socket_write_tls(socket_t* socket, promiseid_t promise, const char* data, size_t size) {
|
||||
static int _socket_write_tls(socket_t* socket, promiseid_t promise, const char* data, size_t size)
|
||||
{
|
||||
return tf_tls_session_write_plain(socket->_tls, data, size);
|
||||
}
|
||||
|
||||
JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_write(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
promiseid_t promise = tf_task_allocate_promise(socket->_task);
|
||||
JSValue write_result = tf_task_get_promise(socket->_task, promise);
|
||||
if (!JS_IsUndefined(argv[0])) {
|
||||
if (socket->_tls) {
|
||||
if (!JS_IsUndefined(argv[0]))
|
||||
{
|
||||
if (socket->_tls)
|
||||
{
|
||||
_socket_reportTlsErrors(socket);
|
||||
int length = 0;
|
||||
int result = _socket_writeBytes(socket, -1, _socket_write_tls, argv[0], &length);
|
||||
char buffer[8192];
|
||||
if (result <= 0 && tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer))) {
|
||||
if (result <= 0 && tf_tls_session_get_error(socket->_tls, buffer, sizeof(buffer)))
|
||||
{
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, buffer));
|
||||
} else if (result < length) {
|
||||
}
|
||||
else if (result < length)
|
||||
{
|
||||
tf_task_reject_promise(socket->_task, promise, JS_NewInt32(context, result));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_task_resolve_promise(socket->_task, promise, JS_NewInt32(context, result));
|
||||
}
|
||||
_socket_processOutgoingTls(socket);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
int length;
|
||||
int result = _socket_writeBytes(socket, promise, _socket_writeInternal, argv[0], &length);
|
||||
|
||||
if (result != 0) {
|
||||
if (result != 0)
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_write: %s", uv_strerror(result));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(context, error));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_task_reject_promise(socket->_task, promise, JS_NewInt32(context, -2));
|
||||
}
|
||||
return write_result;
|
||||
}
|
||||
|
||||
void _socket_onWrite(uv_write_t* request, int status) {
|
||||
void _socket_onWrite(uv_write_t* request, int status)
|
||||
{
|
||||
socket_t* socket = request->handle->data;
|
||||
promiseid_t promise = (intptr_t)request->data;
|
||||
if (promise != -1) {
|
||||
if (status == 0) {
|
||||
if (promise != -1)
|
||||
{
|
||||
if (status == 0)
|
||||
{
|
||||
tf_task_resolve_promise(socket->_task, promise, JS_NewInt32(tf_task_get_context(socket->_task), status));
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_write: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), error));
|
||||
@ -737,15 +892,18 @@ void _socket_onWrite(uv_write_t* request, int status) {
|
||||
free(request);
|
||||
}
|
||||
|
||||
JSValue _socket_isConnected(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_isConnected(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
return socket->_connected ? JS_TRUE : JS_FALSE;
|
||||
}
|
||||
|
||||
void _socket_onClose(uv_handle_t* handle) {
|
||||
void _socket_onClose(uv_handle_t* handle)
|
||||
{
|
||||
--_open_count;
|
||||
socket_t* socket = handle->data;
|
||||
if (socket->_closePromise != -1) {
|
||||
if (socket->_closePromise != -1)
|
||||
{
|
||||
promiseid_t promise = socket->_closePromise;
|
||||
socket->_closePromise = -1;
|
||||
socket->_connected = false;
|
||||
@ -753,12 +911,16 @@ void _socket_onClose(uv_handle_t* handle) {
|
||||
}
|
||||
}
|
||||
|
||||
void _socket_onShutdown(uv_shutdown_t* request, int status) {
|
||||
void _socket_onShutdown(uv_shutdown_t* request, int status)
|
||||
{
|
||||
socket_t* socket = request->handle->data;
|
||||
promiseid_t promise = (intptr_t)request->data;
|
||||
if (status == 0) {
|
||||
if (status == 0)
|
||||
{
|
||||
tf_task_resolve_promise(socket->_task, promise, JS_UNDEFINED);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
char error[256];
|
||||
snprintf(error, sizeof(error), "uv_shutdown: %s", uv_strerror(status));
|
||||
tf_task_reject_promise(socket->_task, promise, JS_ThrowInternalError(tf_task_get_context(socket->_task), "%s", error));
|
||||
@ -766,18 +928,25 @@ void _socket_onShutdown(uv_shutdown_t* request, int status) {
|
||||
free(request);
|
||||
}
|
||||
|
||||
JSValue _socket_getPeerName(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_getPeerName(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
struct sockaddr_in6 addr;
|
||||
int nameLength = sizeof(addr);
|
||||
if (uv_tcp_getpeername(&socket->_socket, (struct sockaddr*)&addr, &nameLength) == 0) {
|
||||
if (uv_tcp_getpeername(&socket->_socket, (struct sockaddr*)&addr, &nameLength) == 0)
|
||||
{
|
||||
char name[1024];
|
||||
if ((size_t)nameLength > sizeof(struct sockaddr_in)) {
|
||||
if (uv_ip6_name(&addr, name, sizeof(name)) == 0) {
|
||||
if ((size_t)nameLength > sizeof(struct sockaddr_in))
|
||||
{
|
||||
if (uv_ip6_name(&addr, name, sizeof(name)) == 0)
|
||||
{
|
||||
return JS_NewString(context, name);
|
||||
}
|
||||
} else {
|
||||
if (uv_ip4_name((struct sockaddr_in*)&addr, name, sizeof(name)) == 0) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (uv_ip4_name((struct sockaddr_in*)&addr, name, sizeof(name)) == 0)
|
||||
{
|
||||
return JS_NewString(context, name);
|
||||
}
|
||||
}
|
||||
@ -785,24 +954,29 @@ JSValue _socket_getPeerName(JSContext* context, JSValueConst this_val, int argc,
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _socket_getPeerCertificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_getPeerCertificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
if (socket->_tls) {
|
||||
if (socket->_tls)
|
||||
{
|
||||
char buffer[128 * 1024];
|
||||
int result = tf_tls_session_get_peer_certificate(socket->_tls, buffer, sizeof(buffer));
|
||||
if (result > 0) {
|
||||
if (result > 0)
|
||||
{
|
||||
return _newUint8Array(tf_task_get_context(socket->_task), buffer, sizeof(buffer));
|
||||
}
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _socket_getNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_getNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
return JS_NewBool(context, socket->_noDelay);
|
||||
}
|
||||
|
||||
JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _socket_setNoDelay(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
socket_t* socket = JS_GetOpaque(this_val, _classId);
|
||||
int result = JS_ToBool(context, argv[0]);
|
||||
socket->_noDelay = result > 0;
|
||||
|
@ -22,14 +22,16 @@ typedef struct _tf_ssb_connections_t
|
||||
static void _tf_ssb_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_change_t change, tf_ssb_connection_t* connection, void* user_data)
|
||||
{
|
||||
tf_ssb_connections_t* connections = user_data;
|
||||
switch (change) {
|
||||
switch (change)
|
||||
{
|
||||
case k_tf_ssb_change_create:
|
||||
{
|
||||
char key[ID_BASE64_LEN];
|
||||
if (tf_ssb_connection_get_host(connection) &&
|
||||
*tf_ssb_connection_get_host(connection) &&
|
||||
tf_ssb_connection_get_port(connection) &&
|
||||
tf_ssb_connection_get_id(connection, key, sizeof(key))) {
|
||||
tf_ssb_connection_get_id(connection, key, sizeof(key)))
|
||||
{
|
||||
tf_ssb_connections_store(connections, tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection), key);
|
||||
tf_ssb_connections_set_attempted(connections, tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection), key);
|
||||
}
|
||||
@ -38,7 +40,8 @@ static void _tf_ssb_connections_changed_callback(tf_ssb_t* ssb, tf_ssb_change_t
|
||||
case k_tf_ssb_change_connect:
|
||||
{
|
||||
char key[ID_BASE64_LEN];
|
||||
if (tf_ssb_connection_get_id(connection, key, sizeof(key))) {
|
||||
if (tf_ssb_connection_get_id(connection, key, sizeof(key)))
|
||||
{
|
||||
tf_ssb_connections_set_succeeded(connections, tf_ssb_connection_get_host(connection), tf_ssb_connection_get_port(connection), key);
|
||||
}
|
||||
}
|
||||
@ -52,16 +55,20 @@ static bool _tf_ssb_connections_get_next_connection(tf_ssb_connections_t* connec
|
||||
{
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > $1) ORDER BY last_attempt LIMIT 1", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(connections->db, "SELECT host, port, key FROM connections WHERE last_attempt IS NULL OR (strftime('%s', 'now') - last_attempt > $1) ORDER BY last_attempt LIMIT 1", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int(statement, 1, 60000) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
snprintf(host, host_size, "%s", sqlite3_column_text(statement, 0));
|
||||
*port = sqlite3_column_int(statement, 1);
|
||||
snprintf(key, key_size, "%s", sqlite3_column_text(statement, 2));
|
||||
result = true;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare: %s\n", sqlite3_errmsg(connections->db));
|
||||
}
|
||||
return result;
|
||||
@ -72,13 +79,16 @@ static void _tf_ssb_connections_timer(uv_timer_t* timer)
|
||||
tf_ssb_connections_t* connections = timer->data;
|
||||
tf_ssb_connection_t* active[4];
|
||||
int count = tf_ssb_get_connections(connections->ssb, active, _countof(active));
|
||||
if (count < _countof(active)) {
|
||||
if (count < _countof(active))
|
||||
{
|
||||
char host[256];
|
||||
int port;
|
||||
char key[ID_BASE64_LEN];
|
||||
if (_tf_ssb_connections_get_next_connection(connections, host, sizeof(host), &port, key, sizeof(key))) {
|
||||
if (_tf_ssb_connections_get_next_connection(connections, host, sizeof(host), &port, key, sizeof(key)))
|
||||
{
|
||||
uint8_t key_bin[ID_BIN_LEN];
|
||||
if (tf_ssb_id_str_to_bin(key_bin, key)) {
|
||||
if (tf_ssb_id_str_to_bin(key_bin, key))
|
||||
{
|
||||
tf_ssb_connect(connections->ssb, host, port, key_bin);
|
||||
}
|
||||
}
|
||||
@ -118,12 +128,15 @@ void tf_ssb_connections_destroy(tf_ssb_connections_t* connections)
|
||||
void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "INSERT INTO connections (host, port, key) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(connections->db, "INSERT INTO connections (host, port, key) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, key, -1, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_text(statement, 3, key, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
int r = sqlite3_step(statement);
|
||||
if (r != SQLITE_DONE) {
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
printf("tf_ssb_connections_store: %d, %s.\n", r, sqlite3_errmsg(connections->db));
|
||||
}
|
||||
}
|
||||
@ -134,11 +147,14 @@ void tf_ssb_connections_store(tf_ssb_connections_t* connections, const char* hos
|
||||
void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "UPDATE connections SET last_attempt = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(connections->db, "UPDATE connections SET last_attempt = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, key, -1, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||
sqlite3_bind_text(statement, 3, key, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
printf("tf_ssb_connections_set_attempted: %s.\n", sqlite3_errmsg(connections->db));
|
||||
}
|
||||
}
|
||||
@ -149,11 +165,14 @@ void tf_ssb_connections_set_attempted(tf_ssb_connections_t* connections, const c
|
||||
void tf_ssb_connections_set_succeeded(tf_ssb_connections_t* connections, const char* host, int port, const char* key)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(connections->db, "UPDATE connections SET last_success = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(connections->db, "UPDATE connections SET last_success = strftime('%s', 'now') WHERE host = $1 AND port = $2 AND key = $3", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, host, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, port) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, key, -1, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_step(statement) != SQLITE_DONE) {
|
||||
sqlite3_bind_text(statement, 3, key, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_step(statement) != SQLITE_DONE)
|
||||
{
|
||||
printf("tf_ssb_connections_set_succeeded: %s.\n", sqlite3_errmsg(connections->db));
|
||||
}
|
||||
}
|
||||
|
174
src/ssb.db.c
174
src/ssb.db.c
@ -82,7 +82,8 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
|
||||
sqlite3_stmt* statement;
|
||||
int64_t last_row_id = -1;
|
||||
const char* query = "INSERT INTO messages (id, previous, author, sequence, timestamp, content, hash, signature) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT DO NOTHING";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
(previous ? sqlite3_bind_text(statement, 2, previous, -1, NULL) : sqlite3_bind_null(statement, 2)) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, author, -1, NULL) == SQLITE_OK &&
|
||||
@ -90,9 +91,11 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
|
||||
sqlite3_bind_int64(statement, 5, timestamp) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 6, contentstr, content_len, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 7, "sha256", 6, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_text(statement, 8, signature, -1, NULL) == SQLITE_OK)
|
||||
{
|
||||
int r = sqlite3_step(statement);
|
||||
if (r != SQLITE_DONE) {
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
printf("%s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
stored = r == SQLITE_DONE && sqlite3_changes(db) != 0;
|
||||
@ -102,27 +105,34 @@ bool tf_ssb_db_store_message(tf_ssb_t* ssb, JSContext* context, const char* id,
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
if (last_row_id != -1)
|
||||
{
|
||||
query = "INSERT INTO blob_wants (id) SELECT DISTINCT json.value FROM messages, json_tree(messages.content) AS json LEFT OUTER JOIN blobs ON json.value = blobs.id WHERE messages.rowid = ?1 AND json.value LIKE '&%%.sha256' AND length(json.value) = ?2 AND blobs.content IS NULL ON CONFLICT DO NOTHING RETURNING id";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_int64(statement, 1, last_row_id) == SQLITE_OK &&
|
||||
sqlite3_bind_int(statement, 2, BLOB_ID_LEN - 1) == SQLITE_OK) {
|
||||
sqlite3_bind_int(statement, 2, BLOB_ID_LEN - 1) == SQLITE_OK)
|
||||
{
|
||||
int r = SQLITE_OK;
|
||||
while ((r = sqlite3_step(statement)) == SQLITE_ROW)
|
||||
{
|
||||
tf_ssb_notify_blob_want_added(ssb, (const char*)sqlite3_column_text(statement, 0));
|
||||
}
|
||||
if (r != SQLITE_DONE) {
|
||||
if (r != SQLITE_DONE)
|
||||
{
|
||||
printf("%s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
@ -141,17 +151,21 @@ bool tf_ssb_db_message_content_get(tf_ssb_t* ssb, const char* id, uint8_t** out_
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
const char* query = "SELECT content FROM messages WHERE id = ?";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
const uint8_t* blob = sqlite3_column_blob(statement, 0);
|
||||
int size = sqlite3_column_bytes(statement, 0);
|
||||
if (out_blob) {
|
||||
if (out_blob)
|
||||
{
|
||||
*out_blob = malloc(size + 1);
|
||||
memcpy(*out_blob, blob, size);
|
||||
(*out_blob)[size] = '\0';
|
||||
}
|
||||
if (out_size) {
|
||||
if (out_size)
|
||||
{
|
||||
*out_size = size;
|
||||
}
|
||||
result = true;
|
||||
@ -166,17 +180,21 @@ bool tf_ssb_db_blob_get(tf_ssb_t* ssb, const char* id, uint8_t** out_blob, size_
|
||||
bool result = false;
|
||||
sqlite3_stmt* statement;
|
||||
const char* query = "SELECT content FROM blobs WHERE id = $1";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
const uint8_t* blob = sqlite3_column_blob(statement, 0);
|
||||
int size = sqlite3_column_bytes(statement, 0);
|
||||
if (out_blob) {
|
||||
if (out_blob)
|
||||
{
|
||||
*out_blob = malloc(size + 1);
|
||||
memcpy(*out_blob, blob, size);
|
||||
(*out_blob)[size] = '\0';
|
||||
}
|
||||
if (out_size) {
|
||||
if (out_size)
|
||||
{
|
||||
*out_size = size;
|
||||
}
|
||||
result = true;
|
||||
@ -203,19 +221,26 @@ bool tf_ssb_db_blob_store(tf_ssb_t* ssb, const uint8_t* blob, size_t size, char*
|
||||
printf("blob store %s\n", id);
|
||||
|
||||
const char* query = "INSERT INTO blobs (id, content, created) VALUES ($1, $2, CAST(strftime('%s') AS INTEGER)) ON CONFLICT DO NOTHING";
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_blob(statement, 2, blob, size, NULL) == SQLITE_OK) {
|
||||
sqlite3_bind_blob(statement, 2, blob, size, NULL) == SQLITE_OK)
|
||||
{
|
||||
result = sqlite3_step(statement) == SQLITE_DONE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("bind failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
|
||||
if (result && out_id) {
|
||||
if (result && out_id)
|
||||
{
|
||||
snprintf(out_id, out_id_size, "%s", id);
|
||||
}
|
||||
return result;
|
||||
@ -226,23 +251,30 @@ bool tf_ssb_db_get_message_by_author_and_sequence(tf_ssb_t* ssb, const char* aut
|
||||
bool found = false;
|
||||
sqlite3_stmt* statement;
|
||||
const char* query = "SELECT id, timestamp, content FROM messages WHERE author = $1 AND sequence = $2";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_int64(statement, 2, sequence) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
if (out_message_id) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
if (out_message_id)
|
||||
{
|
||||
strncpy(out_message_id, (const char*)sqlite3_column_text(statement, 0), out_message_id_size - 1);
|
||||
}
|
||||
if (out_timestamp) {
|
||||
if (out_timestamp)
|
||||
{
|
||||
*out_timestamp = sqlite3_column_int64(statement, 1);
|
||||
}
|
||||
if (out_content) {
|
||||
if (out_content)
|
||||
{
|
||||
*out_content = strdup((const char*)sqlite3_column_text(statement, 2));
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare failed: %s\n", sqlite3_errmsg(tf_ssb_get_db(ssb)));
|
||||
}
|
||||
return found;
|
||||
@ -253,77 +285,105 @@ bool tf_ssb_db_get_latest_message_by_author(tf_ssb_t* ssb, const char* author, i
|
||||
bool found = false;
|
||||
sqlite3_stmt* statement;
|
||||
const char* query = "SELECT id, sequence FROM messages WHERE author = $1 AND sequence = (SELECT MAX(sequence) FROM messages WHERE author = $1)";
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, author, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
if (out_sequence) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
if (out_sequence)
|
||||
{
|
||||
*out_sequence = sqlite3_column_int64(statement, 1);
|
||||
}
|
||||
if (out_message_id) {
|
||||
if (out_message_id)
|
||||
{
|
||||
strncpy(out_message_id, (const char*)sqlite3_column_text(statement, 0), out_message_id_size - 1);
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare failed: %s\n", sqlite3_errmsg(tf_ssb_get_db(ssb)));
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
static bool _tf_ssb_sqlite_bind_json(JSContext* context, sqlite3* db, sqlite3_stmt* statement, JSValue binds) {
|
||||
static bool _tf_ssb_sqlite_bind_json(JSContext* context, sqlite3* db, sqlite3_stmt* statement, JSValue binds)
|
||||
{
|
||||
bool all_bound = true;
|
||||
int32_t length = 0;
|
||||
if (JS_IsUndefined(binds)) {
|
||||
if (JS_IsUndefined(binds))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
JSValue lengthval = JS_GetPropertyStr(context, binds, "length");
|
||||
if (JS_ToInt32(context, &length, lengthval) == 0) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (JS_ToInt32(context, &length, lengthval) == 0)
|
||||
{
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
JSValue value = JS_GetPropertyUint32(context, binds, i);
|
||||
if (JS_IsString(value)) {
|
||||
if (JS_IsString(value))
|
||||
{
|
||||
size_t str_len = 0;
|
||||
const char* str = JS_ToCStringLen(context, &str_len, value);
|
||||
if (str) {
|
||||
if (sqlite3_bind_text(statement, i + 1, str, str_len, SQLITE_TRANSIENT) != SQLITE_OK) {
|
||||
if (str)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, i + 1, str, str_len, SQLITE_TRANSIENT) != SQLITE_OK)
|
||||
{
|
||||
printf("failed to bind: %s\n", sqlite3_errmsg(db));
|
||||
all_bound = false;
|
||||
}
|
||||
JS_FreeCString(context, str);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("expected cstring\n");
|
||||
}
|
||||
} else if (JS_IsNumber(value)) {
|
||||
}
|
||||
else if (JS_IsNumber(value))
|
||||
{
|
||||
int64_t number = 0;
|
||||
JS_ToInt64(context, &number, value);
|
||||
if (sqlite3_bind_int64(statement, i + 1, number) != SQLITE_OK) {
|
||||
if (sqlite3_bind_int64(statement, i + 1, number) != SQLITE_OK)
|
||||
{
|
||||
printf("failed to bind: %s\n", sqlite3_errmsg(db));
|
||||
all_bound = false;
|
||||
}
|
||||
} else if (JS_IsNull(value)) {
|
||||
if (sqlite3_bind_null(statement, i + 1) != SQLITE_OK) {
|
||||
}
|
||||
else if (JS_IsNull(value))
|
||||
{
|
||||
if (sqlite3_bind_null(statement, i + 1) != SQLITE_OK)
|
||||
{
|
||||
printf("failed to bind: %s\n", sqlite3_errmsg(db));
|
||||
all_bound = false;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* str = JS_ToCString(context, value);
|
||||
printf("expected string: %s\n", str);
|
||||
JS_FreeCString(context, str);
|
||||
}
|
||||
JS_FreeValue(context, value);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("expected array\n");
|
||||
}
|
||||
JS_FreeValue(context, lengthval);
|
||||
return all_bound;
|
||||
}
|
||||
|
||||
static JSValue _tf_ssb_sqlite_row_to_json(JSContext* context, sqlite3_stmt* row) {
|
||||
static JSValue _tf_ssb_sqlite_row_to_json(JSContext* context, sqlite3_stmt* row)
|
||||
{
|
||||
JSValue result = JS_NewObject(context);
|
||||
for (int i = 0; i < sqlite3_column_count(row); i++) {
|
||||
for (int i = 0; i < sqlite3_column_count(row); i++)
|
||||
{
|
||||
const char* name = sqlite3_column_name(row, i);
|
||||
switch (sqlite3_column_type(row, i)) {
|
||||
switch (sqlite3_column_type(row, i))
|
||||
{
|
||||
case SQLITE_INTEGER:
|
||||
JS_SetPropertyStr(context, result, name, JS_NewInt64(context, sqlite3_column_int64(row, i)));
|
||||
break;
|
||||
@ -346,7 +406,8 @@ static JSValue _tf_ssb_sqlite_row_to_json(JSContext* context, sqlite3_stmt* row)
|
||||
|
||||
static int _tf_ssb_sqlite_authorizer(void* user_data, int action_code, const char* arg0, const char* arg1, const char* arg2, const char* arg3)
|
||||
{
|
||||
switch (action_code) {
|
||||
switch (action_code)
|
||||
{
|
||||
case SQLITE_SELECT:
|
||||
case SQLITE_FUNCTION:
|
||||
return SQLITE_OK;
|
||||
@ -365,10 +426,13 @@ void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds
|
||||
sqlite3* db = tf_ssb_get_db(ssb);
|
||||
sqlite3_stmt* statement;
|
||||
sqlite3_set_authorizer(db, _tf_ssb_sqlite_authorizer, ssb);
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(db, query, -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
JSContext* context = tf_ssb_get_context(ssb);
|
||||
if (_tf_ssb_sqlite_bind_json(context, db, statement, binds)) {
|
||||
while (sqlite3_step(statement) == SQLITE_ROW) {
|
||||
if (_tf_ssb_sqlite_bind_json(context, db, statement, binds))
|
||||
{
|
||||
while (sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
JSValue row = _tf_ssb_sqlite_row_to_json(context, statement);
|
||||
tf_trace_t* trace = tf_ssb_get_trace(ssb);
|
||||
tf_trace_begin(trace, "callback");
|
||||
@ -378,7 +442,9 @@ void tf_ssb_db_visit_query(tf_ssb_t* ssb, const char* query, const JSValue binds
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
sqlite3_set_authorizer(db, NULL, NULL);
|
||||
|
@ -11,10 +11,13 @@
|
||||
static void _write_file(const char* path, void* blob, size_t size)
|
||||
{
|
||||
FILE* file = fopen(path, "wb");
|
||||
if (file) {
|
||||
if (file)
|
||||
{
|
||||
fwrite(blob, 1, size, file);
|
||||
fclose(file);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Failed to open %s for write: %s.\n", path, strerror(errno));
|
||||
}
|
||||
}
|
||||
@ -23,7 +26,8 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
{
|
||||
char user[256] = { 0 };
|
||||
char path[256] = { 0 };
|
||||
if (sscanf(key, "/~%255[^/]/%255s", user, path) != 2) {
|
||||
if (sscanf(key, "/~%255[^/]/%255s", user, path) != 2)
|
||||
{
|
||||
printf("Unable to export %s.\n", key);
|
||||
return;
|
||||
}
|
||||
@ -31,12 +35,15 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
char app_blob_id[64] = { 0 };
|
||||
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'path:' || $2", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(tf_ssb_get_db(ssb), "SELECT value FROM properties WHERE id = $1 AND key = 'path:' || $2", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
if (sqlite3_bind_text(statement, 1, user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, path, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_ROW) {
|
||||
sqlite3_step(statement) == SQLITE_ROW)
|
||||
{
|
||||
int len = sqlite3_column_bytes(statement, 0);
|
||||
if (len >= (int)sizeof(app_blob_id)) {
|
||||
if (len >= (int)sizeof(app_blob_id))
|
||||
{
|
||||
len = sizeof(app_blob_id) - 1;
|
||||
}
|
||||
memcpy(app_blob_id, sqlite3_column_text(statement, 0), len);
|
||||
@ -45,14 +52,16 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
sqlite3_finalize(statement);
|
||||
}
|
||||
|
||||
if (!*app_blob_id) {
|
||||
if (!*app_blob_id)
|
||||
{
|
||||
printf("Did not find app blob ID for %s.\n", key);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t* blob = NULL;
|
||||
size_t size = 0;
|
||||
if (!tf_ssb_db_blob_get(ssb, app_blob_id, &blob, &size)) {
|
||||
if (!tf_ssb_db_blob_get(ssb, app_blob_id, &blob, &size))
|
||||
{
|
||||
printf("Did not find blob for %s: %s.\n", key, app_blob_id);
|
||||
return;
|
||||
}
|
||||
@ -66,17 +75,21 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
JSValue files = JS_GetPropertyStr(context, app, "files");
|
||||
JSPropertyEnum* ptab = NULL;
|
||||
uint32_t plen = 0;
|
||||
if (JS_GetOwnPropertyNames(context, &ptab, &plen, files, JS_GPN_STRING_MASK) == 0) {
|
||||
for (uint32_t i = 0; i < plen; ++i) {
|
||||
if (JS_GetOwnPropertyNames(context, &ptab, &plen, files, JS_GPN_STRING_MASK) == 0)
|
||||
{
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JSPropertyDescriptor desc;
|
||||
if (JS_GetOwnProperty(context, &desc, files, ptab[i].atom) == 1) {
|
||||
if (JS_GetOwnProperty(context, &desc, files, ptab[i].atom) == 1)
|
||||
{
|
||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
||||
const char* file_name = JS_ToCString(context, key);
|
||||
const char* blob_id = JS_ToCString(context, desc.value);
|
||||
|
||||
uint8_t* file_blob = NULL;
|
||||
size_t file_size = 0;
|
||||
if (tf_ssb_db_blob_get(ssb, blob_id, &file_blob, &file_size)) {
|
||||
if (tf_ssb_db_blob_get(ssb, blob_id, &file_blob, &file_size))
|
||||
{
|
||||
snprintf(file_path, sizeof(file_path), "apps/%s/%s/%s", user, path, file_name);
|
||||
_write_file(file_path, file_blob, file_size);
|
||||
free(file_blob);
|
||||
@ -90,7 +103,8 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key)
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < plen; ++i) {
|
||||
for (uint32_t i = 0; i < plen; ++i)
|
||||
{
|
||||
JS_FreeAtom(context, ptab[i].atom);
|
||||
}
|
||||
js_free(context, ptab);
|
||||
|
@ -33,17 +33,22 @@ static void _tf_ssb_import_file_read(uv_fs_t* req)
|
||||
{
|
||||
tf_import_file_t* file = req->data;
|
||||
char id[k_id_base64_len];
|
||||
if (req->result >= 0) {
|
||||
if (tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id))) {
|
||||
if (req->result >= 0)
|
||||
{
|
||||
if (tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id)))
|
||||
{
|
||||
printf("Stored %s/%s as %s.\n", file->parent, file->name, id);
|
||||
if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0) {
|
||||
if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0)
|
||||
{
|
||||
sqlite3_stmt* statement;
|
||||
if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK) {
|
||||
if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK)
|
||||
{
|
||||
((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0';
|
||||
if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK &&
|
||||
sqlite3_step(statement) == SQLITE_DONE) {
|
||||
sqlite3_step(statement) == SQLITE_DONE)
|
||||
{
|
||||
printf("Registered %s path:%s as %s.\n", file->user, file->name, id);
|
||||
}
|
||||
sqlite3_finalize(statement);
|
||||
@ -75,13 +80,17 @@ static void _tf_ssb_import_scandir(uv_fs_t* req)
|
||||
{
|
||||
tf_import_t* import = req->data;
|
||||
uv_dirent_t ent;
|
||||
while (uv_fs_scandir_next(req, &ent) == 0) {
|
||||
while (uv_fs_scandir_next(req, &ent) == 0)
|
||||
{
|
||||
size_t len = strlen(import->parent) + strlen(ent.name) + 2;
|
||||
char* path = malloc(len);
|
||||
snprintf(path, len, "%s/%s", import->parent, ent.name);
|
||||
if (ent.type == UV_DIRENT_DIR) {
|
||||
if (ent.type == UV_DIRENT_DIR)
|
||||
{
|
||||
tf_ssb_import(import->ssb, import->user, path);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t size = sizeof(tf_import_file_t) + strlen(import->parent) +1 + strlen(ent.name) + 1;
|
||||
tf_import_file_t* file = malloc(size);
|
||||
memset(file, 0, size);
|
||||
@ -96,7 +105,8 @@ static void _tf_ssb_import_scandir(uv_fs_t* req)
|
||||
|
||||
import->work_left++;
|
||||
int r = uv_fs_open(tf_ssb_get_loop(import->ssb), &file->req, path, 0, 0, _tf_ssb_import_file_open);
|
||||
if (r < 0) {
|
||||
if (r < 0)
|
||||
{
|
||||
printf("Failed to open %s: %s.\n", path, uv_strerror(r));
|
||||
free(file);
|
||||
import->work_left--;
|
||||
@ -117,11 +127,13 @@ void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path)
|
||||
};
|
||||
import.req.data = &import;
|
||||
int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &import.req, path, 0, _tf_ssb_import_scandir);
|
||||
if (r) {
|
||||
if (r)
|
||||
{
|
||||
printf("Failed to scan directory %s: %s.", path, uv_strerror(r));
|
||||
}
|
||||
|
||||
while (import.work_left > 0) {
|
||||
while (import.work_left > 0)
|
||||
{
|
||||
uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE);
|
||||
}
|
||||
uv_fs_req_cleanup(&import.req);
|
||||
|
150
src/ssb.qjs.c
150
src/ssb.qjs.c
@ -19,9 +19,11 @@ static JSValue _tf_ssb_whoami(JSContext* context, JSValueConst this_val, int arg
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
printf("WHOAMI on %p\n", ssb);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
char id[512];
|
||||
if (tf_ssb_whoami(ssb, id, sizeof(id))) {
|
||||
if (tf_ssb_whoami(ssb, id, sizeof(id)))
|
||||
{
|
||||
return JS_NewString(context, id);
|
||||
}
|
||||
}
|
||||
@ -32,13 +34,15 @@ static JSValue _tf_ssb_getMessage(JSContext* context, JSValueConst this_val, int
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
const char* id = JS_ToCString(context, argv[0]);
|
||||
int64_t sequence = 0;
|
||||
JS_ToInt64(context, &sequence, argv[1]);
|
||||
int64_t timestamp = -1;
|
||||
char* contents = NULL;
|
||||
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, ×tamp, &contents)) {
|
||||
if (tf_ssb_db_get_message_by_author_and_sequence(ssb, id, sequence, NULL, 0, ×tamp, &contents))
|
||||
{
|
||||
result = JS_NewObject(context);
|
||||
JS_SetPropertyStr(context, result, "timestamp", JS_NewInt64(context, timestamp));
|
||||
JS_SetPropertyStr(context, result, "content", JS_NewString(context, contents));
|
||||
@ -53,11 +57,13 @@ static JSValue _tf_ssb_blobGet(JSContext* context, JSValueConst this_val, int ar
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
const char* id = JS_ToCString(context, argv[0]);
|
||||
uint8_t* blob = NULL;
|
||||
size_t size = 0;
|
||||
if (tf_ssb_db_blob_get(ssb, id, &blob, &size)) {
|
||||
if (tf_ssb_db_blob_get(ssb, id, &blob, &size))
|
||||
{
|
||||
result = JS_NewArrayBufferCopy(context, blob, size);
|
||||
free(blob);
|
||||
}
|
||||
@ -70,18 +76,24 @@ static JSValue _tf_ssb_blobStore(JSContext* context, JSValueConst this_val, int
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
uint8_t* blob = NULL;
|
||||
size_t size = 0;
|
||||
char id[512];
|
||||
if (JS_IsString(argv[0])) {
|
||||
if (JS_IsString(argv[0]))
|
||||
{
|
||||
const char* text = JS_ToCStringLen(context, &size, argv[0]);
|
||||
if (tf_ssb_db_blob_store(ssb, (const uint8_t*)text, size, id, sizeof(id))) {
|
||||
if (tf_ssb_db_blob_store(ssb, (const uint8_t*)text, size, id, sizeof(id)))
|
||||
{
|
||||
result = JS_NewString(context, id);
|
||||
}
|
||||
JS_FreeCString(context, text);
|
||||
} else if ((blob = tf_try_get_array_buffer(context, &size, argv[0])) != 0) {
|
||||
if (tf_ssb_db_blob_store(ssb, blob, size, id, sizeof(id))) {
|
||||
}
|
||||
else if ((blob = tf_try_get_array_buffer(context, &size, argv[0])) != 0)
|
||||
{
|
||||
if (tf_ssb_db_blob_store(ssb, blob, size, id, sizeof(id)))
|
||||
{
|
||||
result = JS_NewString(context, id);
|
||||
}
|
||||
}
|
||||
@ -93,11 +105,13 @@ static JSValue _tf_ssb_messageContentGet(JSContext* context, JSValueConst this_v
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
const char* id = JS_ToCString(context, argv[0]);
|
||||
uint8_t* blob = NULL;
|
||||
size_t size = 0;
|
||||
if (tf_ssb_db_message_content_get(ssb, id, &blob, &size)) {
|
||||
if (tf_ssb_db_message_content_get(ssb, id, &blob, &size))
|
||||
{
|
||||
result = JS_NewArrayBufferCopy(context, blob, size);
|
||||
free(blob);
|
||||
}
|
||||
@ -110,12 +124,15 @@ static JSValue _tf_ssb_connections(JSContext* context, JSValueConst this_val, in
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
const char** connections = tf_ssb_get_connection_ids(ssb);
|
||||
if (connections) {
|
||||
if (connections)
|
||||
{
|
||||
result = JS_NewArray(context);
|
||||
uint32_t i = 0;
|
||||
for (const char** p = connections; *p; p++, i++) {
|
||||
for (const char** p = connections; *p; p++, i++)
|
||||
{
|
||||
JS_SetPropertyUint32(context, result, i, JS_NewString(context, *p));
|
||||
}
|
||||
free(connections);
|
||||
@ -132,7 +149,8 @@ static void _check_call(JSContext* context, JSValue result)
|
||||
printf("ERROR: %s\n", value);
|
||||
JS_FreeCString(context, value);
|
||||
JSValue stack = JS_GetPropertyStr(context, result, "stack");
|
||||
if (!JS_IsUndefined(stack)) {
|
||||
if (!JS_IsUndefined(stack))
|
||||
{
|
||||
const char* stack_str = JS_ToCString(context, stack);
|
||||
printf("%s\n", stack_str);
|
||||
JS_FreeCString(context, stack_str);
|
||||
@ -157,7 +175,8 @@ typedef struct _sqlStream_callback_t
|
||||
JSValue callback;
|
||||
} sqlStream_callback_t;
|
||||
|
||||
static void _tf_ssb_sqlStream_callback(JSValue row, void* user_data) {
|
||||
static void _tf_ssb_sqlStream_callback(JSValue row, void* user_data)
|
||||
{
|
||||
sqlStream_callback_t* info = user_data;
|
||||
JSValue response = JS_Call(info->context, info->callback, JS_UNDEFINED, 1, &row);
|
||||
_check_call(info->context, response);
|
||||
@ -171,9 +190,11 @@ static void _tf_ssb_sqlStream_callback(JSValue row, void* user_data) {
|
||||
static JSValue _tf_ssb_sqlStream(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
const char* query = JS_ToCString(context, argv[0]);
|
||||
if (query) {
|
||||
if (query)
|
||||
{
|
||||
sqlStream_callback_t info = {
|
||||
.context = context,
|
||||
.callback = argv[2],
|
||||
@ -188,9 +209,11 @@ static JSValue _tf_ssb_sqlStream(JSContext* context, JSValueConst this_val, int
|
||||
static JSValue _tf_ssb_post(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
const char* post_text = JS_ToCString(context, argv[0]);
|
||||
if (post_text) {
|
||||
if (post_text)
|
||||
{
|
||||
tf_ssb_append_post(ssb, post_text);
|
||||
JS_FreeCString(context, post_text);
|
||||
}
|
||||
@ -201,7 +224,8 @@ static JSValue _tf_ssb_post(JSContext* context, JSValueConst this_val, int argc,
|
||||
static JSValue _tf_ssb_appendMessage(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
tf_ssb_append_message(ssb, argv[0]);
|
||||
}
|
||||
return JS_NULL;
|
||||
@ -213,9 +237,12 @@ static JSValue _tf_ssb_storeMessage(JSContext* context, JSValueConst this_val, i
|
||||
char signature[crypto_sign_BYTES + 128];
|
||||
char id[crypto_hash_sha256_BYTES * 2 + 1];
|
||||
tf_ssb_calculate_message_id(context, argv[0], id, sizeof(id));
|
||||
if (tf_ssb_verify_and_strip_signature(context, argv[0], signature, sizeof(signature))) {
|
||||
if (tf_ssb_verify_and_strip_signature(context, argv[0], signature, sizeof(signature)))
|
||||
{
|
||||
tf_ssb_db_store_message(ssb, context, id, argv[0], signature);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("failed to verify message\n");
|
||||
}
|
||||
return JS_UNDEFINED;
|
||||
@ -246,7 +273,8 @@ static JSValue _tf_ssb_getBroadcasts(JSContext* context, JSValueConst this_val,
|
||||
{
|
||||
JSValue result = JS_UNDEFINED;
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (ssb)
|
||||
{
|
||||
result = JS_NewArray(context);
|
||||
broadcasts_t broadcasts = {
|
||||
.context = context,
|
||||
@ -262,13 +290,17 @@ static JSValue _tf_ssb_connect(JSContext* context, JSValueConst this_val, int ar
|
||||
{
|
||||
JSValue args = argv[0];
|
||||
tf_ssb_t* ssb = JS_GetOpaque(this_val, _tf_ssb_classId);
|
||||
if (ssb) {
|
||||
if (JS_IsString(args)) {
|
||||
if (ssb)
|
||||
{
|
||||
if (JS_IsString(args))
|
||||
{
|
||||
const char* address_str = JS_ToCString(context, args);
|
||||
printf("Connecting to %s\n", address_str);
|
||||
tf_ssb_connect_str(ssb, address_str);
|
||||
JS_FreeCString(context, address_str);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
JSValue address = JS_GetPropertyStr(context, args, "address");
|
||||
JSValue port = JS_GetPropertyStr(context, args, "port");
|
||||
JSValue pubkey = JS_GetPropertyStr(context, args, "pubkey");
|
||||
@ -296,7 +328,8 @@ static void _tf_ssb_call_callback(tf_ssb_t* ssb, const char* name, void* user_da
|
||||
JSValue global = JS_GetGlobalObject(context);
|
||||
JSValue ssbo = JS_GetPropertyStr(context, global, "ssb");
|
||||
JSValue callback = JS_GetPropertyStr(context, ssbo, name);
|
||||
if (JS_IsFunction(context, callback)) {
|
||||
if (JS_IsFunction(context, callback))
|
||||
{
|
||||
JSValue args = JS_UNDEFINED;
|
||||
JSValue response = JS_Call(context, callback, JS_UNDEFINED, 0, &args);
|
||||
_check_call(context, response);
|
||||
@ -358,7 +391,8 @@ static JSValue _tf_ssb_rpc_send_binary(JSContext* context, JSValueConst this_val
|
||||
|
||||
size_t size;
|
||||
uint8_t* message = tf_try_get_array_buffer(context, &size, argv[0]);
|
||||
if (message) {
|
||||
if (message)
|
||||
{
|
||||
tf_ssb_connection_rpc_send(
|
||||
connection,
|
||||
k_ssb_rpc_flag_binary | k_ssb_rpc_flag_stream,
|
||||
@ -550,7 +584,8 @@ void tf_ssb_run_file(JSContext* context, const char* file_name)
|
||||
printf("ERROR: %s\n", value);
|
||||
JS_FreeCString(context, value);
|
||||
JSValue stack = JS_GetPropertyStr(context, result, "stack");
|
||||
if (!JS_IsUndefined(stack)) {
|
||||
if (!JS_IsUndefined(stack))
|
||||
{
|
||||
const char* stack_str = JS_ToCString(context, stack);
|
||||
printf("%s\n", stack_str);
|
||||
JS_FreeCString(context, stack_str);
|
||||
@ -568,12 +603,14 @@ void tf_ssb_run_file(JSContext* context, const char* file_name)
|
||||
}
|
||||
|
||||
JSRuntime* runtime = JS_GetRuntime(context);
|
||||
while (JS_IsJobPending(runtime)) {
|
||||
while (JS_IsJobPending(runtime))
|
||||
{
|
||||
JSContext* context2 = NULL;
|
||||
int r = JS_ExecutePendingJob(runtime, &context2);
|
||||
JSValue result = JS_GetException(context2);
|
||||
_check_call(context, result);
|
||||
if (r == 0) {
|
||||
if (r == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -582,11 +619,16 @@ void tf_ssb_run_file(JSContext* context, const char* file_name)
|
||||
free(source);
|
||||
}
|
||||
|
||||
JSValue _print(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
if (JS_IsNull(argv[i])) {
|
||||
JSValue _print(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
for (int i = 0; i < argc; ++i)
|
||||
{
|
||||
if (JS_IsNull(argv[i]))
|
||||
{
|
||||
printf(" null");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* value = JS_ToCString(context, argv[i]);
|
||||
printf(" %s", value);
|
||||
JS_FreeCString(context, value);
|
||||
@ -596,27 +638,37 @@ JSValue _print(JSContext* context, JSValueConst this_val, int argc, JSValueConst
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue _utf8Decode(JSContext* context, uint8_t* data, size_t length) {
|
||||
static JSValue _utf8Decode(JSContext* context, uint8_t* data, size_t length)
|
||||
{
|
||||
return JS_NewStringLen(context, (const char*)data, length);
|
||||
}
|
||||
|
||||
static JSValue _utf8_decode(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _utf8_decode(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
JSValue result = JS_NULL;
|
||||
size_t length;
|
||||
if (JS_IsString(argv[0])) {
|
||||
if (JS_IsString(argv[0]))
|
||||
{
|
||||
result = JS_DupValue(context, argv[0]);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8_t* array = tf_try_get_array_buffer(context, &length, argv[0]);
|
||||
if (array) {
|
||||
if (array)
|
||||
{
|
||||
result = _utf8Decode(context, array, length);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t offset;
|
||||
size_t element_size;
|
||||
JSValue buffer = tf_try_get_typed_array_buffer(context, argv[0], &offset, &length, &element_size);
|
||||
size_t size;
|
||||
if (!JS_IsException(buffer)) {
|
||||
if (!JS_IsException(buffer))
|
||||
{
|
||||
array = tf_try_get_array_buffer(context, &size, buffer);
|
||||
if (array) {
|
||||
if (array)
|
||||
{
|
||||
result = _utf8Decode(context, array, size);
|
||||
}
|
||||
}
|
||||
@ -629,10 +681,12 @@ static JSValue _utf8_decode(JSContext* context, JSValueConst this_val, int argc,
|
||||
void tf_ssb_init(JSContext* context, tf_ssb_t* ssb)
|
||||
{
|
||||
JS_NewClassID(&_tf_ssb_classId);
|
||||
JSClassDef def = {
|
||||
JSClassDef def =
|
||||
{
|
||||
.class_name = "ssb",
|
||||
};
|
||||
if (JS_NewClass(JS_GetRuntime(context), _tf_ssb_classId, &def) != 0) {
|
||||
if (JS_NewClass(JS_GetRuntime(context), _tf_ssb_classId, &def) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to register ssb.\n");
|
||||
}
|
||||
|
||||
|
@ -37,15 +37,19 @@ static void _ssb_test_connections_changed(tf_ssb_t* ssb, tf_ssb_change_t change,
|
||||
|
||||
int count = 0;
|
||||
const char** c = tf_ssb_get_connection_ids(ssb);
|
||||
for (const char** p = c; *p; p++) {
|
||||
for (const char** p = c; *p; p++)
|
||||
{
|
||||
count++;
|
||||
}
|
||||
free(c);
|
||||
|
||||
if (ssb == test->ssb0) {
|
||||
if (ssb == test->ssb0)
|
||||
{
|
||||
printf("callback0 change=%d connection=%p\n", change, connection);
|
||||
test->connection_count0 = count;
|
||||
} else if (ssb == test->ssb1) {
|
||||
}
|
||||
else if (ssb == test->ssb1)
|
||||
{
|
||||
printf("callback1 change=%d connection=%p\n", change, connection);
|
||||
test->connection_count1 = count;
|
||||
}
|
||||
@ -131,17 +135,20 @@ void tf_ssb_test_ssb(const tf_test_options_t* options)
|
||||
tf_ssb_connect(ssb1, "127.0.0.1", 12347, id0bin);
|
||||
|
||||
while (test.connection_count0 != 1 ||
|
||||
test.connection_count1 != 1) {
|
||||
test.connection_count1 != 1)
|
||||
{
|
||||
uv_run(&loop, UV_RUN_ONCE);
|
||||
}
|
||||
tf_ssb_server_close(ssb0);
|
||||
|
||||
while (_ssb_test_count_messages(ssb1) < 3) {
|
||||
while (_ssb_test_count_messages(ssb1) < 3)
|
||||
{
|
||||
uv_run(&loop, UV_RUN_ONCE);
|
||||
}
|
||||
|
||||
printf("waiting for blob\n");
|
||||
while (!tf_ssb_db_blob_get(ssb1, blob_id, NULL, NULL)) {
|
||||
while (!tf_ssb_db_blob_get(ssb1, blob_id, NULL, NULL))
|
||||
{
|
||||
uv_run(&loop, UV_RUN_ONCE);
|
||||
}
|
||||
printf("done\n");
|
||||
@ -202,16 +209,19 @@ void tf_ssb_test_following(const tf_test_options_t* options)
|
||||
#define DUMP(id, depth)
|
||||
#else
|
||||
#define DUMP(id, depth) \
|
||||
do { \
|
||||
do \
|
||||
{ \
|
||||
printf("following %d:\n", depth); \
|
||||
const char** tf_ssb_get_following_deep(tf_ssb_t* ssb_param, const char** ids, int depth_param); \
|
||||
const char** f = tf_ssb_get_following_deep(ssb0, (const char*[]) { id, NULL }, depth); \
|
||||
for (const char** p = f; p && *p; p++) { \
|
||||
for (const char** p = f; p && *p; p++) \
|
||||
{ \
|
||||
printf("* %s\n", *p); \
|
||||
} \
|
||||
printf("\n"); \
|
||||
free(f); \
|
||||
} while (0)
|
||||
} \
|
||||
while (0)
|
||||
#endif
|
||||
|
||||
FOLLOW(ssb0, id1, true);
|
||||
|
540
src/task.c
540
src/task.c
File diff suppressed because it is too large
Load Diff
118
src/taskstub.c
118
src/taskstub.c
@ -38,9 +38,11 @@ typedef struct _tf_taskstub_t {
|
||||
bool _finalized;
|
||||
} tf_taskstub_t;
|
||||
|
||||
void tf_taskstub_startup() {
|
||||
void tf_taskstub_startup()
|
||||
{
|
||||
static bool initialized;
|
||||
if (!initialized) {
|
||||
if (!initialized)
|
||||
{
|
||||
JS_NewClassID(&_classId);
|
||||
size_t size = sizeof(_executable);
|
||||
uv_exepath(_executable, &size);
|
||||
@ -62,7 +64,8 @@ static JSValue _taskstub_loadFile(JSContext* context, JSValueConst this_val, int
|
||||
static void _taskstub_on_process_exit(uv_process_t* process, int64_t status, int terminationSignal);
|
||||
static void _taskstub_finalizer(JSRuntime *runtime, JSValue value);
|
||||
|
||||
static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_task_t* parent = tf_task_get(context);
|
||||
tf_taskstub_t* stub = malloc(sizeof(tf_taskstub_t));
|
||||
memset(stub, 0, sizeof(*stub));
|
||||
@ -106,7 +109,8 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
|
||||
JS_SetPropertyStr(context, taskObject, "loadFile", JS_NewCFunction(context, _taskstub_loadFile, "loadFile", 1));
|
||||
|
||||
taskid_t id = k_task_parent_id;
|
||||
if (parent) {
|
||||
if (parent)
|
||||
{
|
||||
id = tf_task_allocate_task_id(parent, (tf_taskstub_t*)stub);
|
||||
}
|
||||
stub->_id = id;
|
||||
@ -116,7 +120,8 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
|
||||
|
||||
uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream);
|
||||
memset(pipe, 0, sizeof(*pipe));
|
||||
if (uv_pipe_init(tf_task_get_loop(parent), pipe, 1) != 0) {
|
||||
if (uv_pipe_init(tf_task_get_loop(parent), pipe, 1) != 0)
|
||||
{
|
||||
fprintf(stderr, "uv_pipe_init failed\n");
|
||||
}
|
||||
|
||||
@ -138,55 +143,67 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
|
||||
JSValue result = JS_NULL;
|
||||
stub->_process.data = stub;
|
||||
int spawn_result = uv_spawn(tf_task_get_loop(parent), &stub->_process, &options);
|
||||
if (spawn_result == 0) {
|
||||
if (spawn_result == 0)
|
||||
{
|
||||
tf_packetstream_set_on_receive(stub->_stream, tf_task_on_receive_packet, stub);
|
||||
tf_packetstream_start(stub->_stream);
|
||||
|
||||
result = taskObject;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "uv_spawn failed: %s\n", uv_strerror(spawn_result));
|
||||
JS_FreeValue(context, taskObject);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _taskstub_gc_mark(JSRuntime* rt, JSValueConst value, JS_MarkFunc mark_func) {
|
||||
void _taskstub_gc_mark(JSRuntime* rt, JSValueConst value, JS_MarkFunc mark_func)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(value, _classId);
|
||||
if (stub) {
|
||||
if (stub)
|
||||
{
|
||||
JS_MarkValue(rt, stub->_on_exit, mark_func);
|
||||
JS_MarkValue(rt, stub->_on_error, mark_func);
|
||||
}
|
||||
}
|
||||
|
||||
JSValue tf_taskstub_init(JSContext* context) {
|
||||
JSValue tf_taskstub_init(JSContext* context)
|
||||
{
|
||||
JSClassDef def = {
|
||||
.class_name = "TaskStub",
|
||||
.finalizer = &_taskstub_finalizer,
|
||||
.gc_mark = _taskstub_gc_mark,
|
||||
};
|
||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0) {
|
||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to register TaskStub class.\n");
|
||||
}
|
||||
return JS_NewCFunction2(context, _taskstub_create, "TaskStub", 0, JS_CFUNC_constructor, 0);
|
||||
}
|
||||
|
||||
taskid_t tf_taskstub_get_id(const tf_taskstub_t* stub) {
|
||||
taskid_t tf_taskstub_get_id(const tf_taskstub_t* stub)
|
||||
{
|
||||
return stub->_id;
|
||||
}
|
||||
|
||||
JSValue tf_taskstub_get_task_object(const tf_taskstub_t* stub) {
|
||||
JSValue tf_taskstub_get_task_object(const tf_taskstub_t* stub)
|
||||
{
|
||||
return stub->_taskObject;
|
||||
}
|
||||
|
||||
tf_packetstream_t* tf_taskstub_get_stream(const tf_taskstub_t* stub) {
|
||||
tf_packetstream_t* tf_taskstub_get_stream(const tf_taskstub_t* stub)
|
||||
{
|
||||
return stub->_stream;
|
||||
}
|
||||
|
||||
tf_task_t* tf_taskstub_get_owner(const tf_taskstub_t* stub) {
|
||||
tf_task_t* tf_taskstub_get_owner(const tf_taskstub_t* stub)
|
||||
{
|
||||
return stub->_owner;
|
||||
}
|
||||
|
||||
tf_taskstub_t* tf_taskstub_create_parent(tf_task_t* task, uv_file file) {
|
||||
tf_taskstub_t* tf_taskstub_create_parent(tf_task_t* task, uv_file file)
|
||||
{
|
||||
JSValue parentObject = JS_NewObject(tf_task_get_context(task));
|
||||
tf_taskstub_t* parentStub = malloc(sizeof(tf_taskstub_t));
|
||||
memset(parentStub, 0, sizeof(tf_taskstub_t));
|
||||
@ -200,11 +217,13 @@ tf_taskstub_t* tf_taskstub_create_parent(tf_task_t* task, uv_file file) {
|
||||
parentStub->_id = k_task_parent_id;
|
||||
parentStub->_object = JS_DupValue(tf_task_get_context(task), parentObject);
|
||||
|
||||
if (uv_pipe_init(tf_task_get_loop(task), tf_packetstream_get_pipe(parentStub->_stream), 1) != 0) {
|
||||
if (uv_pipe_init(tf_task_get_loop(task), tf_packetstream_get_pipe(parentStub->_stream), 1) != 0)
|
||||
{
|
||||
fprintf(stderr, "uv_pipe_init failed\n");
|
||||
}
|
||||
tf_packetstream_set_on_receive(parentStub->_stream, tf_task_on_receive_packet, parentStub);
|
||||
if (uv_pipe_open(tf_packetstream_get_pipe(parentStub->_stream), file) != 0) {
|
||||
if (uv_pipe_open(tf_packetstream_get_pipe(parentStub->_stream), file) != 0)
|
||||
{
|
||||
fprintf(stderr, "uv_pipe_open failed\n");
|
||||
}
|
||||
tf_packetstream_start(parentStub->_stream);
|
||||
@ -216,12 +235,14 @@ static void _taskstub_cleanup(tf_taskstub_t* stub)
|
||||
{
|
||||
if (!stub->_process.data &&
|
||||
JS_IsUndefined(stub->_object) &&
|
||||
stub->_finalized) {
|
||||
stub->_finalized)
|
||||
{
|
||||
free(stub);
|
||||
}
|
||||
}
|
||||
|
||||
static void _taskstub_finalizer(JSRuntime* runtime, JSValue value) {
|
||||
static void _taskstub_finalizer(JSRuntime* runtime, JSValue value)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(value, _classId);
|
||||
stub->_on_exit = JS_UNDEFINED;
|
||||
stub->_on_error = JS_UNDEFINED;
|
||||
@ -240,10 +261,12 @@ static void _taskstub_on_handle_close(uv_handle_t* handle)
|
||||
_taskstub_cleanup(stub);
|
||||
}
|
||||
|
||||
static void _taskstub_on_process_exit(uv_process_t* process, int64_t status, int terminationSignal) {
|
||||
static void _taskstub_on_process_exit(uv_process_t* process, int64_t status, int terminationSignal)
|
||||
{
|
||||
tf_taskstub_t* stub = process->data;
|
||||
JSContext* context = tf_task_get_context(stub->_owner);
|
||||
if (!JS_IsUndefined(stub->_on_exit)) {
|
||||
if (!JS_IsUndefined(stub->_on_exit))
|
||||
{
|
||||
JSValue argv[] = { JS_NewInt32(context, status), JS_NewInt32(context, terminationSignal) };
|
||||
JSValue result = JS_Call(context, stub->_on_exit, JS_NULL, 2, argv);
|
||||
tf_task_report_error(stub->_owner, result);
|
||||
@ -257,14 +280,16 @@ static void _taskstub_on_process_exit(uv_process_t* process, int64_t status, int
|
||||
tf_taskstub_destroy(stub);
|
||||
}
|
||||
|
||||
static JSValue _taskstub_getExports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_getExports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
promiseid_t promise = tf_task_allocate_promise(stub->_owner);
|
||||
tf_task_send_promise_message(stub->_owner, (tf_taskstub_t*)stub, kGetExports, promise, JS_UNDEFINED);
|
||||
return tf_task_get_promise(stub->_owner, promise);
|
||||
}
|
||||
|
||||
static JSValue _taskstub_setImports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_setImports(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
void* buffer;
|
||||
size_t size;
|
||||
@ -273,7 +298,8 @@ static JSValue _taskstub_setImports(JSContext* context, JSValueConst this_val, i
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _taskstub_setRequires(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_setRequires(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
void* buffer;
|
||||
size_t size;
|
||||
@ -282,7 +308,8 @@ static JSValue _taskstub_setRequires(JSContext* context, JSValueConst this_val,
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _taskstub_loadFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_loadFile(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
void* buffer;
|
||||
size_t size;
|
||||
@ -291,57 +318,69 @@ static JSValue _taskstub_loadFile(JSContext* context, JSValueConst this_val, int
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _taskstub_get_on_exit(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_get_on_exit(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
return JS_DupValue(context, stub->_on_exit);
|
||||
}
|
||||
|
||||
static JSValue _taskstub_set_on_exit(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_set_on_exit(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
if (!JS_IsUndefined(stub->_on_exit)) {
|
||||
if (!JS_IsUndefined(stub->_on_exit))
|
||||
{
|
||||
JS_FreeValue(context, stub->_on_exit);
|
||||
}
|
||||
stub->_on_exit = JS_DupValue(context, argv[0]);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _taskstub_get_on_error(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_get_on_error(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
return JS_DupValue(context, stub->_on_error);
|
||||
}
|
||||
|
||||
static JSValue _taskstub_set_on_error(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_set_on_error(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
if (!JS_IsUndefined(stub->_on_error)) {
|
||||
if (!JS_IsUndefined(stub->_on_error))
|
||||
{
|
||||
JS_FreeValue(context, stub->_on_error);
|
||||
}
|
||||
stub->_on_error = JS_DupValue(context, argv[0]);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
static JSValue _taskstub_activate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_activate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
if (stub) {
|
||||
if (stub)
|
||||
{
|
||||
tf_packetstream_send(stub->_stream, kActivate, 0, 0);
|
||||
}
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
static JSValue _taskstub_execute(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_execute(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
promiseid_t promise = tf_task_allocate_promise(stub->_owner);
|
||||
tf_task_send_promise_message(stub->_owner, (tf_taskstub_t*)stub, kExecute, promise, argv[0]);
|
||||
return tf_task_get_promise(stub->_owner, promise);
|
||||
}
|
||||
|
||||
static JSValue _taskstub_kill(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
static JSValue _taskstub_kill(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_taskstub_t* stub = JS_GetOpaque(this_val, _classId);
|
||||
uv_process_kill(&stub->_process, SIGTERM);
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
void tf_taskstub_destroy(tf_taskstub_t* stub) {
|
||||
if (!JS_IsUndefined(stub->_object)) {
|
||||
void tf_taskstub_destroy(tf_taskstub_t* stub)
|
||||
{
|
||||
if (!JS_IsUndefined(stub->_object))
|
||||
{
|
||||
JSValue object = stub->_object;
|
||||
stub->_object = JS_UNDEFINED;
|
||||
JS_FreeValue(tf_task_get_context(stub->_owner), object);
|
||||
@ -351,7 +390,8 @@ void tf_taskstub_destroy(tf_taskstub_t* stub) {
|
||||
void tf_taskstub_on_error(tf_taskstub_t* stub, JSValue error)
|
||||
{
|
||||
JSContext* context = tf_task_get_context(stub->_owner);
|
||||
if (!JS_IsUndefined(stub->_on_error)) {
|
||||
if (!JS_IsUndefined(stub->_on_error))
|
||||
{
|
||||
JSValue result = JS_Call(context, stub->_on_error, JS_NULL, 1, &error);
|
||||
tf_task_report_error(stub->_owner, result);
|
||||
JS_FreeValue(context, result);
|
||||
|
12
src/tests.c
12
src/tests.c
@ -513,12 +513,15 @@ static void _test_file(const tf_test_options_t* options)
|
||||
static void _tf_test_run(const tf_test_options_t* options, const char* name, void (*test)(const tf_test_options_t* options))
|
||||
{
|
||||
bool specified = false;
|
||||
if (options->tests) {
|
||||
if (options->tests)
|
||||
{
|
||||
char* dup = strdup(options->tests);
|
||||
char* state = NULL;
|
||||
const char* t = NULL;
|
||||
while ((t = strtok_r(t ? NULL : dup, ",", &state)) != NULL) {
|
||||
if (strcmp(t, name) == 0) {
|
||||
while ((t = strtok_r(t ? NULL : dup, ",", &state)) != NULL)
|
||||
{
|
||||
if (strcmp(t, name) == 0)
|
||||
{
|
||||
specified = true;
|
||||
break;
|
||||
}
|
||||
@ -526,7 +529,8 @@ static void _tf_test_run(const tf_test_options_t* options, const char* name, voi
|
||||
free(dup);
|
||||
}
|
||||
|
||||
if (!options->tests || specified) {
|
||||
if (!options->tests || specified)
|
||||
{
|
||||
printf("Running test %s.\n", name);
|
||||
test(options);
|
||||
printf("[\e[1;32mpass\e[0m] %s\n", name);
|
||||
|
@ -23,7 +23,8 @@ static JSValue _tls_context_wrapper_add_trusted_certificate(JSContext* context,
|
||||
static JSValue _tls_context_wrapper_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||
static void _tls_context_wrapper_finalizer(JSRuntime *runtime, JSValue value);
|
||||
|
||||
JSValue _tls_context_wrapper_set_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _tls_context_wrapper_set_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_tls_context_wrapper_t* wrapper = JS_GetOpaque(this_val, _classId);
|
||||
const char* value = JS_ToCString(context, argv[0]);
|
||||
tf_tls_context_set_certificate(wrapper->context, value);
|
||||
@ -31,7 +32,8 @@ JSValue _tls_context_wrapper_set_certificate(JSContext* context, JSValueConst th
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _tls_context_wrapper_set_private_key(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _tls_context_wrapper_set_private_key(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_tls_context_wrapper_t* wrapper = JS_GetOpaque(this_val, _classId);
|
||||
const char* value = JS_ToCString(context, argv[0]);
|
||||
tf_tls_context_set_private_key(wrapper->context, value);
|
||||
@ -39,7 +41,8 @@ JSValue _tls_context_wrapper_set_private_key(JSContext* context, JSValueConst th
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue _tls_context_wrapper_add_trusted_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _tls_context_wrapper_add_trusted_certificate(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_tls_context_wrapper_t* wrapper = JS_GetOpaque(this_val, _classId);
|
||||
const char* value = JS_ToCString(context, argv[0]);
|
||||
tf_tls_context_add_trusted_certificate(wrapper->context, value);
|
||||
@ -47,28 +50,34 @@ JSValue _tls_context_wrapper_add_trusted_certificate(JSContext* context, JSValue
|
||||
return JS_UNDEFINED;
|
||||
}
|
||||
|
||||
JSValue tf_tls_context_wrapper_init(JSContext* context) {
|
||||
JSValue tf_tls_context_wrapper_init(JSContext* context)
|
||||
{
|
||||
JS_NewClassID(&_classId);
|
||||
JSClassDef def = {
|
||||
JSClassDef def =
|
||||
{
|
||||
.class_name = "TlsContext",
|
||||
.finalizer = _tls_context_wrapper_finalizer,
|
||||
};
|
||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0) {
|
||||
if (JS_NewClass(JS_GetRuntime(context), _classId, &def) != 0)
|
||||
{
|
||||
fprintf(stderr, "Failed to register TlsContext.\n");
|
||||
}
|
||||
return JS_NewCFunction2(context, _tls_context_wrapper_create, "TlsContext", 0, JS_CFUNC_constructor, 0);
|
||||
}
|
||||
|
||||
tf_tls_context_t* tf_tls_context_wrapper_get(JSValue value) {
|
||||
tf_tls_context_t* tf_tls_context_wrapper_get(JSValue value)
|
||||
{
|
||||
tf_tls_context_wrapper_t* wrapper = JS_GetOpaque(value, _classId);
|
||||
return wrapper ? wrapper->context : NULL;
|
||||
}
|
||||
|
||||
int tf_tls_context_wrapper_get_count() {
|
||||
int tf_tls_context_wrapper_get_count()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
JSValue _tls_context_wrapper_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
||||
JSValue _tls_context_wrapper_create(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||
{
|
||||
tf_tls_context_wrapper_t* wrapper = malloc(sizeof(tf_tls_context_wrapper_t));
|
||||
memset(wrapper, 0, sizeof(*wrapper));
|
||||
|
||||
@ -86,9 +95,11 @@ JSValue _tls_context_wrapper_create(JSContext* context, JSValueConst this_val, i
|
||||
return wrapper->object;
|
||||
}
|
||||
|
||||
void _tls_context_wrapper_finalizer(JSRuntime *runtime, JSValue value) {
|
||||
void _tls_context_wrapper_finalizer(JSRuntime *runtime, JSValue value)
|
||||
{
|
||||
tf_tls_context_wrapper_t* wrapper = JS_GetOpaque(value, _classId);
|
||||
if (wrapper->context) {
|
||||
if (wrapper->context)
|
||||
{
|
||||
tf_tls_context_destroy(wrapper->context);
|
||||
wrapper->context = NULL;
|
||||
}
|
||||
|
49
src/trace.c
49
src/trace.c
@ -34,7 +34,8 @@ static int64_t _trace_ts()
|
||||
{
|
||||
int64_t result = 0;
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
|
||||
{
|
||||
result = (ts.tv_sec * 1e9 + ts.tv_nsec) / 1e3;
|
||||
}
|
||||
return result;
|
||||
@ -42,12 +43,14 @@ static int64_t _trace_ts()
|
||||
|
||||
static void _trace_append(tf_trace_t* trace, const char* buffer, size_t size)
|
||||
{
|
||||
if (trace->write_offset + size >= k_buffer_size) {
|
||||
if (trace->write_offset + size >= k_buffer_size)
|
||||
{
|
||||
trace->buffer[trace->write_offset] = '\0';
|
||||
trace->write_offset = 0;
|
||||
}
|
||||
|
||||
if (trace->write_offset + size < k_buffer_size) {
|
||||
if (trace->write_offset + size < k_buffer_size)
|
||||
{
|
||||
memcpy(trace->buffer + trace->write_offset, buffer, size);
|
||||
trace->write_offset += size;
|
||||
trace->buffer[trace->write_offset++] = '\n';
|
||||
@ -56,7 +59,8 @@ static void _trace_append(tf_trace_t* trace, const char* buffer, size_t size)
|
||||
|
||||
void tf_trace_counter(tf_trace_t* trace, const char* name, int argc, const char** arg_names, const int64_t* arg_values)
|
||||
{
|
||||
if (!trace) {
|
||||
if (!trace)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -73,18 +77,22 @@ void tf_trace_counter(tf_trace_t* trace, const char* name, int argc, const char*
|
||||
|
||||
void tf_trace_begin(tf_trace_t* trace, const char* name)
|
||||
{
|
||||
if (!trace) {
|
||||
if (!trace)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char line[1024];
|
||||
int p = snprintf(line, sizeof(line), "{\"ph\": \"B\", \"pid\": %d, \"ts\": %" PRId64 ", \"name\": \"", getpid(), _trace_ts());
|
||||
for (const char* c = name; *c && p < (int)sizeof(line); c++) {
|
||||
switch (*c) {
|
||||
for (const char* c = name; *c && p < (int)sizeof(line); c++)
|
||||
{
|
||||
switch (*c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
line[p++] = '\\';
|
||||
if (p < (int)sizeof(line)) {
|
||||
if (p < (int)sizeof(line))
|
||||
{
|
||||
line[p++] = *c;
|
||||
}
|
||||
break;
|
||||
@ -99,7 +107,8 @@ void tf_trace_begin(tf_trace_t* trace, const char* name)
|
||||
|
||||
void tf_trace_end(tf_trace_t* trace)
|
||||
{
|
||||
if (!trace) {
|
||||
if (!trace)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@ -110,7 +119,8 @@ void tf_trace_end(tf_trace_t* trace)
|
||||
|
||||
char* tf_trace_export(tf_trace_t* trace)
|
||||
{
|
||||
if (!trace) {
|
||||
if (!trace)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -120,14 +130,16 @@ char* tf_trace_export(tf_trace_t* trace)
|
||||
int begin = newline ? newline - trace->buffer : 0;
|
||||
size_t size = 0;
|
||||
size += snprintf(buffer, k_buffer_size, "{\"displayTimeUnit\": \"ns\",\n\"traceEvents\": [\n");
|
||||
if (begin) {
|
||||
if (begin)
|
||||
{
|
||||
size_t this_size = strlen(trace->buffer + begin);
|
||||
memcpy(buffer + size, trace->buffer + begin, this_size);
|
||||
size += this_size;
|
||||
}
|
||||
memcpy(buffer + size, trace->buffer, trace->write_offset);
|
||||
size += trace->write_offset;
|
||||
if (size > 2 && buffer[size - 1] == '\n' && buffer[size - 2] == ',') {
|
||||
if (size > 2 && buffer[size - 1] == '\n' && buffer[size - 2] == ',')
|
||||
{
|
||||
buffer[size - 2] = '\n';
|
||||
size--;
|
||||
}
|
||||
@ -140,11 +152,13 @@ char* tf_trace_export(tf_trace_t* trace)
|
||||
static int _tf_trace_sqlite_callback(unsigned int t, void* c, void* p, void* x)
|
||||
{
|
||||
tf_trace_t* trace = c;
|
||||
switch (t) {
|
||||
switch (t)
|
||||
{
|
||||
case SQLITE_TRACE_STMT:
|
||||
{
|
||||
const char* statement = x;
|
||||
if (statement[0] != '-' || statement[1] != '-') {
|
||||
if (statement[0] != '-' || statement[1] != '-')
|
||||
{
|
||||
tf_trace_begin(trace, statement);
|
||||
}
|
||||
}
|
||||
@ -158,9 +172,12 @@ static int _tf_trace_sqlite_callback(unsigned int t, void* c, void* p, void* x)
|
||||
|
||||
void tf_trace_sqlite(tf_trace_t* trace, sqlite3* sqlite)
|
||||
{
|
||||
if (sqlite) {
|
||||
if (sqlite)
|
||||
{
|
||||
sqlite3_trace_v2(sqlite, SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE, _tf_trace_sqlite_callback, trace);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_trace_v2(sqlite, 0, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user