Skip to content
This repository has been archived by the owner on Jun 2, 2021. It is now read-only.

Commit

Permalink
rewrite with ES2015
Browse files Browse the repository at this point in the history
  • Loading branch information
zensh committed Aug 27, 2016
1 parent 26636e5 commit 8075b47
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 159 deletions.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: node_js
node_js:
- "0.12"
- "4"
- "5"
- "6"
Expand Down
165 changes: 81 additions & 84 deletions lib/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@
//
// **License:** MIT

var util = require('util')
var http = require('http')
var Stream = require('stream')
var thunks = require('thunks')
var accepts = require('accepts')
var Cookies = require('cookies')
var statuses = require('statuses')
var EventEmitter = require('events')

var contextProto = require('./context')
var requestProto = require('./request')
var responseProto = require('./response')
var packageInfo = require('../package.json')

var pwdReg = new RegExp(process.cwd().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
var OnErrorHeaderReg = /^(Accept|Allow|Retry-After|Warning|Access-Control-Allow-)/i
const util = require('util')
const http = require('http')
const Stream = require('stream')
const thunks = require('thunks')
const accepts = require('accepts')
const Cookies = require('cookies')
const statuses = require('statuses')
const EventEmitter = require('events')

const contextProto = require('./context')
const requestProto = require('./request')
const responseProto = require('./response')
const packageInfo = require('../package.json')

const pwdReg = new RegExp(process.cwd().replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g')
const OnErrorHeaderReg = /^(Accept|Allow|Retry-After|Warning|Access-Control-Allow-)/i

module.exports = Toa
Toa.NAME = packageInfo.name
Expand All @@ -27,7 +27,8 @@ Toa.AUTHORS = packageInfo.authors

function Toa (server, mainHandle, options) {
if (!(this instanceof Toa)) return new Toa(server, mainHandle, options)
var app = this
const app = this

this.middleware = []
this.request = Object.create(requestProto)
this.response = Object.create(responseProto)
Expand Down Expand Up @@ -91,24 +92,23 @@ function Toa (server, mainHandle, options) {
// this will improve concurrency performance
this._preEndHooks = [nextTick]

var context = this
this.on('error', this.onerror)

// socket error should be handled by server's 'clientError' listener then destroy
res.on('finish', function () {
if (context._finished == null) {
context._finished = true // context finished successfully
context.emit('finish')
res.on('finish', () => {
if (this._finished == null) {
this._finished = true // context finished successfully
this.emit('finish')
}
})
// Maybe no 'close' event on res, we should listen req.
req.on('close', function () {
if (context._finished == null) {
context._finished = false // context finished unexpectedly
context.emit('close')
req.on('close', () => {
if (this._finished == null) {
this._finished = false // context finished unexpectedly
this.emit('close')
}
})
}

Context.prototype = app.context
Context.prototype.constructor = Context
this.Context = Context
Expand All @@ -135,7 +135,7 @@ function Toa (server, mainHandle, options) {
this.errorHandle = isFn(options.onerror) ? options.onerror : null
}

var config = {
const config = {
proxy: false,
poweredBy: 'Toa',
subdomainOffset: 2,
Expand All @@ -144,15 +144,9 @@ function Toa (server, mainHandle, options) {

Object.defineProperty(this, 'config', {
enumerable: true,
get: function () {
return config
},
set: function (obj) {
var keys = Object.keys(obj || {})
if (!keys.length) throw new Error(util.inspect(obj) + ' is invalid config object.')
keys.forEach(function (key) {
config[key] = obj[key]
})
get: () => config,
set: (obj) => {
for (let key of Object.keys(obj)) config[key] = obj[key]
}
})
}
Expand Down Expand Up @@ -197,20 +191,20 @@ Toa.prototype.listen = function () {
* @api public
*/
Toa.prototype.toListener = function () {
var Context = this.Context
var debug = this.debug
var errorHandle = this.errorHandle || noOp
const Context = this.Context
const debug = this.debug
const errorHandle = this.errorHandle || noOp

var middleware = this.middleware.slice()
const middleware = this.middleware.slice()
if (this.mainHandle) middleware.push(toThunkableFn(this.mainHandle))
middleware.push(function (done) { this.seq(this.onPreEnd)(done) })

function worker (done) {
this.seq(middleware)(done)
function * worker () {
for (let fn of middleware) yield fn
for (let fn of this.onPreEnd) yield fn
}

return function requestListener (req, res) {
var ctx = new Context(req, res)
let ctx = new Context(req, res)
res.statusCode = 404
ctx.thunk = thunks(new Scope(ctx, errorHandle, debug))
ctx.seq = ctx.thunk.seq
Expand All @@ -228,50 +222,54 @@ Toa.prototype.onerror = function (err) {
// ignore null and response error
if (err.expose || (err.status && err.status < 500)) return
if (!util.isError(err)) err = new Error('non-error thrown: ' + util.inspect(err))
var msg = err.stack || err.toString()
let msg = err.stack || err.toString()
console.error(msg.replace(/^/gm, ' '))
}

function Scope (ctx, errorHandle, debug) {
this.ctx = ctx
this.errorHandle = errorHandle
this.debug = debug
this.stopped = false
}
util.inherits(Scope, thunks.Scope)

Scope.prototype.onstop = function (sig) {
var ctx = this.ctx
if (this.stopped) return ctx.onerror(ctx.createError(sig, 500))

this.stopped = true
if (ctx.status === 404) {
ctx.status = 418
ctx.message = sig.message
/**
* Scope of context.thunk.
*
* @api private
*/
class Scope extends thunks.Scope {
constructor (ctx, errorHandle, debug) {
super()
this.ctx = ctx
this.errorHandle = errorHandle
this.debug = debug
this.stopped = false
}
ctx.seq(ctx.onPreEnd)(respond)
}

Scope.prototype.onerror = function (err) {
var ctx = this.ctx
if (err == null) return
try {
err = this.errorHandle.call(ctx, err) || err
} catch (error) {
err = error
onstop (sig) {
let ctx = this.ctx
if (this.stopped) return ctx.onerror(ctx.createError(sig, 500))
this.stopped = true
if (ctx.status === 404) {
ctx.status = 418
ctx.message = sig.message
}
ctx.seq(ctx.onPreEnd)(respond)
}
onerror (err) {
let ctx = this.ctx
if (err == null) return
try {
err = this.errorHandle.call(ctx, err) || err
} catch (error) {
err = error
}
// if err === true, ignore err and response to client
if (err === true) respond.call(ctx)
else ctx.onerror(err)
}
// if err === true, ignore err and response to client
if (err === true) respond.call(ctx)
else ctx.onerror(err)
}

/**
* Response middleware.
*/
function respond () {
var res = this.res
var body = this.body
var code = this.status
let res = this.res
let body = this.body
let code = this.status

if (this.respond === false) return endContext(this)
if (res.headersSent || this._finished != null) return
Expand All @@ -296,8 +294,7 @@ function respond () {
body.pipe(res)
// to ensure `res.headersSent === true` before `context.emit("end")`
// if "error" occured before "data", it will goto `onResError(error)`
var ctx = this
body.once('data', function () { endContext(ctx) })
body.once('data', () => endContext(this))
return
} else {
// body: json
Expand Down Expand Up @@ -331,12 +328,12 @@ function onResError (err) {
return
}
// unset headers
var _headers = this.res._headers
let _headers = this.res._headers
if (_headers) {
// retain headers on error
Object.keys(_headers).map(function (key) {
for (let key of Object.keys(_headers)) {
if (!OnErrorHeaderReg.test(key)) delete _headers[key]
})
}
}
// force text/plain
this.type = 'text'
Expand All @@ -345,7 +342,7 @@ function onResError (err) {
else if (typeof err.status !== 'number' || !statuses[err.status]) this.status = 500
else this.status = err.status

var msg = err.expose ? err.message : statuses[this.status]
let msg = err.expose ? err.message : statuses[this.status]
// hide server directory for error response
this.body = msg.replace(pwdReg, '[application]')
respond.call(this)
Expand Down
47 changes: 25 additions & 22 deletions lib/context.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
'use strict'
// Modified from https://github.com/koajs/koa/tree/master/lib

var destroy = require('destroy')
var delegate = require('delegates')
var EventEmitter = require('events')
var createError = require('http-errors')
var IncomingMessage = require('http').IncomingMessage
var proto = module.exports = Object.create(EventEmitter.prototype)
const destroy = require('destroy')
const delegate = require('delegates')
const EventEmitter = require('events')
const createError = require('http-errors')
const IncomingMessage = require('http').IncomingMessage

/**
* context prototype.
*/
const proto = module.exports = Object.create(EventEmitter.prototype)

/**
* Create a error object, but don't throw
Expand Down Expand Up @@ -83,30 +87,21 @@ proto.end = function (message) {
* @api public
*/
proto.catchStream = function (stream) {
var ctx = this
if (stream.toaCleanHandle) throw new Error('"catchStream" has been applied on the stream')
stream.toaCleanHandle = toaCleanHandle
// makesure ctx.onerror not exist
// because new error handle will emit error to ctx.onerror
stream.removeListener('error', ctx.onerror)
stream.on('error', toaCleanHandle)
this.on('finish', toaCleanHandle)
this.on('close', toaCleanHandle)
return stream

function toaCleanHandle (err) {
let toaCleanHandle = (err) => {
stream.removeListener('error', toaCleanHandle)
ctx.removeListener('finish', toaCleanHandle)
ctx.removeListener('close', toaCleanHandle)
this.removeListener('finish', toaCleanHandle)
this.removeListener('close', toaCleanHandle)
stream.toaCleanHandle = null
// ensure that stream has the error listener to application
// stream may emit error even if destroyed,
// such as fs.createReadStream('not_exist_file')
if (!~stream.listeners('error').indexOf(ctx.onerror)) stream.on('error', ctx.onerror)
if (err != null) ctx.onerror(err)
else if (ctx._finished) {
if (!~stream.listeners('error').indexOf(this.onerror)) stream.on('error', this.onerror)
if (err != null) this.onerror(err)
else if (this._finished) {
// finished normally
var shouldKeepAlive = stream instanceof IncomingMessage && stream.req.shouldKeepAlive
let shouldKeepAlive = stream instanceof IncomingMessage && stream.req.shouldKeepAlive
// If incomingMessage stream is ended and agent is keepalived
// the socket should be disbanded, in order not to be destroy.
if (shouldKeepAlive && stream._readableState.ended === true) {
Expand All @@ -115,6 +110,14 @@ proto.catchStream = function (stream) {
}
destroy(stream)
}
stream.toaCleanHandle = toaCleanHandle
// makesure ctx.onerror not exist
// because new error handle will emit error to this.onerror
stream.removeListener('error', this.onerror)
stream.on('error', toaCleanHandle)
this.on('finish', toaCleanHandle)
this.on('close', toaCleanHandle)
return stream
}

/**
Expand Down

0 comments on commit 8075b47

Please sign in to comment.