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:
		
							
								
								
									
										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); | ||||
| } | ||||
|  | ||||
| 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) | ||||
| { | ||||
| 	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, 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. */ | ||||
| 	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[] = { | ||||
| 		request_object, | ||||
| 		response_object, | ||||
| @@ -1024,8 +1030,12 @@ typedef struct _app_blob_t | ||||
| 	tf_http_request_t* request; | ||||
| 	bool found; | ||||
| 	bool not_modified; | ||||
| 	bool use_handler; | ||||
| 	void* data; | ||||
| 	size_t size; | ||||
| 	char app_blob_id[k_blob_id_len]; | ||||
| 	const char* file; | ||||
| 	user_app_t* user_app; | ||||
| 	char etag[256]; | ||||
| } 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; | ||||
| 	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, '/'); | ||||
| @@ -1042,18 +1050,17 @@ static void _httpd_endpoint_app_blob_work(tf_ssb_t* ssb, void* user_data) | ||||
| 		{ | ||||
| 			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) | ||||
| 		data->user_app = last_slash ? _parse_user_app_from_path(request->path, last_slash) : NULL; | ||||
| 		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); | ||||
| 			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); | ||||
| 			snprintf(app_path, path_length, "path:%s", data->user_app->app); | ||||
| 			const char* value = tf_ssb_db_get_property(ssb, data->user_app->user, app_path); | ||||
| 			snprintf(data->app_blob_id, sizeof(data->app_blob_id), "%s", value); | ||||
| 			tf_free(app_path); | ||||
| 			tf_free((void*)value); | ||||
| 			file = last_slash + 1; | ||||
| 			tf_free(user_app); | ||||
| 			data->file = last_slash + 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/"); | ||||
| 		if (end) | ||||
| 		{ | ||||
| 			snprintf(app_id, sizeof(app_id), "%.*s", (int)(end + strlen(".sha256") - request->path - 1), request->path + 1); | ||||
| 			file = end + strlen(".sha256/"); | ||||
| 			snprintf(data->app_blob_id, sizeof(data->app_blob_id), "%.*s", (int)(end + strlen(".sha256") - request->path - 1), request->path + 1); | ||||
| 			data->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)) | ||||
| 	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 }; | ||||
| 		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 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) | ||||
| 		JSValue blob_id = JS_GetPropertyStr(context, files, data->file); | ||||
| 		if (JS_IsUndefined(blob_id)) | ||||
| 		{ | ||||
| 			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) | ||||
| 			blob_id = JS_GetPropertyStr(context, files, "handler.js"); | ||||
| 			if (!JS_IsUndefined(blob_id)) | ||||
| 			{ | ||||
| 				data->not_modified = true; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				data->found = tf_ssb_db_blob_get(ssb, blob_id_str, (uint8_t**)&data->data, &data->size); | ||||
| 				data->use_handler = true; | ||||
| 			} | ||||
| 		} | ||||
| 		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, files); | ||||
| 		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) | ||||
| { | ||||
| 	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); | ||||
| 	} | ||||
| 	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) | ||||
| 	{ | ||||
| 		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_free(data->user_app); | ||||
| 	tf_free(data->data); | ||||
| 	tf_http_request_unref(data->request); | ||||
| 	tf_free(data); | ||||
|   | ||||
| @@ -1869,7 +1869,6 @@ void tf_task_destroy(tf_task_t* task) | ||||
| 	{ | ||||
| 		JSValue global = JS_GetGlobalObject(task->_context); | ||||
| 		JS_SetPropertyStr(task->_context, global, "httpd", JS_UNDEFINED); | ||||
| 		JS_SetPropertyStr(task->_context, global, "gProcesses", JS_NewObject(task->_context)); | ||||
| 		JS_FreeValue(task->_context, global); | ||||
| 	} | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user