remove more dependencies

master
Jonatan Nilsson 2021-01-04 14:06:20 +00:00
parent cd811a7046
commit 8fcdbb003c
7 changed files with 1744 additions and 4 deletions

456
lib/content-disposition.js Normal file
View 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
View 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
}

View File

@ -9,8 +9,8 @@ const URL = require('url').URL;
const net = require('net');
const stringify = require('url').format;
const qs = require('querystring');
const fresh = require('fresh');
const util = require('util');
const fresh = require('./fresh');
const fastparse = require('./fastparse');
const accepts = require('./accepts');

View File

@ -6,7 +6,7 @@
*/
const ReadStream = require('fs').ReadStream;
const contentDisposition = require('content-disposition');
const contentDisposition = require('./content-disposition');
const assert = require('assert');
const extname = require('path').extname;
const util = require('util');

View File

@ -22,9 +22,7 @@
],
"license": "MIT",
"dependencies": {
"content-disposition": "jharrilim/content-disposition#572383f",
"debug-ms": "~4.1.2",
"fresh": "~0.5.2",
"http-errors-lite": "^2.0.2"
},
"devDependencies": {

View File

@ -3,6 +3,7 @@
const assert = require('assert');
const context = require('../helpers/context');
const fresh = require('../../lib/fresh')
describe('ctx.fresh', () => {
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
View 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?=' }
})
})
})
})
})