From 26105034da4fcce7ac883c899d781f016559310d Mon Sep 17 00:00:00 2001 From: ruki Date: Thu, 8 Nov 2018 00:38:48 +0800 Subject: switch to vuepress --- node_modules/koa/lib/request.js | 727 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 727 insertions(+) create mode 100644 node_modules/koa/lib/request.js (limited to 'node_modules/koa/lib/request.js') 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; +} -- cgit v1.2.3