remove more dependencies
This commit is contained in:
parent
cd811a7046
commit
8fcdbb003c
7 changed files with 1744 additions and 4 deletions
456
lib/content-disposition.js
Normal file
456
lib/content-disposition.js
Normal file
|
@ -0,0 +1,456 @@
|
||||||
|
/*!
|
||||||
|
* content-disposition
|
||||||
|
* Copyright(c) 2014-2017 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = contentDisposition
|
||||||
|
module.exports.parse = parse
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module dependencies.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { basename } = require('path')
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match non attr-char, *after* encodeURIComponent (i.e. not including "%")
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const ENCODE_URL_ATTR_CHAR_REGEXP = /[\x00-\x20"'()*,/:;<=>?@[\\\]{}\x7f]/g // eslint-disable-line no-control-regex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match percent encoding escape.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const HEX_ESCAPE_REGEXP = /%[0-9A-Fa-f]{2}/
|
||||||
|
const HEX_ESCAPE_REPLACE_REGEXP = /%([0-9A-Fa-f]{2})/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match non-latin1 characters.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const NON_LATIN1_REGEXP = /[^\x20-\x7e\xa0-\xff]/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match quoted-pair in RFC 2616
|
||||||
|
*
|
||||||
|
* quoted-pair = "\" CHAR
|
||||||
|
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const QESC_REGEXP = /\\([\u0000-\u007f])/g // eslint-disable-line no-control-regex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to match chars that must be quoted-pair in RFC 2616
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const QUOTE_REGEXP = /([\\"])/g
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp for various RFC 2616 grammar
|
||||||
|
*
|
||||||
|
* parameter = token "=" ( token | quoted-string )
|
||||||
|
* token = 1*<any CHAR except CTLs or separators>
|
||||||
|
* separators = "(" | ")" | "<" | ">" | "@"
|
||||||
|
* | "," | ";" | ":" | "\" | <">
|
||||||
|
* | "/" | "[" | "]" | "?" | "="
|
||||||
|
* | "{" | "}" | SP | HT
|
||||||
|
* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
|
||||||
|
* qdtext = <any TEXT except <">>
|
||||||
|
* quoted-pair = "\" CHAR
|
||||||
|
* CHAR = <any US-ASCII character (octets 0 - 127)>
|
||||||
|
* TEXT = <any OCTET except CTLs, but including LWS>
|
||||||
|
* LWS = [CRLF] 1*( SP | HT )
|
||||||
|
* CRLF = CR LF
|
||||||
|
* CR = <US-ASCII CR, carriage return (13)>
|
||||||
|
* LF = <US-ASCII LF, linefeed (10)>
|
||||||
|
* SP = <US-ASCII SP, space (32)>
|
||||||
|
* HT = <US-ASCII HT, horizontal-tab (9)>
|
||||||
|
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
|
||||||
|
* OCTET = <any 8-bit sequence of data>
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const PARAM_REGEXP = /;[\x09\x20]*([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*=[\x09\x20]*("(?:[\x20!\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*/g // eslint-disable-line no-control-regex
|
||||||
|
const TEXT_REGEXP = /^[\x20-\x7e\x80-\xff]+$/
|
||||||
|
const TOKEN_REGEXP = /^[!#$%&'*+.0-9A-Z^_`a-z|~-]+$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp for various RFC 5987 grammar
|
||||||
|
*
|
||||||
|
* ext-value = charset "'" [ language ] "'" value-chars
|
||||||
|
* charset = "UTF-8" / "ISO-8859-1" / mime-charset
|
||||||
|
* mime-charset = 1*mime-charsetc
|
||||||
|
* mime-charsetc = ALPHA / DIGIT
|
||||||
|
* / "!" / "#" / "$" / "%" / "&"
|
||||||
|
* / "+" / "-" / "^" / "_" / "`"
|
||||||
|
* / "{" / "}" / "~"
|
||||||
|
* language = ( 2*3ALPHA [ extlang ] )
|
||||||
|
* / 4ALPHA
|
||||||
|
* / 5*8ALPHA
|
||||||
|
* extlang = *3( "-" 3ALPHA )
|
||||||
|
* value-chars = *( pct-encoded / attr-char )
|
||||||
|
* pct-encoded = "%" HEXDIG HEXDIG
|
||||||
|
* attr-char = ALPHA / DIGIT
|
||||||
|
* / "!" / "#" / "$" / "&" / "+" / "-" / "."
|
||||||
|
* / "^" / "_" / "`" / "|" / "~"
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const EXT_VALUE_REGEXP = /^([A-Za-z0-9!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+.^_`|~-])+)$/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp for various RFC 6266 grammar
|
||||||
|
*
|
||||||
|
* disposition-type = "inline" | "attachment" | disp-ext-type
|
||||||
|
* disp-ext-type = token
|
||||||
|
* disposition-parm = filename-parm | disp-ext-parm
|
||||||
|
* filename-parm = "filename" "=" value
|
||||||
|
* | "filename*" "=" ext-value
|
||||||
|
* disp-ext-parm = token "=" value
|
||||||
|
* | ext-token "=" ext-value
|
||||||
|
* ext-token = <the characters in token, followed by "*">
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
const DISPOSITION_TYPE_REGEXP = /^([!#$%&'*+.0-9A-Z^_`a-z|~-]+)[\x09\x20]*(?:$|;)/ // eslint-disable-line no-control-regex
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an attachment Content-Disposition header.
|
||||||
|
*
|
||||||
|
* @param {string} [filename]
|
||||||
|
* @param {object} [options]
|
||||||
|
* @param {string} [options.type=attachment]
|
||||||
|
* @param {string|boolean} [options.fallback=true]
|
||||||
|
* @return {string}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function contentDisposition (filename, options) {
|
||||||
|
const opts = options || {}
|
||||||
|
|
||||||
|
// get type
|
||||||
|
const type = opts.type || 'attachment'
|
||||||
|
|
||||||
|
// get parameters
|
||||||
|
const params = createparams(filename, opts.fallback)
|
||||||
|
|
||||||
|
// format into string
|
||||||
|
return format(new ContentDisposition(type, params))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create parameters object from filename and fallback.
|
||||||
|
*
|
||||||
|
* @param {string} [filename]
|
||||||
|
* @param {string|boolean} [fallback=true]
|
||||||
|
* @return {object}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function createparams (filename, fallback) {
|
||||||
|
if (filename === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = {}
|
||||||
|
|
||||||
|
if (typeof filename !== 'string') {
|
||||||
|
throw new TypeError('filename must be a string')
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback defaults to true
|
||||||
|
if (fallback === undefined) {
|
||||||
|
fallback = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof fallback !== 'string' && typeof fallback !== 'boolean') {
|
||||||
|
throw new TypeError('fallback must be a string or boolean')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof fallback === 'string' && NON_LATIN1_REGEXP.test(fallback)) {
|
||||||
|
throw new TypeError('fallback must be ISO-8859-1 string')
|
||||||
|
}
|
||||||
|
|
||||||
|
// restrict to file base name
|
||||||
|
const name = basename(filename)
|
||||||
|
|
||||||
|
// determine if name is suitable for quoted string
|
||||||
|
const isQuotedString = TEXT_REGEXP.test(name)
|
||||||
|
|
||||||
|
// generate fallback name
|
||||||
|
const fallbackName = typeof fallback !== 'string'
|
||||||
|
? fallback && getlatin1(name)
|
||||||
|
: basename(fallback)
|
||||||
|
const hasFallback = typeof fallbackName === 'string' && fallbackName !== name
|
||||||
|
|
||||||
|
// set extended filename parameter
|
||||||
|
if (hasFallback || !isQuotedString || HEX_ESCAPE_REGEXP.test(name)) {
|
||||||
|
params['filename*'] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
// set filename parameter
|
||||||
|
if (isQuotedString || hasFallback) {
|
||||||
|
params.filename = hasFallback
|
||||||
|
? fallbackName
|
||||||
|
: name
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format object to Content-Disposition header.
|
||||||
|
*
|
||||||
|
* @param {object} obj
|
||||||
|
* @param {string} obj.type
|
||||||
|
* @param {object} [obj.parameters]
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function format (obj) {
|
||||||
|
const parameters = obj.parameters
|
||||||
|
const type = obj.type
|
||||||
|
|
||||||
|
if (!type || typeof type !== 'string' || !TOKEN_REGEXP.test(type)) {
|
||||||
|
throw new TypeError('invalid type')
|
||||||
|
}
|
||||||
|
|
||||||
|
// start with normalized type
|
||||||
|
let string = String(type).toLowerCase()
|
||||||
|
|
||||||
|
// append parameters
|
||||||
|
if (parameters && typeof parameters === 'object') {
|
||||||
|
const params = Object.keys(parameters).sort()
|
||||||
|
|
||||||
|
for (let i = 0; i < params.length; i++) {
|
||||||
|
const param = params[i]
|
||||||
|
|
||||||
|
const val = param.substr(-1) === '*'
|
||||||
|
? ustring(parameters[param])
|
||||||
|
: qstring(parameters[param])
|
||||||
|
|
||||||
|
string += '; ' + param + '=' + val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a RFC 6987 field value (gracefully).
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function decodefield (str) {
|
||||||
|
const match = EXT_VALUE_REGEXP.exec(str)
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw new TypeError('invalid extended field value')
|
||||||
|
}
|
||||||
|
|
||||||
|
const charset = match[1].toLowerCase()
|
||||||
|
const encoded = match[2]
|
||||||
|
let value
|
||||||
|
|
||||||
|
// to binary string
|
||||||
|
const binary = encoded.replace(HEX_ESCAPE_REPLACE_REGEXP, pdecode)
|
||||||
|
|
||||||
|
switch (charset) {
|
||||||
|
case 'iso-8859-1':
|
||||||
|
value = getlatin1(binary)
|
||||||
|
break
|
||||||
|
case 'utf-8':
|
||||||
|
value = Buffer.from(binary, 'binary').toString('utf8')
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new TypeError('unsupported charset in extended field')
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get ISO-8859-1 version of string.
|
||||||
|
*
|
||||||
|
* @param {string} val
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function getlatin1 (val) {
|
||||||
|
// simple Unicode -> ISO-8859-1 transformation
|
||||||
|
return String(val).replace(NON_LATIN1_REGEXP, '?')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Content-Disposition header string.
|
||||||
|
*
|
||||||
|
* @param {string} string
|
||||||
|
* @return {object}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parse (string) {
|
||||||
|
if (!string || typeof string !== 'string') {
|
||||||
|
throw new TypeError('argument string is required')
|
||||||
|
}
|
||||||
|
|
||||||
|
let match = DISPOSITION_TYPE_REGEXP.exec(string)
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw new TypeError('invalid type format')
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize type
|
||||||
|
let index = match[0].length
|
||||||
|
const type = match[1].toLowerCase()
|
||||||
|
|
||||||
|
let key
|
||||||
|
let value
|
||||||
|
const names = []
|
||||||
|
const params = {}
|
||||||
|
|
||||||
|
// calculate index to start at
|
||||||
|
index = PARAM_REGEXP.lastIndex = match[0].substr(-1) === ';'
|
||||||
|
? index - 1
|
||||||
|
: index
|
||||||
|
|
||||||
|
// match parameters
|
||||||
|
while ((match = PARAM_REGEXP.exec(string))) {
|
||||||
|
if (match.index !== index) {
|
||||||
|
throw new TypeError('invalid parameter format')
|
||||||
|
}
|
||||||
|
|
||||||
|
index += match[0].length
|
||||||
|
key = match[1].toLowerCase()
|
||||||
|
value = match[2]
|
||||||
|
|
||||||
|
if (names.indexOf(key) !== -1) {
|
||||||
|
throw new TypeError('invalid duplicate parameter')
|
||||||
|
}
|
||||||
|
|
||||||
|
names.push(key)
|
||||||
|
|
||||||
|
if (key.indexOf('*') + 1 === key.length) {
|
||||||
|
// decode extended value
|
||||||
|
key = key.slice(0, -1)
|
||||||
|
value = decodefield(value)
|
||||||
|
|
||||||
|
// overwrite existing value
|
||||||
|
params[key] = value
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof params[key] === 'string') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value[0] === '"') {
|
||||||
|
// remove quotes and escapes
|
||||||
|
value = value
|
||||||
|
.substr(1, value.length - 2)
|
||||||
|
.replace(QESC_REGEXP, '$1')
|
||||||
|
}
|
||||||
|
|
||||||
|
params[key] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index !== -1 && index !== string.length) {
|
||||||
|
throw new TypeError('invalid parameter format')
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ContentDisposition(type, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Percent decode a single character.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @param {string} hex
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function pdecode (str, hex) {
|
||||||
|
return String.fromCharCode(parseInt(hex, 16))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Percent encode a single character.
|
||||||
|
*
|
||||||
|
* @param {string} char
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function pencode (char) {
|
||||||
|
return '%' + String(char)
|
||||||
|
.charCodeAt(0)
|
||||||
|
.toString(16)
|
||||||
|
.toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quote a string for HTTP.
|
||||||
|
*
|
||||||
|
* @param {string} val
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function qstring (val) {
|
||||||
|
const str = String(val)
|
||||||
|
|
||||||
|
return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a Unicode string for HTTP (RFC 5987).
|
||||||
|
*
|
||||||
|
* @param {string} val
|
||||||
|
* @return {string}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function ustring (val) {
|
||||||
|
const str = String(val)
|
||||||
|
|
||||||
|
// percent encode as UTF-8
|
||||||
|
const encoded = encodeURIComponent(str)
|
||||||
|
.replace(ENCODE_URL_ATTR_CHAR_REGEXP, pencode)
|
||||||
|
|
||||||
|
return 'UTF-8\'\'' + encoded
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class for parsed Content-Disposition header for v8 optimization
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {string} type
|
||||||
|
* @param {object} parameters
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
|
||||||
|
function ContentDisposition (type, parameters) {
|
||||||
|
this.type = type
|
||||||
|
this.parameters = parameters
|
||||||
|
}
|
137
lib/fresh.js
Normal file
137
lib/fresh.js
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*!
|
||||||
|
* fresh
|
||||||
|
* Copyright(c) 2012 TJ Holowaychuk
|
||||||
|
* Copyright(c) 2016-2017 Douglas Christopher Wilson
|
||||||
|
* MIT Licensed
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RegExp to check for no-cache token in Cache-Control.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
var CACHE_CONTROL_NO_CACHE_REGEXP = /(?:^|,)\s*?no-cache\s*?(?:,|$)/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module exports.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = fresh
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check freshness of the response using request and response headers.
|
||||||
|
*
|
||||||
|
* @param {Object} reqHeaders
|
||||||
|
* @param {Object} resHeaders
|
||||||
|
* @return {Boolean}
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
|
||||||
|
function fresh (reqHeaders, resHeaders) {
|
||||||
|
// fields
|
||||||
|
var modifiedSince = reqHeaders['if-modified-since']
|
||||||
|
var noneMatch = reqHeaders['if-none-match']
|
||||||
|
|
||||||
|
// unconditional request
|
||||||
|
if (!modifiedSince && !noneMatch) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return stale when Cache-Control: no-cache
|
||||||
|
// to support end-to-end reload requests
|
||||||
|
// https://tools.ietf.org/html/rfc2616#section-14.9.4
|
||||||
|
var cacheControl = reqHeaders['cache-control']
|
||||||
|
if (cacheControl && CACHE_CONTROL_NO_CACHE_REGEXP.test(cacheControl)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// if-none-match
|
||||||
|
if (noneMatch && noneMatch !== '*') {
|
||||||
|
var etag = resHeaders['etag']
|
||||||
|
|
||||||
|
if (!etag) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var etagStale = true
|
||||||
|
var matches = parseTokenList(noneMatch)
|
||||||
|
for (var i = 0; i < matches.length; i++) {
|
||||||
|
var match = matches[i]
|
||||||
|
if (match === etag || match === 'W/' + etag || 'W/' + match === etag) {
|
||||||
|
etagStale = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (etagStale) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if-modified-since
|
||||||
|
if (modifiedSince) {
|
||||||
|
var lastModified = resHeaders['last-modified']
|
||||||
|
var modifiedStale = !lastModified || !(parseHttpDate(lastModified) <= parseHttpDate(modifiedSince))
|
||||||
|
|
||||||
|
if (modifiedStale) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an HTTP Date into a number.
|
||||||
|
*
|
||||||
|
* @param {string} date
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parseHttpDate (date) {
|
||||||
|
var timestamp = date && Date.parse(date)
|
||||||
|
|
||||||
|
// istanbul ignore next: guard against date.js Date.parse patching
|
||||||
|
return typeof timestamp === 'number'
|
||||||
|
? timestamp
|
||||||
|
: NaN
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a HTTP token list.
|
||||||
|
*
|
||||||
|
* @param {string} str
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
|
||||||
|
function parseTokenList (str) {
|
||||||
|
var end = 0
|
||||||
|
var list = []
|
||||||
|
var start = 0
|
||||||
|
|
||||||
|
// gather tokens
|
||||||
|
for (var i = 0, len = str.length; i < len; i++) {
|
||||||
|
switch (str.charCodeAt(i)) {
|
||||||
|
case 0x20: /* */
|
||||||
|
if (start === end) {
|
||||||
|
start = end = i + 1
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 0x2c: /* , */
|
||||||
|
list.push(str.substring(start, end))
|
||||||
|
start = end = i + 1
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
end = i + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// final token
|
||||||
|
list.push(str.substring(start, end))
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
|
@ -9,8 +9,8 @@ const URL = require('url').URL;
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const stringify = require('url').format;
|
const stringify = require('url').format;
|
||||||
const qs = require('querystring');
|
const qs = require('querystring');
|
||||||
const fresh = require('fresh');
|
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
const fresh = require('./fresh');
|
||||||
const fastparse = require('./fastparse');
|
const fastparse = require('./fastparse');
|
||||||
const accepts = require('./accepts');
|
const accepts = require('./accepts');
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const ReadStream = require('fs').ReadStream;
|
const ReadStream = require('fs').ReadStream;
|
||||||
const contentDisposition = require('content-disposition');
|
const contentDisposition = require('./content-disposition');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const extname = require('path').extname;
|
const extname = require('path').extname;
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
|
|
@ -22,9 +22,7 @@
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"content-disposition": "jharrilim/content-disposition#572383f",
|
|
||||||
"debug-ms": "~4.1.2",
|
"debug-ms": "~4.1.2",
|
||||||
"fresh": "~0.5.2",
|
|
||||||
"http-errors-lite": "^2.0.2"
|
"http-errors-lite": "^2.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
const context = require('../helpers/context');
|
const context = require('../helpers/context');
|
||||||
|
const fresh = require('../../lib/fresh')
|
||||||
|
|
||||||
describe('ctx.fresh', () => {
|
describe('ctx.fresh', () => {
|
||||||
describe('the request method is not GET and HEAD', () => {
|
describe('the request method is not GET and HEAD', () => {
|
||||||
|
@ -48,3 +49,189 @@ describe('ctx.fresh', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('fresh(reqHeaders, resHeaders)', function () {
|
||||||
|
describe('when a non-conditional GET is performed', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = {}
|
||||||
|
var resHeaders = {}
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when requested with If-None-Match', function () {
|
||||||
|
describe('when ETags match', function () {
|
||||||
|
it('should be fresh', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when ETags mismatch', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"' }
|
||||||
|
var resHeaders = { 'etag': '"bar"' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when at least one matches', function () {
|
||||||
|
it('should be fresh', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': ' "bar" , "foo"' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when etag is missing', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"' }
|
||||||
|
var resHeaders = {}
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when ETag is weak', function () {
|
||||||
|
it('should be fresh on exact match', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': 'W/"foo"' }
|
||||||
|
var resHeaders = { 'etag': 'W/"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be fresh on strong match', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': 'W/"foo"' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when ETag is strong', function () {
|
||||||
|
it('should be fresh on exact match', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should be fresh on weak match', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"' }
|
||||||
|
var resHeaders = { 'etag': 'W/"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when * is given', function () {
|
||||||
|
it('should be fresh', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '*' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should get ignored if not only value', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '*, "bar"' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when requested with If-Modified-Since', function () {
|
||||||
|
describe('when modified since the date', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-modified-since': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
var resHeaders = { 'last-modified': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when unmodified since the date', function () {
|
||||||
|
it('should be fresh', function () {
|
||||||
|
var reqHeaders = { 'if-modified-since': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
var resHeaders = { 'last-modified': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when Last-Modified is missing', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-modified-since': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
var resHeaders = {}
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with invalid If-Modified-Since date', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-modified-since': 'foo' }
|
||||||
|
var resHeaders = { 'last-modified': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with invalid Last-Modified date', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-modified-since': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
var resHeaders = { 'last-modified': 'foo' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when requested with If-Modified-Since and If-None-Match', function () {
|
||||||
|
describe('when both match', function () {
|
||||||
|
it('should be fresh', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"', 'if-modified-since': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
var resHeaders = { 'etag': '"foo"', 'last-modified': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
assert.ok(fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when only ETag matches', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"', 'if-modified-since': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
var resHeaders = { 'etag': '"foo"', 'last-modified': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when only Last-Modified matches', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"', 'if-modified-since': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
var resHeaders = { 'etag': '"bar"', 'last-modified': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when none match', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'if-none-match': '"foo"', 'if-modified-since': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
var resHeaders = { 'etag': '"bar"', 'last-modified': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when requested with Cache-Control: no-cache', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'cache-control': ' no-cache' }
|
||||||
|
var resHeaders = {}
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when ETags match', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'cache-control': ' no-cache', 'if-none-match': '"foo"' }
|
||||||
|
var resHeaders = { 'etag': '"foo"' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when unmodified since the date', function () {
|
||||||
|
it('should be stale', function () {
|
||||||
|
var reqHeaders = { 'cache-control': ' no-cache', 'if-modified-since': 'Sat, 01 Jan 2000 01:00:00 GMT' }
|
||||||
|
var resHeaders = { 'last-modified': 'Sat, 01 Jan 2000 00:00:00 GMT' }
|
||||||
|
assert.ok(!fresh(reqHeaders, resHeaders))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
962
test/response/content.js
Normal file
962
test/response/content.js
Normal file
|
@ -0,0 +1,962 @@
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const assert = require('assert');
|
||||||
|
const contentDisposition = require('../../lib/content-disposition')
|
||||||
|
|
||||||
|
describe('contentDisposition()', function () {
|
||||||
|
it('should create an attachment header', function () {
|
||||||
|
assert.strictEqual(contentDisposition(), 'attachment')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('contentDisposition(filename)', function () {
|
||||||
|
it('should require a string', function () {
|
||||||
|
assert.throws(contentDisposition.bind(null, 42),
|
||||||
|
/filename.*string/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a header with file name', function () {
|
||||||
|
assert.strictEqual(contentDisposition('plans.pdf'),
|
||||||
|
'attachment; filename="plans.pdf"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use the basename of the string', function () {
|
||||||
|
assert.strictEqual(contentDisposition('/path/to/plans.pdf'),
|
||||||
|
'attachment; filename="plans.pdf"')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "filename" is US-ASCII', function () {
|
||||||
|
it('should only include filename parameter', function () {
|
||||||
|
assert.strictEqual(contentDisposition('plans.pdf'),
|
||||||
|
'attachment; filename="plans.pdf"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should escape quotes', function () {
|
||||||
|
assert.strictEqual(contentDisposition('the "plans".pdf'),
|
||||||
|
'attachment; filename="the \\"plans\\".pdf"')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "filename" is ISO-8859-1', function () {
|
||||||
|
it('should only include filename parameter', function () {
|
||||||
|
assert.strictEqual(contentDisposition('«plans».pdf'),
|
||||||
|
'attachment; filename="«plans».pdf"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should escape quotes', function () {
|
||||||
|
assert.strictEqual(contentDisposition('the "plans" (1µ).pdf'),
|
||||||
|
'attachment; filename="the \\"plans\\" (1µ).pdf"')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "filename" is Unicode', function () {
|
||||||
|
it('should include filename* parameter', function () {
|
||||||
|
assert.strictEqual(contentDisposition('планы.pdf'),
|
||||||
|
'attachment; filename="?????.pdf"; filename*=UTF-8\'\'%D0%BF%D0%BB%D0%B0%D0%BD%D1%8B.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should include filename fallback', function () {
|
||||||
|
assert.strictEqual(contentDisposition('£ and € rates.pdf'),
|
||||||
|
'attachment; filename="£ and ? rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
|
||||||
|
assert.strictEqual(contentDisposition('€ rates.pdf'),
|
||||||
|
'attachment; filename="? rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should encode special characters', function () {
|
||||||
|
assert.strictEqual(contentDisposition('€\'*%().pdf'),
|
||||||
|
'attachment; filename="?\'*%().pdf"; filename*=UTF-8\'\'%E2%82%AC%27%2A%25%28%29.pdf')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "filename" contains hex escape', function () {
|
||||||
|
it('should include filename* parameter', function () {
|
||||||
|
assert.strictEqual(contentDisposition('the%20plans.pdf'),
|
||||||
|
'attachment; filename="the%20plans.pdf"; filename*=UTF-8\'\'the%2520plans.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should handle Unicode', function () {
|
||||||
|
assert.strictEqual(contentDisposition('€%20£.pdf'),
|
||||||
|
'attachment; filename="?%20£.pdf"; filename*=UTF-8\'\'%E2%82%AC%2520%C2%A3.pdf')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('contentDisposition(filename, options)', function () {
|
||||||
|
describe('with "fallback" option', function () {
|
||||||
|
it('should require a string or Boolean', function () {
|
||||||
|
assert.throws(contentDisposition.bind(null, 'plans.pdf', { fallback: 42 }),
|
||||||
|
/fallback.*string/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should default to true', function () {
|
||||||
|
assert.strictEqual(contentDisposition('€ rates.pdf'),
|
||||||
|
'attachment; filename="? rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "false"', function () {
|
||||||
|
it('should not generate ISO-8859-1 fallback', function () {
|
||||||
|
assert.strictEqual(contentDisposition('£ and € rates.pdf', { fallback: false }),
|
||||||
|
'attachment; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should keep ISO-8859-1 filename', function () {
|
||||||
|
assert.strictEqual(contentDisposition('£ rates.pdf', { fallback: false }),
|
||||||
|
'attachment; filename="£ rates.pdf"')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when "true"', function () {
|
||||||
|
it('should generate ISO-8859-1 fallback', function () {
|
||||||
|
assert.strictEqual(contentDisposition('£ and € rates.pdf', { fallback: true }),
|
||||||
|
'attachment; filename="£ and ? rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should pass through ISO-8859-1 filename', function () {
|
||||||
|
assert.strictEqual(contentDisposition('£ rates.pdf', { fallback: true }),
|
||||||
|
'attachment; filename="£ rates.pdf"')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('when a string', function () {
|
||||||
|
it('should require an ISO-8859-1 string', function () {
|
||||||
|
assert.throws(contentDisposition.bind(null, '€ rates.pdf', { fallback: '€ rates.pdf' }),
|
||||||
|
/fallback.*iso-8859-1/i)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use as ISO-8859-1 fallback', function () {
|
||||||
|
assert.strictEqual(contentDisposition('£ and € rates.pdf', { fallback: '£ and EURO rates.pdf' }),
|
||||||
|
'attachment; filename="£ and EURO rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use as fallback even when filename is ISO-8859-1', function () {
|
||||||
|
assert.strictEqual(contentDisposition('"£ rates".pdf', { fallback: '£ rates.pdf' }),
|
||||||
|
'attachment; filename="£ rates.pdf"; filename*=UTF-8\'\'%22%C2%A3%20rates%22.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should do nothing if equal to filename', function () {
|
||||||
|
assert.strictEqual(contentDisposition('plans.pdf', { fallback: 'plans.pdf' }),
|
||||||
|
'attachment; filename="plans.pdf"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should use the basename of the string', function () {
|
||||||
|
assert.strictEqual(contentDisposition('€ rates.pdf', { fallback: '/path/to/EURO rates.pdf' }),
|
||||||
|
'attachment; filename="EURO rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should do nothing without filename option', function () {
|
||||||
|
assert.strictEqual(contentDisposition(undefined, { fallback: 'plans.pdf' }),
|
||||||
|
'attachment')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with "type" option', function () {
|
||||||
|
it('should default to attachment', function () {
|
||||||
|
assert.strictEqual(contentDisposition(),
|
||||||
|
'attachment')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should require a string', function () {
|
||||||
|
assert.throws(contentDisposition.bind(null, undefined, { type: 42 }),
|
||||||
|
/invalid type/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should require a valid type', function () {
|
||||||
|
assert.throws(contentDisposition.bind(null, undefined, { type: 'invlaid;type' }),
|
||||||
|
/invalid type/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a header with inline type', function () {
|
||||||
|
assert.strictEqual(contentDisposition(undefined, { type: 'inline' }),
|
||||||
|
'inline')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should create a header with inline type & filename', function () {
|
||||||
|
assert.strictEqual(contentDisposition('plans.pdf', { type: 'inline' }),
|
||||||
|
'inline; filename="plans.pdf"')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should normalize type', function () {
|
||||||
|
assert.strictEqual(contentDisposition(undefined, { type: 'INLINE' }),
|
||||||
|
'inline')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('contentDisposition.parse(string)', function () {
|
||||||
|
it('should require string', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null), /argument string.*required/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject non-strings', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 42), /argument string.*required/)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with only type', function () {
|
||||||
|
it('should reject quoted value', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, '"attachment"'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject trailing semicolon', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment;'),
|
||||||
|
/invalid.*format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "inline"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('inline'), {
|
||||||
|
type: 'inline',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "form-data"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('form-data'), {
|
||||||
|
type: 'form-data',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse with trailing LWS', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment \t '), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should normalize to lower-case', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('ATTACHMENT'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with parameters', function () {
|
||||||
|
it('should reject trailing semicolon', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="rates.pdf";'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject invalid parameter name', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename@="rates.pdf"'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject missing parameter value', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename='),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject invalid parameter value', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=trolly,trains'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject invalid parameters', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=total/; foo=bar'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject duplicate parameters', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo; filename=bar'),
|
||||||
|
/invalid duplicate parameter/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject missing type', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'filename="plans.pdf"'),
|
||||||
|
/invalid type format/)
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, '; filename="plans.pdf"'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should lower-case parameter name', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; FILENAME="plans.pdf"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'plans.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse quoted parameter value', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="plans.pdf"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'plans.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse & unescape quoted value', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="the \\"plans\\".pdf"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'the "plans".pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should include all parameters', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="plans.pdf"; foo=bar'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'plans.pdf', foo: 'bar' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse parameters separated with any LWS', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment;filename="plans.pdf" \t; \t\t foo=bar'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'plans.pdf', foo: 'bar' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse token filename', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename=plans.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'plans.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse ISO-8859-1 filename', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="£ rates.pdf"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '£ rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('with extended parameters', function () {
|
||||||
|
it('should reject quoted extended parameter value', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*="UTF-8\'\'%E2%82%AC%20rates.pdf"'),
|
||||||
|
/invalid extended.*value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse UTF-8 extended parameter value', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '€ rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse UTF-8 extended parameter value', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '€ rates.pdf' }
|
||||||
|
})
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E4%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '\ufffd rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse ISO-8859-1 extended parameter value', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=ISO-8859-1\'\'%A3%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '£ rates.pdf' }
|
||||||
|
})
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=ISO-8859-1\'\'%82%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '? rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should not be case-sensitive for charser', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=utf-8\'\'%E2%82%AC%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '€ rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject unsupported charset', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=ISO-8859-2\'\'%A4%20rates.pdf'),
|
||||||
|
/unsupported charset/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse with embedded language', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'en\'%E2%82%AC%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '€ rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should prefer extended parameter value', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="EURO rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '€ rates.pdf' }
|
||||||
|
})
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf; filename="EURO rates.pdf"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename': '€ rates.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('from TC 2231', function () {
|
||||||
|
describe('Disposition-Type Inline', function () {
|
||||||
|
it('should parse "inline"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('inline'), {
|
||||||
|
type: 'inline',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject ""inline""', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, '"inline"'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "inline; filename="foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('inline; filename="foo.html"'), {
|
||||||
|
type: 'inline',
|
||||||
|
parameters: { filename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "inline; filename="Not an attachment!""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('inline; filename="Not an attachment!"'), {
|
||||||
|
type: 'inline',
|
||||||
|
parameters: { filename: 'Not an attachment!' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "inline; filename="foo.pdf""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('inline; filename="foo.pdf"'), {
|
||||||
|
type: 'inline',
|
||||||
|
parameters: { filename: 'foo.pdf' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Disposition-Type Attachment', function () {
|
||||||
|
it('should parse "attachment"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject ""attachment""', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, '"attachment"'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "ATTACHMENT"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('ATTACHMENT'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="0000000000111111111122222""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="0000000000111111111122222"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '0000000000111111111122222' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="00000000001111111111222222222233333""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="00000000001111111111222222222233333"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '00000000001111111111222222222233333' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="f\\oo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="f\\oo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="\\"quoting\\" tested.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="\\"quoting\\" tested.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '"quoting" tested.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="Here\'s a semicolon;.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="Here\'s a semicolon;.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'Here\'s a semicolon;.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; foo="bar"; filename="foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; foo="bar"; filename="foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html', foo: 'bar' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; foo="\\"\\\\";filename="foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; foo="\\"\\\\";filename="foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html', foo: '"\\' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; FILENAME="foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; FILENAME="foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename=foo.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename=foo.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo,bar.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo,bar.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo.html ;"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo.html ;'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; ;filename=foo"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; ;filename=foo'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo bar.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo bar.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename=\'foo.bar\'', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename=\'foo.bar\''), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '\'foo.bar\'' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="foo-ä.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo-ä.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="foo-ä.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo-ä.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="foo-%41.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo-%41.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-%41.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="50%.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="50%.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '50%.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="foo-%\\41.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo-%\\41.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-%41.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; name="foo-%41.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; name="foo-%41.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { name: 'foo-%41.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="ä-%41.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="ä-%41.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'ä-%41.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="foo-%c3%a4-%e2%82%ac.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo-%c3%a4-%e2%82%ac.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-%c3%a4-%e2%82%ac.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename ="foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename ="foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename="foo.html"; filename="bar.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="foo.html"; filename="bar.html"'),
|
||||||
|
/invalid duplicate parameter/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo[1](2).html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo[1](2).html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo-ä.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo-ä.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo-ä.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo-ä.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "filename=foo.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'filename=foo.html'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "x=y; filename=foo.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'x=y; filename=foo.html'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject ""foo; filename=bar;baz"; filename=qux"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, '"foo; filename=bar;baz"; filename=qux'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "filename=foo.html, filename=bar.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'filename=foo.html, filename=bar.html'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "; filename=foo.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, '; filename=foo.html'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject ": inline; attachment; filename=foo.html', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, ': inline; attachment; filename=foo.html'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "inline; attachment; filename=foo.html', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'inline; attachment; filename=foo.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; inline; filename=foo.html', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; inline; filename=foo.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename="foo.html".txt', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="foo.html".txt'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename="bar', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="bar'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo"bar;baz"qux', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo"bar;baz"qux'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=foo.html, attachment; filename=bar.html', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo.html, attachment; filename=bar.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; foo=foo filename=bar', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; foo=foo filename=bar'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename=bar foo=foo', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=bar foo=foo'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment filename=bar', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment filename=bar'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "filename=foo.html; attachment', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'filename=foo.html; attachment'),
|
||||||
|
/invalid type format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; xfilename=foo.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; xfilename=foo.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { xfilename: 'foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="/foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="/foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '/foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="\\\\foo.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="\\\\foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '\\foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Additional Parameters', function () {
|
||||||
|
it('should parse "attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'creation-date': 'Wed, 12 Feb 1997 16:29:51 -0500' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; modification-date="Wed, 12 Feb 1997 16:29:51 -0500""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; modification-date="Wed, 12 Feb 1997 16:29:51 -0500"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'modification-date': 'Wed, 12 Feb 1997 16:29:51 -0500' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Disposition-Type Extension', function () {
|
||||||
|
it('should parse "foobar"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('foobar'), {
|
||||||
|
type: 'foobar',
|
||||||
|
parameters: {}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; example="filename=example.txt""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; example="filename=example.txt"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { example: 'filename=example.txt' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('RFC 2231/5987 Encoding: Character Sets', function () {
|
||||||
|
it('should parse "attachment; filename*=iso-8859-1\'\'foo-%E4.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=iso-8859-1\'\'foo-%E4.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=UTF-8\'\'foo-%c3%a4-%e2%82%ac.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'foo-%c3%a4-%e2%82%ac.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä-€.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename*=\'\'foo-%c3%a4-%e2%82%ac.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=\'\'foo-%c3%a4-%e2%82%ac.html'),
|
||||||
|
/invalid extended.*value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=UTF-8\'\'foo-a%cc%88.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'foo-a%cc%88.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=iso-8859-1\'\'foo-%c3%a4-%e2%82%ac.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=iso-8859-1\'\'foo-%c3%a4-%e2%82%ac.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä-�.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=utf-8\'\'foo-%E4.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=utf-8\'\'foo-%E4.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-\ufffd.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename *=UTF-8\'\'foo-%c3%a4.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename *=UTF-8\'\'foo-%c3%a4.html'),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*= UTF-8\'\'foo-%c3%a4.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*= UTF-8\'\'foo-%c3%a4.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename* =UTF-8\'\'foo-%c3%a4.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename* =UTF-8\'\'foo-%c3%a4.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename*="UTF-8\'\'foo-%c3%a4.html""', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*="UTF-8\'\'foo-%c3%a4.html"'),
|
||||||
|
/invalid extended field value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename*="foo%20bar.html""', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*="foo%20bar.html"'),
|
||||||
|
/invalid extended field value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename*=UTF-8\'foo-%c3%a4.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=UTF-8\'foo-%c3%a4.html'),
|
||||||
|
/invalid extended field value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename*=UTF-8\'\'foo%"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=UTF-8\'\'foo%'),
|
||||||
|
/invalid extended field value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should reject "attachment; filename*=UTF-8\'\'f%oo.html"', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=UTF-8\'\'f%oo.html'),
|
||||||
|
/invalid extended field value/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=UTF-8\'\'A-%2541.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'A-%2541.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'A-%41.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=UTF-8\'\'%5cfoo.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%5cfoo.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '\\foo.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('RFC2231 Encoding: Continuations', function () {
|
||||||
|
it('should parse "attachment; filename*0="foo."; filename*1="html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo."; filename*1="html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*0': 'foo.', 'filename*1': 'html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*0="foo"; filename*1="\\b\\a\\r.html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo"; filename*1="\\b\\a\\r.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*0': 'foo', 'filename*1': 'bar.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*0*=UTF-8\'\'foo-%c3%a4; filename*1=".html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*0*=UTF-8\'\'foo-%c3%a4; filename*1=".html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*0*': 'UTF-8\'\'foo-%c3%a4', 'filename*1': '.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*0="foo"; filename*01="bar""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo"; filename*01="bar"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*0': 'foo', 'filename*01': 'bar' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*0="foo"; filename*2="bar""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo"; filename*2="bar"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*0': 'foo', 'filename*2': 'bar' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*1="foo."; filename*2="html""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*1="foo."; filename*2="html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*1': 'foo.', 'filename*2': 'html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*1="bar"; filename*0="foo""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*1="bar"; filename*0="foo"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { 'filename*1': 'bar', 'filename*0': 'foo' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('RFC2231 Encoding: Fallback Behaviour', function () {
|
||||||
|
it('should parse "attachment; filename="foo-ae.html"; filename*=UTF-8\'\'foo-%c3%a4.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="foo-ae.html"; filename*=UTF-8\'\'foo-%c3%a4.html'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*=UTF-8\'\'foo-%c3%a4.html; filename="foo-ae.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'foo-%c3%a4.html; filename="foo-ae.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo-ä.html' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename*0*=ISO-8859-15\'\'euro-sign%3d%a4; filename*=ISO-8859-1\'\'currency-sign%3d%a4', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename*0*=ISO-8859-15\'\'euro-sign%3d%a4; filename*=ISO-8859-1\'\'currency-sign%3d%a4'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'currency-sign=¤', 'filename*0*': 'ISO-8859-15\'\'euro-sign%3d%a4' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; foobar=x; filename="foo.html"', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; foobar=x; filename="foo.html"'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: 'foo.html', foobar: 'x' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('RFC2047 Encoding', function () {
|
||||||
|
it('should reject "attachment; filename==?ISO-8859-1?Q?foo-=E4.html?="', function () {
|
||||||
|
assert.throws(contentDisposition.parse.bind(null, 'attachment; filename==?ISO-8859-1?Q?foo-=E4.html?='),
|
||||||
|
/invalid parameter format/)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should parse "attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?=""', function () {
|
||||||
|
assert.deepEqual(contentDisposition.parse('attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?="'), {
|
||||||
|
type: 'attachment',
|
||||||
|
parameters: { filename: '=?ISO-8859-1?Q?foo-=E4.html?=' }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in a new issue