Skip to main content
Module

x/http_server/lib/server.ts

🌐 Light-weight http server
Latest
File
import { Middleware, MiddlewareHandler } from './middleware.ts';import { Router } from './router.ts';import { Data } from './data/data.ts';
import { getContentType } from './utils/getContentType.ts';
type AddHandlers = (...handlers: MiddlewareHandler[]) => Server;
export class Server { #middleware = new Array<Middleware>(); #listeners = new Array<Middleware>(); #server: Deno.Listener | null = null
/** * Launch the server. * * @param port - The port to listen on. * @example * server.listen(); * server.listen(8080); */ async listen(port = 8080): Promise<void> { this.#server = Deno.listen({ port });
try { for await (const conn of this.#server!) (async () => { for await (const { request, respondWith } of Deno.serveHttp(conn)) { let responded = false;
this.#run(request, (response: Response) => { if (responded) return null; respondWith(response); responded = true; }); } })(); } catch { throw new Error('Something went wrong'); } }
#run(request: Request, respond: (resonse: Response) => void) { const data = new Data(request, respond); const handlers = [...this.#middleware, ...this.#listeners]; const used = new Set<number>();
function next(index = -1): () => void { if (used.has(index) || !handlers[index + 1]) return () => undefined
return async () => { if (!used.has(index)) await handlers[index + 1](data, next(index + 1)); used.add(index); next(index + 1)(); } }
next()(); }
/** * Listen for incoming requests. * @param route - The route to listen on. * @param method - The method to listen for. * @example * server.on()(({ respond }) => respond('Hello World')); * server.on('/')(({ respond }) => respond('Hello World')); * server.on('/', 'GET')(({ respond }) => respond('Hello World')); * * @returns A function that can later be used to add handlers. */ on(route = '/*', method = 'GET'): AddHandlers { /** * A function to add handlers. * * @param handlers - The handlers to add. * @example * server.on()(({ respond }) => respond('Hello World')); * server.on('/')(({ respond }) => respond('Hello World')); * server.on('/', 'GET')(({ respond }) => respond('Hello World')); */ return (...handlers: MiddlewareHandler[]) => { handlers.forEach(handler => this.#listeners.push(Middleware(method, route, handler)));
return this; }; } /** * Add middleware. * * @param route - The route to listen on. * @param method - The method to listen for. * @example * server.use()(() => console.log('Request')); * server.use('/')(() => console.log('Request')); * server.use('/', 'ANY')(() => console.log('Request')); * * @returns A function that can later be used to add handlers. */ use(route = '/*', method = 'ANY'): AddHandlers { /** * A function to add handlers. * * @param handlers - The handlers to add. * @example * server.use()(() => console.log('Request')); * server.use('/')(() => console.log('Request')); * server.use('/', 'ANY')(() => console.log('Request')); */ return (...handlers: MiddlewareHandler[] | Router[]): Server => { handlers[0] instanceof Router ? (handlers as Router[]).forEach(router => this.#middleware.push(...router.handlers())) : (handlers as MiddlewareHandler[]).forEach(handler => this.#middleware.push(Middleware(method, route, handler))); return this; } }
/** * Add a static file server. * * @param route - The route to serve on. * @param root - The path to serve files from. * @example * server.static(); * server.static('/files'); * server.static('/files', '/path/to/files'); */ static(route = '/*', root = ''): Server { route = `/${route.replace(/(\/\*)?$/, '/*')}`.replace(/\/+/g, '/'); this.use(route, 'GET')(async ({ request, respond }) => { const { path: upath } = request.url;
const base = route.split('/').filter((item) => item != '*' && item != ''); const rest = upath.replace(/^\//, '').split('/').filter((_, index) => index >= base.length); const path = `./${[root, ...rest].join('/')}`; await Deno.readFile(path) .then((content) => { respond({ body: content, headers: { 'content-type': getContentType(path) } }); }) .catch(() => respond({ status: 404 })); });
return this; }
/** * Close the server. * * @example * server.close(); */ close(): void { this.#server?.close(); }}