forked from cory/tildefriends
		
	httpd: Move starting the http server into C.
This commit is contained in:
		@@ -775,7 +775,9 @@ class TfMessageElement extends LitElement {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				return this.render_small_frame(
 | 
									return this.render_small_frame(
 | 
				
			||||||
					html`<div class="w3-container"><p><b>type</b>: ${content.type}</p></div>`
 | 
										html`<div class="w3-container">
 | 
				
			||||||
 | 
											<p><b>type</b>: ${content.type}</p>
 | 
				
			||||||
 | 
										</div>`
 | 
				
			||||||
				);
 | 
									);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if (typeof this.message.content == 'string') {
 | 
							} else if (typeof this.message.content == 'string') {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -81,9 +81,8 @@ App.prototype.send = function (message) {
 | 
				
			|||||||
 * TODOC
 | 
					 * TODOC
 | 
				
			||||||
 * @param {*} request
 | 
					 * @param {*} request
 | 
				
			||||||
 * @param {*} response
 | 
					 * @param {*} response
 | 
				
			||||||
 * @param {*} client
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
async function socket(request, response, client) {
 | 
					exports.app_socket = async function socket(request, response) {
 | 
				
			||||||
	let process;
 | 
						let process;
 | 
				
			||||||
	let options = {};
 | 
						let options = {};
 | 
				
			||||||
	let credentials = await httpd.auth_query(request.headers);
 | 
						let credentials = await httpd.auth_query(request.headers);
 | 
				
			||||||
@@ -245,6 +244,6 @@ async function socket(request, response, client) {
 | 
				
			|||||||
	};
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	response.upgrade(100, {});
 | 
						response.upgrade(100, {});
 | 
				
			||||||
}
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {socket, App};
 | 
					export {App};
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								core/core.js
									
									
									
									
									
								
							
							
						
						
									
										57
									
								
								core/core.js
									
									
									
									
									
								
							@@ -835,61 +835,4 @@ exports.callAppHandler = async function callAppHandler(
 | 
				
			|||||||
	response.end(answer?.data);
 | 
						response.end(answer?.data);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * TODOC
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
loadSettings()
 | 
					 | 
				
			||||||
	.then(function (settings) {
 | 
					 | 
				
			||||||
		if (tildefriends.https_port && settings.http_redirect) {
 | 
					 | 
				
			||||||
			httpd.set_http_redirect(settings.http_redirect);
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		httpd.all('/app/socket', app.socket);
 | 
					 | 
				
			||||||
		if (tildefriends.http_port > 0 || tildefriends.args.out_http_port_file) {
 | 
					 | 
				
			||||||
			let port = httpd.start(tildefriends.http_port);
 | 
					 | 
				
			||||||
			if (tildefriends.args.out_http_port_file) {
 | 
					 | 
				
			||||||
				print('Writing the port file.');
 | 
					 | 
				
			||||||
				File.writeFile(
 | 
					 | 
				
			||||||
					tildefriends.args.out_http_port_file,
 | 
					 | 
				
			||||||
					port.toString() + '\n'
 | 
					 | 
				
			||||||
				)
 | 
					 | 
				
			||||||
					.then(function (r) {
 | 
					 | 
				
			||||||
						print(
 | 
					 | 
				
			||||||
							'Wrote the port file:',
 | 
					 | 
				
			||||||
							tildefriends.args.out_http_port_file,
 | 
					 | 
				
			||||||
							r
 | 
					 | 
				
			||||||
						);
 | 
					 | 
				
			||||||
					})
 | 
					 | 
				
			||||||
					.catch(function () {
 | 
					 | 
				
			||||||
						print('Failed to write the port file.');
 | 
					 | 
				
			||||||
					});
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if (tildefriends.https_port) {
 | 
					 | 
				
			||||||
			async function start_tls() {
 | 
					 | 
				
			||||||
				const kCertificatePath = 'data/httpd/certificate.pem';
 | 
					 | 
				
			||||||
				const kPrivateKeyPath = 'data/httpd/privatekey.pem';
 | 
					 | 
				
			||||||
				let privateKey;
 | 
					 | 
				
			||||||
				let certificate;
 | 
					 | 
				
			||||||
				try {
 | 
					 | 
				
			||||||
					privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
 | 
					 | 
				
			||||||
					certificate = utf8Decode(await File.readFile(kCertificatePath));
 | 
					 | 
				
			||||||
				} catch (e) {
 | 
					 | 
				
			||||||
					print(`TLS disabled (${e.message}).`);
 | 
					 | 
				
			||||||
					return;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				let context = new TlsContext();
 | 
					 | 
				
			||||||
				context.setPrivateKey(privateKey);
 | 
					 | 
				
			||||||
				context.setCertificate(certificate);
 | 
					 | 
				
			||||||
				httpd.start(tildefriends.https_port, context);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			start_tls();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
	.catch(function (error) {
 | 
					 | 
				
			||||||
		print('Failed to load settings.');
 | 
					 | 
				
			||||||
		printError({print: print}, error);
 | 
					 | 
				
			||||||
		exit(1);
 | 
					 | 
				
			||||||
	});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export {invoke, getProcessBlob};
 | 
					export {invoke, getProcessBlob};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -212,14 +212,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	tf_task_t* task = JS_GetContextOpaque(context);
 | 
						tf_task_t* task = JS_GetContextOpaque(context);
 | 
				
			||||||
	const char* file_name = JS_ToCString(context, argv[0]);
 | 
						const char* file_name = JS_ToCString(context, argv[0]);
 | 
				
			||||||
	const char* actual = file_name;
 | 
						const char* actual = tf_task_get_path_with_root(task, file_name);
 | 
				
			||||||
	if (tf_task_get_root_path(task))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		size_t size = strlen(tf_task_get_root_path(task)) + strlen(file_name) + 2;
 | 
					 | 
				
			||||||
		char* buffer = alloca(size);
 | 
					 | 
				
			||||||
		snprintf(buffer, size, "%s/%s", tf_task_get_root_path(task), file_name);
 | 
					 | 
				
			||||||
		actual = buffer;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	promiseid_t promise = -1;
 | 
						promiseid_t promise = -1;
 | 
				
			||||||
	JSValue promise_value = tf_task_allocate_promise(task, &promise);
 | 
						JSValue promise_value = tf_task_allocate_promise(task, &promise);
 | 
				
			||||||
@@ -241,6 +234,7 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar
 | 
				
			|||||||
		tf_free(req);
 | 
							tf_free(req);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	JS_FreeCString(context, file_name);
 | 
						JS_FreeCString(context, file_name);
 | 
				
			||||||
 | 
						tf_free((char*)actual);
 | 
				
			||||||
	return promise_value;
 | 
						return promise_value;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										283
									
								
								src/httpd.js.c
									
									
									
									
									
								
							
							
						
						
									
										283
									
								
								src/httpd.js.c
									
									
									
									
									
								
							@@ -7,6 +7,7 @@
 | 
				
			|||||||
#include "ssb.db.h"
 | 
					#include "ssb.db.h"
 | 
				
			||||||
#include "ssb.h"
 | 
					#include "ssb.h"
 | 
				
			||||||
#include "task.h"
 | 
					#include "task.h"
 | 
				
			||||||
 | 
					#include "tls.h"
 | 
				
			||||||
#include "tlscontext.js.h"
 | 
					#include "tlscontext.js.h"
 | 
				
			||||||
#include "trace.h"
 | 
					#include "trace.h"
 | 
				
			||||||
#include "util.js.h"
 | 
					#include "util.js.h"
 | 
				
			||||||
@@ -233,46 +234,6 @@ static JSValue _httpd_make_response_object(JSContext* context, tf_http_request_t
 | 
				
			|||||||
	return response_object;
 | 
						return response_object;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _httpd_callback_internal(tf_http_request_t* request, bool is_websocket)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	http_handler_data_t* data = request->user_data;
 | 
					 | 
				
			||||||
	JSContext* context = data->context;
 | 
					 | 
				
			||||||
	JSValue request_object = JS_NewObject(context);
 | 
					 | 
				
			||||||
	JS_SetPropertyStr(context, request_object, "method", JS_NewString(context, request->method));
 | 
					 | 
				
			||||||
	JS_SetPropertyStr(context, request_object, "uri", JS_NewString(context, request->path));
 | 
					 | 
				
			||||||
	JSValue headers = JS_NewObject(context);
 | 
					 | 
				
			||||||
	for (int i = 0; i < request->headers_count; i++)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		JS_SetPropertyStr(context, headers, request->headers[i].name, JS_NewString(context, request->headers[i].value));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	JS_SetPropertyStr(context, request_object, "headers", headers);
 | 
					 | 
				
			||||||
	if (request->query)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		JS_SetPropertyStr(context, request_object, "query", JS_NewString(context, request->query));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (request->body)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		JS_SetPropertyStr(context, request_object, "body", tf_util_new_uint8_array(context, request->body, request->content_length));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	JSValue client = JS_NewObject(context);
 | 
					 | 
				
			||||||
	JS_SetPropertyStr(context, client, "tls", request->is_tls ? JS_TRUE : JS_FALSE);
 | 
					 | 
				
			||||||
	JS_SetPropertyStr(context, request_object, "client", client);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	JSValue response_object = _httpd_make_response_object(context, request);
 | 
					 | 
				
			||||||
	/* The ref is owned by the JS object and will be released by the finalizer. */
 | 
					 | 
				
			||||||
	tf_http_request_ref(request);
 | 
					 | 
				
			||||||
	JSValue args[] = {
 | 
					 | 
				
			||||||
		request_object,
 | 
					 | 
				
			||||||
		response_object,
 | 
					 | 
				
			||||||
	};
 | 
					 | 
				
			||||||
	JSValue response = JS_Call(context, data->callback, JS_UNDEFINED, 2, args);
 | 
					 | 
				
			||||||
	tf_util_report_error(context, response);
 | 
					 | 
				
			||||||
	JS_FreeValue(context, request_object);
 | 
					 | 
				
			||||||
	JS_FreeValue(context, response);
 | 
					 | 
				
			||||||
	JS_FreeValue(context, response_object);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static bool _httpd_redirect(tf_http_request_t* request)
 | 
					static bool _httpd_redirect(tf_http_request_t* request)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (request->is_tls)
 | 
						if (request->is_tls)
 | 
				
			||||||
@@ -292,16 +253,6 @@ static bool _httpd_redirect(tf_http_request_t* request)
 | 
				
			|||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _httpd_callback(tf_http_request_t* request)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (_httpd_redirect(request))
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	_httpd_callback_internal(request, false);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
					static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
 | 
						tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
 | 
				
			||||||
@@ -388,71 +339,19 @@ static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_va
 | 
				
			|||||||
	return JS_UNDEFINED;
 | 
						return JS_UNDEFINED;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _httpd_cleanup_callback(void* user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	http_handler_data_t* data = user_data;
 | 
					 | 
				
			||||||
	JS_FreeValue(data->context, data->callback);
 | 
					 | 
				
			||||||
	tf_free(data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static JSValue _httpd_endpoint_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
 | 
					 | 
				
			||||||
	const char* pattern = JS_ToCString(context, argv[0]);
 | 
					 | 
				
			||||||
	http_handler_data_t* data = tf_malloc(sizeof(http_handler_data_t));
 | 
					 | 
				
			||||||
	*data = (http_handler_data_t) { .context = context, .callback = JS_DupValue(context, argv[1]) };
 | 
					 | 
				
			||||||
	tf_http_add_handler(http, pattern, _httpd_callback, _httpd_cleanup_callback, data);
 | 
					 | 
				
			||||||
	JS_FreeCString(context, pattern);
 | 
					 | 
				
			||||||
	return JS_UNDEFINED;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typedef struct _httpd_listener_t
 | 
					typedef struct _httpd_listener_t
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	JSContext* context;
 | 
						tf_tls_context_t* tls;
 | 
				
			||||||
	JSValue tls;
 | 
					 | 
				
			||||||
} httpd_listener_t;
 | 
					} httpd_listener_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _httpd_listener_cleanup(void* user_data)
 | 
					static void _httpd_listener_cleanup(void* user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	httpd_listener_t* listener = user_data;
 | 
						httpd_listener_t* listener = user_data;
 | 
				
			||||||
	JS_FreeValue(listener->context, listener->tls);
 | 
						if (listener->tls)
 | 
				
			||||||
	tf_free(listener);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static JSValue _httpd_endpoint_start(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
 | 
					 | 
				
			||||||
	int port = 0;
 | 
					 | 
				
			||||||
	JS_ToInt32(context, &port, argv[0]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
 | 
					 | 
				
			||||||
	*listener = (httpd_listener_t) { .context = context, .tls = JS_DupValue(context, argv[1]) };
 | 
					 | 
				
			||||||
	tf_tls_context_t* tls = tf_tls_context_get(listener->tls);
 | 
					 | 
				
			||||||
	int assigned_port = tf_http_listen(http, port, tls, _httpd_listener_cleanup, listener);
 | 
					 | 
				
			||||||
	tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http%s://127.0.0.1:%d/" RESET ".\n", tls ? "s" : "", assigned_port);
 | 
					 | 
				
			||||||
	return JS_NewInt32(context, assigned_port);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void _httpd_free_user_data(void* user_data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	tf_free(user_data);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static JSValue _httpd_set_http_redirect(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
 | 
					 | 
				
			||||||
	http_user_data_t* user_data = tf_http_get_user_data(http);
 | 
					 | 
				
			||||||
	if (!user_data)
 | 
					 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		user_data = tf_malloc(sizeof(http_user_data_t));
 | 
							tf_tls_context_destroy(listener->tls);
 | 
				
			||||||
		memset(user_data, 0, sizeof(http_user_data_t));
 | 
					 | 
				
			||||||
		tf_http_set_user_data(http, user_data, _httpd_free_user_data);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						tf_free(listener);
 | 
				
			||||||
	const char* redirect = JS_ToCString(context, argv[0]);
 | 
					 | 
				
			||||||
	snprintf(user_data->redirect, sizeof(user_data->redirect), "%s", redirect ? redirect : "");
 | 
					 | 
				
			||||||
	JS_FreeCString(context, redirect);
 | 
					 | 
				
			||||||
	return JS_UNDEFINED;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct _auth_query_work_t
 | 
					typedef struct _auth_query_work_t
 | 
				
			||||||
@@ -2267,6 +2166,100 @@ static void _httpd_endpoint_logout(tf_http_request_t* request)
 | 
				
			|||||||
	tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
 | 
						tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void _httpd_endpoint_app_socket(tf_http_request_t* request)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						tf_task_t* task = request->user_data;
 | 
				
			||||||
 | 
						tf_ssb_t* ssb = tf_task_get_ssb(task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						JSContext* context = tf_ssb_get_context(ssb);
 | 
				
			||||||
 | 
						JSValue global = JS_GetGlobalObject(context);
 | 
				
			||||||
 | 
						JSValue exports = JS_GetPropertyStr(context, global, "exports");
 | 
				
			||||||
 | 
						JSValue app_socket = JS_GetPropertyStr(context, exports, "app_socket");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						JSValue request_object = JS_NewObject(context);
 | 
				
			||||||
 | 
						JSValue headers = JS_NewObject(context);
 | 
				
			||||||
 | 
						for (int i = 0; i < request->headers_count; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							JS_SetPropertyStr(context, headers, request->headers[i].name, JS_NewString(context, request->headers[i].value));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						JS_SetPropertyStr(context, request_object, "headers", headers);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						JSValue response = _httpd_make_response_object(context, request);
 | 
				
			||||||
 | 
						tf_http_request_ref(request);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						JSValue args[] = {
 | 
				
			||||||
 | 
							request_object,
 | 
				
			||||||
 | 
							response,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						JSValue result = JS_Call(context, app_socket, JS_NULL, tf_countof(args), args);
 | 
				
			||||||
 | 
						tf_util_report_error(context, result);
 | 
				
			||||||
 | 
						JS_FreeValue(context, result);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < tf_countof(args); i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							JS_FreeValue(context, args[i]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						JS_FreeValue(context, app_socket);
 | 
				
			||||||
 | 
						JS_FreeValue(context, exports);
 | 
				
			||||||
 | 
						JS_FreeValue(context, global);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int _tf_httpd_get_tildefriends_int(JSContext* context, const char* arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						JSValue global = JS_GetGlobalObject(context);
 | 
				
			||||||
 | 
						JSValue tildefriends = JS_GetPropertyStr(context, global, "tildefriends");
 | 
				
			||||||
 | 
						JSValue arg_value = JS_GetPropertyStr(context, tildefriends, arg);
 | 
				
			||||||
 | 
						int value = 0;
 | 
				
			||||||
 | 
						JS_ToInt32(context, &value, arg_value);
 | 
				
			||||||
 | 
						JS_FreeValue(context, arg_value);
 | 
				
			||||||
 | 
						JS_FreeValue(context, tildefriends);
 | 
				
			||||||
 | 
						JS_FreeValue(context, global);
 | 
				
			||||||
 | 
						return value;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char* _tf_httpd_get_tildefriends_arg_string(JSContext* context, const char* arg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						JSValue global = JS_GetGlobalObject(context);
 | 
				
			||||||
 | 
						JSValue tildefriends = JS_GetPropertyStr(context, global, "tildefriends");
 | 
				
			||||||
 | 
						JSValue args = JS_GetPropertyStr(context, tildefriends, "args");
 | 
				
			||||||
 | 
						JSValue arg_value = JS_GetPropertyStr(context, args, arg);
 | 
				
			||||||
 | 
						const char* value = !JS_IsUndefined(arg_value) ? JS_ToCString(context, arg_value) : NULL;
 | 
				
			||||||
 | 
						const char* result = value ? tf_strdup(value) : NULL;
 | 
				
			||||||
 | 
						JS_FreeCString(context, value);
 | 
				
			||||||
 | 
						JS_FreeValue(context, arg_value);
 | 
				
			||||||
 | 
						JS_FreeValue(context, args);
 | 
				
			||||||
 | 
						JS_FreeValue(context, tildefriends);
 | 
				
			||||||
 | 
						JS_FreeValue(context, global);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void _httpd_free_user_data(void* user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						tf_free(user_data);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char* _httpd_read_file(tf_task_t* task, const char* path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* actual = tf_task_get_path_with_root(task, path);
 | 
				
			||||||
 | 
						const size_t k_max_read = 8 * 1024 * 1024;
 | 
				
			||||||
 | 
						char* result = NULL;
 | 
				
			||||||
 | 
						char* buffer = tf_malloc(k_max_read);
 | 
				
			||||||
 | 
						FILE* file = fopen(actual, "rb");
 | 
				
			||||||
 | 
						if (file)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							size_t size = fread(buffer, 1, k_max_read, file);
 | 
				
			||||||
 | 
							result = tf_malloc(size + 1);
 | 
				
			||||||
 | 
							memcpy(result, buffer, size);
 | 
				
			||||||
 | 
							result[size] = '\0';
 | 
				
			||||||
 | 
							fclose(file);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						tf_free(buffer);
 | 
				
			||||||
 | 
						tf_free((char*)actual);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_httpd_register(JSContext* context)
 | 
					void tf_httpd_register(JSContext* context)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	JS_NewClassID(&_httpd_class_id);
 | 
						JS_NewClassID(&_httpd_class_id);
 | 
				
			||||||
@@ -2287,15 +2280,41 @@ void tf_httpd_register(JSContext* context)
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		fprintf(stderr, "Failed to register Request.\n");
 | 
							fprintf(stderr, "Failed to register Request.\n");
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int http_port = _tf_httpd_get_tildefriends_int(context, "http_port");
 | 
				
			||||||
 | 
						int https_port = _tf_httpd_get_tildefriends_int(context, "https_port");
 | 
				
			||||||
 | 
						const char* out_http_port_file = _tf_httpd_get_tildefriends_arg_string(context, "out_http_port_file");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	JSValue global = JS_GetGlobalObject(context);
 | 
						JSValue global = JS_GetGlobalObject(context);
 | 
				
			||||||
	JSValue httpd = JS_NewObjectClass(context, _httpd_class_id);
 | 
						JSValue httpd = JS_NewObjectClass(context, _httpd_class_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_task_t* task = tf_task_get(context);
 | 
						tf_task_t* task = tf_task_get(context);
 | 
				
			||||||
 | 
						tf_ssb_t* ssb = tf_task_get_ssb(task);
 | 
				
			||||||
	uv_loop_t* loop = tf_task_get_loop(task);
 | 
						uv_loop_t* loop = tf_task_get_loop(task);
 | 
				
			||||||
	tf_http_t* http = tf_http_create(loop);
 | 
						tf_http_t* http = tf_http_create(loop);
 | 
				
			||||||
	tf_http_set_trace(http, tf_task_get_trace(task));
 | 
						tf_http_set_trace(http, tf_task_get_trace(task));
 | 
				
			||||||
	JS_SetOpaque(httpd, http);
 | 
						JS_SetOpaque(httpd, http);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (https_port)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							http_user_data_t* user_data = tf_http_get_user_data(http);
 | 
				
			||||||
 | 
							if (!user_data)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								user_data = tf_malloc(sizeof(http_user_data_t));
 | 
				
			||||||
 | 
								memset(user_data, 0, sizeof(http_user_data_t));
 | 
				
			||||||
 | 
								tf_http_set_user_data(http, user_data, _httpd_free_user_data);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							sqlite3* db = tf_ssb_acquire_db_reader(ssb);
 | 
				
			||||||
 | 
							tf_ssb_db_get_global_setting_string(db, "http_redirect", user_data->redirect, sizeof(user_data->redirect));
 | 
				
			||||||
 | 
							tf_ssb_release_db_reader(ssb, db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/* Workaround. */
 | 
				
			||||||
 | 
							if (strcmp(user_data->redirect, "0") == 0)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								*user_data->redirect = '\0';
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tf_http_add_handler(http, "/", _httpd_endpoint_root, NULL, task);
 | 
						tf_http_add_handler(http, "/", _httpd_endpoint_root, NULL, task);
 | 
				
			||||||
	tf_http_add_handler(http, "/codemirror/*", _httpd_endpoint_static, NULL, task);
 | 
						tf_http_add_handler(http, "/codemirror/*", _httpd_endpoint_static, NULL, task);
 | 
				
			||||||
	tf_http_add_handler(http, "/lit/*", _httpd_endpoint_static, NULL, task);
 | 
						tf_http_add_handler(http, "/lit/*", _httpd_endpoint_static, NULL, task);
 | 
				
			||||||
@@ -2324,10 +2343,56 @@ void tf_httpd_register(JSContext* context)
 | 
				
			|||||||
	tf_http_add_handler(http, "/login/logout", _httpd_endpoint_logout, NULL, task);
 | 
						tf_http_add_handler(http, "/login/logout", _httpd_endpoint_logout, NULL, task);
 | 
				
			||||||
	tf_http_add_handler(http, "/login", _httpd_endpoint_login, NULL, task);
 | 
						tf_http_add_handler(http, "/login", _httpd_endpoint_login, NULL, task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_endpoint_all, "all", 2));
 | 
						tf_http_add_handler(http, "/app/socket", _httpd_endpoint_app_socket, NULL, task);
 | 
				
			||||||
	JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_endpoint_start, "start", 2));
 | 
					
 | 
				
			||||||
	JS_SetPropertyStr(context, httpd, "set_http_redirect", JS_NewCFunction(context, _httpd_set_http_redirect, "set_http_redirect", 1));
 | 
					 | 
				
			||||||
	JS_SetPropertyStr(context, httpd, "auth_query", JS_NewCFunction(context, _httpd_auth_query, "auth_query", 1));
 | 
						JS_SetPropertyStr(context, httpd, "auth_query", JS_NewCFunction(context, _httpd_auth_query, "auth_query", 1));
 | 
				
			||||||
	JS_SetPropertyStr(context, global, "httpd", httpd);
 | 
						JS_SetPropertyStr(context, global, "httpd", httpd);
 | 
				
			||||||
	JS_FreeValue(context, global);
 | 
						JS_FreeValue(context, global);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (http_port > 0 || out_http_port_file)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
 | 
				
			||||||
 | 
							*listener = (httpd_listener_t) { 0 };
 | 
				
			||||||
 | 
							int assigned_port = tf_http_listen(http, http_port, NULL, _httpd_listener_cleanup, listener);
 | 
				
			||||||
 | 
							tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "http://127.0.0.1:%d/" RESET ".\n", assigned_port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (out_http_port_file)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								const char* actual_http_port_file = tf_task_get_path_with_root(task, out_http_port_file);
 | 
				
			||||||
 | 
								FILE* file = fopen(actual_http_port_file, "wb");
 | 
				
			||||||
 | 
								if (file)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									fprintf(file, "%d", assigned_port);
 | 
				
			||||||
 | 
									fclose(file);
 | 
				
			||||||
 | 
									tf_printf("Wrote the port file: %s.\n", out_http_port_file);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								else
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									tf_printf("Failed to open %s for write: %s.\n", out_http_port_file, strerror(errno));
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								tf_free((char*)actual_http_port_file);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (https_port)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								const char* k_certificate = "data/httpd/certificate.pem";
 | 
				
			||||||
 | 
								const char* k_private_key = "data/httpd/privatekey.pem";
 | 
				
			||||||
 | 
								const char* certificate = _httpd_read_file(task, k_certificate);
 | 
				
			||||||
 | 
								const char* private_key = _httpd_read_file(task, k_private_key);
 | 
				
			||||||
 | 
								if (certificate && private_key)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									tf_tls_context_t* tls = tf_tls_context_create();
 | 
				
			||||||
 | 
									tf_tls_context_set_certificate(tls, certificate);
 | 
				
			||||||
 | 
									tf_tls_context_set_private_key(tls, private_key);
 | 
				
			||||||
 | 
									httpd_listener_t* listener = tf_malloc(sizeof(httpd_listener_t));
 | 
				
			||||||
 | 
									*listener = (httpd_listener_t) { .tls = tls };
 | 
				
			||||||
 | 
									int assigned_port = tf_http_listen(http, https_port, tls, _httpd_listener_cleanup, listener);
 | 
				
			||||||
 | 
									tf_printf(CYAN "~😎 Tilde Friends" RESET " " YELLOW VERSION_NUMBER RESET " is now up at " MAGENTA "https://127.0.0.1:%d/" RESET ".\n", assigned_port);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								tf_free((char*)certificate);
 | 
				
			||||||
 | 
								tf_free((char*)private_key);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tf_free((void*)out_http_port_file);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								src/task.c
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								src/task.c
									
									
									
									
									
								
							@@ -376,7 +376,6 @@ static const char* _task_loadFile(tf_task_t* task, const char* fileName, size_t*
 | 
				
			|||||||
			snprintf(buffer, size, "%s/%s", task->_root_path, fileName);
 | 
								snprintf(buffer, size, "%s/%s", task->_root_path, fileName);
 | 
				
			||||||
			actual = fileName;
 | 
								actual = fileName;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		tf_printf("opening %s\n", actual);
 | 
					 | 
				
			||||||
		FILE* file = fopen(actual, "rb");
 | 
							FILE* file = fopen(actual, "rb");
 | 
				
			||||||
		if (file)
 | 
							if (file)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
@@ -1727,7 +1726,6 @@ void tf_task_activate(tf_task_t* task)
 | 
				
			|||||||
		JS_SetPropertyStr(context, global, "TlsContext", tf_tls_context_register(context));
 | 
							JS_SetPropertyStr(context, global, "TlsContext", tf_tls_context_register(context));
 | 
				
			||||||
		tf_file_register(context);
 | 
							tf_file_register(context);
 | 
				
			||||||
		tf_database_register(context);
 | 
							tf_database_register(context);
 | 
				
			||||||
		tf_httpd_register(context);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
 | 
							task->_ssb = tf_ssb_create(&task->_loop, task->_context, task->_db_path, task->_network_key);
 | 
				
			||||||
		tf_ssb_set_trace(task->_ssb, task->_trace);
 | 
							tf_ssb_set_trace(task->_ssb, task->_trace);
 | 
				
			||||||
@@ -1744,10 +1742,18 @@ void tf_task_activate(tf_task_t* task)
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		JS_SetPropertyStr(context, tildefriends, "ssb_port", JS_NewInt32(context, actual_ssb_port));
 | 
							JS_SetPropertyStr(context, tildefriends, "ssb_port", JS_NewInt32(context, actual_ssb_port));
 | 
				
			||||||
		JS_SetPropertyStr(context, tildefriends, "http_port", JS_NewInt32(context, task->_http_port));
 | 
							if (task->_http_port)
 | 
				
			||||||
		JS_SetPropertyStr(context, tildefriends, "https_port", JS_NewInt32(context, task->_https_port));
 | 
							{
 | 
				
			||||||
 | 
								JS_SetPropertyStr(context, tildefriends, "http_port", JS_NewInt32(context, task->_http_port));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (task->_https_port)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								JS_SetPropertyStr(context, tildefriends, "https_port", JS_NewInt32(context, task->_https_port));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		JS_SetPropertyStr(context, global, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0));
 | 
							JS_SetPropertyStr(context, global, "getStats", JS_NewCFunction(context, _tf_task_getStats, "getStats", 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tf_httpd_register(context);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@@ -2043,6 +2049,19 @@ const char* tf_task_get_root_path(tf_task_t* task)
 | 
				
			|||||||
	return *task->_root_path ? task->_root_path : NULL;
 | 
						return *task->_root_path ? task->_root_path : NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const char* tf_task_get_path_with_root(tf_task_t* task, const char* path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!*task->_root_path)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return tf_strdup(path);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t size = strlen(task->_root_path) + 1 + strlen(path) + 1;
 | 
				
			||||||
 | 
						char* result = tf_malloc(size);
 | 
				
			||||||
 | 
						snprintf(result, size, "%s/%s", task->_root_path, path);
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void tf_task_set_args(tf_task_t* task, const char* args)
 | 
					void tf_task_set_args(tf_task_t* task, const char* args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	task->_args = args;
 | 
						task->_args = args;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -132,6 +132,14 @@ const char* tf_task_get_zip_path(tf_task_t* task);
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
const char* tf_task_get_root_path(tf_task_t* task);
 | 
					const char* tf_task_get_root_path(tf_task_t* task);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					** Get the path to use for reading a given loose file.
 | 
				
			||||||
 | 
					** @param task The task.
 | 
				
			||||||
 | 
					** @param path The path to the file.
 | 
				
			||||||
 | 
					** @return The path or NULL.  Free with tf_free().
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					const char* tf_task_get_path_with_root(tf_task_t* task, const char* path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
** Set arbitrary named arguments that will be made available to the task.
 | 
					** Set arbitrary named arguments that will be made available to the task.
 | 
				
			||||||
** @param task The task.
 | 
					** @param task The task.
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user