UNPKG

@tunframework/tun

Version:

tun framework for node with typescript

110 lines (109 loc) 3.9 kB
import { EventEmitter } from 'events'; import { createServer } from 'http'; import { RAW_RESPONSE } from './constants/http/symbol.js'; import { HttpError } from './HttpError.js'; import { compose } from './TunComposable.js'; import { TunContext } from './TunContext.js'; import { _stringifyTunCookie } from './TunCookie.js'; export class TunApplication extends EventEmitter { middleware = []; mountObj = {}; constructor() { super(); } use(fn) { Array.isArray(fn) ? this.middleware.push(...fn) : this.middleware.push(fn); return this; } mount(name, obj) { this.mountObj[name] = obj; } // unmount(name: string) { // delete this.mountObj[name]; // } callback() { return (_req, _res) => { const fn = compose(this.middleware); const ctx = new TunContext(_req, _res); // 挂载收集到的属性到ctx.state上 Object.assign(ctx.state, this.mountObj); return (Promise.resolve(fn(ctx)) .catch(genError) // finish 处理 设置到 应用上下文 的数据 // body 响应内容 .then((body) => dealResponse(ctx, body)) .catch((err) => dealResponse(ctx, err))); }; } listen(option) { const server = createServer(this.callback()); server.on('error', (err) => this.emit('error', err, server)); const host = process.env.HOST ? process.env.HOST : 'localhost'; const port = process.env.PORT ? parseInt(process.env.PORT) : 3000; let _option = { host, port, // path: '/', ...(option || {}) }; server.listen(_option); // Handle listening manual: // server.on('listening', () => { // let addr = (server.address() || {}) as AddressInfo // const url = 'http://' + [addr.address, addr.port].filter(Boolean).join(':') // console.log(`app listening: ${url}`) // }) return server; } } function genError(err) { if (err instanceof HttpError) { return err; } else { return new HttpError({ error: err }); } } async function dealResponse(ctx, _body) { const _res = ctx.res[RAW_RESPONSE]; // Return if response handled by other middleware if (_res.headersSent || _res.writableEnded) return; if (typeof _body !== 'undefined') { ctx.body = _body; } ctx.body = await ctx.body; if (ctx.body instanceof HttpError) { ctx.res.status = ctx.body.status; ctx.body = { status: ctx.res.status, message: ctx.body.message }; } else if (ctx.body instanceof Error) { ctx.res.status = 500; ctx.body = { status: ctx.res.status, message: ctx.body.message }; } // Write response header if (!(_res.headersSent || _res.writableEnded)) { if (typeof ctx.body !== 'undefined' && typeof ctx.body === 'object' && !ctx.res.type.rawType) { ctx.res.type = { rawType: 'application/json; charset=utf-8' }; } // Set response header for collected cookies const cookies = ctx.res.cookies.map(_stringifyTunCookie); _res.setHeader.call(_res, 'set-cookie', cookies); _res.writeHead(ctx.res.status, ctx.res.message); } // Write response body if (!_res.writableEnded) { if (ctx.body !== null && typeof ctx.body !== 'undefined') { // fixme: Shouldcheck other types. e.g. Buffer. if (typeof ctx.body === 'object') { _res.write(JSON.stringify(ctx.body)); } else { _res.write(ctx.body); } } _res.end(); } }