2015-10-05 23:18:03 +00:00
2015-10-11 22:59:51 +00:00
'use strict' ;
2013-08-17 07:15:57 +00:00
/ * *
* Module dependencies .
* /
2015-10-14 00:45:18 +00:00
const isGeneratorFunction = require ( 'is-generator-function' ) ;
2015-10-05 18:23:47 +00:00
const debug = require ( 'debug' ) ( 'koa:application' ) ;
2015-10-24 15:30:25 +00:00
const Emitter = require ( 'events' ) ;
2015-10-05 18:23:47 +00:00
const onFinished = require ( 'on-finished' ) ;
const response = require ( './response' ) ;
const compose = require ( 'koa-compose' ) ;
const isJSON = require ( 'koa-is-json' ) ;
const context = require ( './context' ) ;
const request = require ( './request' ) ;
const statuses = require ( 'statuses' ) ;
const Cookies = require ( 'cookies' ) ;
const accepts = require ( 'accepts' ) ;
const assert = require ( 'assert' ) ;
const Stream = require ( 'stream' ) ;
const http = require ( 'http' ) ;
const only = require ( 'only' ) ;
2013-08-17 07:15:57 +00:00
/ * *
2015-10-13 06:19:42 +00:00
* Expose ` Application ` class .
* Inherits from ` Emitter.prototype ` .
2013-08-17 07:15:57 +00:00
* /
2015-10-13 06:19:42 +00:00
module . exports = class Application extends Emitter {
2013-08-17 07:15:57 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Initialize a new ` Application ` .
*
* @ api public
* /
2014-03-11 18:01:33 +00:00
2015-10-13 06:19:42 +00:00
constructor ( ) {
super ( ) ;
2014-03-11 18:01:33 +00:00
2015-10-29 16:51:11 +00:00
this . proxy = false ;
2015-10-13 06:19:42 +00:00
this . middleware = [ ] ;
2015-10-29 16:51:11 +00:00
this . subdomainOffset = 2 ;
this . env = process . env . NODE _ENV || 'development' ;
2015-10-13 06:19:42 +00:00
this . context = Object . create ( context ) ;
this . request = Object . create ( request ) ;
this . response = Object . create ( response ) ;
2014-12-18 02:06:45 +00:00
}
2013-08-17 07:15:57 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Shorthand for :
*
* http . createServer ( app . callback ( ) ) . listen ( ... )
*
* @ param { Mixed } ...
* @ return { Server }
* @ api public
* /
listen ( ) {
debug ( 'listen' ) ;
const server = http . createServer ( this . callback ( ) ) ;
return server . listen . apply ( server , arguments ) ;
}
2014-05-02 00:46:09 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Return JSON representation .
* We only bother showing settings .
*
* @ return { Object }
* @ api public
* /
toJSON ( ) {
return only ( this , [
'subdomainOffset' ,
2015-10-29 16:51:11 +00:00
'proxy' ,
2015-10-13 06:19:42 +00:00
'env'
] ) ;
2013-08-17 07:15:57 +00:00
}
2015-10-29 16:51:11 +00:00
/ * *
* Inspect implementation .
*
* @ return { Object }
* @ api public
* /
2015-10-13 06:19:42 +00:00
inspect ( ) {
return this . toJSON ( ) ;
}
2013-11-14 02:34:15 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Use the given middleware ` fn ` .
*
* @ param { GeneratorFunction } fn
* @ return { Application } self
* @ api public
* /
use ( fn ) {
debug ( 'use %s' , fn . _name || fn . name || '-' ) ;
2015-10-14 00:45:18 +00:00
if ( typeof fn !== 'function' ) throw new TypeError ( 'middleware must be a function!' ) ;
2015-10-29 17:25:46 +00:00
if ( isGeneratorFunction ( fn ) ) throw new TypeError ( 'Support for generators has been removed. See the documentation for examples of how to convert old middleware https://github.com/koajs/koa#example-with-old-signature' ) ;
2015-10-13 06:19:42 +00:00
this . middleware . push ( fn ) ;
return this ;
}
2013-11-14 02:34:15 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Return a request handler callback
* for node ' s native http server .
*
* @ return { Function }
* @ api public
* /
callback ( ) {
2015-10-14 00:45:18 +00:00
const fn = compose ( this . middleware ) ;
2015-10-13 06:19:42 +00:00
if ( ! this . listeners ( 'error' ) . length ) this . on ( 'error' , this . onerror ) ;
return ( req , res ) => {
res . statusCode = 404 ;
const ctx = this . createContext ( req , res ) ;
onFinished ( res , ctx . onerror ) ;
2015-10-24 14:19:56 +00:00
fn ( ctx ) . then ( ( ) => respond ( ctx ) ) . catch ( ctx . onerror ) ;
2015-10-12 20:36:03 +00:00
} ;
2015-10-13 06:19:42 +00:00
}
2013-08-22 02:47:56 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Initialize a new context .
*
* @ api private
* /
createContext ( req , res ) {
const context = Object . create ( this . context ) ;
const request = context . request = Object . create ( this . request ) ;
const response = context . response = Object . create ( this . response ) ;
context . app = request . app = response . app = this ;
context . req = request . req = response . req = req ;
context . res = request . res = response . res = res ;
request . ctx = response . ctx = context ;
request . response = response ;
response . request = request ;
context . onerror = context . onerror . bind ( context ) ;
context . originalUrl = request . originalUrl = req . url ;
context . cookies = new Cookies ( req , res , this . keys ) ;
context . accept = request . accept = accepts ( req ) ;
context . state = { } ;
return context ;
}
2014-05-23 13:15:03 +00:00
2015-10-13 06:19:42 +00:00
/ * *
* Default error handler .
*
* @ param { Error } err
* @ api private
* /
onerror ( err ) {
assert ( err instanceof Error , ` non-error thrown: ${ err } ` ) ;
if ( 404 == err . status || err . expose ) return ;
if ( this . silent ) return ;
const msg = err . stack || err . toString ( ) ;
console . error ( ) ;
console . error ( msg . replace ( /^/gm , ' ' ) ) ;
console . error ( ) ;
}
2014-05-23 13:15:03 +00:00
2015-10-12 20:36:03 +00:00
} ;
2013-08-22 02:47:56 +00:00
2013-08-17 07:15:57 +00:00
/ * *
2015-08-23 16:08:54 +00:00
* Response helper .
2013-08-17 07:15:57 +00:00
* /
2015-10-14 00:45:18 +00:00
function respond ( ctx ) {
2014-04-15 15:39:40 +00:00
// allow bypassing koa
2015-10-14 00:45:18 +00:00
if ( false === ctx . respond ) return ;
2014-01-24 22:38:40 +00:00
2015-10-14 00:45:18 +00:00
const res = ctx . res ;
if ( res . headersSent || ! ctx . writable ) return ;
2013-12-22 17:26:21 +00:00
2015-10-14 00:45:18 +00:00
let body = ctx . body ;
const code = ctx . status ;
2013-08-17 22:36:51 +00:00
2013-11-08 00:15:47 +00:00
// ignore body
2014-10-01 12:12:20 +00:00
if ( statuses . empty [ code ] ) {
2014-04-09 16:21:15 +00:00
// strip headers
2015-10-14 00:45:18 +00:00
ctx . body = null ;
2014-04-09 16:21:15 +00:00
return res . end ( ) ;
}
2013-08-17 07:15:57 +00:00
2015-10-14 00:45:18 +00:00
if ( 'HEAD' == ctx . method ) {
if ( isJSON ( body ) ) ctx . length = Buffer . byteLength ( JSON . stringify ( body ) ) ;
2014-04-15 02:06:32 +00:00
return res . end ( ) ;
}
2014-03-23 11:01:14 +00:00
2014-04-15 15:39:40 +00:00
// status body
2014-04-15 02:06:32 +00:00
if ( null == body ) {
2015-10-14 00:45:18 +00:00
ctx . type = 'text' ;
body = ctx . message || String ( code ) ;
ctx . length = Buffer . byteLength ( body ) ;
2014-04-13 02:23:57 +00:00
return res . end ( body ) ;
2013-11-08 00:15:47 +00:00
}
2013-08-17 07:15:57 +00:00
2014-04-15 15:39:40 +00:00
// responses
2014-04-15 02:06:32 +00:00
if ( Buffer . isBuffer ( body ) ) return res . end ( body ) ;
if ( 'string' == typeof body ) return res . end ( body ) ;
if ( body instanceof Stream ) return body . pipe ( res ) ;
2013-08-17 07:15:57 +00:00
2013-11-08 00:15:47 +00:00
// body: json
2014-02-01 02:39:47 +00:00
body = JSON . stringify ( body ) ;
2015-10-14 00:45:18 +00:00
ctx . length = Buffer . byteLength ( body ) ;
2013-11-08 00:15:47 +00:00
res . end ( body ) ;
2014-03-24 18:21:15 +00:00
}