forked from cory/tildefriends
		
	http+js: Move app blob handling from JS to C. handler.js support has been temporarily removed.
This commit is contained in:
		| @@ -1 +1,3 @@ | |||||||
| app.setDocument('<p style="color: #fff">Maybe one day this app will run tests, but for now there is nothing to see here.</p>'); | app.setDocument( | ||||||
|  | 	'<p style="color: #fff">Maybe one day this app will run tests, but for now there is nothing to see here.</p>' | ||||||
|  | ); | ||||||
|   | |||||||
							
								
								
									
										160
									
								
								core/core.js
									
									
									
									
									
								
							
							
						
						
									
										160
									
								
								core/core.js
									
									
									
									
									
								
							| @@ -851,154 +851,6 @@ function sendData(response, data, type, headers, status_code) { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| let g_handler_index = 0; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * TODOC |  | ||||||
|  * @param {*} response |  | ||||||
|  * @param {*} handler_blob_id |  | ||||||
|  * @param {*} path |  | ||||||
|  * @param {*} query |  | ||||||
|  * @param {*} headers |  | ||||||
|  * @param {*} packageOwner |  | ||||||
|  * @param {*} packageName |  | ||||||
|  * @returns |  | ||||||
|  */ |  | ||||||
| async function useAppHandler( |  | ||||||
| 	response, |  | ||||||
| 	handler_blob_id, |  | ||||||
| 	path, |  | ||||||
| 	query, |  | ||||||
| 	headers, |  | ||||||
| 	packageOwner, |  | ||||||
| 	packageName |  | ||||||
| ) { |  | ||||||
| 	print('useAppHandler', packageOwner, packageName); |  | ||||||
| 	let do_resolve; |  | ||||||
| 	let promise = new Promise(async function (resolve, reject) { |  | ||||||
| 		do_resolve = resolve; |  | ||||||
| 	}); |  | ||||||
| 	let process; |  | ||||||
| 	let result; |  | ||||||
| 	try { |  | ||||||
| 		process = await getProcessBlob( |  | ||||||
| 			handler_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: packageOwner, |  | ||||||
| 				packageName: packageName, |  | ||||||
| 			} |  | ||||||
| 		); |  | ||||||
| 		await process.ready; |  | ||||||
|  |  | ||||||
| 		result = await promise; |  | ||||||
| 	} finally { |  | ||||||
| 		if (process?.task) { |  | ||||||
| 			await process.task.kill(); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return result; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * TODOC |  | ||||||
|  * @param {*} request |  | ||||||
|  * @param {*} response |  | ||||||
|  * @param {*} blobId |  | ||||||
|  * @param {*} uri |  | ||||||
|  * @returns |  | ||||||
|  */ |  | ||||||
| async function blobHandler(request, response, blobId, uri) { |  | ||||||
| 	let process; |  | ||||||
| 	let data; |  | ||||||
| 	let match; |  | ||||||
| 	let id; |  | ||||||
| 	let app_id = blobId; |  | ||||||
| 	let packageOwner; |  | ||||||
| 	let packageName; |  | ||||||
| 	if ((match = /^\/\~(\w+)\/(\w+)$/.exec(blobId))) { |  | ||||||
| 		packageOwner = match[1]; |  | ||||||
| 		packageName = match[2]; |  | ||||||
| 		let db = new Database(match[1]); |  | ||||||
| 		app_id = await db.get('path:' + match[2]); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	let app_object = JSON.parse(utf8Decode(await ssb.blobGet(app_id))); |  | ||||||
| 	id = app_object?.files[uri.substring(1)]; |  | ||||||
| 	if (!id && app_object?.files['handler.js']) { |  | ||||||
| 		let answer; |  | ||||||
| 		try { |  | ||||||
| 			answer = await useAppHandler( |  | ||||||
| 				response, |  | ||||||
| 				app_id, |  | ||||||
| 				uri.substring(1), |  | ||||||
| 				request.query ? form.decodeForm(request.query) : undefined, |  | ||||||
| 				request.headers, |  | ||||||
| 				packageOwner, |  | ||||||
| 				packageName |  | ||||||
| 			); |  | ||||||
| 		} catch (error) { |  | ||||||
| 			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 (answer && typeof answer.data == 'string') { |  | ||||||
| 			answer.data = utf8Encode(answer.data); |  | ||||||
| 		} |  | ||||||
| 		sendData( |  | ||||||
| 			response, |  | ||||||
| 			answer?.data, |  | ||||||
| 			answer?.content_type, |  | ||||||
| 			Object.assign(answer?.headers ?? {}, { |  | ||||||
| 				'Access-Control-Allow-Origin': '*', |  | ||||||
| 				'Content-Security-Policy': k_content_security_policy, |  | ||||||
| 			}), |  | ||||||
| 			answer.status_code |  | ||||||
| 		); |  | ||||||
| 	} else if (id) { |  | ||||||
| 		if ( |  | ||||||
| 			request.headers['if-none-match'] && |  | ||||||
| 			request.headers['if-none-match'] == '"' + id + '"' |  | ||||||
| 		) { |  | ||||||
| 			let headers = { |  | ||||||
| 				'Access-Control-Allow-Origin': '*', |  | ||||||
| 				'Content-Security-Policy': k_content_security_policy, |  | ||||||
| 				'Content-Length': '0', |  | ||||||
| 			}; |  | ||||||
| 			response.writeHead(304, headers); |  | ||||||
| 			response.end(); |  | ||||||
| 		} else { |  | ||||||
| 			let headers = { |  | ||||||
| 				ETag: '"' + id + '"', |  | ||||||
| 				'Access-Control-Allow-Origin': '*', |  | ||||||
| 				'Content-Security-Policy': k_content_security_policy, |  | ||||||
| 			}; |  | ||||||
| 			data = await ssb.blobGet(id); |  | ||||||
| 			let type = |  | ||||||
| 				httpd.mime_type_from_extension(uri) || |  | ||||||
| 				httpd.mime_type_from_magic_bytes(data); |  | ||||||
| 			sendData(response, data, type, headers); |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		sendData(response, data, undefined, {}); |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| ssb.addEventListener('message', function () { | ssb.addEventListener('message', function () { | ||||||
| 	broadcastEvent('onMessage', [...arguments]); | 	broadcastEvent('onMessage', [...arguments]); | ||||||
| }); | }); | ||||||
| @@ -1059,18 +911,6 @@ loadSettings() | |||||||
| 			httpd.set_http_redirect(settings.http_redirect); | 			httpd.set_http_redirect(settings.http_redirect); | ||||||
| 		} | 		} | ||||||
| 		httpd.all('/app/socket', app.socket); | 		httpd.all('/app/socket', app.socket); | ||||||
| 		httpd.all('/~{word}/{word}/*', function default_http_handler(request, response) { |  | ||||||
| 			let match; |  | ||||||
| 			if ((match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri))) { |  | ||||||
| 				return blobHandler(request, response, match[1], match[2]); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 		httpd.all('/&*.sha256/*', function default_http_handler(request, response) { |  | ||||||
| 			let match; |  | ||||||
| 			if ((match = /^\/([&\%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(request.uri))) { |  | ||||||
| 				return blobHandler(request, response, match[1], match[2]); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 		let port = httpd.start(tildefriends.http_port); | 		let port = httpd.start(tildefriends.http_port); | ||||||
| 		if (tildefriends.args.out_http_port_file) { | 		if (tildefriends.args.out_http_port_file) { | ||||||
| 			print('Writing the port file.'); | 			print('Writing the port file.'); | ||||||
|   | |||||||
							
								
								
									
										134
									
								
								src/httpd.js.c
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								src/httpd.js.c
									
									
									
									
									
								
							| @@ -1034,6 +1034,131 @@ static user_app_t* _parse_user_app_from_path(const char* path, const char* expec | |||||||
| 	return result; | 	return result; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | typedef struct _app_blob_t | ||||||
|  | { | ||||||
|  | 	tf_http_request_t* request; | ||||||
|  | 	bool found; | ||||||
|  | 	bool not_modified; | ||||||
|  | 	void* data; | ||||||
|  | 	size_t size; | ||||||
|  | 	char etag[256]; | ||||||
|  | } app_blob_t; | ||||||
|  |  | ||||||
|  | static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data) | ||||||
|  | { | ||||||
|  | 	app_blob_t* data = user_data; | ||||||
|  | 	tf_http_request_t* request = data->request; | ||||||
|  | 	char app_id[256] = ""; | ||||||
|  | 	const char* file = NULL; | ||||||
|  | 	if (request->path[0] == '/' && request->path[1] == '~') | ||||||
|  | 	{ | ||||||
|  | 		const char* last_slash = strchr(request->path + 1, '/'); | ||||||
|  | 		if (last_slash) | ||||||
|  | 		{ | ||||||
|  | 			last_slash = strchr(last_slash + 1, '/'); | ||||||
|  | 		} | ||||||
|  | 		user_app_t* user_app = last_slash ? _parse_user_app_from_path(request->path, last_slash) : NULL; | ||||||
|  | 		if (user_app) | ||||||
|  | 		{ | ||||||
|  | 			size_t path_length = strlen("path:") + strlen(user_app->app) + 1; | ||||||
|  | 			char* app_path = tf_malloc(path_length); | ||||||
|  | 			snprintf(app_path, path_length, "path:%s", user_app->app); | ||||||
|  | 			const char* value = tf_ssb_db_get_property(ssb, user_app->user, app_path); | ||||||
|  | 			snprintf(app_id, sizeof(app_id), "%s", value); | ||||||
|  | 			tf_free(app_path); | ||||||
|  | 			tf_free((void*)value); | ||||||
|  | 			file = last_slash + 1; | ||||||
|  | 			tf_free(user_app); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else if (request->path[0] == '/' && request->path[1] == '&') | ||||||
|  | 	{ | ||||||
|  | 		const char* end = strstr(request->path, ".sha256/"); | ||||||
|  | 		if (end) | ||||||
|  | 		{ | ||||||
|  | 			snprintf(app_id, sizeof(app_id), "%.*s", (int)(end + strlen(".sha256") - request->path - 1), request->path + 1); | ||||||
|  | 			file = end + strlen(".sha256/"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	char* app_blob = NULL; | ||||||
|  | 	size_t app_blob_size = 0; | ||||||
|  | 	if (*app_id && tf_ssb_db_blob_get(ssb, app_id, (uint8_t**)&app_blob, &app_blob_size)) | ||||||
|  | 	{ | ||||||
|  | 		JSMallocFunctions funcs = { 0 }; | ||||||
|  | 		tf_get_js_malloc_functions(&funcs); | ||||||
|  | 		JSRuntime* runtime = JS_NewRuntime2(&funcs, NULL); | ||||||
|  | 		JSContext* context = JS_NewContext(runtime); | ||||||
|  |  | ||||||
|  | 		JSValue app_object = JS_ParseJSON(context, app_blob, app_blob_size, NULL); | ||||||
|  | 		JSValue files = JS_GetPropertyStr(context, app_object, "files"); | ||||||
|  | 		JSValue blob_id = JS_GetPropertyStr(context, files, file); | ||||||
|  | 		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, files); | ||||||
|  | 		JS_FreeValue(context, app_object); | ||||||
|  |  | ||||||
|  | 		JS_FreeContext(context); | ||||||
|  | 		JS_FreeRuntime(runtime); | ||||||
|  | 		tf_free(app_blob); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void _httpd_endpoint_app_blob_after_work(tf_ssb_t* ssb, int status, void* user_data) | ||||||
|  | { | ||||||
|  | 	app_blob_t* data = user_data; | ||||||
|  | 	if (data->not_modified) | ||||||
|  | 	{ | ||||||
|  | 		tf_http_respond(data->request, 304, NULL, 0, NULL, 0); | ||||||
|  | 	} | ||||||
|  | 	else if (data->found) | ||||||
|  | 	{ | ||||||
|  | 		const char* mime_type = _ext_to_content_type(strrchr(data->request->path, '.'), false); | ||||||
|  | 		if (!mime_type) | ||||||
|  | 		{ | ||||||
|  | 			mime_type = _httpd_mime_type_from_magic_bytes_internal(data->data, data->size); | ||||||
|  | 		} | ||||||
|  | 		const char* headers[] = { | ||||||
|  | 			"Access-Control-Allow-Origin", | ||||||
|  | 			"*", | ||||||
|  | 			"Content-Security-Policy", | ||||||
|  | 			"sandbox allow-downloads allow-top-navigation-by-user-activation", | ||||||
|  | 			"Content-Type", | ||||||
|  | 			mime_type ? mime_type : "application/binary", | ||||||
|  | 			"etag", | ||||||
|  | 			data->etag, | ||||||
|  | 		}; | ||||||
|  | 		tf_http_respond(data->request, 200, headers, tf_countof(headers) / 2, data->data, data->size); | ||||||
|  | 	} | ||||||
|  | 	tf_free(data->data); | ||||||
|  | 	tf_http_request_unref(data->request); | ||||||
|  | 	tf_free(data); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void _httpd_endpoint_app_blob(tf_http_request_t* request) | ||||||
|  | { | ||||||
|  | 	tf_http_request_ref(request); | ||||||
|  | 	tf_task_t* task = request->user_data; | ||||||
|  | 	tf_ssb_t* ssb = tf_task_get_ssb(task); | ||||||
|  | 	app_blob_t* data = tf_malloc(sizeof(app_blob_t)); | ||||||
|  | 	*data = (app_blob_t) { .request = request }; | ||||||
|  | 	tf_ssb_run_work(ssb, _httpd_endpoint_app_blob_work, _httpd_endpoint_app_blob_after_work, data); | ||||||
|  | } | ||||||
|  |  | ||||||
| typedef struct _view_t | typedef struct _view_t | ||||||
| { | { | ||||||
| 	tf_http_request_t* request; | 	tf_http_request_t* request; | ||||||
| @@ -2117,14 +2242,17 @@ void tf_httpd_register(JSContext* context) | |||||||
| 	tf_http_add_handler(http, "/speedscope/*", _httpd_endpoint_static, NULL, task); | 	tf_http_add_handler(http, "/speedscope/*", _httpd_endpoint_static, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task); | 	tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task); | 	tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task); | ||||||
|  | 	tf_http_add_handler(http, "/&*.sha256", _httpd_endpoint_add_slash, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/&*.sha256/", _httpd_endpoint_static, NULL, task); | 	tf_http_add_handler(http, "/&*.sha256/", _httpd_endpoint_static, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/*/view", _httpd_endpoint_view, NULL, task); | 	tf_http_add_handler(http, "/&*.sha256/view", _httpd_endpoint_view, NULL, task); | ||||||
|  | 	tf_http_add_handler(http, "/&*.sha256/*", _httpd_endpoint_app_blob, NULL, task); | ||||||
|  | 	tf_http_add_handler(http, "/~{word}/{word}", _httpd_endpoint_add_slash, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/~{word}/{word}/", _httpd_endpoint_static, NULL, task); | 	tf_http_add_handler(http, "/~{word}/{word}/", _httpd_endpoint_static, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/~{word}/{word}/save", _httpd_endpoint_save, NULL, task); | 	tf_http_add_handler(http, "/~{word}/{word}/save", _httpd_endpoint_save, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/~{word}/{word}/delete", _httpd_endpoint_delete, NULL, task); | 	tf_http_add_handler(http, "/~{word}/{word}/delete", _httpd_endpoint_delete, NULL, task); | ||||||
|  | 	tf_http_add_handler(http, "/~{word}/{word}/view", _httpd_endpoint_view, NULL, task); | ||||||
|  | 	tf_http_add_handler(http, "/~{word}/{word}/*", _httpd_endpoint_app_blob, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/save", _httpd_endpoint_save, NULL, task); | 	tf_http_add_handler(http, "/save", _httpd_endpoint_save, NULL, task); | ||||||
| 	tf_http_add_handler(http, "/~{word}/{word}", _httpd_endpoint_add_slash, NULL, task); |  | ||||||
| 	tf_http_add_handler(http, "/&*.sha256", _httpd_endpoint_add_slash, NULL, task); |  | ||||||
|  |  | ||||||
| 	tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL); | 	tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL); | ||||||
| 	tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task); | 	tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user