forked from cory/tildefriends
Made File.readFile async.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3667 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
24a91219c1
commit
470814f147
@ -120,7 +120,8 @@ function authHandler(request, response) {
|
|||||||
response.writeHead(303, {"Location": formData.return, "Set-Cookie": cookie});
|
response.writeHead(303, {"Location": formData.return, "Set-Cookie": cookie});
|
||||||
response.end();
|
response.end();
|
||||||
} else {
|
} else {
|
||||||
var html = new TextDecoder("UTF-8").decode(File.readFile("core/auth.html"));
|
File.readFile("core/auth.html").then(function(data) {
|
||||||
|
var html = new TextDecoder("UTF-8").decode(data);
|
||||||
var contents = "";
|
var contents = "";
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
@ -157,6 +158,10 @@ function authHandler(request, response) {
|
|||||||
var text = html.replace("<!--SESSION-->", contents);
|
var text = html.replace("<!--SESSION-->", contents);
|
||||||
response.writeHead(200, {"Content-Type": "text/html; charset=utf-8", "Set-Cookie": cookie, "Content-Length": text.length});
|
response.writeHead(200, {"Content-Type": "text/html; charset=utf-8", "Set-Cookie": cookie, "Content-Length": text.length});
|
||||||
response.end(text);
|
response.end(text);
|
||||||
|
}).catch(function(error) {
|
||||||
|
response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
|
||||||
|
response.end("404 File not found");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (request.uri == "/login/logout") {
|
} else if (request.uri == "/login/logout") {
|
||||||
removeSession(session);
|
removeSession(session);
|
||||||
|
66
core/core.js
66
core/core.js
@ -138,48 +138,14 @@ async function getSessionProcessBlob(blobId, session, options) {
|
|||||||
return getProcessBlob(blobId, 'session_' + session, actualOptions);
|
return getProcessBlob(blobId, 'session_' + session, actualOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readFileUtf8(fileName) {
|
async function readFileUtf8(fileName) {
|
||||||
let data = File.readFile(fileName);
|
let data = await File.readFile(fileName);
|
||||||
data = utf8Decode(data);
|
data = utf8Decode(data);
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
let gManifestCache = {};
|
let gManifestCache = {};
|
||||||
|
|
||||||
async function getManifest(fileName) {
|
|
||||||
let oldEntry = gManifestCache[fileName];
|
|
||||||
let stat = await File.stat(fileName);
|
|
||||||
if (oldEntry) {
|
|
||||||
if (oldEntry.stat.mtime == stat.mtime && oldEntry.stat.size == stat.size) {
|
|
||||||
return oldEntry.manifest;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let manifest = [];
|
|
||||||
let lines = readFileUtf8(fileName).split("\n").map(x => x.trimRight());
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
|
||||||
if (lines[i].substring(0, 4) == "//! ") {
|
|
||||||
manifest.push(lines[i].substring(4));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let result;
|
|
||||||
try {
|
|
||||||
if (manifest.length) {
|
|
||||||
result = JSON.parse(manifest.join("\n"));
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
print("ERROR: getManifest(" + fileName + "): ", error);
|
|
||||||
// Oh well. No manifest.
|
|
||||||
}
|
|
||||||
|
|
||||||
gManifestCache[fileName] = {
|
|
||||||
stat: stat,
|
|
||||||
manifest: result,
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getProcessBlob(blobId, key, options) {
|
async function getProcessBlob(blobId, key, options) {
|
||||||
var process = gProcesses[key];
|
var process = gProcesses[key];
|
||||||
if (!process
|
if (!process
|
||||||
@ -319,15 +285,6 @@ function setGlobalSettings(settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
var data = readFileUtf8(kGlobalSettingsFile);
|
|
||||||
if (data) {
|
|
||||||
gGlobalSettings = JSON.parse(data);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
print("Error loading settings from " + kGlobalSettingsFile + ": " + error);
|
|
||||||
}
|
|
||||||
|
|
||||||
var kStaticFiles = [
|
var kStaticFiles = [
|
||||||
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
|
{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
|
||||||
{uri: '/style.css', path: 'style.css', type: 'text/css; charset=UTF-8'},
|
{uri: '/style.css', path: 'style.css', type: 'text/css; charset=UTF-8'},
|
||||||
@ -351,7 +308,7 @@ function startsWithBytes(data, bytes) {
|
|||||||
async function staticFileHandler(request, response, blobId, uri) {
|
async function staticFileHandler(request, response, blobId, uri) {
|
||||||
for (var i in kStaticFiles) {
|
for (var i in kStaticFiles) {
|
||||||
if (uri === kStaticFiles[i].uri) {
|
if (uri === kStaticFiles[i].uri) {
|
||||||
var data = File.readFile("core/" + kStaticFiles[i].path);
|
var data = await File.readFile("core/" + kStaticFiles[i].path);
|
||||||
response.writeHead(200, {"Content-Type": kStaticFiles[i].type, "Content-Length": data.byteLength});
|
response.writeHead(200, {"Content-Type": kStaticFiles[i].type, "Content-Length": data.byteLength});
|
||||||
response.end(data);
|
response.end(data);
|
||||||
return;
|
return;
|
||||||
@ -403,7 +360,7 @@ async function blobHandler(request, response, blobId, uri) {
|
|||||||
for (var i in kStaticFiles) {
|
for (var i in kStaticFiles) {
|
||||||
if (uri === kStaticFiles[i].uri) {
|
if (uri === kStaticFiles[i].uri) {
|
||||||
found = true;
|
found = true;
|
||||||
var data = File.readFile("core/" + kStaticFiles[i].path);
|
var data = await File.readFile("core/" + kStaticFiles[i].path);
|
||||||
response.writeHead(200, {"Content-Type": kStaticFiles[i].type, "Content-Length": data.byteLength});
|
response.writeHead(200, {"Content-Type": kStaticFiles[i].type, "Content-Length": data.byteLength});
|
||||||
response.end(data);
|
response.end(data);
|
||||||
break;
|
break;
|
||||||
@ -483,6 +440,18 @@ ssb.onConnectionsChanged = function() {
|
|||||||
broadcastEvent('onConnectionsChanged', []);
|
broadcastEvent('onConnectionsChanged', []);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function loadSettings() {
|
||||||
|
try {
|
||||||
|
var data = await readFileUtf8(kGlobalSettingsFile);
|
||||||
|
if (data) {
|
||||||
|
gGlobalSettings = JSON.parse(data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
print("Error loading settings from " + kGlobalSettingsFile + ": " + error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadSettings().then(function() {
|
||||||
var auth = require("auth");
|
var auth = require("auth");
|
||||||
var httpd = require("httpd");
|
var httpd = require("httpd");
|
||||||
httpd.all("/login", auth.handler);
|
httpd.all("/login", auth.handler);
|
||||||
@ -521,3 +490,6 @@ httpd.all("", function(request, response) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
httpd.registerSocketHandler("/app/socket", app.socket);
|
httpd.registerSocketHandler("/app/socket", app.socket);
|
||||||
|
}).catch(function(error) {
|
||||||
|
print('Failed to load settings.');
|
||||||
|
});
|
||||||
|
93
src/file.c
93
src/file.c
@ -45,41 +45,84 @@ void tf_file_init(JSContext* context) {
|
|||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _free_array_buffer_data(JSRuntime* runtime, void* opaque, void* ptr) {
|
static const int k_file_read_max = 4 * 1024 * 1024;
|
||||||
free(ptr);
|
|
||||||
|
static void _file_read_close_callback(uv_fs_t* req)
|
||||||
|
{
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
|
free(req);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) {
|
static void _file_read_read_callback(uv_fs_t* req)
|
||||||
const char* file_name = JS_ToCString(context, argv[0]);
|
{
|
||||||
FILE* file = fopen(file_name, "rb");
|
uv_fs_req_cleanup(req);
|
||||||
JS_FreeCString(context, file_name);
|
tf_task_t* task = req->loop->data;
|
||||||
if (!file) {
|
JSContext* context = tf_task_get_context(task);
|
||||||
return JS_NULL;
|
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
|
||||||
}
|
if (req->result >= 0)
|
||||||
|
{
|
||||||
long size = 0;
|
JSValue arrayBuffer = JS_NewArrayBufferCopy(context, (uint8_t*)(req + 1), req->result);
|
||||||
if (fseek(file, 0, SEEK_END) == 0) {
|
|
||||||
size = ftell(file);
|
|
||||||
}
|
|
||||||
if (size >= 0 &&
|
|
||||||
size < 4 * 1024 * 1024 &&
|
|
||||||
fseek(file, 0, SEEK_SET) == 0) {
|
|
||||||
uint8_t* data = malloc(size);
|
|
||||||
if (data &&
|
|
||||||
fread(data, 1, size, file) == (size_t)size) {
|
|
||||||
JSValue arrayBuffer = JS_NewArrayBuffer(context, data, size, _free_array_buffer_data, NULL, false);
|
|
||||||
JSValue global = JS_GetGlobalObject(context);
|
JSValue global = JS_GetGlobalObject(context);
|
||||||
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
|
JSValue constructor = JS_GetPropertyStr(context, global, "Uint8Array");
|
||||||
JSValue typedArray = JS_CallConstructor(context, constructor, 1, &arrayBuffer);
|
JSValue typedArray = JS_CallConstructor(context, constructor, 1, &arrayBuffer);
|
||||||
JS_FreeValue(context, constructor);
|
JS_FreeValue(context, constructor);
|
||||||
JS_FreeValue(context, global);
|
JS_FreeValue(context, global);
|
||||||
JS_FreeValue(context, arrayBuffer);
|
JS_FreeValue(context, arrayBuffer);
|
||||||
return typedArray;
|
tf_task_resolve_promise(task, promise, typedArray);
|
||||||
} else if (data) {
|
JS_FreeValue(context, typedArray);
|
||||||
free(data);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
|
||||||
|
}
|
||||||
|
int result = uv_fs_close(req->loop, req, req->file, _file_read_close_callback);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
free(req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return JS_NULL;
|
|
||||||
|
static void _file_read_open_callback(uv_fs_t* req)
|
||||||
|
{
|
||||||
|
uv_fs_req_cleanup(req);
|
||||||
|
tf_task_t* task = req->loop->data;
|
||||||
|
JSContext* context = tf_task_get_context(task);
|
||||||
|
promiseid_t promise = (promiseid_t)(intptr_t)req->data;
|
||||||
|
if (req->result >= 0)
|
||||||
|
{
|
||||||
|
uv_buf_t buf = { .base = (char*)(req + 1), .len = k_file_read_max };
|
||||||
|
uv_file file = req->result;
|
||||||
|
int result = uv_fs_read(req->loop, req, file, &buf, 1, 0, _file_read_read_callback);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
|
||||||
|
result = uv_fs_close(req->loop, req, file, _file_read_close_callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(req->result)));
|
||||||
|
free(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
|
||||||
|
promiseid_t promise = tf_task_allocate_promise(task);
|
||||||
|
uv_fs_t* req = malloc(sizeof(uv_fs_t) + k_file_read_max);
|
||||||
|
*req = (uv_fs_t)
|
||||||
|
{
|
||||||
|
.data = (void*)(intptr_t)promise,
|
||||||
|
};
|
||||||
|
int result = uv_fs_open(tf_task_get_loop(task), req, file_name, UV_FS_O_RDONLY, 0, _file_read_open_callback);
|
||||||
|
if (result < 0)
|
||||||
|
{
|
||||||
|
tf_task_reject_promise(task, promise, JS_ThrowInternalError(context, uv_strerror(result)));
|
||||||
|
}
|
||||||
|
JS_FreeCString(context, file_name);
|
||||||
|
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) {
|
||||||
|
@ -1142,6 +1142,7 @@ tf_task_t* tf_task_create() {
|
|||||||
tf_task_t* task = malloc(sizeof(tf_task_t));
|
tf_task_t* task = malloc(sizeof(tf_task_t));
|
||||||
*task = (tf_task_t) { 0 };
|
*task = (tf_task_t) { 0 };
|
||||||
task->_loop = uv_loop_new();
|
task->_loop = uv_loop_new();
|
||||||
|
task->_loop->data = task;
|
||||||
++_count;
|
++_count;
|
||||||
task->_runtime = JS_NewRuntime();
|
task->_runtime = JS_NewRuntime();
|
||||||
task->_context = JS_NewContext(task->_runtime);
|
task->_context = JS_NewContext(task->_runtime);
|
||||||
|
31
src/tests.c
31
src/tests.c
@ -480,6 +480,36 @@ static void _test_socket(const tf_test_options_t* options)
|
|||||||
unlink("out/test.js");
|
unlink("out/test.js");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _test_file(const tf_test_options_t* options)
|
||||||
|
{
|
||||||
|
FILE* file = fopen("out/test.js", "w");
|
||||||
|
fprintf(file,
|
||||||
|
"'use strict';\n"
|
||||||
|
"File.readFile('out/test.js').then(function(data) {\n"
|
||||||
|
" print('READ', utf8Decode(data));\n"
|
||||||
|
"}).catch(function(error) {\n"
|
||||||
|
" print('ERROR', error);\n"
|
||||||
|
" exit(1);\n"
|
||||||
|
"});\n"
|
||||||
|
"File.readFile('out/missing.txt').then(function(data) {\n"
|
||||||
|
" print('READ', utf8Decode(data));\n"
|
||||||
|
" exit(1);\n"
|
||||||
|
"}).catch(function(error) {\n"
|
||||||
|
" print('expected error', error);\n"
|
||||||
|
"});\n");
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
char command[256];
|
||||||
|
snprintf(command, sizeof(command), "%s run --ssb-port=0 -s out/test.js", options->exe_path);
|
||||||
|
printf("%s\n", command);
|
||||||
|
int result = system(command);
|
||||||
|
printf("returned %d\n", WEXITSTATUS(result));
|
||||||
|
assert(WIFEXITED(result));
|
||||||
|
assert(WEXITSTATUS(result) == 0);
|
||||||
|
|
||||||
|
unlink("out/test.js");
|
||||||
|
}
|
||||||
|
|
||||||
static void _tf_test_run(const tf_test_options_t* options, const char* name, void (*test)(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;
|
bool specified = false;
|
||||||
@ -521,5 +551,6 @@ void tf_tests(const tf_test_options_t* options)
|
|||||||
_tf_test_run(options, "icu", _test_icu);
|
_tf_test_run(options, "icu", _test_icu);
|
||||||
_tf_test_run(options, "uint8array", _test_uint8array);
|
_tf_test_run(options, "uint8array", _test_uint8array);
|
||||||
_tf_test_run(options, "socket", _test_socket);
|
_tf_test_run(options, "socket", _test_socket);
|
||||||
|
_tf_test_run(options, "file", _test_file);
|
||||||
printf("Tests completed.\n");
|
printf("Tests completed.\n");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user