const http = require('http')
const EventEmitter = require('events')
const context = require('./context')
const request = require('./request')
const response = require('./response')
class Koa extends EventEmitter {
constructor() {
super()
this.middlewares = []
this.context = context
this.request = request
this.response = response
}
listen(...args) {
http.createServer(this.callback()).listen(...args)
}
use(fn) {
this.middlewares.push(fn)
}
// 引人中间件
compose(ctx) {
const createAsync = function (fn, next) {
return async function () {
await fn(ctx, next)
}
}
let next = async function() { // 返回 Promise 对象, 从而进行后文 fn.then().catch() 调用
return Promise.resolve()
}
for (let i = this.middlewares.length - 1; i >= 0; i--) {
next = createAsync(this.middlewares[i], next)
}
return next()
}
callback() {
return (req, res) => {
const ctx = this.createCtx(req, res)
const handle = () => this.handleRes(ctx)
const errHandle = (err) => this.handleErr(err, ctx)
const fn = this.compose(ctx)
fn.then(handle).catch(errHandle)
}
}
// 将 req, res 封装进 ctx 对象中
createCtx(req, res) {
const ctx = Object.create(this.context)
ctx.request = Object.create(this.request)
ctx.response = Object.create(this.response)
ctx.req = ctx.request.req = req
ctx.res = ctx.response.res = res
return ctx
}
handleRes(ctx) {
if (typeof(ctx.body) === 'string') {
ctx.statusCode && ctx.res.writeHead(ctx.statusCode)
ctx.res.end(ctx.body)
} else if (typeof(ctx.body) === 'object') {
ctx.statusCode && ctx.res.writeHead(ctx.statusCode)
ctx.res.end(JSON.stringify(ctx.body))
}
}
// 引人错误机制
handleErr(err, ctx) {
if (err.code === 'ENOENT') {
ctx.statusCode = 404
} else {
ctx.statusCode = 500
}
const msg = err.message || 'Internal Error'
ctx.res.end(msg)
this.emit('error', err)
}
}
module.exports = Koa