koa-lite/lib/accepts.js

118 lines
3.1 KiB
JavaScript

const getMimetype = require('./getmimetype');
module.exports = function accepts(ctx, type, ask, isReq = true) {
if (!ctx._accept) {
ctx._accept = {};
}
// We don't need to parse content-type
if (!ctx._accept[type] && type !== 'content-type') {
let types = ctx.req.headers[type];
let quality = 9999; // Little bit of a hack :)
if (types) {
types = types.split(',')
.map(x => {
x = x.trim();
let q = quality--;
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 = [];
}
if (type === 'accept-encoding') {
types.push('identity');
}
ctx._accept[type] = types;
}
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 (ask.length === 0 && can) {
return can || false;
}
// If no supported was sent, return the first ask item
// unless we're dealing with content-type we need to be smarter.
if (!can) {
if (type === 'content-type') {
return false;
}
return ask[0];
}
let parsed = ask.slice();
if (type === 'accept' || type === 'content-type') {
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];
}
}
if (type === 'content-type') {
can = [can.split(';')[0]];
}
}
// Loop over the supported can, returning the first
// matching ask type.
for (let i = 0; i < can.length; i++) {
for (let t = 0; t < parsed.length; t++) {
// Check if we allow root checking (application/*)
if (type === 'accept' || type === 'content-type') {
let allowRoot = can[i].indexOf('/*') >= 0
|| parsed[t].indexOf('/*') >= 0;
// Big if :)
if (can[i] === '*/*'
|| can[i].indexOf(parsed[t]) >= 0
|| (allowRoot
&& parsed[t].indexOf('/') >= 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];
}
} else {
if (can[i] === parsed[t]) {
return ask[t];
}
}
}
}
return false;
};