Move mime type shenanigans from JS => C.
This commit is contained in:
		@@ -22,7 +22,8 @@ class TfUserElement extends LitElement {
 | 
				
			|||||||
		let image = html`<span
 | 
							let image = html`<span
 | 
				
			||||||
			class="w3-theme-light w3-circle"
 | 
								class="w3-theme-light w3-circle"
 | 
				
			||||||
			style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
 | 
								style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
 | 
				
			||||||
		>?</span>`;
 | 
								>?</span
 | 
				
			||||||
 | 
							>`;
 | 
				
			||||||
		let name = this.users?.[this.id]?.name;
 | 
							let name = this.users?.[this.id]?.name;
 | 
				
			||||||
		name =
 | 
							name =
 | 
				
			||||||
			name !== undefined
 | 
								name !== undefined
 | 
				
			||||||
@@ -31,7 +32,8 @@ class TfUserElement extends LitElement {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if (this.users[this.id]) {
 | 
							if (this.users[this.id]) {
 | 
				
			||||||
			let image_link = this.users[this.id].image;
 | 
								let image_link = this.users[this.id].image;
 | 
				
			||||||
			image_link = typeof image_link == 'string' ? image_link : image_link?.link;
 | 
								image_link =
 | 
				
			||||||
 | 
									typeof image_link == 'string' ? image_link : image_link?.link;
 | 
				
			||||||
			if (image_link !== undefined) {
 | 
								if (image_link !== undefined) {
 | 
				
			||||||
				image = html`<img
 | 
									image = html`<img
 | 
				
			||||||
					class="w3-circle"
 | 
										class="w3-circle"
 | 
				
			||||||
@@ -41,8 +43,7 @@ class TfUserElement extends LitElement {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return html` <div style="display: inline-block; font-weight: bold">
 | 
							return html` <div style="display: inline-block; font-weight: bold">
 | 
				
			||||||
			${image}
 | 
								${image} ${name}
 | 
				
			||||||
			${name}
 | 
					 | 
				
			||||||
		</div>`;
 | 
							</div>`;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,7 +208,10 @@ class TfNavigationElement extends LitElement {
 | 
				
			|||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
			`;
 | 
								`;
 | 
				
			||||||
		} else if (this.credentials?.session?.name && this.credentials.session.name !== 'guest') {
 | 
							} else if (
 | 
				
			||||||
 | 
								this.credentials?.session?.name &&
 | 
				
			||||||
 | 
								this.credentials.session.name !== 'guest'
 | 
				
			||||||
 | 
							) {
 | 
				
			||||||
			return html`
 | 
								return html`
 | 
				
			||||||
				<link type="text/css" rel="stylesheet" href="/static/w3.css" />
 | 
									<link type="text/css" rel="stylesheet" href="/static/w3.css" />
 | 
				
			||||||
				<button
 | 
									<button
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										141
									
								
								core/core.js
									
									
									
									
									
								
							
							
						
						
									
										141
									
								
								core/core.js
									
									
									
									
									
								
							@@ -8,116 +8,6 @@ let gStatsTimer = false;
 | 
				
			|||||||
const k_content_security_policy =
 | 
					const k_content_security_policy =
 | 
				
			||||||
	'sandbox allow-downloads allow-top-navigation-by-user-activation';
 | 
						'sandbox allow-downloads allow-top-navigation-by-user-activation';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const k_mime_types = {
 | 
					 | 
				
			||||||
	css: 'text/css',
 | 
					 | 
				
			||||||
	html: 'text/html',
 | 
					 | 
				
			||||||
	js: 'text/javascript',
 | 
					 | 
				
			||||||
	json: 'text/json',
 | 
					 | 
				
			||||||
	map: 'application/json',
 | 
					 | 
				
			||||||
	svg: 'image/svg+xml',
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const k_magic_bytes = [
 | 
					 | 
				
			||||||
	{bytes: [0xff, 0xd8, 0xff, 0xdb], type: 'image/jpeg'},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bytes: [
 | 
					 | 
				
			||||||
			0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01,
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'image/jpeg',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{bytes: [0xff, 0xd8, 0xff, 0xee], type: 'image/jpeg'},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bytes: [
 | 
					 | 
				
			||||||
			0xff,
 | 
					 | 
				
			||||||
			0xd8,
 | 
					 | 
				
			||||||
			0xff,
 | 
					 | 
				
			||||||
			0xe1,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			0x45,
 | 
					 | 
				
			||||||
			0x78,
 | 
					 | 
				
			||||||
			0x69,
 | 
					 | 
				
			||||||
			0x66,
 | 
					 | 
				
			||||||
			0x00,
 | 
					 | 
				
			||||||
			0x00,
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'image/jpeg',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{bytes: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a], type: 'image/png'},
 | 
					 | 
				
			||||||
	{bytes: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61], type: 'image/gif'},
 | 
					 | 
				
			||||||
	{bytes: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61], type: 'image/gif'},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bytes: [
 | 
					 | 
				
			||||||
			0x52,
 | 
					 | 
				
			||||||
			0x49,
 | 
					 | 
				
			||||||
			0x46,
 | 
					 | 
				
			||||||
			0x46,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			0x57,
 | 
					 | 
				
			||||||
			0x45,
 | 
					 | 
				
			||||||
			0x42,
 | 
					 | 
				
			||||||
			0x50,
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'image/webp',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{bytes: [0x3c, 0x73, 0x76, 0x67], type: 'image/svg+xml'},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bytes: [
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			0x66,
 | 
					 | 
				
			||||||
			0x74,
 | 
					 | 
				
			||||||
			0x79,
 | 
					 | 
				
			||||||
			0x70,
 | 
					 | 
				
			||||||
			0x6d,
 | 
					 | 
				
			||||||
			0x70,
 | 
					 | 
				
			||||||
			0x34,
 | 
					 | 
				
			||||||
			0x32,
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'audio/mpeg',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bytes: [
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			0x66,
 | 
					 | 
				
			||||||
			0x74,
 | 
					 | 
				
			||||||
			0x79,
 | 
					 | 
				
			||||||
			0x70,
 | 
					 | 
				
			||||||
			0x69,
 | 
					 | 
				
			||||||
			0x73,
 | 
					 | 
				
			||||||
			0x6f,
 | 
					 | 
				
			||||||
			0x6d,
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'video/mp4',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		bytes: [
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			null,
 | 
					 | 
				
			||||||
			0x66,
 | 
					 | 
				
			||||||
			0x74,
 | 
					 | 
				
			||||||
			0x79,
 | 
					 | 
				
			||||||
			0x70,
 | 
					 | 
				
			||||||
			0x6d,
 | 
					 | 
				
			||||||
			0x70,
 | 
					 | 
				
			||||||
			0x34,
 | 
					 | 
				
			||||||
			0x32,
 | 
					 | 
				
			||||||
		],
 | 
					 | 
				
			||||||
		type: 'video/mp4',
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	{bytes: [0x4d, 0x54, 0x68, 0x64], type: 'audio/midi'},
 | 
					 | 
				
			||||||
];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let k_static_files = [
 | 
					let k_static_files = [
 | 
				
			||||||
	{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
 | 
						{uri: '/', path: 'index.html', type: 'text/html; charset=UTF-8'},
 | 
				
			||||||
];
 | 
					];
 | 
				
			||||||
@@ -941,29 +831,6 @@ function startsWithBytes(data, bytes) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * TODOC
 | 
					 | 
				
			||||||
 * @param {*} path
 | 
					 | 
				
			||||||
 * @returns
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
function guessTypeFromName(path) {
 | 
					 | 
				
			||||||
	let extension = path.split('.').pop();
 | 
					 | 
				
			||||||
	return k_mime_types[extension];
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * TODOC
 | 
					 | 
				
			||||||
 * @param {*} data
 | 
					 | 
				
			||||||
 * @returns
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
function guessTypeFromMagicBytes(data) {
 | 
					 | 
				
			||||||
	for (let magic of k_magic_bytes) {
 | 
					 | 
				
			||||||
		if (startsWithBytes(data, magic.bytes)) {
 | 
					 | 
				
			||||||
			return magic.type;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * TODOC
 | 
					 * TODOC
 | 
				
			||||||
 * @param {*} response
 | 
					 * @param {*} response
 | 
				
			||||||
@@ -979,7 +846,9 @@ function sendData(response, data, type, headers, status_code) {
 | 
				
			|||||||
			Object.assign(
 | 
								Object.assign(
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					'Content-Type':
 | 
										'Content-Type':
 | 
				
			||||||
						type || guessTypeFromMagicBytes(data) || 'application/binary',
 | 
											type ||
 | 
				
			||||||
 | 
											httpd.mime_type_from_magic_bytes(data) ||
 | 
				
			||||||
 | 
											'application/binary',
 | 
				
			||||||
					'Content-Length': data.byteLength,
 | 
										'Content-Length': data.byteLength,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
				headers || {}
 | 
									headers || {}
 | 
				
			||||||
@@ -1348,7 +1217,9 @@ async function blobHandler(request, response, blobId, uri) {
 | 
				
			|||||||
					'Content-Security-Policy': k_content_security_policy,
 | 
										'Content-Security-Policy': k_content_security_policy,
 | 
				
			||||||
				};
 | 
									};
 | 
				
			||||||
				data = await getBlobOrContent(id);
 | 
									data = await getBlobOrContent(id);
 | 
				
			||||||
				let type = guessTypeFromName(uri) || guessTypeFromMagicBytes(data);
 | 
									let type =
 | 
				
			||||||
 | 
										httpd.mime_type_from_extension(uri) ||
 | 
				
			||||||
 | 
										httpd.mime_type_from_magic_bytes(data);
 | 
				
			||||||
				sendData(response, data, type, headers);
 | 
									sendData(response, data, type, headers);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										175
									
								
								src/httpd.js.c
									
									
									
									
									
								
							
							
						
						
									
										175
									
								
								src/httpd.js.c
									
									
									
									
									
								
							@@ -498,6 +498,151 @@ static JSValue _httpd_auth_query(JSContext* context, JSValueConst this_val, int
 | 
				
			|||||||
	return result;
 | 
						return result;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct _magic_bytes_t
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* type;
 | 
				
			||||||
 | 
						uint8_t bytes[12];
 | 
				
			||||||
 | 
						uint8_t ignore[12];
 | 
				
			||||||
 | 
					} magic_bytes_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static bool _magic_bytes_match(const magic_bytes_t* magic, const uint8_t* actual, size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (size < sizeof(magic->bytes))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int length = (int)tf_min(sizeof(magic->bytes), size);
 | 
				
			||||||
 | 
						for (int i = 0; i < length; i++)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if ((magic->bytes[i] & ~magic->ignore[i]) != (actual[i] & ~magic->ignore[i]))
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static JSValue _httpd_mime_type_from_magic_bytes(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						JSValue result = JS_UNDEFINED;
 | 
				
			||||||
 | 
						size_t size = 0;
 | 
				
			||||||
 | 
						uint8_t* bytes = tf_util_try_get_array_buffer(context, &size, argv[0]);
 | 
				
			||||||
 | 
						if (bytes)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const magic_bytes_t k_magic_bytes[] = {
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/jpeg",
 | 
				
			||||||
 | 
									.bytes = { 0xff, 0xd8, 0xff, 0xdb },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/jpeg",
 | 
				
			||||||
 | 
									.bytes = { 0xff, 0xd8, 0xff, 0xe0, 0x00, 0x10, 0x4a, 0x46, 0x49, 0x46, 0x00, 0x01 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/jpeg",
 | 
				
			||||||
 | 
									.bytes = { 0xff, 0xd8, 0xff, 0xee },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/jpeg",
 | 
				
			||||||
 | 
									.bytes = { 0xff, 0xd8, 0xff, 0xe1, 0x00, 0x00, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00 },
 | 
				
			||||||
 | 
									.ignore = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/png",
 | 
				
			||||||
 | 
									.bytes = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/gif",
 | 
				
			||||||
 | 
									.bytes = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/gif",
 | 
				
			||||||
 | 
									.bytes = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/webp",
 | 
				
			||||||
 | 
									.bytes = { 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50 },
 | 
				
			||||||
 | 
									.ignore = { 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "image/svg+xml",
 | 
				
			||||||
 | 
									.bytes = { 0x3c, 0x73, 0x76, 0x67 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "audio/mpeg",
 | 
				
			||||||
 | 
									.bytes = { 0x00, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32 },
 | 
				
			||||||
 | 
									.ignore = { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "video/mp4",
 | 
				
			||||||
 | 
									.bytes = { 0x00, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d },
 | 
				
			||||||
 | 
									.ignore = { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "video/mp4",
 | 
				
			||||||
 | 
									.bytes = { 0x00, 0x00, 0x00, 0x00, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32 },
 | 
				
			||||||
 | 
									.ignore = { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									.type = "audio/midi",
 | 
				
			||||||
 | 
									.bytes = { 0x4d, 0x54, 0x68, 0x64 },
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int i = 0; i < tf_countof(k_magic_bytes); i++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (_magic_bytes_match(&k_magic_bytes[i], bytes, size))
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									result = JS_NewString(context, k_magic_bytes[i].type);
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const char* _ext_to_content_type(const char* ext, bool use_fallback)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ext)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							typedef struct _ext_type_t
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								const char* ext;
 | 
				
			||||||
 | 
								const char* type;
 | 
				
			||||||
 | 
							} ext_type_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const ext_type_t k_types[] = {
 | 
				
			||||||
 | 
								{ .ext = ".html", .type = "text/html; charset=UTF-8" },
 | 
				
			||||||
 | 
								{ .ext = ".js", .type = "text/javascript; charset=UTF-8" },
 | 
				
			||||||
 | 
								{ .ext = ".mjs", .type = "text/javascript; charset=UTF-8" },
 | 
				
			||||||
 | 
								{ .ext = ".css", .type = "text/css; charset=UTF-8" },
 | 
				
			||||||
 | 
								{ .ext = ".png", .type = "image/png" },
 | 
				
			||||||
 | 
								{ .ext = ".json", .type = "application/json" },
 | 
				
			||||||
 | 
								{ .ext = ".map", .type = "application/json" },
 | 
				
			||||||
 | 
								{ .ext = ".svg", .type = "image/svg+xml" },
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (int i = 0; i < tf_countof(k_types); i++)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (strcmp(ext, k_types[i].ext) == 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									return k_types[i].type;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return use_fallback ? "application/binary" : NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static JSValue _httpd_mime_type_from_extension(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char* name = JS_ToCString(context, argv[0]);
 | 
				
			||||||
 | 
						const char* type = _ext_to_content_type(strrchr(name, '.'), false);
 | 
				
			||||||
 | 
						JS_FreeCString(context, name);
 | 
				
			||||||
 | 
						return type ? JS_NewString(context, type) : JS_UNDEFINED;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void _httpd_finalizer(JSRuntime* runtime, JSValue value)
 | 
					static void _httpd_finalizer(JSRuntime* runtime, JSValue value)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	tf_http_t* http = JS_GetOpaque(value, _httpd_class_id);
 | 
						tf_http_t* http = JS_GetOpaque(value, _httpd_class_id);
 | 
				
			||||||
@@ -627,30 +772,6 @@ typedef struct _http_file_t
 | 
				
			|||||||
	char etag[512];
 | 
						char etag[512];
 | 
				
			||||||
} http_file_t;
 | 
					} http_file_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const char* _ext_to_content_type(const char* ext)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (ext)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (strcmp(ext, ".html") == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return "text/html; charset=UTF-8";
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else if (strcmp(ext, ".js") == 0 || strcmp(ext, ".mjs") == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return "text/javascript; charset=UTF-8";
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else if (strcmp(ext, ".css") == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return "text/css; charset=UTF-8";
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		else if (strcmp(ext, ".png") == 0)
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			return "image/png";
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return "application/binary";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void _httpd_endpoint_static_read(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
 | 
					static void _httpd_endpoint_static_read(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	http_file_t* file = user_data;
 | 
						http_file_t* file = user_data;
 | 
				
			||||||
@@ -659,7 +780,7 @@ static void _httpd_endpoint_static_read(tf_task_t* task, const char* path, int r
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		if (strcmp(path, "core/tfrpc.js") == 0)
 | 
							if (strcmp(path, "core/tfrpc.js") == 0)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			const char* content_type = _ext_to_content_type(strrchr(path, '.'));
 | 
								const char* content_type = _ext_to_content_type(strrchr(path, '.'), true);
 | 
				
			||||||
			const char* headers[] = {
 | 
								const char* headers[] = {
 | 
				
			||||||
				"Content-Type",
 | 
									"Content-Type",
 | 
				
			||||||
				content_type,
 | 
									content_type,
 | 
				
			||||||
@@ -672,7 +793,7 @@ static void _httpd_endpoint_static_read(tf_task_t* task, const char* path, int r
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			const char* content_type = _ext_to_content_type(strrchr(path, '.'));
 | 
								const char* content_type = _ext_to_content_type(strrchr(path, '.'), true);
 | 
				
			||||||
			const char* headers[] = {
 | 
								const char* headers[] = {
 | 
				
			||||||
				"Content-Type",
 | 
									"Content-Type",
 | 
				
			||||||
				content_type,
 | 
									content_type,
 | 
				
			||||||
@@ -1519,6 +1640,8 @@ void tf_httpd_register(JSContext* context)
 | 
				
			|||||||
	JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_endpoint_start, "start", 2));
 | 
						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, "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, httpd, "mime_type_from_magic_bytes", JS_NewCFunction(context, _httpd_mime_type_from_magic_bytes, "mime_type_from_magic_bytes", 1));
 | 
				
			||||||
 | 
						JS_SetPropertyStr(context, httpd, "mime_type_from_extension", JS_NewCFunction(context, _httpd_mime_type_from_extension, "mime_type_from_extension", 1));
 | 
				
			||||||
	JS_SetPropertyStr(context, global, "httpd", httpd);
 | 
						JS_SetPropertyStr(context, global, "httpd", httpd);
 | 
				
			||||||
	JS_FreeValue(context, global);
 | 
						JS_FreeValue(context, global);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user