http: Bring back handler.js support, mostly. Partly in C, this time.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m25s
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 16m25s
This commit is contained in:
parent
5a765e6f07
commit
a84f850e91
67
core/core.js
67
core/core.js
@ -860,6 +860,73 @@ function sendStats() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let g_handler_index = 0;
|
||||||
|
|
||||||
|
exports.callAppHandler = async function callAppHandler(
|
||||||
|
response,
|
||||||
|
app_blob_id,
|
||||||
|
path,
|
||||||
|
query,
|
||||||
|
headers,
|
||||||
|
package_owner,
|
||||||
|
package_name
|
||||||
|
) {
|
||||||
|
let answer;
|
||||||
|
try {
|
||||||
|
let do_resolve;
|
||||||
|
let promise = new Promise(async function (resolve, reject) {
|
||||||
|
do_resolve = resolve;
|
||||||
|
});
|
||||||
|
let process;
|
||||||
|
try {
|
||||||
|
process = await getProcessBlob(
|
||||||
|
app_blob_id,
|
||||||
|
'handler_' + g_handler_index++,
|
||||||
|
{
|
||||||
|
script: 'handler.js',
|
||||||
|
imports: {
|
||||||
|
request: {
|
||||||
|
path: path,
|
||||||
|
query: query,
|
||||||
|
},
|
||||||
|
respond: do_resolve,
|
||||||
|
},
|
||||||
|
credentials: await httpd.auth_query(headers),
|
||||||
|
packageOwner: package_owner,
|
||||||
|
packageName: package_name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
await process.ready;
|
||||||
|
answer = await promise;
|
||||||
|
} finally {
|
||||||
|
if (process?.task) {
|
||||||
|
await process.task.kill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
let data = utf8Encode(
|
||||||
|
`Internal Server Error\n\n${error?.message}\n${error?.stack}`
|
||||||
|
);
|
||||||
|
response.writeHead(500, {
|
||||||
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
|
'Content-Length': data.length,
|
||||||
|
});
|
||||||
|
response.end(data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (typeof answer?.data == 'string') {
|
||||||
|
answer.data = utf8Encode(answer.data);
|
||||||
|
}
|
||||||
|
response.writeHead(answer?.status_code, {
|
||||||
|
'Content-Type': answer?.content_type,
|
||||||
|
'Content-Length': answer?.data?.length,
|
||||||
|
'Access-Control-Allow-Origin': '*',
|
||||||
|
'Content-Security-Policy':
|
||||||
|
'sandbox allow-downloads allow-top-navigation-by-user-activation',
|
||||||
|
});
|
||||||
|
response.end(answer?.data);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODOC
|
* TODOC
|
||||||
*/
|
*/
|
||||||
|
123
src/httpd.js.c
123
src/httpd.js.c
@ -224,6 +224,17 @@ static void _httpd_message_callback(tf_http_request_t* request, int op_code, con
|
|||||||
JS_FreeValue(context, on_message);
|
JS_FreeValue(context, on_message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSValue _httpd_make_response_object(JSContext* context, tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
JSValue response_object = JS_NewObjectClass(context, _httpd_request_class_id);
|
||||||
|
JS_SetOpaque(response_object, request);
|
||||||
|
JS_SetPropertyStr(context, response_object, "writeHead", JS_NewCFunction(context, _httpd_response_write_head, "writeHead", 2));
|
||||||
|
JS_SetPropertyStr(context, response_object, "end", JS_NewCFunction(context, _httpd_response_end, "end", 1));
|
||||||
|
JS_SetPropertyStr(context, response_object, "send", JS_NewCFunction(context, _httpd_response_send, "send", 2));
|
||||||
|
JS_SetPropertyStr(context, response_object, "upgrade", JS_NewCFunction(context, _httpd_websocket_upgrade, "upgrade", 2));
|
||||||
|
return response_object;
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_callback_internal(tf_http_request_t* request, bool is_websocket)
|
static void _httpd_callback_internal(tf_http_request_t* request, bool is_websocket)
|
||||||
{
|
{
|
||||||
http_handler_data_t* data = request->user_data;
|
http_handler_data_t* data = request->user_data;
|
||||||
@ -250,14 +261,9 @@ static void _httpd_callback_internal(tf_http_request_t* request, bool is_websock
|
|||||||
JS_SetPropertyStr(context, client, "tls", request->is_tls ? JS_TRUE : JS_FALSE);
|
JS_SetPropertyStr(context, client, "tls", request->is_tls ? JS_TRUE : JS_FALSE);
|
||||||
JS_SetPropertyStr(context, request_object, "client", client);
|
JS_SetPropertyStr(context, request_object, "client", client);
|
||||||
|
|
||||||
JSValue response_object = JS_NewObjectClass(context, _httpd_request_class_id);
|
JSValue response_object = _httpd_make_response_object(context, request);
|
||||||
/* The ref is owned by the JS object and will be released by the finalizer. */
|
/* The ref is owned by the JS object and will be released by the finalizer. */
|
||||||
tf_http_request_ref(request);
|
tf_http_request_ref(request);
|
||||||
JS_SetOpaque(response_object, request);
|
|
||||||
JS_SetPropertyStr(context, response_object, "writeHead", JS_NewCFunction(context, _httpd_response_write_head, "writeHead", 2));
|
|
||||||
JS_SetPropertyStr(context, response_object, "end", JS_NewCFunction(context, _httpd_response_end, "end", 1));
|
|
||||||
JS_SetPropertyStr(context, response_object, "send", JS_NewCFunction(context, _httpd_response_send, "send", 2));
|
|
||||||
JS_SetPropertyStr(context, response_object, "upgrade", JS_NewCFunction(context, _httpd_websocket_upgrade, "upgrade", 2));
|
|
||||||
JSValue args[] = {
|
JSValue args[] = {
|
||||||
request_object,
|
request_object,
|
||||||
response_object,
|
response_object,
|
||||||
@ -1024,8 +1030,12 @@ typedef struct _app_blob_t
|
|||||||
tf_http_request_t* request;
|
tf_http_request_t* request;
|
||||||
bool found;
|
bool found;
|
||||||
bool not_modified;
|
bool not_modified;
|
||||||
|
bool use_handler;
|
||||||
void* data;
|
void* data;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
char app_blob_id[k_blob_id_len];
|
||||||
|
const char* file;
|
||||||
|
user_app_t* user_app;
|
||||||
char etag[256];
|
char etag[256];
|
||||||
} app_blob_t;
|
} app_blob_t;
|
||||||
|
|
||||||
@ -1033,8 +1043,6 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
{
|
{
|
||||||
app_blob_t* data = user_data;
|
app_blob_t* data = user_data;
|
||||||
tf_http_request_t* request = data->request;
|
tf_http_request_t* request = data->request;
|
||||||
char app_id[256] = "";
|
|
||||||
const char* file = NULL;
|
|
||||||
if (request->path[0] == '/' && request->path[1] == '~')
|
if (request->path[0] == '/' && request->path[1] == '~')
|
||||||
{
|
{
|
||||||
const char* last_slash = strchr(request->path + 1, '/');
|
const char* last_slash = strchr(request->path + 1, '/');
|
||||||
@ -1042,18 +1050,17 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
{
|
{
|
||||||
last_slash = strchr(last_slash + 1, '/');
|
last_slash = strchr(last_slash + 1, '/');
|
||||||
}
|
}
|
||||||
user_app_t* user_app = last_slash ? _parse_user_app_from_path(request->path, last_slash) : NULL;
|
data->user_app = last_slash ? _parse_user_app_from_path(request->path, last_slash) : NULL;
|
||||||
if (user_app)
|
if (data->user_app)
|
||||||
{
|
{
|
||||||
size_t path_length = strlen("path:") + strlen(user_app->app) + 1;
|
size_t path_length = strlen("path:") + strlen(data->user_app->app) + 1;
|
||||||
char* app_path = tf_malloc(path_length);
|
char* app_path = tf_malloc(path_length);
|
||||||
snprintf(app_path, path_length, "path:%s", user_app->app);
|
snprintf(app_path, path_length, "path:%s", data->user_app->app);
|
||||||
const char* value = tf_ssb_db_get_property(ssb, user_app->user, app_path);
|
const char* value = tf_ssb_db_get_property(ssb, data->user_app->user, app_path);
|
||||||
snprintf(app_id, sizeof(app_id), "%s", value);
|
snprintf(data->app_blob_id, sizeof(data->app_blob_id), "%s", value);
|
||||||
tf_free(app_path);
|
tf_free(app_path);
|
||||||
tf_free((void*)value);
|
tf_free((void*)value);
|
||||||
file = last_slash + 1;
|
data->file = last_slash + 1;
|
||||||
tf_free(user_app);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (request->path[0] == '/' && request->path[1] == '&')
|
else if (request->path[0] == '/' && request->path[1] == '&')
|
||||||
@ -1061,14 +1068,14 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
const char* end = strstr(request->path, ".sha256/");
|
const char* end = strstr(request->path, ".sha256/");
|
||||||
if (end)
|
if (end)
|
||||||
{
|
{
|
||||||
snprintf(app_id, sizeof(app_id), "%.*s", (int)(end + strlen(".sha256") - request->path - 1), request->path + 1);
|
snprintf(data->app_blob_id, sizeof(data->app_blob_id), "%.*s", (int)(end + strlen(".sha256") - request->path - 1), request->path + 1);
|
||||||
file = end + strlen(".sha256/");
|
data->file = end + strlen(".sha256/");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char* app_blob = NULL;
|
char* app_blob = NULL;
|
||||||
size_t app_blob_size = 0;
|
size_t app_blob_size = 0;
|
||||||
if (*app_id && tf_ssb_db_blob_get(ssb, app_id, (uint8_t**)&app_blob, &app_blob_size))
|
if (*data->app_blob_id && tf_ssb_db_blob_get(ssb, data->app_blob_id, (uint8_t**)&app_blob, &app_blob_size))
|
||||||
{
|
{
|
||||||
JSMallocFunctions funcs = { 0 };
|
JSMallocFunctions funcs = { 0 };
|
||||||
tf_get_js_malloc_functions(&funcs);
|
tf_get_js_malloc_functions(&funcs);
|
||||||
@ -1077,22 +1084,33 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
|
|
||||||
JSValue app_object = JS_ParseJSON(context, app_blob, app_blob_size, NULL);
|
JSValue app_object = JS_ParseJSON(context, app_blob, app_blob_size, NULL);
|
||||||
JSValue files = JS_GetPropertyStr(context, app_object, "files");
|
JSValue files = JS_GetPropertyStr(context, app_object, "files");
|
||||||
JSValue blob_id = JS_GetPropertyStr(context, files, file);
|
JSValue blob_id = JS_GetPropertyStr(context, files, data->file);
|
||||||
const char* blob_id_str = JS_ToCString(context, blob_id);
|
if (JS_IsUndefined(blob_id))
|
||||||
if (blob_id_str)
|
|
||||||
{
|
{
|
||||||
snprintf(data->etag, sizeof(data->etag), "\"%s\"", blob_id_str);
|
blob_id = JS_GetPropertyStr(context, files, "handler.js");
|
||||||
const char* match = tf_http_request_get_header(data->request, "if-none-match");
|
if (!JS_IsUndefined(blob_id))
|
||||||
if (match && strcmp(match, data->etag) == 0)
|
|
||||||
{
|
{
|
||||||
data->not_modified = true;
|
data->use_handler = true;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
data->found = tf_ssb_db_blob_get(ssb, blob_id_str, (uint8_t**)&data->data, &data->size);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
JS_FreeCString(context, blob_id_str);
|
else
|
||||||
|
{
|
||||||
|
const char* blob_id_str = JS_ToCString(context, blob_id);
|
||||||
|
if (blob_id_str)
|
||||||
|
{
|
||||||
|
snprintf(data->etag, sizeof(data->etag), "\"%s\"", blob_id_str);
|
||||||
|
const char* match = tf_http_request_get_header(data->request, "if-none-match");
|
||||||
|
if (match && strcmp(match, data->etag) == 0)
|
||||||
|
{
|
||||||
|
data->not_modified = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data->found = tf_ssb_db_blob_get(ssb, blob_id_str, (uint8_t**)&data->data, &data->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, blob_id_str);
|
||||||
|
}
|
||||||
JS_FreeValue(context, blob_id);
|
JS_FreeValue(context, blob_id);
|
||||||
JS_FreeValue(context, files);
|
JS_FreeValue(context, files);
|
||||||
JS_FreeValue(context, app_object);
|
JS_FreeValue(context, app_object);
|
||||||
@ -1103,6 +1121,44 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _httpd_call_app_handler(tf_ssb_t* ssb, tf_http_request_t* request, const char* app_blob_id, const char* path, const char* package_owner, const char* app)
|
||||||
|
{
|
||||||
|
JSContext* context = tf_ssb_get_context(ssb);
|
||||||
|
JSValue global = JS_GetGlobalObject(context);
|
||||||
|
JSValue exports = JS_GetPropertyStr(context, global, "exports");
|
||||||
|
JSValue call_app_handler = JS_GetPropertyStr(context, exports, "callAppHandler");
|
||||||
|
|
||||||
|
JSValue response = _httpd_make_response_object(context, request);
|
||||||
|
tf_http_request_ref(request);
|
||||||
|
JSValue handler_blob_id = JS_NewString(context, app_blob_id);
|
||||||
|
JSValue path_value = JS_NewString(context, path);
|
||||||
|
JSValue package_owner_value = JS_NewString(context, package_owner);
|
||||||
|
JSValue app_value = JS_NewString(context, app);
|
||||||
|
|
||||||
|
JSValue args[] = {
|
||||||
|
response,
|
||||||
|
handler_blob_id,
|
||||||
|
path_value,
|
||||||
|
JS_UNDEFINED,
|
||||||
|
JS_UNDEFINED,
|
||||||
|
package_owner_value,
|
||||||
|
app_value,
|
||||||
|
};
|
||||||
|
|
||||||
|
JSValue result = JS_Call(context, call_app_handler, JS_NULL, tf_countof(args), args);
|
||||||
|
tf_util_report_error(context, result);
|
||||||
|
JS_FreeValue(context, result);
|
||||||
|
|
||||||
|
JS_FreeValue(context, app_value);
|
||||||
|
JS_FreeValue(context, package_owner_value);
|
||||||
|
JS_FreeValue(context, handler_blob_id);
|
||||||
|
JS_FreeValue(context, path_value);
|
||||||
|
JS_FreeValue(context, response);
|
||||||
|
JS_FreeValue(context, call_app_handler);
|
||||||
|
JS_FreeValue(context, exports);
|
||||||
|
JS_FreeValue(context, global);
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_app_blob_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _httpd_endpoint_app_blob_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
{
|
{
|
||||||
app_blob_t* data = user_data;
|
app_blob_t* data = user_data;
|
||||||
@ -1110,6 +1166,10 @@ static void _httpd_endpoint_app_blob_after_work(tf_ssb_t* ssb, int status, void*
|
|||||||
{
|
{
|
||||||
tf_http_respond(data->request, 304, NULL, 0, NULL, 0);
|
tf_http_respond(data->request, 304, NULL, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
|
else if (data->use_handler)
|
||||||
|
{
|
||||||
|
_httpd_call_app_handler(ssb, data->request, data->app_blob_id, data->file, data->user_app->user, data->user_app->app);
|
||||||
|
}
|
||||||
else if (data->found)
|
else if (data->found)
|
||||||
{
|
{
|
||||||
const char* mime_type = _ext_to_content_type(strrchr(data->request->path, '.'), false);
|
const char* mime_type = _ext_to_content_type(strrchr(data->request->path, '.'), false);
|
||||||
@ -1129,6 +1189,7 @@ static void _httpd_endpoint_app_blob_after_work(tf_ssb_t* ssb, int status, void*
|
|||||||
};
|
};
|
||||||
tf_http_respond(data->request, 200, headers, tf_countof(headers) / 2, data->data, data->size);
|
tf_http_respond(data->request, 200, headers, tf_countof(headers) / 2, data->data, data->size);
|
||||||
}
|
}
|
||||||
|
tf_free(data->user_app);
|
||||||
tf_free(data->data);
|
tf_free(data->data);
|
||||||
tf_http_request_unref(data->request);
|
tf_http_request_unref(data->request);
|
||||||
tf_free(data);
|
tf_free(data);
|
||||||
|
@ -1869,7 +1869,6 @@ void tf_task_destroy(tf_task_t* task)
|
|||||||
{
|
{
|
||||||
JSValue global = JS_GetGlobalObject(task->_context);
|
JSValue global = JS_GetGlobalObject(task->_context);
|
||||||
JS_SetPropertyStr(task->_context, global, "httpd", JS_UNDEFINED);
|
JS_SetPropertyStr(task->_context, global, "httpd", JS_UNDEFINED);
|
||||||
JS_SetPropertyStr(task->_context, global, "gProcesses", JS_NewObject(task->_context));
|
|
||||||
JS_FreeValue(task->_context, global);
|
JS_FreeValue(task->_context, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user