@goa/negotiator
Version:
[fork] HTTP Content Negotiation In ES6 Optimised With Google Closure Compiler.
160 lines (131 loc) • 3.14 kB
JavaScript
const simpleEncodingRegExp = /^\s*([^\s;]+)\s*(?:;(.*))?$/
/**
* Parse the Accept-Encoding header.
* @private
*/
function parseAcceptEncoding(accept) {
var accepts = accept.split(',')
var hasIdentity = false
var minQuality = 1
for (var i = 0, j = 0; i < accepts.length; i++) {
var encoding = parseEncoding(accepts[i].trim(), i)
if (encoding) {
accepts[j++] = encoding
hasIdentity = hasIdentity || specify('identity', encoding, undefined)
minQuality = Math.min(minQuality, encoding.q || 1)
}
}
if (!hasIdentity) {
/*
* If identity doesn't explicitly appear in the accept-encoding header,
* it's added to the list of acceptable encoding with the lowest q
*/
accepts[j++] = {
encoding: 'identity',
q: minQuality,
i: i,
}
}
// trim accepts
accepts.length = j
return accepts
}
/**
* Parse an encoding from the Accept-Encoding header.
* @private
*/
function parseEncoding(str, i) {
var match = simpleEncodingRegExp.exec(str)
if (!match) return null
var encoding = match[1]
var q = 1
if (match[2]) {
var params = match[2].split(';')
for (var j = 0; j < params.length; j++) {
var p = params[j].trim().split('=')
if (p[0] == 'q') {
q = parseFloat(p[1])
break
}
}
}
return {
encoding: encoding,
q: q,
i: i,
}
}
/**
* Get the priority of an encoding.
* @private
*/
function getEncodingPriority(encoding, accepted, index) {
var priority = { o: -1, q: 0, s: 0 }
for (var i = 0; i < accepted.length; i++) {
var spec = specify(encoding, accepted[i], index)
if (spec && (priority.s - spec.s || priority.q - spec.q || priority.o - spec.o) < 0) {
priority = spec
}
}
return priority
}
/**
* Get the specificity of the encoding.
* @private
*/
function specify(encoding, spec, index) {
var s = 0
if(spec.encoding.toLowerCase() === encoding.toLowerCase()){
s |= 1
} else if (spec.encoding !== '*' ) {
return null
}
return {
i: index,
o: spec.i,
q: spec.q,
s: s,
}
}
/**
* Get the preferred encodings from an Accept-Encoding header.
* @public
*/
export default function preferredEncodings(accept, provided) {
var accepts = parseAcceptEncoding(accept || '')
if (!provided) {
// sorted list of all encodings
return accepts
.filter(isQuality)
.sort(compareSpecs)
.map(getFullEncoding)
}
var priorities = provided.map(function getPriority(type, index) {
return getEncodingPriority(type, accepts, index)
})
// sorted list of accepted encodings
return priorities.filter(isQuality).sort(compareSpecs).map(function getEncoding(priority) {
return provided[priorities.indexOf(priority)]
})
}
/**
* Compare two specs.
* @private
*/
function compareSpecs(a, b) {
return (b.q - a.q) || (b.s - a.s) || (a.o - b.o) || (a.i - b.i) || 0
}
/**
* Get full encoding string.
* @private
*/
function getFullEncoding(spec) {
return spec.encoding
}
/**
* Check if a spec has any quality.
* @private
*/
function isQuality(spec) {
return spec.q > 0
}