forked from cory/tildefriends
		
	http: Bring back handler.js support, mostly. Partly in C, this time.
This commit is contained in:
		
							
								
								
									
										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 | ||||||
|  */ |  */ | ||||||
|   | |||||||
							
								
								
									
										101
									
								
								src/httpd.js.c
									
									
									
									
									
								
							
							
						
						
									
										101
									
								
								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,7 +1084,17 @@ 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); | ||||||
|  | 		if (JS_IsUndefined(blob_id)) | ||||||
|  | 		{ | ||||||
|  | 			blob_id = JS_GetPropertyStr(context, files, "handler.js"); | ||||||
|  | 			if (!JS_IsUndefined(blob_id)) | ||||||
|  | 			{ | ||||||
|  | 				data->use_handler = true; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
| 			const char* blob_id_str = JS_ToCString(context, blob_id); | 			const char* blob_id_str = JS_ToCString(context, blob_id); | ||||||
| 			if (blob_id_str) | 			if (blob_id_str) | ||||||
| 			{ | 			{ | ||||||
| @@ -1093,6 +1110,7 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data) | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			JS_FreeCString(context, blob_id_str); | 			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); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user