aboutsummaryrefslogtreecommitdiff
path: root/node_modules/koa/lib
diff options
context:
space:
mode:
authorruki <waruqi@gmail.com>2018-11-08 00:38:48 +0800
committerruki <waruqi@gmail.com>2018-11-07 21:53:09 +0800
commit26105034da4fcce7ac883c899d781f016559310d (patch)
treec459a5dc4e3aa0972d9919033ece511ce76dd129 /node_modules/koa/lib
parent2c77f00f1a7ecb6c8192f9c16d3b2001b254a107 (diff)
downloadxmake-docs-26105034da4fcce7ac883c899d781f016559310d.tar.gz
xmake-docs-26105034da4fcce7ac883c899d781f016559310d.zip
switch to vuepress
Diffstat (limited to 'node_modules/koa/lib')
-rw-r--r--node_modules/koa/lib/application.js244
-rw-r--r--node_modules/koa/lib/context.js241
-rw-r--r--node_modules/koa/lib/request.js727
-rw-r--r--node_modules/koa/lib/response.js558
4 files changed, 1770 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);
+}
diff --git a/node_modules/koa/lib/context.js b/node_modules/koa/lib/context.js
new file mode 100644
index 00000000..1b92f018
--- /dev/null
+++ b/node_modules/koa/lib/context.js
@@ -0,0 +1,241 @@
+
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+const util = require('util');
+const createError = require('http-errors');
+const httpAssert = require('http-assert');
+const delegate = require('delegates');
+const statuses = require('statuses');
+const Cookies = require('cookies');
+
+const COOKIES = Symbol('context#cookies');
+
+/**
+ * Context prototype.
+ */
+
+const proto = module.exports = {
+
+ /**
+ * util.inspect() implementation, which
+ * just returns the JSON output.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ inspect() {
+ if (this === proto) return this;
+ return this.toJSON();
+ },
+
+ /**
+ * Return JSON representation.
+ *
+ * Here we explicitly invoke .toJSON() on each
+ * object, as iteration will otherwise fail due
+ * to the getters and cause utilities such as
+ * clone() to fail.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ toJSON() {
+ return {
+ request: this.request.toJSON(),
+ response: this.response.toJSON(),
+ app: this.app.toJSON(),
+ originalUrl: this.originalUrl,
+ req: '<original node req>',
+ res: '<original node res>',
+ socket: '<original node socket>'
+ };
+ },
+
+ /**
+ * Similar to .throw(), adds assertion.
+ *
+ * this.assert(this.user, 401, 'Please login!');
+ *
+ * See: https://github.com/jshttp/http-assert
+ *
+ * @param {Mixed} test
+ * @param {Number} status
+ * @param {String} message
+ * @api public
+ */
+
+ assert: httpAssert,
+
+ /**
+ * Throw an error with `msg` and optional `status`
+ * defaulting to 500. Note that these are user-level
+ * errors, and the message may be exposed to the client.
+ *
+ * this.throw(403)
+ * this.throw('name required', 400)
+ * this.throw(400, 'name required')
+ * this.throw('something exploded')
+ * this.throw(new Error('invalid'), 400);
+ * this.throw(400, new Error('invalid'));
+ *
+ * See: https://github.com/jshttp/http-errors
+ *
+ * @param {String|Number|Error} err, msg or status
+ * @param {String|Number|Error} [err, msg or status]
+ * @param {Object} [props]
+ * @api public
+ */
+
+ throw(...args) {
+ throw createError(...args);
+ },
+
+ /**
+ * Default error handling.
+ *
+ * @param {Error} err
+ * @api private
+ */
+
+ onerror(err) {
+ // don't do anything if there is no error.
+ // this allows you to pass `this.onerror`
+ // to node-style callbacks.
+ if (null == err) return;
+
+ if (!(err instanceof Error)) err = new Error(util.format('non-error thrown: %j', err));
+
+ let headerSent = false;
+ if (this.headerSent || !this.writable) {
+ headerSent = err.headerSent = true;
+ }
+
+ // delegate
+ this.app.emit('error', err, this);
+
+ // nothing we can do here other
+ // than delegate to the app-level
+ // handler and log.
+ if (headerSent) {
+ return;
+ }
+
+ const { res } = this;
+
+ // first unset all headers
+ /* istanbul ignore else */
+ if (typeof res.getHeaderNames === 'function') {
+ res.getHeaderNames().forEach(name => res.removeHeader(name));
+ } else {
+ res._headers = {}; // Node < 7.7
+ }
+
+ // then set those specified
+ this.set(err.headers);
+
+ // force text/plain
+ this.type = 'text';
+
+ // ENOENT support
+ if ('ENOENT' == err.code) err.status = 404;
+
+ // default to 500
+ if ('number' != typeof err.status || !statuses[err.status]) err.status = 500;
+
+ // respond
+ const code = statuses[err.status];
+ const msg = err.expose ? err.message : code;
+ this.status = err.status;
+ this.length = Buffer.byteLength(msg);
+ this.res.end(msg);
+ },
+
+ get cookies() {
+ if (!this[COOKIES]) {
+ this[COOKIES] = new Cookies(this.req, this.res, {
+ keys: this.app.keys,
+ secure: this.request.secure
+ });
+ }
+ return this[COOKIES];
+ },
+
+ set cookies(_cookies) {
+ this[COOKIES] = _cookies;
+ }
+};
+
+/**
+ * Custom inspection implementation for newer Node.js versions.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+/* istanbul ignore else */
+if (util.inspect.custom) {
+ module.exports[util.inspect.custom] = module.exports.inspect;
+}
+
+/**
+ * Response delegation.
+ */
+
+delegate(proto, 'response')
+ .method('attachment')
+ .method('redirect')
+ .method('remove')
+ .method('vary')
+ .method('set')
+ .method('append')
+ .method('flushHeaders')
+ .access('status')
+ .access('message')
+ .access('body')
+ .access('length')
+ .access('type')
+ .access('lastModified')
+ .access('etag')
+ .getter('headerSent')
+ .getter('writable');
+
+/**
+ * Request delegation.
+ */
+
+delegate(proto, 'request')
+ .method('acceptsLanguages')
+ .method('acceptsEncodings')
+ .method('acceptsCharsets')
+ .method('accepts')
+ .method('get')
+ .method('is')
+ .access('querystring')
+ .access('idempotent')
+ .access('socket')
+ .access('search')
+ .access('method')
+ .access('query')
+ .access('path')
+ .access('url')
+ .access('accept')
+ .getter('origin')
+ .getter('href')
+ .getter('subdomains')
+ .getter('protocol')
+ .getter('host')
+ .getter('hostname')
+ .getter('URL')
+ .getter('header')
+ .getter('headers')
+ .getter('secure')
+ .getter('stale')
+ .getter('fresh')
+ .getter('ips')
+ .getter('ip');
diff --git a/node_modules/koa/lib/request.js b/node_modules/koa/lib/request.js
new file mode 100644
index 00000000..0b0b2625
--- /dev/null
+++ b/node_modules/koa/lib/request.js
@@ -0,0 +1,727 @@
+
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+const URL = require('url').URL;
+const net = require('net');
+const accepts = require('accepts');
+const contentType = require('content-type');
+const stringify = require('url').format;
+const parse = require('parseurl');
+const qs = require('querystring');
+const typeis = require('type-is');
+const fresh = require('fresh');
+const only = require('only');
+const util = require('util');
+
+const IP = Symbol('context#ip');
+
+/**
+ * Prototype.
+ */
+
+module.exports = {
+
+ /**
+ * Return request header.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ get header() {
+ return this.req.headers;
+ },
+
+ /**
+ * Set request header.
+ *
+ * @api public
+ */
+
+ set header(val) {
+ this.req.headers = val;
+ },
+
+ /**
+ * Return request header, alias as request.header
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ get headers() {
+ return this.req.headers;
+ },
+
+ /**
+ * Set request header, alias as request.header
+ *
+ * @api public
+ */
+
+ set headers(val) {
+ this.req.headers = val;
+ },
+
+ /**
+ * Get request URL.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get url() {
+ return this.req.url;
+ },
+
+ /**
+ * Set request URL.
+ *
+ * @api public
+ */
+
+ set url(val) {
+ this.req.url = val;
+ },
+
+ /**
+ * Get origin of URL.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get origin() {
+ return `${this.protocol}://${this.host}`;
+ },
+
+ /**
+ * Get full request URL.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get href() {
+ // support: `GET http://example.com/foo`
+ if (/^https?:\/\//i.test(this.originalUrl)) return this.originalUrl;
+ return this.origin + this.originalUrl;
+ },
+
+ /**
+ * Get request method.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get method() {
+ return this.req.method;
+ },
+
+ /**
+ * Set request method.
+ *
+ * @param {String} val
+ * @api public
+ */
+
+ set method(val) {
+ this.req.method = val;
+ },
+
+ /**
+ * Get request pathname.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get path() {
+ return parse(this.req).pathname;
+ },
+
+ /**
+ * Set pathname, retaining the query-string when present.
+ *
+ * @param {String} path
+ * @api public
+ */
+
+ set path(path) {
+ const url = parse(this.req);
+ if (url.pathname === path) return;
+
+ url.pathname = path;
+ url.path = null;
+
+ this.url = stringify(url);
+ },
+
+ /**
+ * Get parsed query-string.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ get query() {
+ const str = this.querystring;
+ const c = this._querycache = this._querycache || {};
+ return c[str] || (c[str] = qs.parse(str));
+ },
+
+ /**
+ * Set query-string as an object.
+ *
+ * @param {Object} obj
+ * @api public
+ */
+
+ set query(obj) {
+ this.querystring = qs.stringify(obj);
+ },
+
+ /**
+ * Get query string.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get querystring() {
+ if (!this.req) return '';
+ return parse(this.req).query || '';
+ },
+
+ /**
+ * Set querystring.
+ *
+ * @param {String} str
+ * @api public
+ */
+
+ set querystring(str) {
+ const url = parse(this.req);
+ if (url.search === `?${str}`) return;
+
+ url.search = str;
+ url.path = null;
+
+ this.url = stringify(url);
+ },
+
+ /**
+ * Get the search string. Same as the querystring
+ * except it includes the leading ?.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get search() {
+ if (!this.querystring) return '';
+ return `?${this.querystring}`;
+ },
+
+ /**
+ * Set the search string. Same as
+ * request.querystring= but included for ubiquity.
+ *
+ * @param {String} str
+ * @api public
+ */
+
+ set search(str) {
+ this.querystring = str;
+ },
+
+ /**
+ * Parse the "Host" header field host
+ * and support X-Forwarded-Host when a
+ * proxy is enabled.
+ *
+ * @return {String} hostname:port
+ * @api public
+ */
+
+ get host() {
+ const proxy = this.app.proxy;
+ let host = proxy && this.get('X-Forwarded-Host');
+ if (!host) {
+ if (this.req.httpVersionMajor >= 2) host = this.get(':authority');
+ if (!host) host = this.get('Host');
+ }
+ if (!host) return '';
+ return host.split(/\s*,\s*/)[0];
+ },
+
+ /**
+ * Parse the "Host" header field hostname
+ * and support X-Forwarded-Host when a
+ * proxy is enabled.
+ *
+ * @return {String} hostname
+ * @api public
+ */
+
+ get hostname() {
+ const host = this.host;
+ if (!host) return '';
+ if ('[' == host[0]) return this.URL.hostname || ''; // IPv6
+ return host.split(':')[0];
+ },
+
+ /**
+ * Get WHATWG parsed URL.
+ * Lazily memoized.
+ *
+ * @return {URL|Object}
+ * @api public
+ */
+
+ get URL() {
+ /* istanbul ignore else */
+ if (!this.memoizedURL) {
+ const protocol = this.protocol;
+ const host = this.host;
+ const originalUrl = this.originalUrl || ''; // avoid undefined in template string
+ try {
+ this.memoizedURL = new URL(`${protocol}://${host}${originalUrl}`);
+ } catch (err) {
+ this.memoizedURL = Object.create(null);
+ }
+ }
+ return this.memoizedURL;
+ },
+
+ /**
+ * Check if the request is fresh, aka
+ * Last-Modified and/or the ETag
+ * still match.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ get fresh() {
+ const method = this.method;
+ const s = this.ctx.status;
+
+ // GET or HEAD for weak freshness validation only
+ if ('GET' != method && 'HEAD' != method) return false;
+
+ // 2xx or 304 as per rfc2616 14.26
+ if ((s >= 200 && s < 300) || 304 == s) {
+ return fresh(this.header, this.response.header);
+ }
+
+ return false;
+ },
+
+ /**
+ * Check if the request is stale, aka
+ * "Last-Modified" and / or the "ETag" for the
+ * resource has changed.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ get stale() {
+ return !this.fresh;
+ },
+
+ /**
+ * Check if the request is idempotent.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ get idempotent() {
+ const methods = ['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'];
+ return !!~methods.indexOf(this.method);
+ },
+
+ /**
+ * Return the request socket.
+ *
+ * @return {Connection}
+ * @api public
+ */
+
+ get socket() {
+ return this.req.socket;
+ },
+
+ /**
+ * Get the charset when present or undefined.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get charset() {
+ let type = this.get('Content-Type');
+ if (!type) return '';
+
+ try {
+ type = contentType.parse(type);
+ } catch (e) {
+ return '';
+ }
+
+ return type.parameters.charset || '';
+ },
+
+ /**
+ * Return parsed Content-Length when present.
+ *
+ * @return {Number}
+ * @api public
+ */
+
+ get length() {
+ const len = this.get('Content-Length');
+ if (len == '') return;
+ return ~~len;
+ },
+
+ /**
+ * Return the protocol string "http" or "https"
+ * when requested with TLS. When the proxy setting
+ * is enabled the "X-Forwarded-Proto" header
+ * field will be trusted. If you're running behind
+ * a reverse proxy that supplies https for you this
+ * may be enabled.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get protocol() {
+ if (this.socket.encrypted) return 'https';
+ if (!this.app.proxy) return 'http';
+ const proto = this.get('X-Forwarded-Proto');
+ return proto ? proto.split(/\s*,\s*/)[0] : 'http';
+ },
+
+ /**
+ * Short-hand for:
+ *
+ * this.protocol == 'https'
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ get secure() {
+ return 'https' == this.protocol;
+ },
+
+ /**
+ * When `app.proxy` is `true`, parse
+ * the "X-Forwarded-For" ip address list.
+ *
+ * For example if the value were "client, proxy1, proxy2"
+ * you would receive the array `["client", "proxy1", "proxy2"]`
+ * where "proxy2" is the furthest down-stream.
+ *
+ * @return {Array}
+ * @api public
+ */
+
+ get ips() {
+ const proxy = this.app.proxy;
+ const val = this.get('X-Forwarded-For');
+ return proxy && val
+ ? val.split(/\s*,\s*/)
+ : [];
+ },
+
+ /**
+ * Return request's remote address
+ * When `app.proxy` is `true`, parse
+ * the "X-Forwarded-For" ip address list and return the first one
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get ip() {
+ if (!this[IP]) {
+ this[IP] = this.ips[0] || this.socket.remoteAddress || '';
+ }
+ return this[IP];
+ },
+
+ set ip(_ip) {
+ this[IP] = _ip;
+ },
+
+ /**
+ * Return subdomains as an array.
+ *
+ * Subdomains are the dot-separated parts of the host before the main domain
+ * of the app. By default, the domain of the app is assumed to be the last two
+ * parts of the host. This can be changed by setting `app.subdomainOffset`.
+ *
+ * For example, if the domain is "tobi.ferrets.example.com":
+ * If `app.subdomainOffset` is not set, this.subdomains is
+ * `["ferrets", "tobi"]`.
+ * If `app.subdomainOffset` is 3, this.subdomains is `["tobi"]`.
+ *
+ * @return {Array}
+ * @api public
+ */
+
+ get subdomains() {
+ const offset = this.app.subdomainOffset;
+ const hostname = this.hostname;
+ if (net.isIP(hostname)) return [];
+ return hostname
+ .split('.')
+ .reverse()
+ .slice(offset);
+ },
+
+ /**
+ * Get accept object.
+ * Lazily memoized.
+ *
+ * @return {Object}
+ * @api private
+ */
+ get accept() {
+ return this._accept || (this._accept = accepts(this.req));
+ },
+
+ /**
+ * Set accept object.
+ *
+ * @param {Object}
+ * @api private
+ */
+ set accept(obj) {
+ return this._accept = obj;
+ },
+
+ /**
+ * Check if the given `type(s)` is acceptable, returning
+ * the best match when true, otherwise `false`, in which
+ * case you should respond with 406 "Not Acceptable".
+ *
+ * The `type` value may be a single mime type string
+ * such as "application/json", the extension name
+ * such as "json" or an array `["json", "html", "text/plain"]`. When a list
+ * or array is given the _best_ match, if any is returned.
+ *
+ * Examples:
+ *
+ * // Accept: text/html
+ * this.accepts('html');
+ * // => "html"
+ *
+ * // Accept: text/*, application/json
+ * this.accepts('html');
+ * // => "html"
+ * this.accepts('text/html');
+ * // => "text/html"
+ * this.accepts('json', 'text');
+ * // => "json"
+ * this.accepts('application/json');
+ * // => "application/json"
+ *
+ * // Accept: text/*, application/json
+ * this.accepts('image/png');
+ * this.accepts('png');
+ * // => false
+ *
+ * // Accept: text/*;q=.5, application/json
+ * this.accepts(['html', 'json']);
+ * this.accepts('html', 'json');
+ * // => "json"
+ *
+ * @param {String|Array} type(s)...
+ * @return {String|Array|false}
+ * @api public
+ */
+
+ accepts(...args) {
+ return this.accept.types(...args);
+ },
+
+ /**
+ * Return accepted encodings or best fit based on `encodings`.
+ *
+ * Given `Accept-Encoding: gzip, deflate`
+ * an array sorted by quality is returned:
+ *
+ * ['gzip', 'deflate']
+ *
+ * @param {String|Array} encoding(s)...
+ * @return {String|Array}
+ * @api public
+ */
+
+ acceptsEncodings(...args) {
+ return this.accept.encodings(...args);
+ },
+
+ /**
+ * Return accepted charsets or best fit based on `charsets`.
+ *
+ * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
+ * an array sorted by quality is returned:
+ *
+ * ['utf-8', 'utf-7', 'iso-8859-1']
+ *
+ * @param {String|Array} charset(s)...
+ * @return {String|Array}
+ * @api public
+ */
+
+ acceptsCharsets(...args) {
+ return this.accept.charsets(...args);
+ },
+
+ /**
+ * Return accepted languages or best fit based on `langs`.
+ *
+ * Given `Accept-Language: en;q=0.8, es, pt`
+ * an array sorted by quality is returned:
+ *
+ * ['es', 'pt', 'en']
+ *
+ * @param {String|Array} lang(s)...
+ * @return {Array|String}
+ * @api public
+ */
+
+ acceptsLanguages(...args) {
+ return this.accept.languages(...args);
+ },
+
+ /**
+ * Check if the incoming request contains the "Content-Type"
+ * header field, and it contains any of the give mime `type`s.
+ * If there is no request body, `null` is returned.
+ * If there is no content type, `false` is returned.
+ * Otherwise, it returns the first `type` that matches.
+ *
+ * Examples:
+ *
+ * // With Content-Type: text/html; charset=utf-8
+ * this.is('html'); // => 'html'
+ * this.is('text/html'); // => 'text/html'
+ * this.is('text/*', 'application/json'); // => 'text/html'
+ *
+ * // When Content-Type is application/json
+ * this.is('json', 'urlencoded'); // => 'json'
+ * this.is('application/json'); // => 'application/json'
+ * this.is('html', 'application/*'); // => 'application/json'
+ *
+ * this.is('html'); // => false
+ *
+ * @param {String|Array} types...
+ * @return {String|false|null}
+ * @api public
+ */
+
+ is(types) {
+ if (!types) return typeis(this.req);
+ if (!Array.isArray(types)) types = [].slice.call(arguments);
+ return typeis(this.req, types);
+ },
+
+ /**
+ * Return the request mime type void of
+ * parameters such as "charset".
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get type() {
+ const type = this.get('Content-Type');
+ if (!type) return '';
+ return type.split(';')[0];
+ },
+
+ /**
+ * Return request header.
+ *
+ * The `Referrer` header field is special-cased,
+ * both `Referrer` and `Referer` are interchangeable.
+ *
+ * Examples:
+ *
+ * this.get('Content-Type');
+ * // => "text/plain"
+ *
+ * this.get('content-type');
+ * // => "text/plain"
+ *
+ * this.get('Something');
+ * // => ''
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+ get(field) {
+ const req = this.req;
+ switch (field = field.toLowerCase()) {
+ case 'referer':
+ case 'referrer':
+ return req.headers.referrer || req.headers.referer || '';
+ default:
+ return req.headers[field] || '';
+ }
+ },
+
+ /**
+ * Inspect implementation.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ inspect() {
+ if (!this.req) return;
+ return this.toJSON();
+ },
+
+ /**
+ * Return JSON representation.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ toJSON() {
+ return only(this, [
+ 'method',
+ 'url',
+ 'header'
+ ]);
+ }
+};
+
+/**
+ * Custom inspection implementation for newer Node.js versions.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+/* istanbul ignore else */
+if (util.inspect.custom) {
+ module.exports[util.inspect.custom] = module.exports.inspect;
+}
diff --git a/node_modules/koa/lib/response.js b/node_modules/koa/lib/response.js
new file mode 100644
index 00000000..371875fe
--- /dev/null
+++ b/node_modules/koa/lib/response.js
@@ -0,0 +1,558 @@
+
+'use strict';
+
+/**
+ * Module dependencies.
+ */
+
+const contentDisposition = require('content-disposition');
+const ensureErrorHandler = require('error-inject');
+const getType = require('cache-content-type');
+const onFinish = require('on-finished');
+const isJSON = require('koa-is-json');
+const escape = require('escape-html');
+const typeis = require('type-is').is;
+const statuses = require('statuses');
+const destroy = require('destroy');
+const assert = require('assert');
+const extname = require('path').extname;
+const vary = require('vary');
+const only = require('only');
+const util = require('util');
+
+/**
+ * Prototype.
+ */
+
+module.exports = {
+
+ /**
+ * Return the request socket.
+ *
+ * @return {Connection}
+ * @api public
+ */
+
+ get socket() {
+ return this.res.socket;
+ },
+
+ /**
+ * Return response header.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ get header() {
+ const { res } = this;
+ return typeof res.getHeaders === 'function'
+ ? res.getHeaders()
+ : res._headers || {}; // Node < 7.7
+ },
+
+ /**
+ * Return response header, alias as response.header
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ get headers() {
+ return this.header;
+ },
+
+ /**
+ * Get response status code.
+ *
+ * @return {Number}
+ * @api public
+ */
+
+ get status() {
+ return this.res.statusCode;
+ },
+
+ /**
+ * Set response status code.
+ *
+ * @param {Number} code
+ * @api public
+ */
+
+ set status(code) {
+ if (this.headerSent) return;
+
+ assert('number' == typeof code, 'status code must be a number');
+ assert(statuses[code], `invalid status code: ${code}`);
+ this._explicitStatus = true;
+ this.res.statusCode = code;
+ if (this.req.httpVersionMajor < 2) this.res.statusMessage = statuses[code];
+ if (this.body && statuses.empty[code]) this.body = null;
+ },
+
+ /**
+ * Get response status message
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get message() {
+ return this.res.statusMessage || statuses[this.status];
+ },
+
+ /**
+ * Set response status message
+ *
+ * @param {String} msg
+ * @api public
+ */
+
+ set message(msg) {
+ this.res.statusMessage = msg;
+ },
+
+ /**
+ * Get response body.
+ *
+ * @return {Mixed}
+ * @api public
+ */
+
+ get body() {
+ return this._body;
+ },
+
+ /**
+ * Set response body.
+ *
+ * @param {String|Buffer|Object|Stream} val
+ * @api public
+ */
+
+ set body(val) {
+ const original = this._body;
+ this._body = val;
+
+ // no content
+ if (null == val) {
+ if (!statuses.empty[this.status]) this.status = 204;
+ this.remove('Content-Type');
+ this.remove('Content-Length');
+ this.remove('Transfer-Encoding');
+ return;
+ }
+
+ // set the status
+ if (!this._explicitStatus) this.status = 200;
+
+ // set the content-type only if not yet set
+ const setType = !this.header['content-type'];
+
+ // string
+ if ('string' == typeof val) {
+ if (setType) this.type = /^\s*</.test(val) ? 'html' : 'text';
+ this.length = Buffer.byteLength(val);
+ return;
+ }
+
+ // buffer
+ if (Buffer.isBuffer(val)) {
+ if (setType) this.type = 'bin';
+ this.length = val.length;
+ return;
+ }
+
+ // stream
+ if ('function' == typeof val.pipe) {
+ onFinish(this.res, destroy.bind(null, val));
+ ensureErrorHandler(val, err => this.ctx.onerror(err));
+
+ // overwriting
+ if (null != original && original != val) this.remove('Content-Length');
+
+ if (setType) this.type = 'bin';
+ return;
+ }
+
+ // json
+ this.remove('Content-Length');
+ this.type = 'json';
+ },
+
+ /**
+ * Set Content-Length field to `n`.
+ *
+ * @param {Number} n
+ * @api public
+ */
+
+ set length(n) {
+ this.set('Content-Length', n);
+ },
+
+ /**
+ * Return parsed response Content-Length when present.
+ *
+ * @return {Number}
+ * @api public
+ */
+
+ get length() {
+ const len = this.header['content-length'];
+ const body = this.body;
+
+ if (null == len) {
+ if (!body) return;
+ if ('string' == typeof body) return Buffer.byteLength(body);
+ if (Buffer.isBuffer(body)) return body.length;
+ if (isJSON(body)) return Buffer.byteLength(JSON.stringify(body));
+ return;
+ }
+
+ return ~~len;
+ },
+
+ /**
+ * Check if a header has been written to the socket.
+ *
+ * @return {Boolean}
+ * @api public
+ */
+
+ get headerSent() {
+ return this.res.headersSent;
+ },
+
+ /**
+ * Vary on `field`.
+ *
+ * @param {String} field
+ * @api public
+ */
+
+ vary(field) {
+ if (this.headerSent) return;
+
+ vary(this.res, field);
+ },
+
+ /**
+ * Perform a 302 redirect to `url`.
+ *
+ * The string "back" is special-cased
+ * to provide Referrer support, when Referrer
+ * is not present `alt` or "/" is used.
+ *
+ * Examples:
+ *
+ * this.redirect('back');
+ * this.redirect('back', '/index.html');
+ * this.redirect('/login');
+ * this.redirect('http://google.com');
+ *
+ * @param {String} url
+ * @param {String} [alt]
+ * @api public
+ */
+
+ redirect(url, alt) {
+ // location
+ if ('back' == url) url = this.ctx.get('Referrer') || alt || '/';
+ this.set('Location', url);
+
+ // status
+ if (!statuses.redirect[this.status]) this.status = 302;
+
+ // html
+ if (this.ctx.accepts('html')) {
+ url = escape(url);
+ this.type = 'text/html; charset=utf-8';
+ this.body = `Redirecting to <a href="${url}">${url}</a>.`;
+ return;
+ }
+
+ // text
+ this.type = 'text/plain; charset=utf-8';
+ this.body = `Redirecting to ${url}.`;
+ },
+
+ /**
+ * Set Content-Disposition header to "attachment" with optional `filename`.
+ *
+ * @param {String} filename
+ * @api public
+ */
+
+ attachment(filename, options) {
+ if (filename) this.type = extname(filename);
+ this.set('Content-Disposition', contentDisposition(filename, options));
+ },
+
+ /**
+ * Set Content-Type response header with `type` through `mime.lookup()`
+ * when it does not contain a charset.
+ *
+ * Examples:
+ *
+ * this.type = '.html';
+ * this.type = 'html';
+ * this.type = 'json';
+ * this.type = 'application/json';
+ * this.type = 'png';
+ *
+ * @param {String} type
+ * @api public
+ */
+
+ set type(type) {
+ type = getType(type);
+ if (type) {
+ this.set('Content-Type', type);
+ } else {
+ this.remove('Content-Type');
+ }
+ },
+
+ /**
+ * Set the Last-Modified date using a string or a Date.
+ *
+ * this.response.lastModified = new Date();
+ * this.response.lastModified = '2013-09-13';
+ *
+ * @param {String|Date} type
+ * @api public
+ */
+
+ set lastModified(val) {
+ if ('string' == typeof val) val = new Date(val);
+ this.set('Last-Modified', val.toUTCString());
+ },
+
+ /**
+ * Get the Last-Modified date in Date form, if it exists.
+ *
+ * @return {Date}
+ * @api public
+ */
+
+ get lastModified() {
+ const date = this.get('last-modified');
+ if (date) return new Date(date);
+ },
+
+ /**
+ * Set the ETag of a response.
+ * This will normalize the quotes if necessary.
+ *
+ * this.response.etag = 'md5hashsum';
+ * this.response.etag = '"md5hashsum"';
+ * this.response.etag = 'W/"123456789"';
+ *
+ * @param {String} etag
+ * @api public
+ */
+
+ set etag(val) {
+ if (!/^(W\/)?"/.test(val)) val = `"${val}"`;
+ this.set('ETag', val);
+ },
+
+ /**
+ * Get the ETag of a response.
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get etag() {
+ return this.get('ETag');
+ },
+
+ /**
+ * Return the response mime type void of
+ * parameters such as "charset".
+ *
+ * @return {String}
+ * @api public
+ */
+
+ get type() {
+ const type = this.get('Content-Type');
+ if (!type) return '';
+ return type.split(';')[0];
+ },
+
+ /**
+ * Check whether the response is one of the listed types.
+ * Pretty much the same as `this.request.is()`.
+ *
+ * @param {String|Array} types...
+ * @return {String|false}
+ * @api public
+ */
+
+ is(types) {
+ const type = this.type;
+ if (!types) return type || false;
+ if (!Array.isArray(types)) types = [].slice.call(arguments);
+ return typeis(type, types);
+ },
+
+ /**
+ * Return response header.
+ *
+ * Examples:
+ *
+ * this.get('Content-Type');
+ * // => "text/plain"
+ *
+ * this.get('content-type');
+ * // => "text/plain"
+ *
+ * @param {String} field
+ * @return {String}
+ * @api public
+ */
+
+ get(field) {
+ return this.header[field.toLowerCase()] || '';
+ },
+
+ /**
+ * Set header `field` to `val`, or pass
+ * an object of header fields.
+ *
+ * Examples:
+ *
+ * this.set('Foo', ['bar', 'baz']);
+ * this.set('Accept', 'application/json');
+ * this.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
+ *
+ * @param {String|Object|Array} field
+ * @param {String} val
+ * @api public
+ */
+
+ set(field, val) {
+ if (this.headerSent) return;
+
+ if (2 == arguments.length) {
+ if (Array.isArray(val)) val = val.map(v => typeof v === 'string' ? v : String(v));
+ else if (typeof val !== 'string') val = String(val);
+ this.res.setHeader(field, val);
+ } else {
+ for (const key in field) {
+ this.set(key, field[key]);
+ }
+ }
+ },
+
+ /**
+ * Append additional header `field` with value `val`.
+ *
+ * Examples:
+ *
+ * ```
+ * this.append('Link', ['<http://localhost/>', '<http://localhost:3000/>']);
+ * this.append('Set-Cookie', 'foo=bar; Path=/; HttpOnly');
+ * this.append('Warning', '199 Miscellaneous warning');
+ * ```
+ *
+ * @param {String} field
+ * @param {String|Array} val
+ * @api public
+ */
+
+ append(field, val) {
+ const prev = this.get(field);
+
+ if (prev) {
+ val = Array.isArray(prev)
+ ? prev.concat(val)
+ : [prev].concat(val);
+ }
+
+ return this.set(field, val);
+ },
+
+ /**
+ * Remove header `field`.
+ *
+ * @param {String} name
+ * @api public
+ */
+
+ remove(field) {
+ if (this.headerSent) return;
+
+ this.res.removeHeader(field);
+ },
+
+ /**
+ * Checks if the request is writable.
+ * Tests for the existence of the socket
+ * as node sometimes does not set it.
+ *
+ * @return {Boolean}
+ * @api private
+ */
+
+ get writable() {
+ // can't write any more after response finished
+ if (this.res.finished) return false;
+
+ const socket = this.res.socket;
+ // There are already pending outgoing res, but still writable
+ // https://github.com/nodejs/node/blob/v4.4.7/lib/_http_server.js#L486
+ if (!socket) return true;
+ return socket.writable;
+ },
+
+ /**
+ * Inspect implementation.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ inspect() {
+ if (!this.res) return;
+ const o = this.toJSON();
+ o.body = this.body;
+ return o;
+ },
+
+ /**
+ * Return JSON representation.
+ *
+ * @return {Object}
+ * @api public
+ */
+
+ toJSON() {
+ return only(this, [
+ 'status',
+ 'message',
+ 'header'
+ ]);
+ },
+
+ /**
+ * Flush any set headers, and begin the body
+ */
+ flushHeaders() {
+ this.res.flushHeaders();
+ }
+};
+
+/**
+ * Custom inspection implementation for newer Node.js versions.
+ *
+ * @return {Object}
+ * @api public
+ */
+if (util.inspect.custom) {
+ module.exports[util.inspect.custom] = module.exports.inspect;
+}