Remove is-type and replace with a helper function :)

This commit is contained in:
Jonatan Nilsson 2019-10-12 14:31:02 +00:00
parent 54aa155328
commit 5a9a3f0dc2
6 changed files with 67 additions and 65 deletions

View file

@ -1,10 +1,12 @@
const getMimetype = require('./getmimetype'); const getMimetype = require('./getmimetype');
module.exports = function accepts(ctx, type, ask) { module.exports = function accepts(ctx, type, ask, isReq = true) {
if (!ctx._accept) { if (!ctx._accept) {
ctx._accept = {}; ctx._accept = {};
} }
if (!ctx._accept[type]) {
// We don't need to parse content-type
if (!ctx._accept[type] && type !== 'content-type') {
let types = ctx.req.headers[type]; let types = ctx.req.headers[type];
let quality = 9999; // Little bit of a hack :) let quality = 9999; // Little bit of a hack :)
if (types) { if (types) {
@ -27,37 +29,67 @@ module.exports = function accepts(ctx, type, ask) {
if (type === 'accept-encoding') { if (type === 'accept-encoding') {
types.push('identity'); types.push('identity');
} }
ctx._accept[type] = types; ctx._accept[type] = types;
} }
let can = ctx._accept[type]; let can;
if (type === 'content-type') {
if (isReq) {
// Check if a request has a request body.
// A request with a body __must__ either have `transfer-encoding`
// or `content-length` headers set.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
if (ctx.req.headers['transfer-encoding'] === undefined
&& isNaN(ctx.req.headers['content-length'])) {
return null;
}
can = ctx.req.headers[type];
} else {
can = ctx.type;
}
} else {
can = ctx._accept[type];
if (!can.length) can = null;
}
// If empty argument, return all supported can // If empty argument, return all supported can
if (ask.length === 0) { if (ask.length === 0 && can) {
return can; return can || false;
} }
// If no supported was sent, return the first ask item // If no supported was sent, return the first ask item
if (!can.length) { // unless we're dealing with content-type we need to be smarter.
if (!can) {
if (type === 'content-type') {
return false;
}
return ask[0]; return ask[0];
} }
let parsed = ask.slice(); let parsed = ask.slice();
if (type === 'accept') { if (type === 'accept' || type === 'content-type') {
for (let t = 0; t < parsed.length; t++) { for (let t = 0; t < parsed.length; t++) {
if (parsed[t].startsWith('*/')) {
parsed[t] = parsed[t].substr(2);
} else if (parsed[t].indexOf('/*') < 0) {
parsed[t] = getMimetype(parsed[t]) || parsed[t]; parsed[t] = getMimetype(parsed[t]) || parsed[t];
} }
} }
if (type === 'content-type') {
can = [can.split(';')[0]];
}
}
// Loop over the supported can, returning the first // Loop over the supported can, returning the first
// matching ask type. // matching ask type.
for (let i = 0; i < can.length; i++) { for (let i = 0; i < can.length; i++) {
for (let t = 0; t < parsed.length; t++) { for (let t = 0; t < parsed.length; t++) {
// Check if we allow root checking (application/*) // Check if we allow root checking (application/*)
if (type === 'accept') { if (type === 'accept' || type === 'content-type') {
let allowRoot = can[i].indexOf('/*') >= 0; let allowRoot = can[i].indexOf('/*') >= 0
|| parsed[t].indexOf('/*') >= 0;
// Big if :) // Big if :)
if (can[i] === '*/*' if (can[i] === '*/*'
@ -66,6 +98,12 @@ module.exports = function accepts(ctx, type, ask) {
&& parsed[t].indexOf('/') >= 0 && parsed[t].indexOf('/') >= 0
&& can[i].split('/')[0] === parsed[t].split('/')[0] && can[i].split('/')[0] === parsed[t].split('/')[0]
)) { )) {
if (type === 'content-type') {
if (ask[t].indexOf('/') === -1) {
return ask[t];
}
return can[i];
}
return ask[t]; return ask[t];
} }
} else { } else {

View file

@ -6,6 +6,10 @@
*/ */
const debug = require('debug-ms')('koa:application'); const debug = require('debug-ms')('koa:application');
const Emitter = require('events');
const util = require('util');
const Stream = require('stream');
const http = require('http');
const onFinished = require('./onfinish'); const onFinished = require('./onfinish');
const response = require('./response'); const response = require('./response');
const compose = require('./compose'); const compose = require('./compose');
@ -13,10 +17,6 @@ const isJSON = require('./isjson');
const context = require('./context'); const context = require('./context');
const request = require('./request'); const request = require('./request');
const statuses = require('./statuses'); const statuses = require('./statuses');
const Emitter = require('events');
const util = require('util');
const Stream = require('stream');
const http = require('http');
/** /**
* Expose `Application` class. * Expose `Application` class.

View file

@ -8,11 +8,10 @@
const URL = require('url').URL; const URL = require('url').URL;
const net = require('net'); const net = require('net');
const stringify = require('url').format; const stringify = require('url').format;
const fastparse = require('./fastparse');
const qs = require('querystring'); const qs = require('querystring');
const typeis = require('type-is');
const fresh = require('fresh'); const fresh = require('fresh');
const util = require('util'); const util = require('util');
const fastparse = require('./fastparse');
const accepts = require('./accepts'); const accepts = require('./accepts');
const IP = Symbol('context#ip'); const IP = Symbol('context#ip');
@ -473,40 +472,6 @@ module.exports = {
.slice(offset); .slice(offset);
}, },
/**
* Get accept object.
* Lazily memoized.
*
* @return {Object}
* @api private
*/
accept(type, ) {
if (!this._accept) {
let types = this.req.headers.accept;
if (types) {
types = types.split(',')
.map(x => {
x = x.trim();
let q = 1;
if (x.indexOf('q=') >= 0) {
q = parseFloat(x.substr(x.indexOf('q=') + 2)) || 1;
x = x.substr(0, x.indexOf(';'));
}
return [x, q];
})
.sort((a, b) => b[1] - a[1])
.map(x => x[0]);
} else {
types = [];
}
this._accept = {
types: types
};
}
return this._accept;
},
/** /**
* Check if the given `type(s)` is acceptable, returning * Check if the given `type(s)` is acceptable, returning
* the best match when true, otherwise `false`, in which * the best match when true, otherwise `false`, in which
@ -658,9 +623,9 @@ module.exports = {
*/ */
is(types) { is(types) {
if (!types) return typeis(this.req); if (!types) return accepts(this, 'content-type', []);
if (!Array.isArray(types)) types = [].slice.call(arguments); if (!Array.isArray(types)) types = [].slice.call(arguments);
return typeis(this.req, types); return accepts(this, 'content-type', types);
}, },
/** /**

View file

@ -7,14 +7,14 @@
const ReadStream = require('fs').ReadStream; const ReadStream = require('fs').ReadStream;
const contentDisposition = require('content-disposition'); const contentDisposition = require('content-disposition');
const onFinish = require('./onfinish');
const isJSON = require('./isjson');
const typeis = require('type-is').is;
const statuses = require('./statuses');
const assert = require('assert'); const assert = require('assert');
const extname = require('path').extname; const extname = require('path').extname;
const getMimetype = require('./getmimetype');
const util = require('util'); const util = require('util');
const onFinish = require('./onfinish');
const isJSON = require('./isjson');
const statuses = require('./statuses');
const getMimetype = require('./getmimetype');
const accepts = require('./accepts');
/** /**
* Prototype. * Prototype.
@ -287,6 +287,8 @@ module.exports = {
// html // html
if (this.ctx.headers.accept && this.ctx.headers.accept.indexOf('html') >= 0) { if (this.ctx.headers.accept && this.ctx.headers.accept.indexOf('html') >= 0) {
// Sanitize the url in case developer does something silly like:
// ctx.redirect(ctx.query.goto) or something without sanitizing himself.
url = url.replace(/&/g, '&amp;') url = url.replace(/&/g, '&amp;')
.replace(/</g, '&lt;') .replace(/</g, '&lt;')
.replace(/>/g, '&gt;') .replace(/>/g, '&gt;')
@ -434,10 +436,9 @@ module.exports = {
*/ */
is(types) { is(types) {
const type = this.type; if (!types) return this.type || false;
if (!types) return type || false;
if (!Array.isArray(types)) types = [].slice.call(arguments); if (!Array.isArray(types)) types = [].slice.call(arguments);
return typeis(type, types); return accepts(this, 'content-type', types, false);
}, },
/** /**

View file

@ -25,8 +25,7 @@
"content-disposition": "jharrilim/content-disposition#572383f", "content-disposition": "jharrilim/content-disposition#572383f",
"debug-ms": "~4.1.2", "debug-ms": "~4.1.2",
"fresh": "~0.5.2", "fresh": "~0.5.2",
"http-errors-lite": "^2.0.2", "http-errors-lite": "^2.0.2"
"type-is": "^1.6.16"
}, },
"devDependencies": { "devDependencies": {
"egg-bin": "^4.13.0", "egg-bin": "^4.13.0",

View file

@ -32,8 +32,7 @@ describe('app', () => {
ctx.socket.writable = false; ctx.socket.writable = false;
ctx.status = 204; ctx.status = 204;
// throw if .writeHead or .end is called // throw if .writeHead or .end is called
ctx.res.writeHead = ctx.res.writeHead = ctx.res.end = () => {
ctx.res.end = () => {
throw new Error('response sent'); throw new Error('response sent');
}; };
}); });