|
import fs from 'node:fs'; |
|
import path from 'node:path'; |
|
import { fileURLToPath } from 'node:url'; |
|
import mime from 'mime-types'; |
|
import { serverDirectory } from './server-directory.js'; |
|
|
|
const originalFetch = globalThis.fetch; |
|
|
|
const ALLOWED_EXTENSIONS = [ |
|
'.wasm', |
|
]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isPathUnderParent(parentPath, childPath) { |
|
const normalizedParent = path.normalize(parentPath); |
|
const normalizedChild = path.normalize(childPath); |
|
|
|
const relativePath = path.relative(normalizedParent, normalizedChild); |
|
|
|
return !relativePath.startsWith('..') && !path.isAbsolute(relativePath); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function isFileURL(request) { |
|
if (typeof request === 'string') { |
|
return request.startsWith('file://'); |
|
} |
|
if (request instanceof URL) { |
|
return request.protocol === 'file:'; |
|
} |
|
if (request instanceof Request) { |
|
return request.url.startsWith('file://'); |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
function getRequestURL(request) { |
|
if (typeof request === 'string') { |
|
return request; |
|
} |
|
if (request instanceof URL) { |
|
return request.href; |
|
} |
|
if (request instanceof Request) { |
|
return request.url; |
|
} |
|
throw new TypeError('Invalid request type'); |
|
} |
|
|
|
|
|
globalThis.fetch = async ( request, options) => { |
|
if (!isFileURL(request)) { |
|
return originalFetch(request, options); |
|
} |
|
const url = getRequestURL(request); |
|
const filePath = path.resolve(fileURLToPath(url)); |
|
const isUnderServerDirectory = isPathUnderParent(serverDirectory, filePath); |
|
if (!isUnderServerDirectory) { |
|
throw new Error('Requested file path is outside of the server directory.'); |
|
} |
|
const parsedPath = path.parse(filePath); |
|
if (!ALLOWED_EXTENSIONS.includes(parsedPath.ext)) { |
|
throw new Error('Unsupported file extension.'); |
|
} |
|
const fileName = parsedPath.base; |
|
const buffer = await fs.promises.readFile(filePath); |
|
const response = new Response(buffer, { |
|
status: 200, |
|
statusText: 'OK', |
|
headers: { |
|
'Content-Type': mime.lookup(fileName) || 'application/octet-stream', |
|
'Content-Length': buffer.length.toString(), |
|
}, |
|
}); |
|
return response; |
|
}; |
|
|