fastify
Version:
Fast and low overhead web framework, for Node.js
153 lines (130 loc) • 4.35 kB
JavaScript
'use strict'
/**
* keyValuePairsReg is used to split the parameters list into associated
* key value pairings.
*
* @see https://httpwg.org/specs/rfc9110.html#parameter
* @type {RegExp}
*/
const keyValuePairsReg = /([\w!
/**
* typeNameReg is used to validate that the first part of the media-type
* does not use disallowed characters.
*
* @see https://httpwg.org/specs/rfc9110.html#rule.token.separators
* @type {RegExp}
*/
const typeNameReg = /^[\w!
/**
* subtypeNameReg is used to validate that the second part of the media-type
* does not use disallowed characters.
*
* @see https://httpwg.org/specs/rfc9110.html#rule.token.separators
* @type {RegExp}
*/
const subtypeNameReg = /^[\w!
/**
* ContentType parses and represents the value of the content-type header.
*
* @see https://httpwg.org/specs/rfc9110.html#media.type
* @see https://httpwg.org/specs/rfc9110.html#parameter
*/
class ContentType {
constructor (headerValue) {
if (headerValue == null || headerValue === '' || headerValue === 'undefined') {
return
}
let sepIdx = headerValue.indexOf(';')
if (sepIdx === -1) {
// The value is the simplest `type/subtype` variant.
sepIdx = headerValue.indexOf('/')
if (sepIdx === -1) {
// Got a string without the correct `type/subtype` format.
return
}
const type = headerValue.slice(0, sepIdx).trimStart().toLowerCase()
const subtype = headerValue.slice(sepIdx + 1).trimEnd().toLowerCase()
if (
typeNameReg.test(type) === true &&
subtypeNameReg.test(subtype) === true
) {
this.
this.
this.
this.
}
return
}
// We have a `type/subtype; params=list...` header value.
const mediaType = headerValue.slice(0, sepIdx).toLowerCase()
const paramsList = headerValue.slice(sepIdx + 1).trim()
sepIdx = mediaType.indexOf('/')
if (sepIdx === -1) {
// We got an invalid string like `something; params=list...`.
return
}
const type = mediaType.slice(0, sepIdx).trimStart()
const subtype = mediaType.slice(sepIdx + 1).trimEnd()
if (
typeNameReg.test(type) === false ||
subtypeNameReg.test(subtype) === false
) {
// Some portion of the media-type is using invalid characters. Therefore,
// the content-type header is invalid.
return
}
this.
this.
this.
this.
let matches = keyValuePairsReg.exec(paramsList)
while (matches) {
const key = matches[1]
const value = matches[2]
if (value[0] === '"') {
if (value.at(-1) !== '"') {
this.
matches = keyValuePairsReg.exec(paramsList)
continue
}
// We should probably verify the value matches a quoted string
// (https://httpwg.org/specs/rfc9110.html#rule.quoted-string) value.
// But we are not really doing much with the parameter values, so we
// are omitting that at this time.
this.
} else {
this.
}
matches = keyValuePairsReg.exec(paramsList)
}
}
get [Symbol.toStringTag] () { return 'ContentType' }
get isEmpty () { return this.
get isValid () { return this.
get mediaType () { return `${this.
get type () { return this.
get subtype () { return this.
get parameters () { return this.
toString () {
/* c8 ignore next: we don't need to verify the cache */
if (this.
const parameters = []
for (const [key, value] of this.
parameters.push(`${key}="${value}"`)
}
const result = [this.
if (parameters.length > 0) {
result.push('; ')
result.push(parameters.join('; '))
}
this.
return this.
}
}
module.exports = ContentType