diff options
Diffstat (limited to 'node_modules/koa/lib/application.js')
| -rw-r--r-- | node_modules/koa/lib/application.js | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/node_modules/koa/lib/application.js b/node_modules/koa/lib/application.js new file mode 100644 index 00000000..84107cb6 --- /dev/null +++ b/node_modules/koa/lib/application.js @@ -0,0 +1,244 @@ + +'use strict'; + +/** + * Module dependencies. + */ + +const isGeneratorFunction = require('is-generator-function'); +const debug = require('debug')('koa:application'); +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 Emitter = require('events'); +const util = require('util'); +const Stream = require('stream'); +const http = require('http'); +const only = require('only'); +const convert = require('koa-convert'); +const deprecate = require('depd')('koa'); + +/** + * Expose `Application` class. + * Inherits from `Emitter.prototype`. + */ + +module.exports = class Application extends Emitter { + /** + * Initialize a new `Application`. + * + * @api public + */ + + constructor() { + super(); + + this.proxy = false; + this.middleware = []; + this.subdomainOffset = 2; + this.env = process.env.NODE_ENV || 'development'; + this.context = Object.create(context); + this.request = Object.create(request); + this.response = Object.create(response); + if (util.inspect.custom) { + this[util.inspect.custom] = this.inspect; + } + } + + /** + * Shorthand for: + * + * http.createServer(app.callback()).listen(...) + * + * @param {Mixed} ... + * @return {Server} + * @api public + */ + + listen(...args) { + debug('listen'); + const server = http.createServer(this.callback()); + return server.listen(...args); + } + + /** + * Return JSON representation. + * We only bother showing settings. + * + * @return {Object} + * @api public + */ + + toJSON() { + return only(this, [ + 'subdomainOffset', + 'proxy', + 'env' + ]); + } + + /** + * Inspect implementation. + * + * @return {Object} + * @api public + */ + + inspect() { + return this.toJSON(); + } + + /** + * Use the given middleware `fn`. + * + * Old-style middleware will be converted. + * + * @param {Function} fn + * @return {Application} self + * @api public + */ + + use(fn) { + if (typeof fn !== 'function') throw new TypeError('middleware must be a function!'); + if (isGeneratorFunction(fn)) { + deprecate('Support for generators will be removed in v3. ' + + 'See the documentation for examples of how to convert old middleware ' + + 'https://github.com/koajs/koa/blob/master/docs/migration.md'); + fn = convert(fn); + } + debug('use %s', fn._name || fn.name || '-'); + this.middleware.push(fn); + return this; + } + + /** + * Return a request handler callback + * for node's native http server. + * + * @return {Function} + * @api public + */ + + callback() { + const fn = compose(this.middleware); + + if (!this.listenerCount('error')) this.on('error', this.onerror); + + const handleRequest = (req, res) => { + const ctx = this.createContext(req, res); + return this.handleRequest(ctx, fn); + }; + + return handleRequest; + } + + /** + * Handle request in callback. + * + * @api private + */ + + handleRequest(ctx, fnMiddleware) { + const res = ctx.res; + res.statusCode = 404; + const onerror = err => ctx.onerror(err); + const handleResponse = () => respond(ctx); + onFinished(res, onerror); + return fnMiddleware(ctx).then(handleResponse).catch(onerror); + } + + /** + * 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.originalUrl = request.originalUrl = req.url; + context.state = {}; + return context; + } + + /** + * Default error handler. + * + * @param {Error} err + * @api private + */ + + onerror(err) { + if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', 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(); + } +}; + +/** + * Response helper. + */ + +function respond(ctx) { + // allow bypassing koa + if (false === ctx.respond) return; + + const res = ctx.res; + if (!ctx.writable) return; + + let body = ctx.body; + const code = ctx.status; + + // ignore body + if (statuses.empty[code]) { + // strip headers + ctx.body = null; + return res.end(); + } + + if ('HEAD' == ctx.method) { + if (!res.headersSent && isJSON(body)) { + ctx.length = Buffer.byteLength(JSON.stringify(body)); + } + return res.end(); + } + + // status body + if (null == body) { + body = ctx.message || String(code); + if (!res.headersSent) { + ctx.type = 'text'; + ctx.length = Buffer.byteLength(body); + } + return res.end(body); + } + + // responses + if (Buffer.isBuffer(body)) return res.end(body); + if ('string' == typeof body) return res.end(body); + if (body instanceof Stream) return body.pipe(res); + + // body: json + body = JSON.stringify(body); + if (!res.headersSent) { + ctx.length = Buffer.byteLength(body); + } + res.end(body); +} |
