import { HttpRequest, HttpResponse } from "uWebSockets.js" import { MiddlewareEvents } from "./middlewares/interface" import * as _ from './utils/misc' import { APIContext, F2EConfigResult } from "./interface" import { MemoryTree } from "./memory-tree" import { createResponseHelper, getHttpHeaders } from "./utils/resp" import engine from "./server-engine" import logger from "./utils/logger" import { IncomingMessage } from "node:http" export const server_all = (conf: F2EConfigResult, events: Required, memory: MemoryTree.MemoryTree) => { const { onRoute, beforeRoute } = events const { handleDirectory, handleNotFound, handleSuccess, handleError } = createResponseHelper(conf) const execute = async (pathname: string, ctx: APIContext) => { const { resp } = ctx const routeResult = await onRoute(pathname, ctx) if (routeResult === false) { return } else if (typeof routeResult === 'string') { pathname = routeResult || pathname } pathname = _.pathname_fixer(pathname) const data = await memory.store.load(pathname) if (conf.page_dir != false && _.isPlainObject(data)) { handleDirectory(resp, pathname, data) } else if (typeof data === 'undefined') { handleNotFound(resp, pathname) } else if (typeof data === 'string' || Buffer.isBuffer(data)) { handleSuccess(ctx, pathname, data) } else { handleError(resp, 'Unknown Error!\n Wrong Data in memory') } } return async (resp: HttpResponse, req: HttpRequest) => { if (!validateHost(conf, req)) { return } const baseUrl = `http://${conf.host === '0.0.0.0' ? 'localhost' : conf.host}:${conf.port}` const location = new URL(req.getUrl() + '?' + (req.getQuery() || ''), baseUrl) let pathname = _.pathname_fixer(_.decode(location.pathname)) const headers = getHttpHeaders('request' in req ? (req.request as IncomingMessage) : req) const ctx: APIContext = { req, resp, location, pathname, store: memory.store, method: req.getMethod() || 'get', headers, responseHeaders: conf.headers || {}, } let pathnameTemp = await beforeRoute(pathname, ctx) if (pathnameTemp === false) { return } else if (typeof pathnameTemp === 'string') { pathname = pathnameTemp } const method = req.getMethod().toUpperCase() let body: Buffer | undefined if (method === 'POST' || method === 'PUT' || method === 'PATCH') { try { body = await engine.parseBody(req, resp, conf.max_body_size) } catch (error) { if (error instanceof Error && error.message.includes('too large')) { resp.writeStatus('413 Payload Too Large') resp.end(error.message) return } throw error } } try { await _.withTimeout(execute(pathname, { ...ctx, body }), conf.timeout, 'Request timeout') } catch (error) { if (error instanceof Error && error.message.includes('timeout')) { resp.writeStatus('408 Request Timeout') resp.end('Request timeout') return } throw error } } } const validateHost = (conf: F2EConfigResult, req: HttpRequest) => { const isDefaultPort = conf.ssl && conf.port === 443 || !conf.ssl && conf.port === 80 const host = req.getHeader('host') || '' const [hostname, port = ''] = host.split(':') || [] if (isDefaultPort && port != '' || Number(port) != conf.port) { logger.error('Wrong Port!') return false } if (conf.host != '0.0.0.0' && conf.host != hostname) { logger.error('Wrong Host!') return false } return true }