Comparing version 6.17.0 to 7.0.0
module.exports = function decodeTransferEncodingChunked(body) { | ||
var chunks = [], | ||
index = 0, | ||
nextChunkLength, | ||
nextChunkLengthHex; | ||
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(body)) { | ||
while (index < body.length) { | ||
nextChunkLengthHex = ''; | ||
while (index < body.length && body[index] !== 0xd) { | ||
var hexChar = String.fromCharCode(body[index]); | ||
if (!/[0-9a-f]/i.test(hexChar)) { | ||
throw new Error('decodeTransferEncodingChunked: Invalid hex char when decoding chunk length: ' + hexChar); | ||
} | ||
nextChunkLengthHex += hexChar; | ||
index += 1; | ||
} | ||
if (body[index] === 0xd && body[index + 1] === 0xa) { | ||
index += 2; | ||
nextChunkLength = parseInt(nextChunkLengthHex, 16); | ||
if (nextChunkLength === 0) { | ||
return Buffer.concat(chunks); | ||
} else if (nextChunkLength > 0 && body.length >= index + nextChunkLength) { | ||
chunks.push(body.slice(index, index + nextChunkLength)); | ||
index += nextChunkLength; | ||
// We do a best effort and exit if we've reached the end of some partial body | ||
if (index === body.length || (index + 2 === body.length && body[index] === 0xd && body[index + 1] === 0xa)) { | ||
return Buffer.concat(chunks); | ||
} | ||
if (index + 2 >= body.length || body[index] !== 0xd || body[index + 1] !== 0xa) { | ||
throw new Error('decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk'); | ||
} else { | ||
index += 2; | ||
} | ||
} else { | ||
throw new Error('decodeTransferEncodingChunked: Parse error, not enough data to consume a chunk of ' + nextChunkLength + ' byte(s)'); | ||
} | ||
} else { | ||
throw new Error('decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk length'); | ||
} | ||
const chunks = []; | ||
let index = 0; | ||
let nextChunkLength; | ||
let nextChunkLengthHex; | ||
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(body)) { | ||
while (index < body.length) { | ||
nextChunkLengthHex = ''; | ||
while (index < body.length && body[index] !== 0xd) { | ||
const hexChar = String.fromCharCode(body[index]); | ||
if (!/[0-9a-f]/i.test(hexChar)) { | ||
throw new Error( | ||
`decodeTransferEncodingChunked: Invalid hex char when decoding chunk length: ${hexChar}` | ||
); | ||
} | ||
} else { | ||
// Assume string | ||
while (index < body.length) { | ||
nextChunkLengthHex = ''; | ||
while (index < body.length && body[index] !== '\r') { | ||
nextChunkLengthHex += body[index]; | ||
index += 1; | ||
} | ||
if (body[index] === '\r' && body[index + 1] === '\n') { | ||
index += 2; | ||
nextChunkLength = parseInt(nextChunkLengthHex, 16); | ||
if (nextChunkLength === 0) { | ||
return chunks.join(''); | ||
} else if (nextChunkLength > 0 && body.length >= index + nextChunkLength) { | ||
chunks.push(body.slice(index, index + nextChunkLength)); | ||
index += nextChunkLength; | ||
// We do a best effort and exit if we've reached the end of some partial body | ||
if (index === body.length || (index + 2 === body.length && body[index] === '\r' && body[index + 1] === '\n')) { | ||
return chunks.join(''); | ||
} | ||
if (index + 2 >= body.length || body[index] !== '\r' || body[index + 1] !== '\n') { | ||
throw new Error('decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk'); | ||
} else { | ||
index += 2; | ||
} | ||
} else { | ||
throw new Error('decodeTransferEncodingChunked: Parse error, not enough data to consume a chunk of ' + nextChunkLength + ' byte(s)'); | ||
} | ||
} else { | ||
throw new Error('decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk length'); | ||
} | ||
nextChunkLengthHex += hexChar; | ||
index += 1; | ||
} | ||
if (body[index] === 0xd && body[index + 1] === 0xa) { | ||
index += 2; | ||
nextChunkLength = parseInt(nextChunkLengthHex, 16); | ||
if (nextChunkLength === 0) { | ||
return Buffer.concat(chunks); | ||
} else if ( | ||
nextChunkLength > 0 && | ||
body.length >= index + nextChunkLength | ||
) { | ||
chunks.push(body.slice(index, index + nextChunkLength)); | ||
index += nextChunkLength; | ||
// We do a best effort and exit if we've reached the end of some partial body | ||
if ( | ||
index === body.length || | ||
(index + 2 === body.length && | ||
body[index] === 0xd && | ||
body[index + 1] === 0xa) | ||
) { | ||
return Buffer.concat(chunks); | ||
} | ||
if ( | ||
index + 2 >= body.length || | ||
body[index] !== 0xd || | ||
body[index + 1] !== 0xa | ||
) { | ||
throw new Error( | ||
'decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk' | ||
); | ||
} else { | ||
index += 2; | ||
} | ||
} else { | ||
throw new Error( | ||
`decodeTransferEncodingChunked: Parse error, not enough data to consume a chunk of ${nextChunkLength} byte(s)` | ||
); | ||
} | ||
} else { | ||
throw new Error( | ||
'decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk length' | ||
); | ||
} | ||
} | ||
} else { | ||
// Assume string | ||
while (index < body.length) { | ||
nextChunkLengthHex = ''; | ||
while (index < body.length && body[index] !== '\r') { | ||
nextChunkLengthHex += body[index]; | ||
index += 1; | ||
} | ||
if (body[index] === '\r' && body[index + 1] === '\n') { | ||
index += 2; | ||
nextChunkLength = parseInt(nextChunkLengthHex, 16); | ||
if (nextChunkLength === 0) { | ||
return chunks.join(''); | ||
} else if ( | ||
nextChunkLength > 0 && | ||
body.length >= index + nextChunkLength | ||
) { | ||
chunks.push(body.slice(index, index + nextChunkLength)); | ||
index += nextChunkLength; | ||
// We do a best effort and exit if we've reached the end of some partial body | ||
if ( | ||
index === body.length || | ||
(index + 2 === body.length && | ||
body[index] === '\r' && | ||
body[index + 1] === '\n') | ||
) { | ||
return chunks.join(''); | ||
} | ||
if ( | ||
index + 2 >= body.length || | ||
body[index] !== '\r' || | ||
body[index + 1] !== '\n' | ||
) { | ||
throw new Error( | ||
'decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk' | ||
); | ||
} else { | ||
index += 2; | ||
} | ||
} else { | ||
throw new Error( | ||
`decodeTransferEncodingChunked: Parse error, not enough data to consume a chunk of ${nextChunkLength} byte(s)` | ||
); | ||
} | ||
} else { | ||
throw new Error( | ||
'decodeTransferEncodingChunked: Parse error, expecting \\r\\n after chunk length' | ||
); | ||
} | ||
} | ||
} | ||
}; |
module.exports = function foldHeaderLine(str, maxLength, firstLineMaxLength) { | ||
maxLength = maxLength || 78; | ||
firstLineMaxLength = firstLineMaxLength || maxLength; | ||
if (str.length <= firstLineMaxLength) { | ||
return str; | ||
maxLength = maxLength || 78; | ||
firstLineMaxLength = firstLineMaxLength || maxLength; | ||
if (str.length <= firstLineMaxLength) { | ||
return str; | ||
} | ||
let result = ''; | ||
let currentLineStartIndex = 0; | ||
let lastSpaceIndex = -1; | ||
let lastSpace; | ||
let isFirstLine = true; | ||
for (var i = 0; i < str.length; i += 1) { | ||
if (/^\s$/.test(str[i])) { | ||
lastSpaceIndex = i; | ||
lastSpace = str[i]; | ||
} | ||
var result = '', | ||
currentLineStartIndex = 0, | ||
lastSpaceIndex = -1, | ||
lastSpace, | ||
isFirstLine = true; | ||
for (var i = 0 ; i < str.length ; i += 1) { | ||
if (/^\s$/.test(str[i])) { | ||
lastSpaceIndex = i; | ||
lastSpace = str[i]; | ||
} | ||
if (i - currentLineStartIndex >= (isFirstLine ? firstLineMaxLength : maxLength - 1) && lastSpaceIndex !== -1) { | ||
result += (isFirstLine ? '' : '\r\n' + lastSpace) + str.substring(currentLineStartIndex, lastSpaceIndex); | ||
isFirstLine = false; | ||
i = lastSpaceIndex; | ||
currentLineStartIndex = i + 1; | ||
lastSpaceIndex = -1; | ||
} | ||
if ( | ||
i - currentLineStartIndex >= | ||
(isFirstLine ? firstLineMaxLength : maxLength - 1) && | ||
lastSpaceIndex !== -1 | ||
) { | ||
result += | ||
(isFirstLine ? '' : `\r\n${lastSpace}`) + | ||
str.substring(currentLineStartIndex, lastSpaceIndex); | ||
isFirstLine = false; | ||
i = lastSpaceIndex; | ||
currentLineStartIndex = i + 1; | ||
lastSpaceIndex = -1; | ||
} | ||
if (i > currentLineStartIndex) { | ||
result += (isFirstLine ? '' : '\r\n' + lastSpace) + str.substring(currentLineStartIndex, str.length); | ||
} | ||
return result; | ||
} | ||
if (i > currentLineStartIndex) { | ||
result += | ||
(isFirstLine ? '' : `\r\n${lastSpace}`) + | ||
str.substring(currentLineStartIndex, str.length); | ||
} | ||
return result; | ||
}; |
@@ -1,2 +0,2 @@ | ||
var headerNameSpecialCases = require('./headerNameSpecialCases'); | ||
const headerNameSpecialCases = require('./headerNameSpecialCases'); | ||
@@ -10,13 +10,21 @@ /** | ||
function formatHeaderName(headerName) { | ||
var lowerCasedHeaderName = headerName.toLowerCase(); | ||
if (headerNameSpecialCases.hasOwnProperty(lowerCasedHeaderName)) { | ||
return headerNameSpecialCases[lowerCasedHeaderName]; | ||
} else { | ||
// Make sure that the first char and all chars following a dash are upper-case: | ||
return lowerCasedHeaderName.replace(/(^|-)([a-z])/g, function ($0, optionalLeadingDash, ch) { | ||
return optionalLeadingDash + ch.toUpperCase(); | ||
}); | ||
} | ||
const lowerCasedHeaderName = headerName.toLowerCase(); | ||
if ( | ||
Object.prototype.hasOwnProperty.call( | ||
headerNameSpecialCases, | ||
lowerCasedHeaderName | ||
) | ||
) { | ||
return headerNameSpecialCases[lowerCasedHeaderName]; | ||
} else { | ||
// Make sure that the first char and all chars following a dash are upper-case: | ||
return lowerCasedHeaderName.replace( | ||
/(^|-)([a-z])/g, | ||
function ($0, optionalLeadingDash, ch) { | ||
return optionalLeadingDash + ch.toUpperCase(); | ||
} | ||
); | ||
} | ||
} | ||
module.exports = formatHeaderName; |
// Headers that cannot be canonicalized by naive camel casing: | ||
module.exports = { | ||
'a-im': 'A-IM', | ||
bcc: 'BCC', | ||
cc: 'CC', | ||
'content-md5': 'Content-MD5', | ||
'c-pep': 'C-PEP', | ||
'c-pep-info': 'C-PEP-Info', | ||
'content-features': 'Content-features', | ||
'content-id': 'Content-ID', | ||
dasl: 'DASL', | ||
dav: 'DAV', | ||
'dl-expansion-history': 'DL-Expansion-History', | ||
'differential-id': 'Differential-ID', | ||
'discarded-x400-ipms-extensions': 'Discarded-X400-IPMS-Extensions', | ||
'discarded-x400-mts-extensions': 'Discarded-X400-MTS-Extensions', | ||
'dkim-signature': 'DKIM-Signature', | ||
'ediint-features': 'EDIINT-Features', | ||
'jabber-id': 'Jabber-ID', | ||
'list-id': 'List-ID', | ||
'mime-version': 'MIME-Version', | ||
'message-id': 'Message-ID', | ||
'mmhs-exempted-address': 'MMHS-Exempted-Address', | ||
'mmhs-extended-authorisation-info': 'MMHS-Extended-Authorisation-Info', | ||
'mmhs-subject-indicator-codes': 'MMHS-Subject-Indicator-Codes', | ||
'mmhs-handling-instructions': 'MMHS-Handling-Instructions', | ||
'mmhs-message-instructions': 'MMHS-Message-Instructions', | ||
'mmhs-codress-message-indicator': 'MMHS-Codress-Message-Indicator', | ||
'mmhs-originator-reference': 'MMHS-Originator-Reference', | ||
'mmhs-primary-precedence': 'MMHS-Primary-Precedence', | ||
'mmhs-copy-precedence': 'MMHS-Copy-Precedence', | ||
'mmhs-message-type': 'MMHS-Message-Type', | ||
'mmhs-other-receipients-indicator-to': 'MMHS-Other-Recipients-Indicator-To', | ||
'mmhs-other-recipients-indicator-cc': 'MMHS-Other-Recipients-Indicator-CC', | ||
'mmhs-acp127-message-identifier': 'MMHS-Acp127-Message-Identifier', | ||
'mmhs-originator-plad': 'MMHS-Originator-PLAD', | ||
'mt-priority': 'MT-Priority', | ||
'nntp-posting-date': 'NNTP-Posting-Date', | ||
'nntp-posting-host': 'NNTP-Posting-Host', | ||
'original-message-id': 'Original-Message-ID', | ||
dnt: 'DNT', | ||
etag: 'ETag', | ||
p3p: 'P3P', | ||
pep: 'PEP̈́', | ||
'pics-label': 'PICS-Label', | ||
'prevent-nondelivery-report': 'Prevent-NonDelivery-Report', | ||
profileobject: 'ProfileObject', | ||
'received-spf': 'Received-SPF', | ||
'resent-message-id': 'Resent-Message-ID', | ||
'sec-websocket-accept': 'Sec-WebSocket-Accept', | ||
'sec-websocket-extensions': 'Sec-WebSocket-Extensions', | ||
'sec-websocket-key': 'Sec-WebSocket-Key', | ||
'sec-websocket-protocol': 'Sec-WebSocket-Protocol', | ||
'sec-websocket-version': 'Sec-WebSocket-Version', | ||
slug: 'SLUG', | ||
soapaction: 'SoapAction', | ||
'status-uri': 'Status-URI', | ||
subok: 'SubOK', | ||
tcn: 'TCN', | ||
te: 'TE', | ||
'ua-color': 'UA-Color', | ||
'ua-media': 'UA-Media', | ||
'ua-pixels': 'UA-Pixels', | ||
'ua-resolution': 'UA-Resolution', | ||
'ua-windowpixels': 'UA-Windowpixels', | ||
uri: 'URI', | ||
'vbr-info': 'VBR-Info', | ||
'www-authenticate': 'WWW-Authenticate', | ||
'x400-mts-identifier': 'X400-MTS-Identifier', | ||
'x-att-deviceid': 'X-ATT-DeviceId', | ||
'x-cdn': 'X-CDN', | ||
'x-csa-complaints': 'x-csa-complaints', | ||
'x-originating-ip': 'X-Originating-IP', | ||
'x-riferimento-message-id': 'X-Riferimento-Message-ID', | ||
'x-sg-eid': 'X-SG-EID', | ||
'x-tiporicevuta': 'X-TipoRicevuta', | ||
'x-ua-compatible': 'X-UA-Compatible', | ||
'x-verificasicurezza': 'X-VerificaSicurezza', | ||
'x-xss-protection': 'X-XSS-Protection' | ||
'a-im': 'A-IM', | ||
bcc: 'BCC', | ||
cc: 'CC', | ||
'content-md5': 'Content-MD5', | ||
'c-pep': 'C-PEP', | ||
'c-pep-info': 'C-PEP-Info', | ||
'content-features': 'Content-features', | ||
'content-id': 'Content-ID', | ||
dasl: 'DASL', | ||
dav: 'DAV', | ||
'dl-expansion-history': 'DL-Expansion-History', | ||
'differential-id': 'Differential-ID', | ||
'discarded-x400-ipms-extensions': 'Discarded-X400-IPMS-Extensions', | ||
'discarded-x400-mts-extensions': 'Discarded-X400-MTS-Extensions', | ||
'dkim-signature': 'DKIM-Signature', | ||
'ediint-features': 'EDIINT-Features', | ||
'jabber-id': 'Jabber-ID', | ||
'list-id': 'List-ID', | ||
'mime-version': 'MIME-Version', | ||
'message-id': 'Message-ID', | ||
'mmhs-exempted-address': 'MMHS-Exempted-Address', | ||
'mmhs-extended-authorisation-info': 'MMHS-Extended-Authorisation-Info', | ||
'mmhs-subject-indicator-codes': 'MMHS-Subject-Indicator-Codes', | ||
'mmhs-handling-instructions': 'MMHS-Handling-Instructions', | ||
'mmhs-message-instructions': 'MMHS-Message-Instructions', | ||
'mmhs-codress-message-indicator': 'MMHS-Codress-Message-Indicator', | ||
'mmhs-originator-reference': 'MMHS-Originator-Reference', | ||
'mmhs-primary-precedence': 'MMHS-Primary-Precedence', | ||
'mmhs-copy-precedence': 'MMHS-Copy-Precedence', | ||
'mmhs-message-type': 'MMHS-Message-Type', | ||
'mmhs-other-receipients-indicator-to': 'MMHS-Other-Recipients-Indicator-To', | ||
'mmhs-other-recipients-indicator-cc': 'MMHS-Other-Recipients-Indicator-CC', | ||
'mmhs-acp127-message-identifier': 'MMHS-Acp127-Message-Identifier', | ||
'mmhs-originator-plad': 'MMHS-Originator-PLAD', | ||
'mt-priority': 'MT-Priority', | ||
'nntp-posting-date': 'NNTP-Posting-Date', | ||
'nntp-posting-host': 'NNTP-Posting-Host', | ||
'original-message-id': 'Original-Message-ID', | ||
dnt: 'DNT', | ||
etag: 'ETag', | ||
p3p: 'P3P', | ||
pep: 'PEP̈́', | ||
'pics-label': 'PICS-Label', | ||
'prevent-nondelivery-report': 'Prevent-NonDelivery-Report', | ||
profileobject: 'ProfileObject', | ||
'received-spf': 'Received-SPF', | ||
'resent-message-id': 'Resent-Message-ID', | ||
'sec-websocket-accept': 'Sec-WebSocket-Accept', | ||
'sec-websocket-extensions': 'Sec-WebSocket-Extensions', | ||
'sec-websocket-key': 'Sec-WebSocket-Key', | ||
'sec-websocket-protocol': 'Sec-WebSocket-Protocol', | ||
'sec-websocket-version': 'Sec-WebSocket-Version', | ||
slug: 'SLUG', | ||
soapaction: 'SoapAction', | ||
'status-uri': 'Status-URI', | ||
subok: 'SubOK', | ||
tcn: 'TCN', | ||
te: 'TE', | ||
'ua-color': 'UA-Color', | ||
'ua-media': 'UA-Media', | ||
'ua-pixels': 'UA-Pixels', | ||
'ua-resolution': 'UA-Resolution', | ||
'ua-windowpixels': 'UA-Windowpixels', | ||
uri: 'URI', | ||
'vbr-info': 'VBR-Info', | ||
'www-authenticate': 'WWW-Authenticate', | ||
'x400-mts-identifier': 'X400-MTS-Identifier', | ||
'x-att-deviceid': 'X-ATT-DeviceId', | ||
'x-cdn': 'X-CDN', | ||
'x-csa-complaints': 'x-csa-complaints', | ||
'x-originating-ip': 'X-Originating-IP', | ||
'x-riferimento-message-id': 'X-Riferimento-Message-ID', | ||
'x-sg-eid': 'X-SG-EID', | ||
'x-tiporicevuta': 'X-TipoRicevuta', | ||
'x-ua-compatible': 'X-UA-Compatible', | ||
'x-verificasicurezza': 'X-VerificaSicurezza', | ||
'x-xss-protection': 'X-XSS-Protection', | ||
}; |
@@ -1,10 +0,10 @@ | ||
var foldHeaderLine = require('./foldHeaderLine'), | ||
formatHeaderName = require('./formatHeaderName'), | ||
isRegExp = require('./isRegExp'), | ||
rfc2231 = require('rfc2231'); | ||
const foldHeaderLine = require('./foldHeaderLine'); | ||
const formatHeaderName = require('./formatHeaderName'); | ||
const isRegExp = require('./isRegExp'); | ||
const rfc2231 = require('rfc2231'); | ||
function Headers(obj, doNotStringify) { | ||
this.namesWithCase = {}; | ||
this.valuesByName = {}; | ||
this.populate(obj, doNotStringify); | ||
this.namesWithCase = {}; | ||
this.valuesByName = {}; | ||
this.populate(obj, doNotStringify); | ||
} | ||
@@ -15,247 +15,270 @@ | ||
Headers.prototype.serializeHeaderValue = function (parsedHeaderValue) { | ||
return parsedHeaderValue; | ||
return parsedHeaderValue; | ||
}; | ||
Headers.prototype.parseHeaderValue = function (serializedHeaderValue) { | ||
return String(serializedHeaderValue); | ||
return String(serializedHeaderValue); | ||
}; | ||
Headers.prototype.populate = function (obj, doNotStringify) { | ||
if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object') { | ||
if (obj instanceof Headers) { | ||
this.populateFromObject(obj.valuesByName, doNotStringify); | ||
} else { | ||
this.populateFromObject(obj, doNotStringify); | ||
} | ||
if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object') { | ||
if (obj instanceof Headers) { | ||
this.populateFromObject(obj.valuesByName, doNotStringify); | ||
} else { | ||
this.populateFromObject(obj, doNotStringify); | ||
} | ||
return this; | ||
} | ||
return this; | ||
}; | ||
Headers.prototype.populateFromString = function (str) { | ||
this.populateFromStringAndReturnBodyStartIndex(str); | ||
return this; | ||
this.populateFromStringAndReturnBodyStartIndex(str); | ||
return this; | ||
}; | ||
Headers.prototype.populateFromStringAndReturnBodyStartIndex = function (str) { | ||
var that = this, | ||
state = 'startLine', | ||
currentHeaderName = '', | ||
currentValue = ''; | ||
const that = this; | ||
let state = 'startLine'; | ||
let currentHeaderName = ''; | ||
let currentValue = ''; | ||
function flush() { | ||
if (currentHeaderName.length > 0) { | ||
that.set(currentHeaderName, currentValue); | ||
} | ||
currentHeaderName = ''; | ||
currentValue = ''; | ||
state = 'startLine'; | ||
function flush() { | ||
if (currentHeaderName.length > 0) { | ||
that.set(currentHeaderName, currentValue); | ||
} | ||
for (var i = 0 ; i < str.length ; i += 1) { | ||
var ch = str[i]; | ||
if (state === 'startLine') { | ||
if (ch === ':') { | ||
state = 'startHeaderValue'; | ||
} else if (ch === '\r' || ch === '\n') { | ||
// Parse error or terminating CRLFCRLF | ||
currentHeaderName = ''; | ||
currentValue = ''; | ||
state = 'startLine'; | ||
} | ||
for (var i = 0; i < str.length; i += 1) { | ||
const ch = str[i]; | ||
if (state === 'startLine') { | ||
if (ch === ':') { | ||
state = 'startHeaderValue'; | ||
} else if (ch === '\r' || ch === '\n') { | ||
// Parse error or terminating CRLFCRLF | ||
if (ch === '\r' && str[i + 1] === '\n' || (ch === '\n' && str[i + 1] === '\r')) { | ||
i += 2; | ||
} else { | ||
i += 1; | ||
} | ||
flush(); | ||
return i; | ||
} else { | ||
currentHeaderName += ch; | ||
} | ||
} else if (state === 'startHeaderValue' || state === 'headerValue') { | ||
if (state === 'startHeaderValue') { | ||
if (ch === ' ') { | ||
// Ignore space after : | ||
continue; | ||
} else { | ||
state = 'headerValue'; | ||
} | ||
} | ||
if (ch === '\r') { | ||
if (str[i + 1] === '\n') { | ||
if (/[ \t]/.test(str[i + 2])) { | ||
// Skip past CRLF fold | ||
i += 1; | ||
} else { | ||
i += 1; | ||
flush(); | ||
} | ||
} else if (/[ \t]/.test(str[i + 1])) { | ||
// Skip past CR fold | ||
} else { | ||
flush(); | ||
} | ||
} else if (ch === '\n') { | ||
if (str[i + 1] === '\r') { | ||
if (/[ \t]/.test(str[i + 2])) { | ||
// Skip past LFCR fold | ||
i += 1; | ||
} else { | ||
i += 1; | ||
flush(); | ||
} | ||
} else if (/[ \t]/.test(str[i + 1])) { | ||
// Skip past LF fold | ||
} else { | ||
flush(); | ||
} | ||
} else { | ||
currentValue += ch; | ||
} | ||
if ( | ||
(ch === '\r' && str[i + 1] === '\n') || | ||
(ch === '\n' && str[i + 1] === '\r') | ||
) { | ||
i += 2; | ||
} else { | ||
i += 1; | ||
} | ||
flush(); | ||
return i; | ||
} else { | ||
currentHeaderName += ch; | ||
} | ||
} else if (state === 'startHeaderValue' || state === 'headerValue') { | ||
if (state === 'startHeaderValue') { | ||
if (ch === ' ') { | ||
// Ignore space after : | ||
continue; | ||
} else { | ||
state = 'headerValue'; | ||
} | ||
} | ||
if (ch === '\r') { | ||
if (str[i + 1] === '\n') { | ||
if (/[ \t]/.test(str[i + 2])) { | ||
// Skip past CRLF fold | ||
i += 1; | ||
} else { | ||
i += 1; | ||
flush(); | ||
} | ||
} else if (/[ \t]/.test(str[i + 1])) { | ||
// Skip past CR fold | ||
} else { | ||
flush(); | ||
} | ||
} else if (ch === '\n') { | ||
if (str[i + 1] === '\r') { | ||
if (/[ \t]/.test(str[i + 2])) { | ||
// Skip past LFCR fold | ||
i += 1; | ||
} else { | ||
i += 1; | ||
flush(); | ||
} | ||
} else if (/[ \t]/.test(str[i + 1])) { | ||
// Skip past LF fold | ||
} else { | ||
flush(); | ||
} | ||
} else { | ||
currentValue += ch; | ||
} | ||
} | ||
flush(); | ||
return i; | ||
} | ||
flush(); | ||
return i; | ||
}; | ||
Headers.prototype.populateFromObject = function (valuesByName, doNotStringify) { | ||
Object.keys(valuesByName).forEach(function (headerName) { | ||
var value = valuesByName[headerName], | ||
headerNameLowerCase = headerName.toLowerCase(); | ||
Object.keys(valuesByName).forEach(function (headerName) { | ||
let value = valuesByName[headerName]; | ||
const headerNameLowerCase = headerName.toLowerCase(); | ||
this.namesWithCase[headerNameLowerCase] = headerName; | ||
this.namesWithCase[headerNameLowerCase] = headerName; | ||
if (Array.isArray(value)) { | ||
if (!doNotStringify) { | ||
value = value.map(function (serializedHeaderValue) { | ||
return this.parseHeaderValue(serializedHeaderValue); | ||
}, this); | ||
} | ||
if (this.valuesByName[headerNameLowerCase]) { | ||
Array.prototype.push.apply(this.valuesByName[headerNameLowerCase], value); | ||
} else { | ||
this.valuesByName[headerNameLowerCase] = [].concat(value); | ||
} | ||
} else if (typeof value === 'undefined' && !doNotStringify) { | ||
// Hmm, this might not behave as intended when the header occurs multiple times in the object with different casing | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
} else { | ||
if (!doNotStringify) { | ||
value = this.parseHeaderValue(value); | ||
} | ||
if (this.valuesByName[headerNameLowerCase]) { | ||
this.valuesByName[headerNameLowerCase].push(value); | ||
} else { | ||
this.valuesByName[headerNameLowerCase] = [value]; | ||
} | ||
} | ||
}, this); | ||
return this; | ||
if (Array.isArray(value)) { | ||
if (!doNotStringify) { | ||
value = value.map(function (serializedHeaderValue) { | ||
return this.parseHeaderValue(serializedHeaderValue); | ||
}, this); | ||
} | ||
if (this.valuesByName[headerNameLowerCase]) { | ||
Array.prototype.push.apply( | ||
this.valuesByName[headerNameLowerCase], | ||
value | ||
); | ||
} else { | ||
this.valuesByName[headerNameLowerCase] = [].concat(value); | ||
} | ||
} else if (typeof value === 'undefined' && !doNotStringify) { | ||
// Hmm, this might not behave as intended when the header occurs multiple times in the object with different casing | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
} else { | ||
if (!doNotStringify) { | ||
value = this.parseHeaderValue(value); | ||
} | ||
if (this.valuesByName[headerNameLowerCase]) { | ||
this.valuesByName[headerNameLowerCase].push(value); | ||
} else { | ||
this.valuesByName[headerNameLowerCase] = [value]; | ||
} | ||
} | ||
}, this); | ||
return this; | ||
}; | ||
Headers.prototype.get = function (headerName, valueNumber) { | ||
valueNumber = valueNumber || 0; | ||
var values = this.valuesByName[headerName.toLowerCase()]; | ||
if (values) { | ||
return values[valueNumber]; | ||
} | ||
valueNumber = valueNumber || 0; | ||
const values = this.valuesByName[headerName.toLowerCase()]; | ||
if (values) { | ||
return values[valueNumber]; | ||
} | ||
}; | ||
Headers.prototype.getAll = function (headerName) { | ||
var values = this.valuesByName[headerName.toLowerCase()]; | ||
if (values) { | ||
return [].concat(values); | ||
} | ||
const values = this.valuesByName[headerName.toLowerCase()]; | ||
if (values) { | ||
return [].concat(values); | ||
} | ||
}; | ||
Headers.prototype.getNames = function () { | ||
return Object.keys(this.valuesByName).map(function (lowerCaseHeaderName) { | ||
return this.namesWithCase[lowerCaseHeaderName]; | ||
}, this); | ||
return Object.keys(this.valuesByName).map(function (lowerCaseHeaderName) { | ||
return this.namesWithCase[lowerCaseHeaderName]; | ||
}, this); | ||
}; | ||
Headers.prototype.getNamesLowerCase = function () { | ||
return Object.keys(this.valuesByName); | ||
return Object.keys(this.valuesByName); | ||
}; | ||
Headers.prototype.count = function (headerName) { | ||
var values = this.valuesByName[headerName.toLowerCase()]; | ||
if (values) { | ||
return values.length; | ||
} else { | ||
return 0; | ||
} | ||
const values = this.valuesByName[headerName.toLowerCase()]; | ||
if (values) { | ||
return values.length; | ||
} else { | ||
return 0; | ||
} | ||
}; | ||
Headers.prototype.set = function (headerName, valueOrValues, valueNumber) { | ||
if (headerName && typeof headerName === 'object') { | ||
Object.keys(headerName).forEach(function (key) { | ||
this.set(key, headerName[key]); | ||
}, this); | ||
if (headerName && typeof headerName === 'object') { | ||
Object.keys(headerName).forEach(function (key) { | ||
this.set(key, headerName[key]); | ||
}, this); | ||
} else { | ||
const headerNameLowerCase = headerName.toLowerCase(); | ||
this.namesWithCase[headerNameLowerCase] = headerName; | ||
if (Array.isArray(valueOrValues)) { | ||
if (typeof valueNumber !== 'undefined') { | ||
throw new Error( | ||
'Headers.set: valueNumber not supported when the values are provided as an array' | ||
); | ||
} | ||
if (valueOrValues.length === 0) { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
} else { | ||
this.valuesByName[headerNameLowerCase] = valueOrValues.map(function ( | ||
value | ||
) { | ||
return this.parseHeaderValue(value); | ||
}, | ||
this); | ||
} | ||
} else if ( | ||
typeof valueNumber === 'number' && | ||
Array.isArray(this.valuesByName[headerNameLowerCase]) && | ||
valueNumber < this.valuesByName[headerNameLowerCase].length | ||
) { | ||
this.valuesByName[headerNameLowerCase][ | ||
valueNumber | ||
] = this.parseHeaderValue(valueOrValues); | ||
} else { | ||
var headerNameLowerCase = headerName.toLowerCase(); | ||
this.namesWithCase[headerNameLowerCase] = headerName; | ||
if (Array.isArray(valueOrValues)) { | ||
if (typeof valueNumber !== 'undefined') { | ||
throw new Error('Headers.set: valueNumber not supported when the values are provided as an array'); | ||
} | ||
if (valueOrValues.length === 0) { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
} else { | ||
this.valuesByName[headerNameLowerCase] = valueOrValues.map(function (value) { | ||
return this.parseHeaderValue(value); | ||
}, this); | ||
} | ||
} else if (typeof valueNumber === 'number' && Array.isArray(this.valuesByName[headerNameLowerCase]) && valueNumber < this.valuesByName[headerNameLowerCase].length) { | ||
this.valuesByName[headerNameLowerCase][valueNumber] = this.parseHeaderValue(valueOrValues); | ||
} else { | ||
(this.valuesByName[headerNameLowerCase] = this.valuesByName[headerNameLowerCase] || []).push(this.parseHeaderValue(valueOrValues)); | ||
} | ||
(this.valuesByName[headerNameLowerCase] = | ||
this.valuesByName[headerNameLowerCase] || []).push( | ||
this.parseHeaderValue(valueOrValues) | ||
); | ||
} | ||
} | ||
}; | ||
Headers.prototype.remove = function (headerNameOrObj, valueOrValuesOrValueNumber) { | ||
var numRemoved = 0; | ||
if (headerNameOrObj && typeof headerNameOrObj === 'object') { | ||
Object.keys(headerNameOrObj).forEach(function (headerName) { | ||
numRemoved += this.remove(headerName, headerNameOrObj[headerName]); | ||
}, this); | ||
return numRemoved; | ||
Headers.prototype.remove = function ( | ||
headerNameOrObj, | ||
valueOrValuesOrValueNumber | ||
) { | ||
let numRemoved = 0; | ||
if (headerNameOrObj && typeof headerNameOrObj === 'object') { | ||
Object.keys(headerNameOrObj).forEach(function (headerName) { | ||
numRemoved += this.remove(headerName, headerNameOrObj[headerName]); | ||
}, this); | ||
return numRemoved; | ||
} | ||
const headerNameLowerCase = headerNameOrObj.toLowerCase(); | ||
const values = this.valuesByName[headerNameLowerCase]; | ||
if (!values) { | ||
return 0; | ||
} else if (typeof valueOrValuesOrValueNumber === 'undefined') { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
return values.length; | ||
} else if (Array.isArray(valueOrValuesOrValueNumber)) { | ||
valueOrValuesOrValueNumber.forEach(function (value) { | ||
numRemoved += this.remove(headerNameLowerCase, value); | ||
}, this); | ||
return numRemoved; | ||
} else if (typeof valueOrValuesOrValueNumber === 'number') { | ||
if (values.length === 1 && valueOrValuesOrValueNumber === 0) { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
numRemoved = 1; | ||
} else if (valueOrValuesOrValueNumber < values.length) { | ||
values.splice(valueOrValuesOrValueNumber, 1); | ||
numRemoved = 1; | ||
} | ||
var headerNameLowerCase = headerNameOrObj.toLowerCase(), | ||
values = this.valuesByName[headerNameLowerCase]; | ||
if (!values) { | ||
return 0; | ||
} else if (typeof valueOrValuesOrValueNumber === 'undefined') { | ||
} else { | ||
const value = String(valueOrValuesOrValueNumber); | ||
const index = values.indexOf(value); | ||
if (index !== -1) { | ||
if (index === 0 && values.length === 1) { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
return values.length; | ||
} else if (Array.isArray(valueOrValuesOrValueNumber)) { | ||
valueOrValuesOrValueNumber.forEach(function (value) { | ||
numRemoved += this.remove(headerNameLowerCase, value); | ||
}, this); | ||
return numRemoved; | ||
} else if (typeof valueOrValuesOrValueNumber === 'number') { | ||
if (values.length === 1 && valueOrValuesOrValueNumber === 0) { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
numRemoved = 1; | ||
} else if (valueOrValuesOrValueNumber < values.length) { | ||
values.splice(valueOrValuesOrValueNumber, 1); | ||
numRemoved = 1; | ||
} | ||
} else { | ||
var value = String(valueOrValuesOrValueNumber), | ||
index = values.indexOf(value); | ||
if (index !== -1) { | ||
if (index === 0 && values.length === 1) { | ||
delete this.valuesByName[headerNameLowerCase]; | ||
delete this.namesWithCase[headerNameLowerCase]; | ||
} else { | ||
values.splice(index, 1); | ||
} | ||
numRemoved = 1; | ||
} | ||
} else { | ||
values.splice(index, 1); | ||
} | ||
numRemoved = 1; | ||
} | ||
return numRemoved; | ||
} | ||
return numRemoved; | ||
}; | ||
@@ -267,155 +290,173 @@ | ||
Headers.prototype.has = function (headerName, stringOrArrayOrRegExp) { | ||
var values = this.valuesByName[headerName.toLowerCase()]; | ||
if (typeof stringOrArrayOrRegExp === 'undefined') { | ||
return !!values; | ||
} else if (typeof values === 'undefined') { | ||
return false; | ||
} else { | ||
if (Array.isArray(stringOrArrayOrRegExp)) { | ||
return stringOrArrayOrRegExp.every(function (expectedValue) { | ||
if (isRegExp(expectedValue)) { | ||
return values.some(function (value) { | ||
return expectedValue.test(value); | ||
}); | ||
} else { | ||
return values.indexOf(String(expectedValue)) !== -1; | ||
} | ||
}); | ||
const values = this.valuesByName[headerName.toLowerCase()]; | ||
if (typeof stringOrArrayOrRegExp === 'undefined') { | ||
return !!values; | ||
} else if (typeof values === 'undefined') { | ||
return false; | ||
} else { | ||
if (Array.isArray(stringOrArrayOrRegExp)) { | ||
return stringOrArrayOrRegExp.every(function (expectedValue) { | ||
if (isRegExp(expectedValue)) { | ||
return values.some(function (value) { | ||
return expectedValue.test(value); | ||
}); | ||
} else { | ||
return values.length === 1 && values[0] === String(stringOrArrayOrRegExp); | ||
return values.indexOf(String(expectedValue)) !== -1; | ||
} | ||
}); | ||
} else { | ||
return values.length === 1 && values[0] === String(stringOrArrayOrRegExp); | ||
} | ||
} | ||
}; | ||
Headers.prototype.parameter = function (headerName, attributeName, attributeValue) { | ||
var headerValue = this.get(headerName, 0), | ||
rfc2231DisabledForThisHeader = this.isMessyHeadersWithRfc2047 && headerName.toLowerCase() === 'content-type'; | ||
if (headerValue) { | ||
// FIXME: Will break if a quoted parameter value contains a semicolon | ||
var tokens = headerValue.split(/\s*;\s*/), | ||
parameters = {}, | ||
usesRfc2231 = false; | ||
for (var i = 1 ; i < tokens.length ; i += 1) { | ||
var matchKeyValue = tokens[i].match(/^([^=]+)=(.*)$/); | ||
if (matchKeyValue && !(matchKeyValue[1] in parameters)) { | ||
var parameterName = matchKeyValue[1], | ||
value = matchKeyValue[2], | ||
matchQuotedValue = value.match(/^"(.*)"$/); | ||
if (matchQuotedValue) { | ||
value = matchQuotedValue[1].replace(/\\/, ''); | ||
} | ||
if (!usesRfc2231 && /\*$/.test(parameterName)) { | ||
usesRfc2231 = true; | ||
} | ||
parameters[parameterName] = value; | ||
} | ||
Headers.prototype.parameter = function ( | ||
headerName, | ||
attributeName, | ||
attributeValue | ||
) { | ||
const headerValue = this.get(headerName, 0); | ||
const rfc2231DisabledForThisHeader = | ||
this.isMessyHeadersWithRfc2047 && | ||
headerName.toLowerCase() === 'content-type'; | ||
if (headerValue) { | ||
// FIXME: Will break if a quoted parameter value contains a semicolon | ||
const tokens = headerValue.split(/\s*;\s*/); | ||
let parameters = {}; | ||
let usesRfc2231 = false; | ||
for (let i = 1; i < tokens.length; i += 1) { | ||
const matchKeyValue = tokens[i].match(/^([^=]+)=(.*)$/); | ||
if (matchKeyValue && !(matchKeyValue[1] in parameters)) { | ||
const parameterName = matchKeyValue[1]; | ||
let value = matchKeyValue[2]; | ||
const matchQuotedValue = value.match(/^"(.*)"$/); | ||
if (matchQuotedValue) { | ||
value = matchQuotedValue[1].replace(/\\/, ''); | ||
} | ||
if (usesRfc2231 && !rfc2231DisabledForThisHeader) { | ||
parameters = rfc2231.unfoldAndDecodeParameters(parameters); | ||
if (!usesRfc2231 && /\*$/.test(parameterName)) { | ||
usesRfc2231 = true; | ||
} | ||
if (attributeName) { | ||
if (attributeValue) { | ||
parameters[attributeName] = attributeValue; | ||
parameters[parameterName] = value; | ||
} | ||
} | ||
if (usesRfc2231 && !rfc2231DisabledForThisHeader) { | ||
parameters = rfc2231.unfoldAndDecodeParameters(parameters); | ||
} | ||
if (attributeName) { | ||
if (attributeValue) { | ||
parameters[attributeName] = attributeValue; | ||
var tokensAfterUpdate = [tokens[0]]; | ||
const tokensAfterUpdate = [tokens[0]]; | ||
if (!rfc2231DisabledForThisHeader) { | ||
parameters = rfc2231.encodeAndFoldParameters(parameters); | ||
} | ||
if (!rfc2231DisabledForThisHeader) { | ||
parameters = rfc2231.encodeAndFoldParameters(parameters); | ||
} | ||
Object.keys(parameters).forEach(function (parameterName) { | ||
tokensAfterUpdate.push(parameterName + '=' + parameters[parameterName]); | ||
}); | ||
Object.keys(parameters).forEach(function (parameterName) { | ||
tokensAfterUpdate.push( | ||
`${parameterName}=${parameters[parameterName]}` | ||
); | ||
}); | ||
this.set(headerName, tokensAfterUpdate.join('; '), 0); | ||
} else { | ||
return parameters[attributeName]; | ||
} | ||
} else { | ||
return parameters; | ||
} | ||
this.set(headerName, tokensAfterUpdate.join('; '), 0); | ||
} else { | ||
return parameters[attributeName]; | ||
} | ||
} else { | ||
return parameters; | ||
} | ||
} | ||
}; | ||
Headers.prototype.equals = function (other) { | ||
if (this === other) { | ||
return true; | ||
if (this === other) { | ||
return true; | ||
} | ||
const headerNames = this.getNamesLowerCase(); | ||
const otherHeaderNames = other.getNamesLowerCase(); | ||
if (headerNames.length !== otherHeaderNames.length) { | ||
return false; | ||
} | ||
headerNames.sort(); | ||
otherHeaderNames.sort(); | ||
for (let i = 0; i < headerNames.length; i += 1) { | ||
const headerName = headerNames[i]; | ||
if (headerName !== otherHeaderNames[i]) { | ||
return false; | ||
} | ||
var headerNames = this.getNamesLowerCase(), | ||
otherHeaderNames = other.getNamesLowerCase(); | ||
if (headerNames.length !== otherHeaderNames.length) { | ||
const headerValues = this.getAll(headerName); | ||
const otherHeaderValues = other.getAll(headerName); | ||
if (headerValues.length !== otherHeaderValues.length) { | ||
return false; | ||
} | ||
if (headerValues.length === 1 && otherHeaderValues.length === 1) { | ||
if (headerValues[0] !== otherHeaderValues[0]) { | ||
return false; | ||
} | ||
headerNames.sort(); | ||
otherHeaderNames.sort(); | ||
for (var i = 0 ; i < headerNames.length ; i += 1) { | ||
var headerName = headerNames[i]; | ||
if (headerName !== otherHeaderNames[i]) { | ||
return false; | ||
} | ||
} else { | ||
headerValues.sort(); | ||
otherHeaderValues.sort(); | ||
for (let j = 0; j < headerValues.length; j += 1) { | ||
if (headerValues[i] !== otherHeaderValues[i]) { | ||
return false; | ||
} | ||
var headerValues = this.getAll(headerName), | ||
otherHeaderValues = other.getAll(headerName); | ||
if (headerValues.length !== otherHeaderValues.length) { | ||
return false; | ||
} | ||
if (headerValues.length === 1 && otherHeaderValues.length === 1) { | ||
if (headerValues[0] !== otherHeaderValues[0]) { | ||
return false; | ||
} | ||
} else { | ||
headerValues.sort(); | ||
otherHeaderValues.sort(); | ||
for (var j = 0 ; j < headerValues.length ; j += 1) { | ||
if (headerValues[i] !== otherHeaderValues[i]) { | ||
return false; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
return true; | ||
}; | ||
Headers.prototype.clone = function () { | ||
var clone = new Headers(), | ||
lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (headerName) { | ||
clone.set(headerName, this.getAll(headerName)); | ||
}, this); | ||
return clone; | ||
const clone = new Headers(); | ||
const lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (headerName) { | ||
clone.set(headerName, this.getAll(headerName)); | ||
}, this); | ||
return clone; | ||
}; | ||
Headers.prototype.toString = function (maxLineLength) { | ||
var result = '', | ||
lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) { | ||
this.valuesByName[lowerCaseHeaderName].forEach(function (value) { | ||
result += formatHeaderName(lowerCaseHeaderName) + ': ' + foldHeaderLine(this.serializeHeaderValue(value), maxLineLength, maxLineLength - lowerCaseHeaderName.length - 2) + '\r\n'; | ||
}, this); | ||
let result = ''; | ||
const lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) { | ||
this.valuesByName[lowerCaseHeaderName].forEach(function (value) { | ||
result += `${formatHeaderName(lowerCaseHeaderName)}: ${foldHeaderLine( | ||
this.serializeHeaderValue(value), | ||
maxLineLength, | ||
maxLineLength - lowerCaseHeaderName.length - 2 | ||
)}\r\n`; | ||
}, this); | ||
return result; | ||
}, this); | ||
return result; | ||
}; | ||
Headers.prototype.toCanonicalObject = function () { | ||
var canonicalObject = {}, | ||
lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) { | ||
canonicalObject[formatHeaderName(lowerCaseHeaderName)] = this.getAll(lowerCaseHeaderName); | ||
}, this); | ||
return canonicalObject; | ||
const canonicalObject = {}; | ||
const lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) { | ||
canonicalObject[formatHeaderName(lowerCaseHeaderName)] = this.getAll( | ||
lowerCaseHeaderName | ||
); | ||
}, this); | ||
return canonicalObject; | ||
}; | ||
Headers.prototype.toJSON = function () { | ||
var obj = {}, | ||
lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) { | ||
var values = this.getAll(lowerCaseHeaderName); | ||
if (values.length === 1) { | ||
obj[formatHeaderName(lowerCaseHeaderName)] = this.get(lowerCaseHeaderName); | ||
} else { | ||
obj[formatHeaderName(lowerCaseHeaderName)] = this.getAll(lowerCaseHeaderName); | ||
} | ||
}, this); | ||
return obj; | ||
const obj = {}; | ||
const lowerCaseHeaderNames = this.getNamesLowerCase(); | ||
lowerCaseHeaderNames.forEach(function (lowerCaseHeaderName) { | ||
const values = this.getAll(lowerCaseHeaderName); | ||
if (values.length === 1) { | ||
obj[formatHeaderName(lowerCaseHeaderName)] = this.get( | ||
lowerCaseHeaderName | ||
); | ||
} else { | ||
obj[formatHeaderName(lowerCaseHeaderName)] = this.getAll( | ||
lowerCaseHeaderName | ||
); | ||
} | ||
}, this); | ||
return obj; | ||
}; | ||
module.exports = Headers; |
@@ -1,7 +0,7 @@ | ||
var Headers = require('./Headers'), | ||
rfc2047 = require('rfc2047'), | ||
util = require('util'); | ||
const Headers = require('./Headers'); | ||
const rfc2047 = require('rfc2047'); | ||
const util = require('util'); | ||
function HeadersWithRfc2047(obj, doNotStringify) { | ||
Headers.call(this, obj, doNotStringify); | ||
Headers.call(this, obj, doNotStringify); | ||
} | ||
@@ -13,10 +13,14 @@ | ||
HeadersWithRfc2047.prototype.serializeHeaderValue = function (parsedHeaderValue) { | ||
return rfc2047.encode(parsedHeaderValue); | ||
HeadersWithRfc2047.prototype.serializeHeaderValue = function ( | ||
parsedHeaderValue | ||
) { | ||
return rfc2047.encode(parsedHeaderValue); | ||
}; | ||
HeadersWithRfc2047.prototype.parseHeaderValue = function (serializedHeaderValue) { | ||
return rfc2047.decode(String(serializedHeaderValue)); | ||
HeadersWithRfc2047.prototype.parseHeaderValue = function ( | ||
serializedHeaderValue | ||
) { | ||
return rfc2047.decode(String(serializedHeaderValue)); | ||
}; | ||
module.exports = HeadersWithRfc2047; | ||
module.exports = HeadersWithRfc2047; |
@@ -1,12 +0,12 @@ | ||
var HttpExchange = require('./HttpExchange'); | ||
const HttpExchange = require('./HttpExchange'); | ||
function HttpConversation(obj, doNotStringify) { | ||
obj = obj || {}; | ||
this.exchanges = (obj.exchanges || []).map(function (httpExchange) { | ||
if (httpExchange instanceof HttpExchange) { | ||
return httpExchange; | ||
} else { | ||
return new HttpExchange(httpExchange, doNotStringify); | ||
} | ||
}); | ||
obj = obj || {}; | ||
this.exchanges = (obj.exchanges || []).map(function (httpExchange) { | ||
if (httpExchange instanceof HttpExchange) { | ||
return httpExchange; | ||
} else { | ||
return new HttpExchange(httpExchange, doNotStringify); | ||
} | ||
}); | ||
} | ||
@@ -17,33 +17,36 @@ | ||
HttpConversation.prototype.clone = function () { | ||
return new HttpConversation({ | ||
exchanges: this.exchanges.map(function (httpExchange) { | ||
return httpExchange.clone(); | ||
}) | ||
}); | ||
return new HttpConversation({ | ||
exchanges: this.exchanges.map(function (httpExchange) { | ||
return httpExchange.clone(); | ||
}), | ||
}); | ||
}; | ||
HttpConversation.prototype.toString = function (maxLineLength) { | ||
return this.exchanges.map(function (httpExchange) { | ||
return httpExchange.toString(maxLineLength); | ||
}).join('\r\n\r\n'); | ||
return this.exchanges | ||
.map(function (httpExchange) { | ||
return httpExchange.toString(maxLineLength); | ||
}) | ||
.join('\r\n\r\n'); | ||
}; | ||
HttpConversation.prototype.equals = function (other) { | ||
return this === other || ( | ||
other instanceof HttpConversation && | ||
this.exchanges.length === other.exchanges.length && | ||
this.exchanges.every(function (httpExchange, i) { | ||
return httpExchange.equals(other.exchanges[i]); | ||
}) | ||
); | ||
return ( | ||
this === other || | ||
(other instanceof HttpConversation && | ||
this.exchanges.length === other.exchanges.length && | ||
this.exchanges.every(function (httpExchange, i) { | ||
return httpExchange.equals(other.exchanges[i]); | ||
})) | ||
); | ||
}; | ||
HttpConversation.prototype.toJSON = function () { | ||
return { | ||
exchanges: this.exchanges.map(function (exchange) { | ||
return exchange.toJSON(); | ||
}) | ||
}; | ||
return { | ||
exchanges: this.exchanges.map(function (exchange) { | ||
return exchange.toJSON(); | ||
}), | ||
}; | ||
}; | ||
module.exports = HttpConversation; |
@@ -1,12 +0,18 @@ | ||
var HttpRequest = require('./HttpRequest'), | ||
HttpResponse = require('./HttpResponse'); | ||
const HttpRequest = require('./HttpRequest'); | ||
const HttpResponse = require('./HttpResponse'); | ||
function HttpExchange(obj, doNotStringify) { | ||
obj = obj || {}; | ||
if (typeof obj.request !== 'undefined') { | ||
this.request = obj.request instanceof HttpRequest ? obj.request : new HttpRequest(obj.request, doNotStringify); | ||
} | ||
if (typeof obj.response !== 'undefined') { | ||
this.response = obj.response instanceof HttpResponse ? obj.response : new HttpResponse(obj.response, doNotStringify); | ||
} | ||
obj = obj || {}; | ||
if (typeof obj.request !== 'undefined') { | ||
this.request = | ||
obj.request instanceof HttpRequest | ||
? obj.request | ||
: new HttpRequest(obj.request, doNotStringify); | ||
} | ||
if (typeof obj.response !== 'undefined') { | ||
this.response = | ||
obj.response instanceof HttpResponse | ||
? obj.response | ||
: new HttpResponse(obj.response, doNotStringify); | ||
} | ||
} | ||
@@ -17,34 +23,42 @@ | ||
HttpExchange.prototype.clone = function () { | ||
return new HttpExchange({ | ||
request: this.request && this.request.clone(), | ||
response: this.response && this.response.clone() | ||
}); | ||
return new HttpExchange({ | ||
request: this.request && this.request.clone(), | ||
response: this.response && this.response.clone(), | ||
}); | ||
}; | ||
HttpExchange.prototype.toString = function (maxLineLength) { | ||
return ( | ||
(this.request ? this.request.toString(maxLineLength) : '<no request>') + '\r\n\r\n' + | ||
(this.response ? this.response.toString(maxLineLength) : '<no response>') | ||
); | ||
return `${ | ||
this.request ? this.request.toString(maxLineLength) : '<no request>' | ||
}\r\n\r\n${ | ||
this.response ? this.response.toString(maxLineLength) : '<no response>' | ||
}`; | ||
}; | ||
HttpExchange.prototype.equals = function (other) { | ||
return this === other || ( | ||
other instanceof HttpExchange && | ||
(this.request === other.request || (this.request && other.request && this.request.equals(other.request))) && | ||
(this.response === other.response || (this.response && other.response && this.response.equals(other.response))) | ||
); | ||
return ( | ||
this === other || | ||
(other instanceof HttpExchange && | ||
(this.request === other.request || | ||
(this.request && | ||
other.request && | ||
this.request.equals(other.request))) && | ||
(this.response === other.response || | ||
(this.response && | ||
other.response && | ||
this.response.equals(other.response)))) | ||
); | ||
}; | ||
HttpExchange.prototype.toJSON = function () { | ||
var obj = {}; | ||
if (this.request) { | ||
obj.request = this.request.toJSON(); | ||
} | ||
if (this.response) { | ||
obj.response = this.response.toJSON(); | ||
} | ||
return obj; | ||
const obj = {}; | ||
if (this.request) { | ||
obj.request = this.request.toJSON(); | ||
} | ||
if (this.response) { | ||
obj.response = this.response.toJSON(); | ||
} | ||
return obj; | ||
}; | ||
module.exports = HttpExchange; |
@@ -1,16 +0,27 @@ | ||
/*global btoa*/ | ||
var Message = require('./Message'), | ||
RequestLine = require('./RequestLine'), | ||
util = require('util'), | ||
_ = require('underscore'); | ||
/* global btoa */ | ||
const Message = require('./Message'); | ||
const RequestLine = require('./RequestLine'); | ||
const util = require('util'); | ||
const omit = require('lodash.omit'); | ||
function HttpRequest(obj, doNotStringify) { | ||
this.requestLine = new RequestLine(); | ||
this.encrypted = false; | ||
Message.call(this, obj); | ||
this.requestLine = new RequestLine(); | ||
this.encrypted = false; | ||
Message.call(this, obj); | ||
} | ||
HttpRequest.metadataPropertyNames = ['host', 'port', 'encrypted', 'cert', 'key', 'ca', 'rejectUnauthorized', 'credentials']; | ||
HttpRequest.metadataPropertyNames = [ | ||
'host', | ||
'port', | ||
'encrypted', | ||
'cert', | ||
'key', | ||
'ca', | ||
'rejectUnauthorized', | ||
'credentials', | ||
]; | ||
var ownPropertyNames = ['requestLine'].concat(RequestLine.propertyNames).concat(HttpRequest.metadataPropertyNames); | ||
const ownPropertyNames = ['requestLine'] | ||
.concat(RequestLine.propertyNames) | ||
.concat(HttpRequest.metadataPropertyNames); | ||
@@ -24,243 +35,277 @@ HttpRequest.propertyNames = Message.propertyNames.concat(ownPropertyNames); | ||
HttpRequest.prototype.populate = function (obj) { | ||
if (obj && typeof obj === 'object' && (typeof Buffer === 'undefined' || !Buffer.isBuffer(obj))) { | ||
this.populateFromObject(obj); | ||
} else { | ||
Message.prototype.populate.call(this, obj); | ||
} | ||
return this; | ||
if ( | ||
obj && | ||
typeof obj === 'object' && | ||
(typeof Buffer === 'undefined' || !Buffer.isBuffer(obj)) | ||
) { | ||
this.populateFromObject(obj); | ||
} else { | ||
Message.prototype.populate.call(this, obj); | ||
} | ||
return this; | ||
}; | ||
HttpRequest.prototype.populateFromObject = function (obj) { | ||
Message.prototype.populateFromObject.call(this, _.omit(obj, ownPropertyNames)); | ||
HttpRequest.metadataPropertyNames.forEach(function (metadataPropertyName) { | ||
if (typeof obj[metadataPropertyName] !== 'undefined') { | ||
this[metadataPropertyName] = obj[metadataPropertyName]; | ||
} | ||
}, this); | ||
if (typeof obj.url === 'string') { | ||
var fragments = obj.url.split(' '); | ||
if (fragments.length > 1) { | ||
this.method = fragments.shift(); | ||
} | ||
if (fragments.length > 0) { | ||
this._updateUrl(fragments[0]); | ||
obj = _.extend({}, obj); | ||
obj.url = this.path; | ||
} | ||
if (fragments.length > 1) { | ||
this.protocol = fragments[1]; | ||
} | ||
Message.prototype.populateFromObject.call(this, omit(obj, ownPropertyNames)); | ||
HttpRequest.metadataPropertyNames.forEach(function (metadataPropertyName) { | ||
if (typeof obj[metadataPropertyName] !== 'undefined') { | ||
this[metadataPropertyName] = obj[metadataPropertyName]; | ||
} | ||
if (typeof obj.requestLine !== 'undefined') { | ||
this.requestLine.populate(obj.requestLine); | ||
}, this); | ||
if (typeof obj.url === 'string') { | ||
const fragments = obj.url.split(' '); | ||
if (fragments.length > 1) { | ||
this.method = fragments.shift(); | ||
} | ||
this.requestLine.populateFromObject(_.omit(obj, 'url')); | ||
return this; | ||
if (fragments.length > 0) { | ||
this._updateUrl(fragments[0]); | ||
obj = { ...obj }; | ||
obj.url = this.path; | ||
} | ||
if (fragments.length > 1) { | ||
this.protocol = fragments[1]; | ||
} | ||
} | ||
if (typeof obj.requestLine !== 'undefined') { | ||
this.requestLine.populate(obj.requestLine); | ||
} | ||
this.requestLine.populateFromObject(omit(obj, 'url')); | ||
return this; | ||
}; | ||
function safeDecodeURIComponent(str) { | ||
try { | ||
return decodeURIComponent(str); | ||
} catch (e) { | ||
// Assume URIError: URI malformed (percent encoded octets that don't decode as UTF-8) | ||
return str; | ||
} | ||
try { | ||
return decodeURIComponent(str); | ||
} catch (e) { | ||
// Assume URIError: URI malformed (percent encoded octets that don't decode as UTF-8) | ||
return str; | ||
} | ||
} | ||
HttpRequest.prototype.populateFromString = function (str) { | ||
var matchRequestLine = str.match(/^([^\r\n]*)(\r\n?|\n\r?|$)/); | ||
const matchRequestLine = str.match(/^([^\r\n]*)(\r\n?|\n\r?|$)/); | ||
if (matchRequestLine) { | ||
Message.prototype.populateFromString.call(this, str.substr(matchRequestLine[0].length)); | ||
var requestLineStr = matchRequestLine[1], | ||
requestLineFragments = requestLineStr.split(' '); | ||
if (requestLineFragments.length === 1) { | ||
requestLineFragments.unshift('GET'); | ||
} | ||
if (requestLineFragments.length >= 2) { | ||
this.url = requestLineFragments[1]; | ||
requestLineFragments[1] = this.requestLine.url; | ||
} | ||
requestLineStr = requestLineFragments.join(' '); | ||
this.requestLine.populateFromString(requestLineStr); | ||
if (matchRequestLine) { | ||
Message.prototype.populateFromString.call( | ||
this, | ||
str.substr(matchRequestLine[0].length) | ||
); | ||
let requestLineStr = matchRequestLine[1]; | ||
const requestLineFragments = requestLineStr.split(' '); | ||
if (requestLineFragments.length === 1) { | ||
requestLineFragments.unshift('GET'); | ||
} | ||
return this; | ||
if (requestLineFragments.length >= 2) { | ||
this.url = requestLineFragments[1]; | ||
requestLineFragments[1] = this.requestLine.url; | ||
} | ||
requestLineStr = requestLineFragments.join(' '); | ||
this.requestLine.populateFromString(requestLineStr); | ||
} | ||
return this; | ||
}; | ||
HttpRequest.prototype.populateFromBuffer = function (buffer) { | ||
var i = 0; | ||
while (i < buffer.length && buffer[i] !== 0x0d && buffer[i] !== 0x0a) { | ||
i += 1; | ||
} | ||
if (i > 0) { | ||
this.requestLine.populateFromString(buffer.slice(0, i).toString('ascii')); | ||
} | ||
if (buffer[i] === 0x0d) { | ||
i += 1; | ||
} | ||
if (buffer[i] === 0x0a) { | ||
i += 1; | ||
} | ||
Message.prototype.populateFromBuffer.call(this, buffer.slice(i)); | ||
return this; | ||
let i = 0; | ||
while (i < buffer.length && buffer[i] !== 0x0d && buffer[i] !== 0x0a) { | ||
i += 1; | ||
} | ||
if (i > 0) { | ||
this.requestLine.populateFromString(buffer.slice(0, i).toString('ascii')); | ||
} | ||
if (buffer[i] === 0x0d) { | ||
i += 1; | ||
} | ||
if (buffer[i] === 0x0a) { | ||
i += 1; | ||
} | ||
Message.prototype.populateFromBuffer.call(this, buffer.slice(i)); | ||
return this; | ||
}; | ||
Object.defineProperty(HttpRequest.prototype, 'basicAuthCredentials', { | ||
get: function () { | ||
var authorizationHeaderValue = this.headers.get('Authorization'); | ||
if (typeof authorizationHeaderValue === 'string') { | ||
var authorizationFragments = authorizationHeaderValue.split(' '); | ||
if (authorizationFragments.length === 2 && authorizationFragments[0] === 'Basic') { | ||
var credentials = new Buffer(authorizationFragments[1], 'base64').toString('utf-8').split(':'), | ||
username = credentials.shift(), | ||
password = credentials.join(':') || undefined; | ||
return { | ||
username: username, | ||
password: password | ||
}; | ||
} | ||
} | ||
get() { | ||
const authorizationHeaderValue = this.headers.get('Authorization'); | ||
if (typeof authorizationHeaderValue === 'string') { | ||
const authorizationFragments = authorizationHeaderValue.split(' '); | ||
if ( | ||
authorizationFragments.length === 2 && | ||
authorizationFragments[0] === 'Basic' | ||
) { | ||
const credentials = Buffer.from(authorizationFragments[1], 'base64') | ||
.toString('utf-8') | ||
.split(':'); | ||
const username = credentials.shift(); | ||
const password = credentials.join(':') || undefined; | ||
return { | ||
username, | ||
password, | ||
}; | ||
} | ||
} | ||
}, | ||
}); | ||
Object.defineProperty(HttpRequest.prototype, 'username', { | ||
get: function () { | ||
var basicAuthCredentials = this.basicAuthCredentials; | ||
return basicAuthCredentials && basicAuthCredentials.username; | ||
} | ||
get() { | ||
const basicAuthCredentials = this.basicAuthCredentials; | ||
return basicAuthCredentials && basicAuthCredentials.username; | ||
}, | ||
}); | ||
Object.defineProperty(HttpRequest.prototype, 'password', { | ||
get: function () { | ||
var basicAuthCredentials = this.basicAuthCredentials; | ||
return basicAuthCredentials && basicAuthCredentials.password; | ||
} | ||
get() { | ||
const basicAuthCredentials = this.basicAuthCredentials; | ||
return basicAuthCredentials && basicAuthCredentials.password; | ||
}, | ||
}); | ||
Object.defineProperty(HttpRequest.prototype, 'url', { | ||
get: function () { | ||
var host = this.host; | ||
if (host) { | ||
var port = this.port, | ||
encrypted = this.encrypted, | ||
basicAuthCredentials = this.basicAuthCredentials; | ||
return ( | ||
'http' + (encrypted ? 's' : '') + '://' + | ||
(basicAuthCredentials ? | ||
encodeURIComponent(basicAuthCredentials.username) + | ||
(basicAuthCredentials.password ? ':' + encodeURIComponent(basicAuthCredentials.password) : '') + '@' : | ||
'' | ||
) + | ||
host + | ||
(typeof port === 'number' && port !== (encrypted ? 443 : 80) ? ':' + port : '') + | ||
(this.requestLine.url || '/') | ||
); | ||
} else { | ||
return this.requestLine.url || '/'; | ||
} | ||
}, | ||
set: function (url) { | ||
this.host = undefined; | ||
this.port = undefined; | ||
this._updateUrl(url, true); | ||
get() { | ||
const host = this.host; | ||
if (host) { | ||
const port = this.port; | ||
const encrypted = this.encrypted; | ||
const basicAuthCredentials = this.basicAuthCredentials; | ||
return `http${encrypted ? 's' : ''}://${ | ||
basicAuthCredentials | ||
? `${ | ||
encodeURIComponent(basicAuthCredentials.username) + | ||
(basicAuthCredentials.password | ||
? `:${encodeURIComponent(basicAuthCredentials.password)}` | ||
: '') | ||
}@` | ||
: '' | ||
}${host}${ | ||
typeof port === 'number' && port !== (encrypted ? 443 : 80) | ||
? `:${port}` | ||
: '' | ||
}${this.requestLine.url || '/'}`; | ||
} else { | ||
return this.requestLine.url || '/'; | ||
} | ||
}, | ||
set(url) { | ||
this.host = undefined; | ||
this.port = undefined; | ||
this._updateUrl(url, true); | ||
}, | ||
}); | ||
HttpRequest.prototype._updateUrl = function (url, invokedAsSetter) { | ||
var fragments = url.split(' '); | ||
if (fragments.length > 1) { | ||
this.method = fragments.shift(); | ||
} | ||
if (fragments.length > 0) { | ||
var matchUrl = fragments[0].match(/^(https?:)\/\/(?:([^:@\/]+(?::[^@\/]+?))@)?((?:[a-z0-9](?:[\-a-z0-9]*[a-z0-9])?\.)*[a-z][\-a-z]*[a-z]|(?:(?:[0-9]|1?[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|1?[0-9][0-9]|2[0-4][0-9]|25[0-5]))(:\d{1,5})?(\/[\w\-\.~%!$&'\(\)*+,;=:@\/]*(?:\?[\w\-\.~%!$&'\(\)*+,;=:@\/?]*)?(?:#[\w\-\.~%!$&'\(\)*+,;=:@\/?#]*)?)?$/); | ||
if (matchUrl) { | ||
var protocol = matchUrl[1], | ||
auth = matchUrl[2], | ||
host = matchUrl[3], | ||
port = matchUrl[4], | ||
path = matchUrl[5]; | ||
if (!this.headers.has('Host')) { | ||
this.headers.set('Host', host + (port || '')); | ||
} | ||
if (typeof this.host !== 'undefined' && this.host !== host) { | ||
throw new Error('the host property and the url specify different hosts, ' + this.host + ' vs. ' + host); | ||
} | ||
this.host = host; | ||
if (typeof port !== 'undefined') { | ||
port = parseInt(port.substr(1), 10); | ||
if (typeof this.port !== 'undefined' && this.port !== port) { | ||
throw new Error('the port property and the url specify different ports, ' + this.port + ' vs. ' + port); | ||
} | ||
this.port = port; | ||
} else if (typeof this.port === 'undefined') { | ||
if (protocol === 'https:') { | ||
this.port = 443; | ||
} else { | ||
this.port = 80; | ||
} | ||
} | ||
const fragments = url.split(' '); | ||
if (fragments.length > 1) { | ||
this.method = fragments.shift(); | ||
} | ||
if (fragments.length > 0) { | ||
const matchUrl = fragments[0].match( | ||
/^(https?:)\/\/(?:([^:@/]+(?::[^@/]+?))@)?((?:[a-z0-9](?:[-a-z0-9]*[a-z0-9])?\.)*[a-z][-a-z]*[a-z]|(?:(?:[0-9]|1?[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|1?[0-9][0-9]|2[0-4][0-9]|25[0-5]))(:\d{1,5})?(\/[\w\-.~%!$&'()*+,;=:@/]*(?:\?[\w\-.~%!$&'()*+,;=:@/?]*)?(?:#[\w\-.~%!$&'()*+,;=:@/?#]*)?)?$/ | ||
); | ||
if (matchUrl) { | ||
const protocol = matchUrl[1]; | ||
const auth = matchUrl[2]; | ||
const host = matchUrl[3]; | ||
let port = matchUrl[4]; | ||
const path = matchUrl[5]; | ||
if (!this.headers.has('Host')) { | ||
this.headers.set('Host', host + (port || '')); | ||
} | ||
if (typeof this.host !== 'undefined' && this.host !== host) { | ||
throw new Error( | ||
`the host property and the url specify different hosts, ${this.host} vs. ${host}` | ||
); | ||
} | ||
this.host = host; | ||
if (typeof port !== 'undefined') { | ||
port = parseInt(port.substr(1), 10); | ||
if (typeof this.port !== 'undefined' && this.port !== port) { | ||
throw new Error( | ||
`the port property and the url specify different ports, ${this.port} vs. ${port}` | ||
); | ||
} | ||
this.port = port; | ||
} else if (typeof this.port === 'undefined') { | ||
if (protocol === 'https:') { | ||
this.port = 443; | ||
} else { | ||
this.port = 80; | ||
} | ||
} | ||
if (invokedAsSetter) { | ||
this.headers.remove('Authorization'); | ||
} | ||
if (typeof auth === 'string' && auth.length > 0) { | ||
var authFragments = auth.split(':'), | ||
username = safeDecodeURIComponent(authFragments.shift()), | ||
password = safeDecodeURIComponent(authFragments.join(':')); | ||
this.headers.set('Authorization', 'Basic ' + (typeof Buffer !== 'undefined' ? new Buffer(username + ':' + password, 'utf-8').toString('base64') : btoa(auth))); | ||
} | ||
if (invokedAsSetter) { | ||
this.headers.remove('Authorization'); | ||
} | ||
if (typeof auth === 'string' && auth.length > 0) { | ||
const authFragments = auth.split(':'); | ||
const username = safeDecodeURIComponent(authFragments.shift()); | ||
const password = safeDecodeURIComponent(authFragments.join(':')); | ||
this.headers.set( | ||
'Authorization', | ||
`Basic ${ | ||
typeof Buffer !== 'undefined' | ||
? Buffer.from(`${username}:${password}`, 'utf-8').toString( | ||
'base64' | ||
) | ||
: btoa(auth) | ||
}` | ||
); | ||
} | ||
this.encrypted = protocol === 'https:'; | ||
this.requestLine.url = path || '/'; | ||
} else { | ||
this.requestLine.url = fragments[0] || '/'; | ||
} | ||
this.encrypted = protocol === 'https:'; | ||
this.requestLine.url = path || '/'; | ||
} else { | ||
this.requestLine.url = fragments[0] || '/'; | ||
} | ||
if (fragments.length >= 2) { | ||
this.protocol = fragments[2]; | ||
} | ||
} | ||
if (fragments.length >= 2) { | ||
this.protocol = fragments[2]; | ||
} | ||
}; | ||
HttpRequest.prototype.clone = function () { | ||
return new HttpRequest({ | ||
requestLine: this.requestLine.clone(), | ||
headers: this.headers.clone(), | ||
body: this.body // Not sure | ||
}); | ||
return new HttpRequest({ | ||
requestLine: this.requestLine.clone(), | ||
headers: this.headers.clone(), | ||
body: this.body, // Not sure | ||
}); | ||
}; | ||
HttpRequest.prototype.toString = function (maxLineLength) { | ||
return ( | ||
this.requestLine.toString() + '\r\n' + | ||
Message.prototype.toString.call(this, maxLineLength) | ||
); | ||
return `${this.requestLine.toString()}\r\n${Message.prototype.toString.call( | ||
this, | ||
maxLineLength | ||
)}`; | ||
}; | ||
HttpRequest.prototype.equals = function (other) { | ||
return this === other || ( | ||
other instanceof HttpRequest && | ||
this.requestLine.equals(other.requestLine) && | ||
Boolean(this.encrypted) === Boolean(other.encrypted) && | ||
Message.prototype.equals.call(this, other) | ||
); | ||
return ( | ||
this === other || | ||
(other instanceof HttpRequest && | ||
this.requestLine.equals(other.requestLine) && | ||
Boolean(this.encrypted) === Boolean(other.encrypted) && | ||
Message.prototype.equals.call(this, other)) | ||
); | ||
}; | ||
RequestLine.propertyNames.forEach(function (requestLinePropertyName) { | ||
if (requestLinePropertyName !== 'url') { | ||
Object.defineProperty(HttpRequest.prototype, requestLinePropertyName, { | ||
enumerable: true, | ||
get: function () { | ||
return this.requestLine[requestLinePropertyName]; | ||
}, | ||
set: function (value) { | ||
this.requestLine[requestLinePropertyName] = value; | ||
} | ||
}); | ||
} | ||
if (requestLinePropertyName !== 'url') { | ||
Object.defineProperty(HttpRequest.prototype, requestLinePropertyName, { | ||
enumerable: true, | ||
get() { | ||
return this.requestLine[requestLinePropertyName]; | ||
}, | ||
set(value) { | ||
this.requestLine[requestLinePropertyName] = value; | ||
}, | ||
}); | ||
} | ||
}); | ||
HttpRequest.prototype.toJSON = function () { | ||
return _.extend(Message.prototype.toJSON.call(this), this.requestLine.toJSON()); | ||
return { | ||
...Message.prototype.toJSON.call(this), | ||
...this.requestLine.toJSON(), | ||
}; | ||
}; | ||
module.exports = HttpRequest; |
@@ -1,12 +0,12 @@ | ||
var Message = require('./Message'), | ||
StatusLine = require('./StatusLine'), | ||
util = require('util'), | ||
_ = require('underscore'); | ||
const Message = require('./Message'); | ||
const StatusLine = require('./StatusLine'); | ||
const util = require('util'); | ||
const omit = require('lodash.omit'); | ||
function HttpResponse(obj, doNotStringify) { | ||
this.statusLine = new StatusLine(); | ||
Message.call(this, obj, doNotStringify); | ||
this.statusLine = new StatusLine(); | ||
Message.call(this, obj, doNotStringify); | ||
} | ||
var ownPropertyNames = ['statusLine'].concat(StatusLine.propertyNames); | ||
const ownPropertyNames = ['statusLine'].concat(StatusLine.propertyNames); | ||
@@ -20,90 +20,101 @@ HttpResponse.propertyNames = Message.propertyNames.concat(ownPropertyNames); | ||
HttpResponse.prototype.populate = function (obj) { | ||
if (typeof obj === 'number') { | ||
this.populateFromObject({ statusCode: obj }); | ||
} else if (obj && typeof obj === 'object' && (typeof Buffer === 'undefined' || !Buffer.isBuffer(obj))) { | ||
this.populateFromObject(obj); | ||
} else { | ||
Message.prototype.populate.call(this, obj); | ||
} | ||
return this; | ||
if (typeof obj === 'number') { | ||
this.populateFromObject({ statusCode: obj }); | ||
} else if ( | ||
obj && | ||
typeof obj === 'object' && | ||
(typeof Buffer === 'undefined' || !Buffer.isBuffer(obj)) | ||
) { | ||
this.populateFromObject(obj); | ||
} else { | ||
Message.prototype.populate.call(this, obj); | ||
} | ||
return this; | ||
}; | ||
HttpResponse.prototype.populateFromObject = function (obj) { | ||
Message.prototype.populateFromObject.call(this, _.omit(obj, ownPropertyNames)); | ||
if (typeof obj.statusLine !== 'undefined') { | ||
this.statusLine.populate(obj.statusLine); | ||
} | ||
this.statusLine.populateFromObject(obj); | ||
return this; | ||
Message.prototype.populateFromObject.call(this, omit(obj, ownPropertyNames)); | ||
if (typeof obj.statusLine !== 'undefined') { | ||
this.statusLine.populate(obj.statusLine); | ||
} | ||
this.statusLine.populateFromObject(obj); | ||
return this; | ||
}; | ||
HttpResponse.prototype.populateFromString = function (str) { | ||
var matchStatusLine = str.match(/^([^\r\n]*)(\r\n?|\n\r?|$)/); | ||
const matchStatusLine = str.match(/^([^\r\n]*)(\r\n?|\n\r?|$)/); | ||
if (matchStatusLine) { | ||
this.statusLine.populateFromString(matchStatusLine[1]); | ||
Message.prototype.populateFromString.call(this, str.substr(matchStatusLine[0].length)); | ||
} | ||
return this; | ||
if (matchStatusLine) { | ||
this.statusLine.populateFromString(matchStatusLine[1]); | ||
Message.prototype.populateFromString.call( | ||
this, | ||
str.substr(matchStatusLine[0].length) | ||
); | ||
} | ||
return this; | ||
}; | ||
HttpResponse.prototype.populateFromBuffer = function (buffer) { | ||
var i = 0; | ||
while (i < buffer.length && buffer[i] !== 0x0d && buffer[i] !== 0x0a) { | ||
i += 1; | ||
} | ||
if (i > 0) { | ||
this.statusLine.populateFromString(buffer.slice(0, i).toString('ascii')); | ||
} else { | ||
return; | ||
} | ||
if (buffer[i] === 0x0d) { | ||
i += 1; | ||
} | ||
if (buffer[i] === 0x0a) { | ||
i += 1; | ||
} | ||
Message.prototype.populateFromBuffer.call(this, buffer.slice(i)); | ||
return this; | ||
let i = 0; | ||
while (i < buffer.length && buffer[i] !== 0x0d && buffer[i] !== 0x0a) { | ||
i += 1; | ||
} | ||
if (i > 0) { | ||
this.statusLine.populateFromString(buffer.slice(0, i).toString('ascii')); | ||
} else { | ||
return; | ||
} | ||
if (buffer[i] === 0x0d) { | ||
i += 1; | ||
} | ||
if (buffer[i] === 0x0a) { | ||
i += 1; | ||
} | ||
Message.prototype.populateFromBuffer.call(this, buffer.slice(i)); | ||
return this; | ||
}; | ||
HttpResponse.prototype.clone = function () { | ||
return new HttpResponse({ | ||
statusLine: this.statusLine.clone(), | ||
headers: this.headers.clone(), | ||
body: this.body // Not sure | ||
}); | ||
return new HttpResponse({ | ||
statusLine: this.statusLine.clone(), | ||
headers: this.headers.clone(), | ||
body: this.body, // Not sure | ||
}); | ||
}; | ||
HttpResponse.prototype.toString = function (maxLineLength) { | ||
return ( | ||
this.statusLine.toString() + '\r\n' + | ||
Message.prototype.toString.call(this, maxLineLength) | ||
); | ||
return `${this.statusLine.toString()}\r\n${Message.prototype.toString.call( | ||
this, | ||
maxLineLength | ||
)}`; | ||
}; | ||
HttpResponse.prototype.equals = function (other) { | ||
return this === other || ( | ||
other instanceof HttpResponse && | ||
this.statusLine.equals(other.statusLine) && | ||
Message.prototype.equals.call(this, other) | ||
); | ||
return ( | ||
this === other || | ||
(other instanceof HttpResponse && | ||
this.statusLine.equals(other.statusLine) && | ||
Message.prototype.equals.call(this, other)) | ||
); | ||
}; | ||
StatusLine.propertyNames.forEach(function (statusLinePropertyName) { | ||
Object.defineProperty(HttpResponse.prototype, statusLinePropertyName, { | ||
enumerable: true, | ||
get: function () { | ||
return this.statusLine[statusLinePropertyName]; | ||
}, | ||
set: function (value) { | ||
this.statusLine[statusLinePropertyName] = value; | ||
} | ||
}); | ||
Object.defineProperty(HttpResponse.prototype, statusLinePropertyName, { | ||
enumerable: true, | ||
get() { | ||
return this.statusLine[statusLinePropertyName]; | ||
}, | ||
set(value) { | ||
this.statusLine[statusLinePropertyName] = value; | ||
}, | ||
}); | ||
}); | ||
HttpResponse.prototype.toJSON = function () { | ||
return _.extend(Message.prototype.toJSON.call(this), this.statusLine.toJSON()); | ||
return { | ||
...Message.prototype.toJSON.call(this), | ||
...this.statusLine.toJSON(), | ||
}; | ||
}; | ||
module.exports = HttpResponse; |
module.exports = { | ||
Headers: require('./Headers'), | ||
Message: require('./Message'), | ||
Mail: require('./Mail'), | ||
RequestLine: require('./RequestLine'), | ||
HttpRequest: require('./HttpRequest'), | ||
StatusLine: require('./StatusLine'), | ||
HttpResponse: require('./HttpResponse'), | ||
HttpExchange: require('./HttpExchange'), | ||
HttpConversation: require('./HttpConversation'), | ||
formatHeaderName: require('./formatHeaderName'), | ||
foldHeaderLine: require('./foldHeaderLine'), | ||
headerNameSpecialCases: require('./headerNameSpecialCases') | ||
Headers: require('./Headers'), | ||
Message: require('./Message'), | ||
Mail: require('./Mail'), | ||
RequestLine: require('./RequestLine'), | ||
HttpRequest: require('./HttpRequest'), | ||
StatusLine: require('./StatusLine'), | ||
HttpResponse: require('./HttpResponse'), | ||
HttpExchange: require('./HttpExchange'), | ||
HttpConversation: require('./HttpConversation'), | ||
formatHeaderName: require('./formatHeaderName'), | ||
foldHeaderLine: require('./foldHeaderLine'), | ||
headerNameSpecialCases: require('./headerNameSpecialCases'), | ||
}; |
module.exports = function isRegExp(re) { | ||
var s; | ||
try { | ||
s = '' + re; | ||
} catch (e) { | ||
return false; | ||
} | ||
let s; | ||
try { | ||
s = `${re}`; | ||
} catch (e) { | ||
return false; | ||
} | ||
return re instanceof RegExp || // easy case | ||
// duck-type for context-switching evalcx case | ||
typeof(re) === 'function' && | ||
re.constructor.name === 'RegExp' && | ||
re.compile && | ||
re.test && | ||
re.exec && | ||
s.match(/^\/.*\/[gim]{0,3}$/); | ||
return ( | ||
re instanceof RegExp || // easy case | ||
// duck-type for context-switching evalcx case | ||
(typeof re === 'function' && | ||
re.constructor.name === 'RegExp' && | ||
re.compile && | ||
re.test && | ||
re.exec && | ||
s.match(/^\/.*\/[gim]{0,3}$/)) | ||
); | ||
}; |
@@ -1,7 +0,7 @@ | ||
var Message = require('./Message'), | ||
HeadersWithRfc2047 = require('./HeadersWithRfc2047'), | ||
util = require('util'); | ||
const Message = require('./Message'); | ||
const HeadersWithRfc2047 = require('./HeadersWithRfc2047'); | ||
const util = require('util'); | ||
function Mail(obj) { | ||
Message.call(this, obj); | ||
Message.call(this, obj); | ||
} | ||
@@ -8,0 +8,0 @@ |
@@ -1,31 +0,43 @@ | ||
/*global unescape, btoa, atob, JSON*/ | ||
var Headers = require('./Headers'), | ||
isRegExp = require('./isRegExp'), | ||
iconvLite = require('iconv-lite'), | ||
quotedPrintable = require('quoted-printable'), | ||
decodeChunkedTransferEncoding = require('./decodeChunkedTransferEncoding'), | ||
zlib; | ||
/* global unescape, btoa, atob, JSON */ | ||
const Headers = require('./Headers'); | ||
const isRegExp = require('./isRegExp'); | ||
const iconvLite = require('iconv-lite'); | ||
const quotedPrintable = require('quoted-printable'); | ||
const decodeChunkedTransferEncoding = require('./decodeChunkedTransferEncoding'); | ||
let zlib; | ||
try { | ||
zlib = require('' + 'zlib'); | ||
zlib = require('' + 'zlib'); | ||
} catch (e) {} | ||
function isDefined(obj) { | ||
return obj !== null && typeof obj !== 'undefined'; | ||
return obj !== null && typeof obj !== 'undefined'; | ||
} | ||
function quoteRegExp(str) { | ||
return str.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
return str.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'); | ||
} | ||
function Message(obj, doNotStringify) { | ||
this.headers = new this.HeadersConstructor(); | ||
this.populate(obj, doNotStringify); | ||
this.headers = new this.HeadersConstructor(); | ||
this.populate(obj, doNotStringify); | ||
} | ||
// Descending priority: | ||
var bodyPropertyNames = ['parts', 'body', 'unchunkedBody', 'decodedBody', 'rawBody']; | ||
const bodyPropertyNames = [ | ||
'parts', | ||
'body', | ||
'unchunkedBody', | ||
'decodedBody', | ||
'rawBody', | ||
]; | ||
Message.propertyNames = ['headers', 'fileName', 'isJson', 'isMultipart', 'boundary', 'charset'].concat(bodyPropertyNames); | ||
Message.propertyNames = [ | ||
'headers', | ||
'fileName', | ||
'isJson', | ||
'isMultipart', | ||
'boundary', | ||
'charset', | ||
].concat(bodyPropertyNames); | ||
@@ -37,512 +49,608 @@ Message.prototype.isMessyMessage = true; | ||
Message.prototype.populate = function (obj) { | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(obj)) { | ||
this.populateFromBuffer(obj); | ||
} else if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object') { | ||
this.populateFromObject(obj); | ||
} | ||
return this; | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(obj)) { | ||
this.populateFromBuffer(obj); | ||
} else if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object') { | ||
this.populateFromObject(obj); | ||
} | ||
return this; | ||
}; | ||
var isSupportedByPropertyName = {}; | ||
const isSupportedByPropertyName = {}; | ||
Message.propertyNames.forEach(function (propertyName) { | ||
isSupportedByPropertyName[propertyName] = true; | ||
isSupportedByPropertyName[propertyName] = true; | ||
}); | ||
Message.prototype.populateFromObject = function (obj) { | ||
var unsupportedPropertyNames = Object.keys(obj).filter(function (propertyName) { | ||
return !isSupportedByPropertyName[propertyName]; | ||
}); | ||
if (unsupportedPropertyNames.length > 0) { | ||
throw new Error('messy.Message: Unsupported property name' + (unsupportedPropertyNames.length === 1 ? '' : 's') + ': ' + unsupportedPropertyNames.join(', ')); | ||
const unsupportedPropertyNames = Object.keys(obj).filter(function ( | ||
propertyName | ||
) { | ||
return !isSupportedByPropertyName[propertyName]; | ||
}); | ||
if (unsupportedPropertyNames.length > 0) { | ||
throw new Error( | ||
`messy.Message: Unsupported property name${ | ||
unsupportedPropertyNames.length === 1 ? '' : 's' | ||
}: ${unsupportedPropertyNames.join(', ')}` | ||
); | ||
} | ||
if (typeof obj.headers !== 'undefined') { | ||
this.headers.populate(obj.headers); | ||
} | ||
if (typeof obj.parts !== 'undefined') { | ||
this.parts = (Array.isArray(obj.parts) ? obj.parts : [obj.parts]).map( | ||
function (part) { | ||
return part && part.isMessyMessage ? part : new Message(part); | ||
} | ||
); | ||
} else if (typeof obj.rawBody !== 'undefined') { | ||
this.rawBody = obj.rawBody; | ||
} else if (typeof obj.body !== 'undefined') { | ||
if ( | ||
(typeof Buffer !== 'undefined' && Buffer.isBuffer(obj.body)) || | ||
typeof obj.body === 'string' | ||
) { | ||
this.unchunkedBody = obj.body; | ||
} else { | ||
this.body = obj.body; | ||
} | ||
if (typeof obj.headers !== 'undefined') { | ||
this.headers.populate(obj.headers); | ||
} | ||
if (typeof obj.parts !== 'undefined') { | ||
this.parts = (Array.isArray(obj.parts) ? obj.parts : [ obj.parts ]).map(function (part) { | ||
return part && part.isMessyMessage ? part : new Message(part); | ||
}); | ||
} else if (typeof obj.rawBody !== 'undefined') { | ||
this.rawBody = obj.rawBody; | ||
} else if (typeof obj.body !== 'undefined') { | ||
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(obj.body) || typeof obj.body === 'string') { | ||
this.unchunkedBody = obj.body; | ||
} else { | ||
this.body = obj.body; | ||
} | ||
} else if (typeof obj.decodedBody !== 'undefined') { | ||
this.decodedBody = obj.decodedBody; | ||
} else if (typeof obj.unchunkedBody !== 'undefined') { | ||
this.unchunkedBody = obj.unchunkedBody; | ||
} | ||
return this; | ||
} else if (typeof obj.decodedBody !== 'undefined') { | ||
this.decodedBody = obj.decodedBody; | ||
} else if (typeof obj.unchunkedBody !== 'undefined') { | ||
this.unchunkedBody = obj.unchunkedBody; | ||
} | ||
return this; | ||
}; | ||
Message.prototype.populateFromBuffer = function (buffer) { | ||
// Hack: Interpret non-ASCII in headers as iso-8859-1: | ||
var str = ''; | ||
for (var i = 0 ; i < buffer.length ; i += 1) { | ||
var octet = buffer[i]; | ||
if (octet > 127) { | ||
str += unescape('%' + octet.toString(16)); | ||
} else { | ||
str += String.fromCharCode(octet); | ||
} | ||
if (/\r\r$|\n\n$|\r\n\r\n$|\n\r\n\r$/.test(str)) { | ||
i += 1; | ||
if (i < buffer.length) { | ||
this.rawBody = buffer.slice(i); | ||
} | ||
break; | ||
} | ||
// Hack: Interpret non-ASCII in headers as iso-8859-1: | ||
let str = ''; | ||
for (let i = 0; i < buffer.length; i += 1) { | ||
const octet = buffer[i]; | ||
if (octet > 127) { | ||
str += unescape(`%${octet.toString(16)}`); | ||
} else { | ||
str += String.fromCharCode(octet); | ||
} | ||
this.headers.populateFromString(str, true); | ||
return this; | ||
if (/\r\r$|\n\n$|\r\n\r\n$|\n\r\n\r$/.test(str)) { | ||
i += 1; | ||
if (i < buffer.length) { | ||
this.rawBody = buffer.slice(i); | ||
} | ||
break; | ||
} | ||
} | ||
this.headers.populateFromString(str, true); | ||
return this; | ||
}; | ||
Message.prototype.populateFromString = function (str) { | ||
var bodyStartIndex = this.headers.populateFromStringAndReturnBodyStartIndex(str); | ||
if (bodyStartIndex < str.length) { | ||
this.rawBody = str.substr(bodyStartIndex); | ||
} | ||
return this; | ||
const bodyStartIndex = this.headers.populateFromStringAndReturnBodyStartIndex( | ||
str | ||
); | ||
if (bodyStartIndex < str.length) { | ||
this.rawBody = str.substr(bodyStartIndex); | ||
} | ||
return this; | ||
}; | ||
Object.defineProperty(Message.prototype, 'hasTextualContentType', { | ||
get: function () { | ||
var contentType = this.headers.get('Content-Type'); | ||
if (typeof contentType === 'string') { | ||
contentType = contentType.toLowerCase().trim().replace(/\s*;.*$/, ''); | ||
return ( | ||
/^text\//.test(contentType) || | ||
/^application\/(?:json|javascript)$/.test(contentType) || | ||
/^application\/xml/.test(contentType) || | ||
/^application\/x-www-form-urlencoded\b/.test(contentType) || | ||
/^application\/graphql\b/.test(contentType) || | ||
/\+xml$/.test(contentType) || | ||
/\+json$/.test(contentType) | ||
); | ||
} | ||
return false; | ||
get() { | ||
let contentType = this.headers.get('Content-Type'); | ||
if (typeof contentType === 'string') { | ||
contentType = contentType | ||
.toLowerCase() | ||
.trim() | ||
.replace(/\s*;.*$/, ''); | ||
return ( | ||
/^text\//.test(contentType) || | ||
/^application\/(?:json|javascript)$/.test(contentType) || | ||
/^application\/xml/.test(contentType) || | ||
/^application\/x-www-form-urlencoded\b/.test(contentType) || | ||
/^application\/graphql\b/.test(contentType) || | ||
/\+xml$/.test(contentType) || | ||
/\+json$/.test(contentType) | ||
); | ||
} | ||
return false; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'isJson', { | ||
get: function () { | ||
return /^application\/json\b|\+json\b/i.test(this.headers.get('Content-Type')); | ||
} | ||
get() { | ||
return /^application\/json\b|\+json\b/i.test( | ||
this.headers.get('Content-Type') | ||
); | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'charset', { | ||
get: function () { | ||
var charset = this.headers.parameter('Content-Type', 'charset'); | ||
if (charset) { | ||
return charset; | ||
} | ||
var contentType = this.headers.get('Content-Type'); | ||
if (contentType && /^application\/json\b|\+json\b/i.test(contentType)) { | ||
return 'utf-8'; | ||
} | ||
return 'iso-8859-1'; | ||
get() { | ||
const charset = this.headers.parameter('Content-Type', 'charset'); | ||
if (charset) { | ||
return charset; | ||
} | ||
const contentType = this.headers.get('Content-Type'); | ||
if (contentType && /^application\/json\b|\+json\b/i.test(contentType)) { | ||
return 'utf-8'; | ||
} | ||
return 'iso-8859-1'; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'isMultipart', { | ||
enumerable: true, | ||
get: function () { | ||
return /^multipart\//.test(this.headers.get('Content-Type')); | ||
} | ||
enumerable: true, | ||
get() { | ||
return /^multipart\//.test(this.headers.get('Content-Type')); | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'boundary', { | ||
enumerable: true, | ||
get: function () { | ||
return this.isMultipart && this.headers.parameter('Content-Type', 'boundary'); | ||
} | ||
enumerable: true, | ||
get() { | ||
return ( | ||
this.isMultipart && this.headers.parameter('Content-Type', 'boundary') | ||
); | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, '_bodyMustBeBuffer', { | ||
get: function () { | ||
if (this._parts) { | ||
return this._parts.some(function (part) { | ||
return part._bodyMustBeBuffer; | ||
}); | ||
} else { | ||
return typeof Buffer === 'function' && Buffer.isBuffer(this.body); | ||
} | ||
get() { | ||
if (this._parts) { | ||
return this._parts.some(function (part) { | ||
return part._bodyMustBeBuffer; | ||
}); | ||
} else { | ||
return typeof Buffer === 'function' && Buffer.isBuffer(this.body); | ||
} | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'decodedBody', { | ||
enumerable: true, | ||
get: function () { | ||
if (!isDefined(this._decodedBody)) { | ||
if (isDefined(this._rawBody) || isDefined(this._unchunkedBody)) { | ||
this._decodedBody = this.unchunkedBody; | ||
if (zlib && zlib.gunzipSync) { | ||
var contentEncoding = this.headers.get('Content-Encoding'); | ||
if (contentEncoding) { | ||
contentEncoding = contentEncoding.trim().toLowerCase(); | ||
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') { | ||
if (typeof Buffer !== 'undefined' && !Buffer.isBuffer(this._body)) { | ||
this._decodedBody = new Buffer(this._decodedBody, 'utf-8'); | ||
} | ||
enumerable: true, | ||
get() { | ||
if (!isDefined(this._decodedBody)) { | ||
if (isDefined(this._rawBody) || isDefined(this._unchunkedBody)) { | ||
this._decodedBody = this.unchunkedBody; | ||
if (zlib && zlib.gunzipSync) { | ||
let contentEncoding = this.headers.get('Content-Encoding'); | ||
if (contentEncoding) { | ||
contentEncoding = contentEncoding.trim().toLowerCase(); | ||
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') { | ||
if ( | ||
typeof Buffer !== 'undefined' && | ||
!Buffer.isBuffer(this._body) | ||
) { | ||
this._decodedBody = Buffer.from(this._decodedBody, 'utf-8'); | ||
} | ||
try { | ||
this._decodedBody = zlib[contentEncoding === 'gzip' ? 'gunzipSync' : 'inflateSync'](this._decodedBody); | ||
} catch (e) {} | ||
} | ||
} | ||
} | ||
var contentTransferEncoding = this.headers.get('Content-Transfer-Encoding'), | ||
contentTransferEncodingIsHonored = !contentTransferEncoding; | ||
if (contentTransferEncoding) { | ||
contentTransferEncoding = contentTransferEncoding.trim().toLowerCase(); | ||
if (contentTransferEncoding === 'quoted-printable') { | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(this._decodedBody)) { | ||
this._decodedBody = this._decodedBody.toString('ascii'); | ||
} | ||
var qpDecodedBodyAsByteString = quotedPrintable.decode(this._decodedBody); | ||
this._decodedBody = new Buffer(qpDecodedBodyAsByteString.length); | ||
for (var i = 0 ; i < qpDecodedBodyAsByteString.length ; i += 1) { | ||
this._decodedBody[i] = qpDecodedBodyAsByteString.charCodeAt(i); | ||
} | ||
contentTransferEncodingIsHonored = true; | ||
} else if (contentTransferEncoding === 'base64') { | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(this._decodedBody)) { | ||
this._decodedBody = this._decodedBody.toString('ascii'); | ||
} | ||
if (typeof Buffer !== 'undefined') { | ||
this._decodedBody = new Buffer(this._decodedBody, 'base64'); | ||
} else { | ||
this._decodedBody = atob(this._decodedBody); | ||
} | ||
contentTransferEncodingIsHonored = true; | ||
} else if (contentTransferEncoding === '8bit' || contentTransferEncoding === '7bit') { | ||
contentTransferEncodingIsHonored = true; | ||
} | ||
} | ||
if (this.hasTextualContentType && contentTransferEncodingIsHonored && this._decodedBody && typeof this._decodedBody !== 'string') { | ||
var charset = this.charset; | ||
if (iconvLite.encodingExists(charset)) { | ||
this._decodedBody = iconvLite.decode(this._decodedBody, charset); | ||
} | ||
} | ||
} else if (isDefined(this._body) || isDefined(this._parts)) { | ||
this._decodedBody = this.body; | ||
if ( | ||
((this.isJson && typeof this._decodedBody !== 'undefined') || this._decodedBody && typeof this._decodedBody === 'object') && | ||
(typeof Buffer === 'undefined' || !Buffer.isBuffer(this._decodedBody)) | ||
) { | ||
try { | ||
this._decodedBody = JSON.stringify(this._decodedBody); | ||
} catch (e) {} | ||
} | ||
try { | ||
this._decodedBody = zlib[ | ||
contentEncoding === 'gzip' ? 'gunzipSync' : 'inflateSync' | ||
](this._decodedBody); | ||
} catch (e) {} | ||
} | ||
} | ||
} | ||
return this._decodedBody; | ||
}, | ||
set: function (decodedBody) { | ||
this._unchunkedBody = null; | ||
this._decodedBody = decodedBody; | ||
this._body = null; | ||
this._rawBody = null; | ||
this._parts = null; | ||
let contentTransferEncoding = this.headers.get( | ||
'Content-Transfer-Encoding' | ||
); | ||
let contentTransferEncodingIsHonored = !contentTransferEncoding; | ||
if (contentTransferEncoding) { | ||
contentTransferEncoding = contentTransferEncoding | ||
.trim() | ||
.toLowerCase(); | ||
if (contentTransferEncoding === 'quoted-printable') { | ||
if ( | ||
typeof Buffer === 'function' && | ||
Buffer.isBuffer(this._decodedBody) | ||
) { | ||
this._decodedBody = this._decodedBody.toString('ascii'); | ||
} | ||
const qpDecodedBodyAsByteString = quotedPrintable.decode( | ||
this._decodedBody | ||
); | ||
this._decodedBody = Buffer.alloc(qpDecodedBodyAsByteString.length); | ||
for (let i = 0; i < qpDecodedBodyAsByteString.length; i += 1) { | ||
this._decodedBody[i] = qpDecodedBodyAsByteString.charCodeAt(i); | ||
} | ||
contentTransferEncodingIsHonored = true; | ||
} else if (contentTransferEncoding === 'base64') { | ||
if ( | ||
typeof Buffer === 'function' && | ||
Buffer.isBuffer(this._decodedBody) | ||
) { | ||
this._decodedBody = this._decodedBody.toString('ascii'); | ||
} | ||
if (typeof Buffer !== 'undefined') { | ||
this._decodedBody = Buffer.from(this._decodedBody, 'base64'); | ||
} else { | ||
this._decodedBody = atob(this._decodedBody); | ||
} | ||
contentTransferEncodingIsHonored = true; | ||
} else if ( | ||
contentTransferEncoding === '8bit' || | ||
contentTransferEncoding === '7bit' | ||
) { | ||
contentTransferEncodingIsHonored = true; | ||
} | ||
} | ||
if ( | ||
this.hasTextualContentType && | ||
contentTransferEncodingIsHonored && | ||
this._decodedBody && | ||
typeof this._decodedBody !== 'string' | ||
) { | ||
const charset = this.charset; | ||
if (iconvLite.encodingExists(charset)) { | ||
this._decodedBody = iconvLite.decode(this._decodedBody, charset); | ||
} | ||
} | ||
} else if (isDefined(this._body) || isDefined(this._parts)) { | ||
this._decodedBody = this.body; | ||
if ( | ||
((this.isJson && typeof this._decodedBody !== 'undefined') || | ||
(this._decodedBody && typeof this._decodedBody === 'object')) && | ||
(typeof Buffer === 'undefined' || !Buffer.isBuffer(this._decodedBody)) | ||
) { | ||
try { | ||
this._decodedBody = JSON.stringify(this._decodedBody); | ||
} catch (e) {} | ||
} | ||
} | ||
} | ||
return this._decodedBody; | ||
}, | ||
set(decodedBody) { | ||
this._unchunkedBody = null; | ||
this._decodedBody = decodedBody; | ||
this._body = null; | ||
this._rawBody = null; | ||
this._parts = null; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'unchunkedBody', { | ||
enumerable: true, | ||
get: function () { | ||
if (!isDefined(this._unchunkedBody)) { | ||
if (isDefined(this._rawBody)) { | ||
this._unchunkedBody = this._rawBody; | ||
var transferEncoding = this.headers.get('Transfer-Encoding'); | ||
if (transferEncoding && transferEncoding === 'chunked') { | ||
try { | ||
this._unchunkedBody = decodeChunkedTransferEncoding(this._unchunkedBody); | ||
} catch (e) {} | ||
} | ||
} else if (isDefined(this._body) || isDefined(this._parts) || isDefined(this._decodedBody)) { | ||
this._unchunkedBody = this.decodedBody; | ||
var charset = this.charset; | ||
if (/^utf-?8$/i.test(charset) && typeof Buffer !== 'undefined') { | ||
this._unchunkedBody = new Buffer(this._unchunkedBody, 'utf-8'); | ||
} else if (iconvLite.encodingExists(charset) && !/^utf-?8$/i.test(charset)) { | ||
this._unchunkedBody = iconvLite.encode(this._unchunkedBody, charset); | ||
} | ||
var contentTransferEncoding = this.headers.get('Content-Transfer-Encoding'); | ||
if (contentTransferEncoding) { | ||
contentTransferEncoding = contentTransferEncoding.trim().toLowerCase(); | ||
if (contentTransferEncoding === 'base64') { | ||
if (typeof Buffer !== 'undefined') { | ||
if (!Buffer.isBuffer(this._unchunkedBody)) { | ||
this._unchunkedBody = new Buffer(this._unchunkedBody, 'utf-8'); | ||
} | ||
this._unchunkedBody = this.rawBody.toString('base64'); | ||
} else { | ||
this._unchunkedBody = btoa(this._unchunkedBody); | ||
} | ||
} else if (contentTransferEncoding === 'quoted-printable') { | ||
if (typeof Buffer !== 'undefined' && Buffer.isBuffer(this._unchunkedBody)) { | ||
this._unchunkedBody = this._unchunkedBody.toString('binary'); | ||
} | ||
this._unchunkedBody = quotedPrintable.encode(this._unchunkedBody); | ||
} | ||
} | ||
if (zlib && zlib.gzipSync) { | ||
var contentEncoding = this.headers.get('Content-Encoding'); | ||
if (contentEncoding) { | ||
contentEncoding = contentEncoding.trim().toLowerCase(); | ||
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') { | ||
try { | ||
this._unchunkedBody = zlib[contentEncoding === 'gzip' ? 'gzipSync' : 'deflateSync'](this._unchunkedBody || ''); | ||
} catch (e) {} | ||
} | ||
} | ||
} | ||
enumerable: true, | ||
get() { | ||
if (!isDefined(this._unchunkedBody)) { | ||
if (isDefined(this._rawBody)) { | ||
this._unchunkedBody = this._rawBody; | ||
const transferEncoding = this.headers.get('Transfer-Encoding'); | ||
if (transferEncoding && transferEncoding === 'chunked') { | ||
try { | ||
this._unchunkedBody = decodeChunkedTransferEncoding( | ||
this._unchunkedBody | ||
); | ||
} catch (e) {} | ||
} | ||
} else if ( | ||
isDefined(this._body) || | ||
isDefined(this._parts) || | ||
isDefined(this._decodedBody) | ||
) { | ||
this._unchunkedBody = this.decodedBody; | ||
const charset = this.charset; | ||
if (/^utf-?8$/i.test(charset) && typeof Buffer !== 'undefined') { | ||
this._unchunkedBody = Buffer.from(this._unchunkedBody, 'utf-8'); | ||
} else if ( | ||
iconvLite.encodingExists(charset) && | ||
!/^utf-?8$/i.test(charset) | ||
) { | ||
this._unchunkedBody = iconvLite.encode(this._unchunkedBody, charset); | ||
} | ||
let contentTransferEncoding = this.headers.get( | ||
'Content-Transfer-Encoding' | ||
); | ||
if (contentTransferEncoding) { | ||
contentTransferEncoding = contentTransferEncoding | ||
.trim() | ||
.toLowerCase(); | ||
if (contentTransferEncoding === 'base64') { | ||
if (typeof Buffer !== 'undefined') { | ||
if (!Buffer.isBuffer(this._unchunkedBody)) { | ||
this._unchunkedBody = Buffer.from(this._unchunkedBody, 'utf-8'); | ||
} | ||
this._unchunkedBody = this.rawBody.toString('base64'); | ||
} else { | ||
this._unchunkedBody = btoa(this._unchunkedBody); | ||
} | ||
} else if (contentTransferEncoding === 'quoted-printable') { | ||
if ( | ||
typeof Buffer !== 'undefined' && | ||
Buffer.isBuffer(this._unchunkedBody) | ||
) { | ||
this._unchunkedBody = this._unchunkedBody.toString('binary'); | ||
} | ||
this._unchunkedBody = quotedPrintable.encode(this._unchunkedBody); | ||
} | ||
} | ||
return this._unchunkedBody; | ||
}, | ||
set: function (unchunkedBody) { | ||
this._unchunkedBody = unchunkedBody; | ||
this._decodedBody = null; | ||
this._body = null; | ||
this._rawBody = null; | ||
this._parts = null; | ||
if (zlib && zlib.gzipSync) { | ||
let contentEncoding = this.headers.get('Content-Encoding'); | ||
if (contentEncoding) { | ||
contentEncoding = contentEncoding.trim().toLowerCase(); | ||
if (contentEncoding === 'gzip' || contentEncoding === 'deflate') { | ||
try { | ||
this._unchunkedBody = zlib[ | ||
contentEncoding === 'gzip' ? 'gzipSync' : 'deflateSync' | ||
](this._unchunkedBody || ''); | ||
} catch (e) {} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return this._unchunkedBody; | ||
}, | ||
set(unchunkedBody) { | ||
this._unchunkedBody = unchunkedBody; | ||
this._decodedBody = null; | ||
this._body = null; | ||
this._rawBody = null; | ||
this._parts = null; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'rawBody', { | ||
enumerable: true, | ||
get: function () { | ||
if (!isDefined(this._rawBody) && (isDefined(this._body) || isDefined(this._parts) || isDefined(this._unchunkedBody) || isDefined(this._decodedBody))) { | ||
this._rawBody = this.unchunkedBody; | ||
var transferEncoding = this.headers.get('Transfer-Encoding'); | ||
if (transferEncoding && transferEncoding === 'chunked') { | ||
if (typeof Buffer !== 'undefined' && !Buffer.isBuffer(this._rawBody)) { | ||
this._rawBody = new Buffer(this._rawBody, 'utf-8'); | ||
} | ||
var chunks = []; | ||
if (this._rawBody.length > 0) { | ||
chunks.push( | ||
new Buffer(this._rawBody.length.toString(16) + '\r\n', 'ascii'), | ||
this._rawBody, | ||
new Buffer('\r\n', 'ascii') | ||
); | ||
} | ||
chunks.push(new Buffer('0\r\n\r\n', 'ascii')); | ||
this._rawBody = Buffer.concat(chunks); | ||
} | ||
enumerable: true, | ||
get() { | ||
if ( | ||
!isDefined(this._rawBody) && | ||
(isDefined(this._body) || | ||
isDefined(this._parts) || | ||
isDefined(this._unchunkedBody) || | ||
isDefined(this._decodedBody)) | ||
) { | ||
this._rawBody = this.unchunkedBody; | ||
const transferEncoding = this.headers.get('Transfer-Encoding'); | ||
if (transferEncoding && transferEncoding === 'chunked') { | ||
if (typeof Buffer !== 'undefined' && !Buffer.isBuffer(this._rawBody)) { | ||
this._rawBody = Buffer.from(this._rawBody, 'utf-8'); | ||
} | ||
return this._rawBody; | ||
}, | ||
set: function (rawBody) { | ||
this._rawBody = rawBody; | ||
this._unchunkedBody = null; | ||
this._decodedBody = null; | ||
this._body = null; | ||
this._parts = null; | ||
const chunks = []; | ||
if (this._rawBody.length > 0) { | ||
chunks.push( | ||
Buffer.from(`${this._rawBody.length.toString(16)}\r\n`, 'ascii'), | ||
this._rawBody, | ||
Buffer.from('\r\n', 'ascii') | ||
); | ||
} | ||
chunks.push(Buffer.from('0\r\n\r\n', 'ascii')); | ||
this._rawBody = Buffer.concat(chunks); | ||
} | ||
} | ||
return this._rawBody; | ||
}, | ||
set(rawBody) { | ||
this._rawBody = rawBody; | ||
this._unchunkedBody = null; | ||
this._decodedBody = null; | ||
this._body = null; | ||
this._parts = null; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'body', { | ||
enumerable: true, | ||
get: function () { | ||
if (this._parts) { | ||
if (this._parts.length === 0) { | ||
return; | ||
} else { | ||
var boundary = this.boundary || ''; | ||
if (this._bodyMustBeBuffer) { | ||
var chunks = []; | ||
enumerable: true, | ||
get() { | ||
if (this._parts) { | ||
if (this._parts.length === 0) { | ||
return; | ||
} else { | ||
const boundary = this.boundary || ''; | ||
if (this._bodyMustBeBuffer) { | ||
const chunks = []; | ||
this._parts.forEach(function (part, i) { | ||
if (i > 0) { | ||
chunks.push(new Buffer('\r\n')); | ||
} | ||
chunks.push(new Buffer('--' + boundary + '\r\n')); | ||
var serializedPart = part.serialize(); | ||
if (!Buffer.isBuffer(serializedPart)) { | ||
serializedPart = new Buffer(serializedPart); | ||
} | ||
chunks.push(serializedPart); | ||
}, this); | ||
chunks.push(new Buffer('\r\n--' + boundary + '--\r\n')); | ||
return Buffer.concat(chunks); | ||
} else { | ||
return '--' + boundary + '\r\n' + this._parts.join('\r\n--' + boundary + '\r\n') + '\r\n--' + boundary + '--\r\n'; | ||
} | ||
this._parts.forEach(function (part, i) { | ||
if (i > 0) { | ||
chunks.push(Buffer.from('\r\n')); | ||
} | ||
} else if (!isDefined(this._body) && (isDefined(this._rawBody) || isDefined(this._unchunkedBody) || isDefined(this._decodedBody))) { | ||
this._body = this.decodedBody; | ||
if (this.isJson && typeof this._body === 'string') { | ||
try { | ||
this._body = JSON.parse(this._body); | ||
} catch (e) {} | ||
chunks.push(Buffer.from(`--${boundary}\r\n`)); | ||
let serializedPart = part.serialize(); | ||
if (!Buffer.isBuffer(serializedPart)) { | ||
serializedPart = Buffer.from(serializedPart); | ||
} | ||
chunks.push(serializedPart); | ||
}, this); | ||
chunks.push(Buffer.from(`\r\n--${boundary}--\r\n`)); | ||
return Buffer.concat(chunks); | ||
} else { | ||
return `--${boundary}\r\n${this._parts.join( | ||
`\r\n--${boundary}\r\n` | ||
)}\r\n--${boundary}--\r\n`; | ||
} | ||
return this._body; | ||
}, | ||
set: function (body) { | ||
this._body = body; | ||
if (this.isJson && typeof this._body === 'string') { | ||
try { | ||
this._body = JSON.parse(this._body); | ||
} catch (e) {} | ||
} | ||
this._rawBody = null; | ||
this._unchunkedBody = null; | ||
this._decodedBody = null; | ||
this._parts = null; | ||
} | ||
} else if ( | ||
!isDefined(this._body) && | ||
(isDefined(this._rawBody) || | ||
isDefined(this._unchunkedBody) || | ||
isDefined(this._decodedBody)) | ||
) { | ||
this._body = this.decodedBody; | ||
if (this.isJson && typeof this._body === 'string') { | ||
try { | ||
this._body = JSON.parse(this._body); | ||
} catch (e) {} | ||
} | ||
} | ||
return this._body; | ||
}, | ||
set(body) { | ||
this._body = body; | ||
if (this.isJson && typeof this._body === 'string') { | ||
try { | ||
this._body = JSON.parse(this._body); | ||
} catch (e) {} | ||
} | ||
this._rawBody = null; | ||
this._unchunkedBody = null; | ||
this._decodedBody = null; | ||
this._parts = null; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'parts', { | ||
enumerable: true, | ||
set: function (parts) { | ||
enumerable: true, | ||
set(parts) { | ||
this._parts = parts; | ||
this._body = null; | ||
this._rawBody = null; | ||
this._unchunkedBody = null; | ||
this._decodedBody = null; | ||
}, | ||
get() { | ||
if (!this._parts && this.isMultipart) { | ||
const boundary = this.boundary || ''; | ||
let bodyAsString; | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) { | ||
bodyAsString = this.body.toString('ascii'); | ||
} else { | ||
bodyAsString = this.body; | ||
} | ||
const boundaryRegExp = new RegExp( | ||
`(^|\r\n?|\n\r?)--${quoteRegExp(boundary)}(--)?(?:\r\n?|\n\r?|$)`, | ||
'g' | ||
); | ||
let startIndex = -1; | ||
const parts = []; | ||
let match; | ||
// TODO: Basic validation of end marker etc. | ||
while ((match = boundaryRegExp.exec(bodyAsString))) { | ||
const index = match.index; | ||
if (startIndex !== -1) { | ||
parts.push(new Message(this.body.slice(startIndex, index))); | ||
} | ||
startIndex = index + match[0].length; | ||
} | ||
if (parts.length > 0) { | ||
this._parts = parts; | ||
this._body = null; | ||
this._rawBody = null; | ||
this._unchunkedBody = null; | ||
this._decodedBody = null; | ||
}, | ||
get: function () { | ||
if (!this._parts && this.isMultipart) { | ||
var boundary = this.boundary || '', | ||
bodyAsString; | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) { | ||
bodyAsString = this.body.toString('ascii'); | ||
} else { | ||
bodyAsString = this.body; | ||
} | ||
var boundaryRegExp = new RegExp('(^|\r\n?|\n\r?)--' + quoteRegExp(boundary) + '(--)?(?:\r\n?|\n\r?|$)', 'g'), | ||
startIndex = -1, | ||
parts = [], | ||
match; | ||
// TODO: Basic validation of end marker etc. | ||
while ((match = boundaryRegExp.exec(bodyAsString))) { | ||
var index = match.index; | ||
if (startIndex !== -1) { | ||
parts.push(new Message(this.body.slice(startIndex, index))); | ||
} | ||
startIndex = index + match[0].length; | ||
} | ||
if (parts.length > 0) { | ||
this._parts = parts; | ||
} | ||
} | ||
return this._parts; | ||
} | ||
} | ||
return this._parts; | ||
}, | ||
}); | ||
Object.defineProperty(Message.prototype, 'fileName', { | ||
get: function () { | ||
return this.headers.parameter('Content-Disposition', 'filename') || (this.isMessyMail && this.headers.parameter('Content-Type', 'name')); | ||
}, | ||
set: function (fileName) { | ||
if (!this.headers.has('Content-Disposition')) { | ||
this.headers.set('Content-Disposition', 'attachment'); | ||
} | ||
this.headers.parameter('Content-Disposition', 'filename', fileName); | ||
if (this.isMessyMail && this.headers.has('Content-Type')) { | ||
this.headers.parameter('Content-Type', 'name', fileName); | ||
} | ||
get() { | ||
return ( | ||
this.headers.parameter('Content-Disposition', 'filename') || | ||
(this.isMessyMail && this.headers.parameter('Content-Type', 'name')) | ||
); | ||
}, | ||
set(fileName) { | ||
if (!this.headers.has('Content-Disposition')) { | ||
this.headers.set('Content-Disposition', 'attachment'); | ||
} | ||
this.headers.parameter('Content-Disposition', 'filename', fileName); | ||
if (this.isMessyMail && this.headers.has('Content-Type')) { | ||
this.headers.parameter('Content-Type', 'name', fileName); | ||
} | ||
}, | ||
}); | ||
function buffersEqual(a, b) { | ||
if (a === b) { | ||
return true; | ||
} | ||
if (a === b) { | ||
return true; | ||
} | ||
if (a.length !== b.length) return false; | ||
if (a.length !== b.length) return false; | ||
for (var i = 0; i < a.length; i += 1) { | ||
if (a[i] !== b[i]) return false; | ||
} | ||
for (let i = 0; i < a.length; i += 1) { | ||
if (a[i] !== b[i]) return false; | ||
} | ||
return true; | ||
return true; | ||
} | ||
function isNonBufferNonRegExpObject(obj) { | ||
return obj && typeof obj === 'object' && (typeof Buffer === 'undefined' || !Buffer.isBuffer(obj)) && !isRegExp(obj); | ||
return ( | ||
obj && | ||
typeof obj === 'object' && | ||
(typeof Buffer === 'undefined' || !Buffer.isBuffer(obj)) && | ||
!isRegExp(obj) | ||
); | ||
} | ||
Message.prototype.clone = function () { | ||
return new Message({ | ||
headers: this.headers.clone(), | ||
body: this.body // Not sure | ||
}); | ||
return new Message({ | ||
headers: this.headers.clone(), | ||
body: this.body, // Not sure | ||
}); | ||
}; | ||
Message.prototype.serialize = function (maxLineLength, forceString) { | ||
if (typeof maxLineLength === 'undefined') { | ||
maxLineLength = 72; | ||
if (typeof maxLineLength === 'undefined') { | ||
maxLineLength = 72; | ||
} | ||
let result = this.headers.toString(maxLineLength); | ||
if (typeof this.body !== 'undefined') { | ||
result += '\r\n'; | ||
if ( | ||
this.body && | ||
typeof this.body === 'object' && | ||
isNonBufferNonRegExpObject(this.body) | ||
) { | ||
result += JSON.stringify(this.body); | ||
} else { | ||
if (!forceString && this._bodyMustBeBuffer) { | ||
result = Buffer.concat([Buffer.from(result), this.body]); | ||
} else { | ||
result += this.body; | ||
} | ||
} | ||
var result = this.headers.toString(maxLineLength); | ||
if (typeof this.body !== 'undefined') { | ||
result += '\r\n'; | ||
if (this.body && typeof this.body === 'object' && isNonBufferNonRegExpObject(this.body)) { | ||
result += JSON.stringify(this.body); | ||
} else { | ||
if (!forceString && this._bodyMustBeBuffer) { | ||
result = Buffer.concat([new Buffer(result), this.body]); | ||
} else { | ||
result += this.body; | ||
} | ||
} | ||
} | ||
return result; | ||
} | ||
return result; | ||
}; | ||
Message.prototype.toString = function (maxLineLength) { | ||
return this.serialize(maxLineLength, true); | ||
return this.serialize(maxLineLength, true); | ||
}; | ||
Message.prototype.equals = function (other) { | ||
return this === other || ( | ||
this.headers.equals(other.headers) && | ||
(this.body === other.body || | ||
(typeof Buffer === 'function' && Buffer.isBuffer(this.body) && Buffer.isBuffer(other.body) && buffersEqual(this.body, other.body))) | ||
); | ||
return ( | ||
this === other || | ||
(this.headers.equals(other.headers) && | ||
(this.body === other.body || | ||
(typeof Buffer === 'function' && | ||
Buffer.isBuffer(this.body) && | ||
Buffer.isBuffer(other.body) && | ||
buffersEqual(this.body, other.body)))) | ||
); | ||
}; | ||
Message.prototype.hasEmptyBody = function () { | ||
if (typeof this.body === 'string') { | ||
return this.body.length === 0; | ||
} else if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) { | ||
return this.body.length === 0; | ||
} else if (this.body && typeof this.body === 'object') { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
if (typeof this.body === 'string') { | ||
return this.body.length === 0; | ||
} else if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) { | ||
return this.body.length === 0; | ||
} else if (this.body && typeof this.body === 'object') { | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
}; | ||
Message.prototype.toJSON = function () { | ||
var obj = {}; | ||
if (this.headers.getNames().length > 0) { | ||
obj.headers = this.headers.toJSON(); | ||
const obj = {}; | ||
if (this.headers.getNames().length > 0) { | ||
obj.headers = this.headers.toJSON(); | ||
} | ||
bodyPropertyNames.some(function (bodyPropertyName) { | ||
let propertyValue = this[`_${bodyPropertyName}`]; | ||
if (propertyValue !== null && typeof propertyValue !== 'undefined') { | ||
// An empty string is OK, but we use both null and undefined | ||
if (bodyPropertyName === 'parts') { | ||
propertyValue = propertyValue.map(function (part) { | ||
return part.toJSON(); | ||
}); | ||
} | ||
obj[bodyPropertyName] = propertyValue; | ||
return true; | ||
} | ||
bodyPropertyNames.some(function (bodyPropertyName) { | ||
var propertyValue = this['_' + bodyPropertyName]; | ||
if (propertyValue !== null && typeof propertyValue !== 'undefined') { // An empty string is OK, but we use both null and undefined | ||
if (bodyPropertyName === 'parts') { | ||
propertyValue = propertyValue.map(function (part) { | ||
return part.toJSON(); | ||
}); | ||
} | ||
obj[bodyPropertyName] = propertyValue; | ||
return true; | ||
} | ||
}, this); | ||
return obj; | ||
}, this); | ||
return obj; | ||
}; | ||
module.exports = Message; |
function RequestLine(obj) { | ||
this.populate(obj); | ||
this.populate(obj); | ||
} | ||
RequestLine.nonComputedPropertyNames = ['method', 'url', 'protocolName', 'protocolVersion']; | ||
RequestLine.propertyNames = ['protocol', 'path', 'search', 'query'].concat(RequestLine.nonComputedPropertyNames); | ||
RequestLine.nonComputedPropertyNames = [ | ||
'method', | ||
'url', | ||
'protocolName', | ||
'protocolVersion', | ||
]; | ||
RequestLine.propertyNames = ['protocol', 'path', 'search', 'query'].concat( | ||
RequestLine.nonComputedPropertyNames | ||
); | ||
@@ -11,127 +18,140 @@ RequestLine.prototype.isMessyRequestLine = true; | ||
RequestLine.prototype.populate = function (obj) { | ||
if (obj && typeof obj === 'object' && !Buffer.isBuffer(obj)) { | ||
this.populateFromObject(obj); | ||
} else if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} | ||
return this; | ||
if (obj && typeof obj === 'object' && !Buffer.isBuffer(obj)) { | ||
this.populateFromObject(obj); | ||
} else if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} | ||
return this; | ||
}; | ||
RequestLine.prototype.populateFromObject = function (obj) { | ||
RequestLine.propertyNames.forEach(function (propertyName) { | ||
if (typeof obj[propertyName] !== 'undefined') { | ||
this[propertyName] = obj[propertyName]; | ||
} | ||
}, this); | ||
return this; | ||
RequestLine.propertyNames.forEach(function (propertyName) { | ||
if (typeof obj[propertyName] !== 'undefined') { | ||
this[propertyName] = obj[propertyName]; | ||
} | ||
}, this); | ||
return this; | ||
}; | ||
RequestLine.prototype.populateProtocolFromString = function (str) { | ||
if (str !== '') { | ||
var protocolFragments = str.split('/'); | ||
if (protocolFragments.length === 2) { | ||
this.protocolName = protocolFragments[0]; | ||
this.protocolVersion = protocolFragments[1]; | ||
} else { | ||
throw new Error('Could not parse protocol: ' + str); | ||
} | ||
if (str !== '') { | ||
const protocolFragments = str.split('/'); | ||
if (protocolFragments.length === 2) { | ||
this.protocolName = protocolFragments[0]; | ||
this.protocolVersion = protocolFragments[1]; | ||
} else { | ||
throw new Error(`Could not parse protocol: ${str}`); | ||
} | ||
return this; | ||
} | ||
return this; | ||
}; | ||
RequestLine.prototype.populateFromString = function (str) { | ||
if (str !== '') { | ||
var requestLineFragments = str.split(/\s+/); | ||
if (requestLineFragments.length > 3) { | ||
throw new Error('Could not parse request line: ' + str); | ||
} else { | ||
if (requestLineFragments.length > 0) { | ||
this.method = requestLineFragments[0].toUpperCase(); | ||
} | ||
if (requestLineFragments.length > 1) { | ||
this.url = requestLineFragments[1]; | ||
} | ||
if (requestLineFragments.length > 2) { | ||
this.populateProtocolFromString(requestLineFragments[2]); | ||
} | ||
} | ||
if (str !== '') { | ||
const requestLineFragments = str.split(/\s+/); | ||
if (requestLineFragments.length > 3) { | ||
throw new Error(`Could not parse request line: ${str}`); | ||
} else { | ||
if (requestLineFragments.length > 0) { | ||
this.method = requestLineFragments[0].toUpperCase(); | ||
} | ||
if (requestLineFragments.length > 1) { | ||
this.url = requestLineFragments[1]; | ||
} | ||
if (requestLineFragments.length > 2) { | ||
this.populateProtocolFromString(requestLineFragments[2]); | ||
} | ||
} | ||
return this; | ||
} | ||
return this; | ||
}; | ||
RequestLine.prototype.populateUrlFromString = function (url) { | ||
var matchUrl = url.match(/^([^?]*)(\?.*)?$/); | ||
this.path = matchUrl[1] ? matchUrl[1].replace(/^\/?/, '/') : '/'; | ||
this.search = matchUrl[2] || undefined; | ||
return this; | ||
const matchUrl = url.match(/^([^?]*)(\?.*)?$/); | ||
this.path = matchUrl[1] ? matchUrl[1].replace(/^\/?/, '/') : '/'; | ||
this.search = matchUrl[2] || undefined; | ||
return this; | ||
}; | ||
Object.defineProperty(RequestLine.prototype, 'protocol', { | ||
enumerable: true, | ||
get: function () { | ||
var fragments = []; | ||
if (typeof this.protocolName !== 'undefined') { | ||
fragments.push(String(this.protocolName).toUpperCase()); | ||
} | ||
if (typeof this.protocolVersion !== 'undefined') { | ||
fragments.push('/' + this.protocolVersion); | ||
} | ||
if (fragments.length > 0) { | ||
return fragments.join(''); | ||
} | ||
}, | ||
set: function (protocol) { | ||
this.populateProtocolFromString(protocol); | ||
enumerable: true, | ||
get() { | ||
const fragments = []; | ||
if (typeof this.protocolName !== 'undefined') { | ||
fragments.push(String(this.protocolName).toUpperCase()); | ||
} | ||
if (typeof this.protocolVersion !== 'undefined') { | ||
fragments.push(`/${this.protocolVersion}`); | ||
} | ||
if (fragments.length > 0) { | ||
return fragments.join(''); | ||
} | ||
}, | ||
set(protocol) { | ||
this.populateProtocolFromString(protocol); | ||
}, | ||
}); | ||
Object.defineProperty(RequestLine.prototype, 'url', { | ||
enumerable: true, | ||
get: function () { | ||
return (this.path || '') + (this.search || ''); | ||
}, | ||
set: function (url) { | ||
this.populateUrlFromString(url); | ||
} | ||
enumerable: true, | ||
get() { | ||
return (this.path || '') + (this.search || ''); | ||
}, | ||
set(url) { | ||
this.populateUrlFromString(url); | ||
}, | ||
}); | ||
Object.defineProperty(RequestLine.prototype, 'query', { | ||
enumerable: true, | ||
get: function () { | ||
return typeof this.search === 'undefined' ? undefined : String(this.search).replace(/^\?/, ''); | ||
}, | ||
set: function (query) { | ||
this.url = this.url.replace(/(?:\?.*)?$/, typeof query === 'undefined' ? '' : '?' + String(query)); | ||
} | ||
enumerable: true, | ||
get() { | ||
return typeof this.search === 'undefined' | ||
? undefined | ||
: String(this.search).replace(/^\?/, ''); | ||
}, | ||
set(query) { | ||
this.url = this.url.replace( | ||
/(?:\?.*)?$/, | ||
typeof query === 'undefined' ? '' : `?${String(query)}` | ||
); | ||
}, | ||
}); | ||
RequestLine.prototype.clone = function () { | ||
return new RequestLine(this); | ||
return new RequestLine(this); | ||
}; | ||
RequestLine.prototype.toString = function (maxLineLength) { | ||
return String(this.method).toUpperCase() + (typeof this.url === 'string' ? ' ' + this.url + (typeof this.protocol === 'string' ? ' ' + this.protocol : '') : ''); | ||
return ( | ||
String(this.method).toUpperCase() + | ||
(typeof this.url === 'string' | ||
? ` ${this.url}${ | ||
typeof this.protocol === 'string' ? ` ${this.protocol}` : '' | ||
}` | ||
: '') | ||
); | ||
}; | ||
RequestLine.prototype.equals = function (other) { | ||
return this === other || ( | ||
other instanceof RequestLine && | ||
this.method === other.method && | ||
(this.path || '') === (other.path || '') && | ||
(this.search || '') === (other.search || '') && | ||
this.protocolName === other.protocolName && | ||
this.protocolVersion === other.protocolVersion | ||
); | ||
return ( | ||
this === other || | ||
(other instanceof RequestLine && | ||
this.method === other.method && | ||
(this.path || '') === (other.path || '') && | ||
(this.search || '') === (other.search || '') && | ||
this.protocolName === other.protocolName && | ||
this.protocolVersion === other.protocolVersion) | ||
); | ||
}; | ||
RequestLine.prototype.toJSON = function () { | ||
var obj = {}; | ||
RequestLine.nonComputedPropertyNames.forEach(function (propertyName) { | ||
if (typeof this[propertyName] !== 'undefined') { | ||
obj[propertyName] = this[propertyName]; | ||
} | ||
}, this); | ||
return obj; | ||
const obj = {}; | ||
RequestLine.nonComputedPropertyNames.forEach(function (propertyName) { | ||
if (typeof this[propertyName] !== 'undefined') { | ||
obj[propertyName] = this[propertyName]; | ||
} | ||
}, this); | ||
return obj; | ||
}; | ||
module.exports = RequestLine; |
function StatusLine(obj) { | ||
this.populate(obj); | ||
this.populate(obj); | ||
} | ||
StatusLine.nonComputedPropertyNames = ['protocolName', 'protocolVersion', 'statusCode', 'statusMessage']; | ||
StatusLine.propertyNames = ['protocol'].concat(StatusLine.nonComputedPropertyNames); | ||
StatusLine.nonComputedPropertyNames = [ | ||
'protocolName', | ||
'protocolVersion', | ||
'statusCode', | ||
'statusMessage', | ||
]; | ||
StatusLine.propertyNames = ['protocol'].concat( | ||
StatusLine.nonComputedPropertyNames | ||
); | ||
@@ -11,95 +18,96 @@ StatusLine.prototype.isMessyStatusLine = true; | ||
StatusLine.prototype.populate = function (obj) { | ||
if (typeof obj === 'number') { | ||
this.populateFromObject({ statusCode: obj }); | ||
} else if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object' && !Buffer.isBuffer(obj)) { | ||
this.populateFromObject(obj); | ||
} | ||
return this; | ||
if (typeof obj === 'number') { | ||
this.populateFromObject({ statusCode: obj }); | ||
} else if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object' && !Buffer.isBuffer(obj)) { | ||
this.populateFromObject(obj); | ||
} | ||
return this; | ||
}; | ||
StatusLine.prototype.populateFromObject = function (obj) { | ||
StatusLine.propertyNames.forEach(function (propertyName) { | ||
if (typeof obj[propertyName] !== 'undefined') { | ||
this[propertyName] = obj[propertyName]; | ||
} | ||
}, this); | ||
return this; | ||
StatusLine.propertyNames.forEach(function (propertyName) { | ||
if (typeof obj[propertyName] !== 'undefined') { | ||
this[propertyName] = obj[propertyName]; | ||
} | ||
}, this); | ||
return this; | ||
}; | ||
StatusLine.prototype.populateFromString = function (str) { | ||
var statusLineFragments = str.split(/\s+/); | ||
if (statusLineFragments.length > 0) { | ||
this.populateProtocolFromString(statusLineFragments[0]); | ||
} | ||
if (statusLineFragments.length > 1) { | ||
this.statusCode = parseInt(statusLineFragments[1], 10); | ||
} | ||
if (statusLineFragments.length > 2) { | ||
this.statusMessage = statusLineFragments.slice(2).join(' '); | ||
} | ||
return this; | ||
const statusLineFragments = str.split(/\s+/); | ||
if (statusLineFragments.length > 0) { | ||
this.populateProtocolFromString(statusLineFragments[0]); | ||
} | ||
if (statusLineFragments.length > 1) { | ||
this.statusCode = parseInt(statusLineFragments[1], 10); | ||
} | ||
if (statusLineFragments.length > 2) { | ||
this.statusMessage = statusLineFragments.slice(2).join(' '); | ||
} | ||
return this; | ||
}; | ||
StatusLine.prototype.populateProtocolFromString = function (protocol) { | ||
if (protocol !== '') { | ||
var protocolFragments = protocol.split('/'); | ||
if (protocolFragments.length === 2) { | ||
this.protocolName = protocolFragments[0]; | ||
this.protocolVersion = protocolFragments[1]; | ||
} else { | ||
throw new Error('Could not parse protocol: ' + protocol); | ||
} | ||
if (protocol !== '') { | ||
const protocolFragments = protocol.split('/'); | ||
if (protocolFragments.length === 2) { | ||
this.protocolName = protocolFragments[0]; | ||
this.protocolVersion = protocolFragments[1]; | ||
} else { | ||
throw new Error(`Could not parse protocol: ${protocol}`); | ||
} | ||
return this; | ||
} | ||
return this; | ||
}; | ||
Object.defineProperty(StatusLine.prototype, 'protocol', { | ||
enumerable: true, | ||
get: function () { | ||
var fragments = []; | ||
if (typeof this.protocolName !== 'undefined') { | ||
fragments.push(String(this.protocolName).toUpperCase()); | ||
} | ||
if (typeof this.protocolVersion !== 'undefined') { | ||
fragments.push(this.protocolVersion); | ||
} | ||
if (fragments.length > 0) { | ||
return fragments.join('/'); | ||
} | ||
}, | ||
set: function (protocol) { | ||
this.populateProtocolFromString(protocol); | ||
enumerable: true, | ||
get() { | ||
const fragments = []; | ||
if (typeof this.protocolName !== 'undefined') { | ||
fragments.push(String(this.protocolName).toUpperCase()); | ||
} | ||
if (typeof this.protocolVersion !== 'undefined') { | ||
fragments.push(this.protocolVersion); | ||
} | ||
if (fragments.length > 0) { | ||
return fragments.join('/'); | ||
} | ||
}, | ||
set(protocol) { | ||
this.populateProtocolFromString(protocol); | ||
}, | ||
}); | ||
StatusLine.prototype.clone = function () { | ||
return new StatusLine(this); | ||
return new StatusLine(this); | ||
}; | ||
StatusLine.prototype.toString = function () { | ||
return this.protocol + ' ' + this.statusCode + ' ' + this.statusMessage; | ||
return `${this.protocol} ${this.statusCode} ${this.statusMessage}`; | ||
}; | ||
StatusLine.prototype.equals = function (other) { | ||
return this === other || ( | ||
other instanceof StatusLine && | ||
this.protocolName === other.protocolName && | ||
this.protocolVersion === other.protocolVersion && | ||
this.statusCode === other.statusCode && | ||
this.statusMessage === other.statusMessage | ||
); | ||
return ( | ||
this === other || | ||
(other instanceof StatusLine && | ||
this.protocolName === other.protocolName && | ||
this.protocolVersion === other.protocolVersion && | ||
this.statusCode === other.statusCode && | ||
this.statusMessage === other.statusMessage) | ||
); | ||
}; | ||
StatusLine.prototype.toJSON = function () { | ||
var obj = {}; | ||
StatusLine.nonComputedPropertyNames.forEach(function (propertyName) { | ||
if (typeof this[propertyName] !== 'undefined') { | ||
obj[propertyName] = this[propertyName]; | ||
} | ||
}, this); | ||
return obj; | ||
const obj = {}; | ||
StatusLine.nonComputedPropertyNames.forEach(function (propertyName) { | ||
if (typeof this[propertyName] !== 'undefined') { | ||
obj[propertyName] = this[propertyName]; | ||
} | ||
}, this); | ||
return obj; | ||
}; | ||
module.exports = StatusLine; |
{ | ||
"name": "messy", | ||
"version": "6.17.0", | ||
"version": "7.0.0", | ||
"description": "Object model for HTTP and RFC822 messages", | ||
@@ -10,6 +10,6 @@ "main": "lib/index.js", | ||
"scripts": { | ||
"lint": "jshint .", | ||
"test": "mocha && npm run lint", | ||
"travis": "npm test && npm run coverage && (<coverage/lcov.info coveralls || true)", | ||
"coverage": "NODE_ENV=development istanbul cover _mocha -- --reporter dot && echo google-chrome coverage/lcov-report/index.html" | ||
"lint": "eslint . && prettier --check '**/*.{js,json,md}'", | ||
"test": "mocha", | ||
"test:ci": "npm run coverage", | ||
"coverage": "nyc --reporter=lcov --reporter=text --all -- npm test && echo google-chrome coverage/lcov-report/index.html" | ||
}, | ||
@@ -29,15 +29,28 @@ "keywords": [ | ||
"devDependencies": { | ||
"coveralls": "^2.12.0", | ||
"istanbul": "^0.4.5", | ||
"jshint": "^2.9.4", | ||
"mocha": "^3.2.0", | ||
"unexpected": "^10.26.3" | ||
"coveralls": "^3.0.5", | ||
"eslint": "^7.0.0", | ||
"eslint-config-prettier": "^6.0.0", | ||
"eslint-config-standard": "^15.0.0", | ||
"eslint-plugin-import": "^2.18.2", | ||
"eslint-plugin-mocha": "^8.0.0", | ||
"eslint-plugin-node": "^11.0.0", | ||
"eslint-plugin-promise": "^4.2.1", | ||
"eslint-plugin-standard": "^4.0.0", | ||
"mocha": "^7.0.0", | ||
"nyc": "^15.0.0", | ||
"prettier": "^2.2.0", | ||
"unexpected": "^12.0.0" | ||
}, | ||
"dependencies": { | ||
"iconv-lite": "^0.4.13", | ||
"quoted-printable": "1.0.0", | ||
"rfc2047": "2.0.0", | ||
"rfc2231": "1.3.0", | ||
"underscore": "^1.6.0" | ||
"iconv-lite": "^0.6.0", | ||
"lodash.omit": "^4.5.0", | ||
"quoted-printable": "^1.0.1", | ||
"rfc2047": "^3.0.1", | ||
"rfc2231": "^1.3.0" | ||
}, | ||
"nyc": { | ||
"include": [ | ||
"lib/**" | ||
] | ||
} | ||
} |
@@ -1,3 +0,2 @@ | ||
messy | ||
===== | ||
# messy | ||
@@ -11,5 +10,4 @@ Object model for HTTP messages (requests and responses) and RFC822 (mail). | ||
License | ||
------- | ||
## License | ||
Messy is licensed under a standard 3-clause BSD license -- see the `LICENSE` file for details. |
@@ -1,265 +0,356 @@ | ||
/*global describe, it*/ | ||
var unexpected = require('unexpected'), | ||
Headers = require('../lib/Headers'); | ||
/* global describe, it */ | ||
const unexpected = require('unexpected'); | ||
const Headers = require('../lib/Headers'); | ||
describe('Headers', function () { | ||
var expect = unexpected.clone(); | ||
const expect = unexpected.clone(); | ||
// Not published yet | ||
try { | ||
expect.installPlugin(require('unexpected-messy')); | ||
} catch (e) {} | ||
// Not published yet | ||
try { | ||
expect.installPlugin(require('unexpected-messy')); | ||
} catch (e) {} | ||
it('should accept a string', function () { | ||
var headers = new Headers('Subject: hey, dude!'); | ||
expect(headers.get('subject'), 'to equal', 'hey, dude!'); | ||
expect(headers.toString(), 'to equal', 'Subject: hey, dude!\r\n'); | ||
}); | ||
it('should accept a string', function () { | ||
const headers = new Headers('Subject: hey, dude!'); | ||
expect(headers.get('subject'), 'to equal', 'hey, dude!'); | ||
expect(headers.toString(), 'to equal', 'Subject: hey, dude!\r\n'); | ||
}); | ||
it('should fold the lines when serializing', function () { | ||
var headers = new Headers({subject: 'hey there, dude!'}); | ||
expect(headers.toString(10), 'to equal', 'Subject: hey\r\n there,\r\n dude!\r\n'); | ||
}); | ||
it('should fold the lines when serializing', function () { | ||
const headers = new Headers({ subject: 'hey there, dude!' }); | ||
expect( | ||
headers.toString(10), | ||
'to equal', | ||
'Subject: hey\r\n there,\r\n dude!\r\n' | ||
); | ||
}); | ||
it('should accept an array header value when instantiating via an Object', function () { | ||
var headers = new Headers({received: ['foo', 'bar']}); | ||
it('should accept an array header value when instantiating via an Object', function () { | ||
const headers = new Headers({ received: ['foo', 'bar'] }); | ||
expect(headers.toString(), 'to equal', 'Received: foo\r\nReceived: bar\r\n'); | ||
}); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Received: foo\r\nReceived: bar\r\n' | ||
); | ||
}); | ||
it('should accept a string header value', function () { | ||
var headers = new Headers({received: 'foo'}); | ||
it('should accept a string header value', function () { | ||
const headers = new Headers({ received: 'foo' }); | ||
expect(headers.toString(), 'to equal', 'Received: foo\r\n'); | ||
}); | ||
expect(headers.toString(), 'to equal', 'Received: foo\r\n'); | ||
}); | ||
it('should accept multiple occurrences of the same header with different casing', function () { | ||
var headers = new Headers({cookie: 'foo=bar', Cookie: 'quux=baz'}); | ||
it('should accept multiple occurrences of the same header with different casing', function () { | ||
const headers = new Headers({ cookie: 'foo=bar', Cookie: 'quux=baz' }); | ||
expect(headers.toString(), 'to equal', 'Cookie: foo=bar\r\nCookie: quux=baz\r\n'); | ||
}); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Cookie: foo=bar\r\nCookie: quux=baz\r\n' | ||
); | ||
}); | ||
it('should accept multiple occurrences of the same header with different casing when the first is given as an array', function () { | ||
var headers = new Headers({cookie: ['foo=bar'], Cookie: 'quux=baz'}); | ||
it('should accept multiple occurrences of the same header with different casing when the first is given as an array', function () { | ||
const headers = new Headers({ cookie: ['foo=bar'], Cookie: 'quux=baz' }); | ||
expect(headers.toString(), 'to equal', 'Cookie: foo=bar\r\nCookie: quux=baz\r\n'); | ||
}); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Cookie: foo=bar\r\nCookie: quux=baz\r\n' | ||
); | ||
}); | ||
it('should accept multiple occurrences of the same header with different casing when the second is given as an array', function () { | ||
var headers = new Headers({cookie: 'foo=bar', Cookie: ['quux=baz']}); | ||
it('should accept multiple occurrences of the same header with different casing when the second is given as an array', function () { | ||
const headers = new Headers({ cookie: 'foo=bar', Cookie: ['quux=baz'] }); | ||
expect(headers.toString(), 'to equal', 'Cookie: foo=bar\r\nCookie: quux=baz\r\n'); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Cookie: foo=bar\r\nCookie: quux=baz\r\n' | ||
); | ||
}); | ||
it('should return the header names in their original case', function () { | ||
expect(new Headers({ 'X-in-ORIGNAL-Case': 'baz' }).getNames(), 'to equal', [ | ||
'X-in-ORIGNAL-Case', | ||
]); | ||
}); | ||
it('should return lower case header names when requested', function () { | ||
expect( | ||
new Headers({ 'X-in-ORIGNAL-Case': 'baz' }).getNamesLowerCase(), | ||
'to equal', | ||
['x-in-orignal-case'] | ||
); | ||
}); | ||
describe('#remove', function () { | ||
it('should remove all header values for the given header when only passed one argument', function () { | ||
const headers = new Headers({ foo: ['bla', 'bar'], quux: 'baz' }); | ||
headers.remove('foo'); | ||
expect(headers, 'to equal', new Headers({ quux: 'baz' })); | ||
}); | ||
it('should return the header names in their original case', function () { | ||
expect(new Headers({'X-in-ORIGNAL-Case': 'baz'}).getNames(), 'to equal', ['X-in-ORIGNAL-Case']); | ||
it('should remove a single header value', function () { | ||
const headers = new Headers({ foo: 'bar', quux: 'baz' }); | ||
headers.remove('foo', 'bar'); | ||
expect(headers, 'to equal', new Headers({ quux: 'baz' })); | ||
}); | ||
it('should return lower case header names when requested', function () { | ||
expect(new Headers({'X-in-ORIGNAL-Case': 'baz'}).getNamesLowerCase(), 'to equal', ['x-in-orignal-case']); | ||
it('should remove one out of multiple values', function () { | ||
const headers = new Headers({ foo: ['bar', 'bla'], quux: 'baz' }); | ||
headers.remove('foo', 'bar'); | ||
expect(headers, 'to equal', new Headers({ foo: 'bla', quux: 'baz' })); | ||
}); | ||
describe('#remove', function () { | ||
it('should remove all header values for the given header when only passed one argument', function () { | ||
var headers = new Headers({foo: ['bla', 'bar'], quux: 'baz'}); | ||
headers.remove('foo'); | ||
expect(headers, 'to equal', new Headers({quux: 'baz'})); | ||
}); | ||
it('should remove multiple values, leaving one', function () { | ||
const headers = new Headers({ foo: ['bar', 'bla', 'hey'], quux: 'baz' }); | ||
headers.remove('foo', ['bar', 'hey']); | ||
expect(headers, 'to equal', new Headers({ foo: 'bla', quux: 'baz' })); | ||
}); | ||
it('should remove a single header value', function () { | ||
var headers = new Headers({foo: 'bar', quux: 'baz'}); | ||
headers.remove('foo', 'bar'); | ||
expect(headers, 'to equal', new Headers({quux: 'baz'})); | ||
}); | ||
it('should remove multiple values, leaving none', function () { | ||
const headers = new Headers({ foo: ['bla', 'hey'], quux: 'baz' }); | ||
headers.remove('foo', ['hey', 'bla']); | ||
expect(headers, 'to equal', new Headers({ quux: 'baz' })); | ||
expect(headers.valuesByName.foo, 'to be undefined'); | ||
}); | ||
it('should remove one out of multiple values', function () { | ||
var headers = new Headers({foo: ['bar', 'bla'], quux: 'baz'}); | ||
headers.remove('foo', 'bar'); | ||
expect(headers, 'to equal', new Headers({foo: 'bla', quux: 'baz'})); | ||
}); | ||
it('should remove all header values found in object', function () { | ||
const headers = new Headers({ foo: ['bla', 'bar'], quux: 'baz' }); | ||
expect(headers.remove({ foo: 'bar', quux: 'baz' }), 'to equal', 2); | ||
expect(headers, 'to equal', new Headers({ foo: 'bla' })); | ||
}); | ||
it('should remove multiple values, leaving one', function () { | ||
var headers = new Headers({foo: ['bar', 'bla', 'hey'], quux: 'baz'}); | ||
headers.remove('foo', ['bar', 'hey']); | ||
expect(headers, 'to equal', new Headers({foo: 'bla', quux: 'baz'})); | ||
}); | ||
it('should remove header value specified by number', function () { | ||
const headers = new Headers({ foo: ['bla', 'bar'], quux: 'baz' }); | ||
expect(headers.remove('foo', 1), 'to equal', 1); | ||
expect(headers.remove('foo', 1), 'to equal', 0); | ||
expect(headers, 'to equal', new Headers({ foo: 'bla', quux: 'baz' })); | ||
}); | ||
it('should remove multiple values, leaving none', function () { | ||
var headers = new Headers({foo: ['bla', 'hey'], quux: 'baz'}); | ||
headers.remove('foo', ['hey', 'bla']); | ||
expect(headers, 'to equal', new Headers({quux: 'baz'})); | ||
expect(headers.valuesByName.foo, 'to be undefined'); | ||
}); | ||
it('should return the number of removed values when removing all values of a header', function () { | ||
expect( | ||
new Headers({ foo: ['bla', 'hey'], quux: 'baz' }).remove('foo'), | ||
'to equal', | ||
2 | ||
); | ||
}); | ||
it('should remove all header values found in object', function () { | ||
var headers = new Headers({foo: ['bla', 'bar'], quux: 'baz'}); | ||
expect(headers.remove({foo: 'bar', quux: 'baz'}), 'to equal', 2); | ||
expect(headers, 'to equal', new Headers({foo: 'bla'})); | ||
}); | ||
it('should return the number of removed values when removing one out of two', function () { | ||
expect( | ||
new Headers({ foo: ['bla', 'hey'], quux: 'baz' }).remove('foo', 'hey'), | ||
'to equal', | ||
1 | ||
); | ||
}); | ||
it('should remove header value specified by number', function () { | ||
var headers = new Headers({foo: ['bla', 'bar'], quux: 'baz'}); | ||
expect(headers.remove('foo', 1), 'to equal', 1); | ||
expect(headers.remove('foo', 1), 'to equal', 0); | ||
expect(headers, 'to equal', new Headers({foo: 'bla', quux: 'baz'})); | ||
}); | ||
it('should return the number of removed values when removing two out of two', function () { | ||
expect( | ||
new Headers({ foo: ['bla', 'hey'], quux: 'baz' }).remove('foo', [ | ||
'bla', | ||
'hey', | ||
]), | ||
'to equal', | ||
2 | ||
); | ||
}); | ||
it('should return the number of removed values when removing all values of a header', function () { | ||
expect(new Headers({foo: ['bla', 'hey'], quux: 'baz'}).remove('foo'), 'to equal', 2); | ||
}); | ||
it('should return 0 when the attempting to remove a single value that was not found', function () { | ||
expect( | ||
new Headers({ foo: 'hey', quux: 'baz' }).remove('foo', 'dah'), | ||
'to equal', | ||
0 | ||
); | ||
}); | ||
it('should return the number of removed values when removing one out of two', function () { | ||
expect(new Headers({foo: ['bla', 'hey'], quux: 'baz'}).remove('foo', 'hey'), 'to equal', 1); | ||
}); | ||
it('should return 0 when the attempting to remove multiple value that were not found', function () { | ||
expect( | ||
new Headers({ foo: 'hey', quux: 'baz' }).remove('foo', ['dah', 'bla']), | ||
'to equal', | ||
0 | ||
); | ||
}); | ||
}); | ||
it('should return the number of removed values when removing two out of two', function () { | ||
expect(new Headers({foo: ['bla', 'hey'], quux: 'baz'}).remove('foo', ['bla', 'hey']), 'to equal', 2); | ||
}); | ||
describe('#toCanonicalObject', function () { | ||
expect( | ||
new Headers({ foo: 'hey', quux: 'baz' }).toCanonicalObject(), | ||
'to equal', | ||
{ | ||
Foo: ['hey'], | ||
Quux: ['baz'], | ||
} | ||
); | ||
}); | ||
it('should return 0 when the attempting to remove a single value that was not found', function () { | ||
expect(new Headers({foo: 'hey', quux: 'baz'}).remove('foo', 'dah'), 'to equal', 0); | ||
}); | ||
describe('#set', function () { | ||
it('should preserve existing values when adding a string value', function () { | ||
const headers = new Headers({ foo: 'bar' }); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\n'); | ||
headers.set('foo', 'quux'); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\nFoo: quux\r\n'); | ||
}); | ||
it('should return 0 when the attempting to remove multiple value that were not found', function () { | ||
expect(new Headers({foo: 'hey', quux: 'baz'}).remove('foo', ['dah', 'bla']), 'to equal', 0); | ||
}); | ||
it('should remove all existing values when passed an empty array', function () { | ||
const headers = new Headers({ foo: 'bar' }); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\n'); | ||
headers.set('foo', []); | ||
expect(headers.valuesByName.foo, 'to be undefined'); | ||
expect(headers.toString(), 'to equal', ''); | ||
}); | ||
describe('#toCanonicalObject', function () { | ||
expect(new Headers({foo: 'hey', quux: 'baz'}).toCanonicalObject(), 'to equal', { | ||
Foo: ['hey'], | ||
Quux: ['baz'] | ||
}); | ||
it('should replace existing values when passed an array of strings', function () { | ||
const headers = new Headers({ foo: 'bar' }); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\n'); | ||
headers.set('foo', ['hey']); | ||
expect(headers.toString(), 'to equal', 'Foo: hey\r\n'); | ||
}); | ||
describe('#set', function () { | ||
it('should preserve existing values when adding a string value', function () { | ||
var headers = new Headers({foo: 'bar'}); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\n'); | ||
headers.set('foo', 'quux'); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\nFoo: quux\r\n'); | ||
describe('when passed an object', function () { | ||
it('should act as a shorthand for calling set with each (key, value)', function () { | ||
const headers = new Headers({ foo: 'bar', baz: 'quux' }); | ||
headers.set({ | ||
foo: ['bar2'], | ||
baz: 'quux2', | ||
yadda: 'blah', | ||
}); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Foo: bar2\r\nBaz: quux\r\nBaz: quux2\r\nYadda: blah\r\n' | ||
); | ||
}); | ||
}); | ||
}); | ||
it('should remove all existing values when passed an empty array', function () { | ||
var headers = new Headers({foo: 'bar'}); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\n'); | ||
headers.set('foo', []); | ||
expect(headers.valuesByName.foo, 'to be undefined'); | ||
expect(headers.toString(), 'to equal', ''); | ||
describe('#parameter()', function () { | ||
describe('when called with just a header name', function () { | ||
it('should return a hash of attributes when the header has attributes', function () { | ||
expect(new Headers('Foo: bar; quux=baz').parameter('Foo'), 'to equal', { | ||
quux: 'baz', | ||
}); | ||
}); | ||
it('should replace existing values when passed an array of strings', function () { | ||
var headers = new Headers({foo: 'bar'}); | ||
expect(headers.toString(), 'to equal', 'Foo: bar\r\n'); | ||
headers.set('foo', ['hey']); | ||
expect(headers.toString(), 'to equal', 'Foo: hey\r\n'); | ||
}); | ||
it('should unquote quoted parameters', function () { | ||
expect( | ||
new Headers('Foo: bar; quux="baz"').parameter('Foo'), | ||
'to equal', | ||
{ quux: 'baz' } | ||
); | ||
}); | ||
describe('when passed an object', function () { | ||
it('should act as a shorthand for calling set with each (key, value)', function () { | ||
var headers = new Headers({foo: 'bar', baz: 'quux'}); | ||
headers.set({ | ||
foo: [ 'bar2' ], | ||
baz: 'quux2', | ||
yadda: 'blah' | ||
}); | ||
expect(headers.toString(), 'to equal', 'Foo: bar2\r\nBaz: quux\r\nBaz: quux2\r\nYadda: blah\r\n'); | ||
}); | ||
}); | ||
}); | ||
it('should return an empty hash when the header has no attributes', function () { | ||
expect(new Headers('Foo: bar').parameter('Foo'), 'to equal', {}); | ||
}); | ||
describe('#parameter()', function () { | ||
describe('when called with just a header name', function () { | ||
it('should return a hash of attributes when the header has attributes', function () { | ||
expect(new Headers('Foo: bar; quux=baz').parameter('Foo'), 'to equal', {quux: 'baz'}); | ||
}); | ||
it('should return undefined when the header does not exist', function () { | ||
expect( | ||
new Headers('Foo: bar').parameter('Quux'), | ||
'to equal', | ||
undefined | ||
); | ||
}); | ||
it('should unquote quoted parameters', function () { | ||
expect(new Headers('Foo: bar; quux="baz"').parameter('Foo'), 'to equal', {quux: 'baz'}); | ||
}); | ||
it('should decode rfc2231-encoded attributes', function () { | ||
expect( | ||
new Headers( | ||
'Content-Type: text/plain;\r\n' + | ||
" filename*0*=utf-8''%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n" + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA' | ||
).parameter('Content-Type'), | ||
'to equal', | ||
{ | ||
filename: | ||
'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺', | ||
} | ||
); | ||
}); | ||
}); | ||
it('should return an empty hash when the header has no attributes', function () { | ||
expect(new Headers('Foo: bar').parameter('Foo'), 'to equal', {}); | ||
}); | ||
describe('when called with a header name and an attribute name', function () { | ||
it('should return the attribute value', function () { | ||
expect(new Headers('Foo: hey').parameter('Foo'), 'to equal', {}); | ||
}); | ||
it('should return undefined when the header does not exist', function () { | ||
expect(new Headers('Foo: bar').parameter('Quux'), 'to equal', undefined); | ||
}); | ||
it('should return undefined when the header has no attributes', function () { | ||
expect( | ||
new Headers('Foo: hey').parameter('Foo', 'bar'), | ||
'to equal', | ||
undefined | ||
); | ||
}); | ||
it('should decode rfc2231-encoded attributes', function () { | ||
expect(new Headers( | ||
'Content-Type: text/plain;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA').parameter('Content-Type'), 'to equal', { | ||
filename: 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺' | ||
}); | ||
}); | ||
}); | ||
it('should return undefined when the header has attributes, but not the one being asked for', function () { | ||
expect( | ||
new Headers('Foo: hey; quux=blah').parameter('Foo', 'bar'), | ||
'to equal', | ||
undefined | ||
); | ||
}); | ||
describe('when called with a header name and an attribute name', function () { | ||
it('should return the attribute value', function () { | ||
expect(new Headers('Foo: hey').parameter('Foo'), 'to equal', {}); | ||
}); | ||
it('should return undefined when the header is not there', function () { | ||
expect( | ||
new Headers('Foo: bar').parameter('Bar', 'quux'), | ||
'to equal', | ||
undefined | ||
); | ||
}); | ||
}); | ||
it('should return undefined when the header has no attributes', function () { | ||
expect(new Headers('Foo: hey').parameter('Foo', 'bar'), 'to equal', undefined); | ||
}); | ||
describe('when called with a header name, an attribute name, and an attribute value', function () { | ||
it('should define the attribute if it does not exist', function () { | ||
const headers = new Headers('Foo: hey'); | ||
headers.parameter('Foo', 'blah', 'baz'); | ||
expect(headers.toString(), 'to equal', 'Foo: hey; blah=baz\r\n'); | ||
}); | ||
it('should return undefined when the header has attributes, but not the one being asked for', function () { | ||
expect(new Headers('Foo: hey; quux=blah').parameter('Foo', 'bar'), 'to equal', undefined); | ||
}); | ||
it('should update the attribute if it already exists', function () { | ||
const headers = new Headers('Foo: hey; blah=quux'); | ||
headers.parameter('Foo', 'blah', 'baz'); | ||
expect(headers.toString(), 'to equal', 'Foo: hey; blah=baz\r\n'); | ||
}); | ||
it('should return undefined when the header is not there', function () { | ||
expect(new Headers('Foo: bar').parameter('Bar', 'quux'), 'to equal', undefined); | ||
}); | ||
}); | ||
it('should transparently encode non-ASCII attribute values using rfc2231', function () { | ||
const headers = new Headers('Content-Type: text/plain'); | ||
headers.parameter( | ||
'Content-Type', | ||
'filename', | ||
'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺' | ||
); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Content-Type: text/plain;\r\n' + | ||
" filename*0*=utf-8''%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n" + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' | ||
); | ||
}); | ||
}); | ||
}); | ||
describe('when called with a header name, an attribute name, and an attribute value', function () { | ||
it('should define the attribute if it does not exist', function () { | ||
var headers = new Headers('Foo: hey'); | ||
headers.parameter('Foo', 'blah', 'baz'); | ||
expect(headers.toString(), 'to equal', 'Foo: hey; blah=baz\r\n'); | ||
}); | ||
it('should update the attribute if it already exists', function () { | ||
var headers = new Headers('Foo: hey; blah=quux'); | ||
headers.parameter('Foo', 'blah', 'baz'); | ||
expect(headers.toString(), 'to equal', 'Foo: hey; blah=baz\r\n'); | ||
}); | ||
it('should transparently encode non-ASCII attribute values using rfc2231', function () { | ||
var headers = new Headers('Content-Type: text/plain'); | ||
headers.parameter('Content-Type', 'filename', 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Content-Type: text/plain;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' | ||
); | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should output a single-value header as a string', function () { | ||
expect(new Headers('Foo: bar').toJSON(), 'to equal', { Foo: 'bar' }); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should output a single-value header as a string', function () { | ||
expect(new Headers('Foo: bar').toJSON(), 'to equal', { Foo: 'bar' }); | ||
}); | ||
it('should output a multi-value header as an array of strings', function () { | ||
expect(new Headers('Foo: bar\r\nFoo: quux').toJSON(), 'to equal', { Foo: [ 'bar', 'quux' ] }); | ||
}); | ||
it('should output a multi-value header as an array of strings', function () { | ||
expect(new Headers('Foo: bar\r\nFoo: quux').toJSON(), 'to equal', { | ||
Foo: ['bar', 'quux'], | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -1,202 +0,225 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
HttpConversation = require('../lib/HttpConversation'), | ||
HttpExchange = require('../lib/HttpExchange'), | ||
HttpRequest = require('../lib/HttpRequest'), | ||
HttpResponse = require('../lib/HttpResponse'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const HttpConversation = require('../lib/HttpConversation'); | ||
const HttpExchange = require('../lib/HttpExchange'); | ||
const HttpRequest = require('../lib/HttpRequest'); | ||
const HttpResponse = require('../lib/HttpResponse'); | ||
describe('HttpConversation', function () { | ||
it('should accept an object with an exchanges property containing an array of objects containing an HttpRequest and HttpResponse instances', function () { | ||
var httpConversation = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf') | ||
} | ||
] | ||
}); | ||
expect(httpConversation.exchanges, 'to have items satisfying', 'to be an', HttpExchange); | ||
expect( | ||
httpConversation.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
it('should accept an object with an exchanges property containing an array of objects containing an HttpRequest and HttpResponse instances', function () { | ||
const httpConversation = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf'), | ||
}, | ||
], | ||
}); | ||
it('should accept an object with an exchanges property containing array of objects containing request and response as strings', function () { | ||
var httpConversation = new HttpConversation({ | ||
exchanges: [ | ||
new HttpExchange({ | ||
request: 'GET / HTTP/1.1\nFoo: Bar\n\nblah', | ||
response: 'HTTP/1.1 200 OK\nQuux: Baz\n\nblaf' | ||
}) | ||
] | ||
}); | ||
expect( | ||
httpConversation.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
expect( | ||
httpConversation.exchanges, | ||
'to have items satisfying', | ||
'to be an', | ||
HttpExchange | ||
); | ||
expect( | ||
httpConversation.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
}); | ||
it('should accept an object with an exchanges property containing array of objects containing request and response as strings', function () { | ||
const httpConversation = new HttpConversation({ | ||
exchanges: [ | ||
new HttpExchange({ | ||
request: 'GET / HTTP/1.1\nFoo: Bar\n\nblah', | ||
response: 'HTTP/1.1 200 OK\nQuux: Baz\n\nblaf', | ||
}), | ||
], | ||
}); | ||
expect( | ||
httpConversation.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
}); | ||
it('should accept an object with an exchanges property containing HttpRequest and HttpResponse options objects', function () { | ||
var httpConversation = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: { | ||
requestLine: { | ||
method: 'GET', | ||
protocol: 'HTTP/1.1', | ||
path: '/' | ||
}, | ||
headers: { | ||
'Content-Type': 'text/html' | ||
}, | ||
body: 'The Body' | ||
}, | ||
response: { | ||
statusLine: 'HTTP/1.1 404 Not Found', | ||
headers: 'Content-Type: application/json', | ||
body: {foo: 123} | ||
} | ||
} | ||
] | ||
}); | ||
expect( | ||
httpConversation.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nContent-Type: text/html\r\n\r\nThe Body\r\n\r\nHTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n\r\n{"foo":123}' | ||
); | ||
it('should accept an object with an exchanges property containing HttpRequest and HttpResponse options objects', function () { | ||
const httpConversation = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: { | ||
requestLine: { | ||
method: 'GET', | ||
protocol: 'HTTP/1.1', | ||
path: '/', | ||
}, | ||
headers: { | ||
'Content-Type': 'text/html', | ||
}, | ||
body: 'The Body', | ||
}, | ||
response: { | ||
statusLine: 'HTTP/1.1 404 Not Found', | ||
headers: 'Content-Type: application/json', | ||
body: { foo: 123 }, | ||
}, | ||
}, | ||
], | ||
}); | ||
expect( | ||
httpConversation.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nContent-Type: text/html\r\n\r\nThe Body\r\n\r\nHTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n\r\n{"foo":123}' | ||
); | ||
}); | ||
it('should consider identical instances equal', function () { | ||
var httpConversation1 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: 'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK' | ||
}, | ||
body: 'blaf' | ||
} | ||
} | ||
] | ||
}), httpConversation2 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
headers: { | ||
host: 'foo.com' | ||
}, | ||
body: 'blah' | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf' | ||
} | ||
] | ||
}); | ||
expect(httpConversation1.equals(httpConversation2), 'to be true'); | ||
expect(httpConversation1.toString(), 'to equal', httpConversation2.toString()); | ||
it('should consider identical instances equal', function () { | ||
const httpConversation1 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: 'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK', | ||
}, | ||
body: 'blaf', | ||
}, | ||
}, | ||
], | ||
}); | ||
const httpConversation2 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
headers: { | ||
host: 'foo.com', | ||
}, | ||
body: 'blah', | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf', | ||
}, | ||
], | ||
}); | ||
expect(httpConversation1.equals(httpConversation2), 'to be true'); | ||
expect( | ||
httpConversation1.toString(), | ||
'to equal', | ||
httpConversation2.toString() | ||
); | ||
}); | ||
it('should consider different instances unequal', function () { | ||
var httpConversation1 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: 'GET /foo HTTP/1.0\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK' | ||
}, | ||
body: 'blaf' | ||
} | ||
} | ||
] | ||
}), httpConversation2 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1' | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf' | ||
} | ||
] | ||
}); | ||
expect(httpConversation1.equals(httpConversation2), 'to be false'); | ||
expect(httpConversation1.toString(), 'not to equal', httpConversation2.toString()); | ||
it('should consider different instances unequal', function () { | ||
const httpConversation1 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: 'GET /foo HTTP/1.0\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK', | ||
}, | ||
body: 'blaf', | ||
}, | ||
}, | ||
], | ||
}); | ||
const httpConversation2 = new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf', | ||
}, | ||
], | ||
}); | ||
expect(httpConversation1.equals(httpConversation2), 'to be false'); | ||
expect( | ||
httpConversation1.toString(), | ||
'not to equal', | ||
httpConversation2.toString() | ||
); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should return an object with the exchanges JSONified', function () { | ||
expect(new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf') | ||
}, | ||
{ | ||
request: new HttpRequest('GET /foo HTTP/1.1\nFoo: Barrr\n\nblahhh'), | ||
response: new HttpResponse('HTTP/1.1 412 Precondition Failed\nQuux: Bazzz\n\nblafff') | ||
} | ||
] | ||
}).toJSON(), 'to equal', { | ||
exchanges: [ | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'Bar' | ||
}, | ||
rawBody: 'blah' | ||
}, | ||
response: { | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Quux: 'Baz' | ||
}, | ||
rawBody: 'blaf' | ||
} | ||
}, | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'Barrr' | ||
}, | ||
rawBody: 'blahhh' | ||
}, | ||
response: { | ||
statusCode: 412, | ||
statusMessage: 'Precondition Failed', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Quux: 'Bazzz' | ||
}, | ||
rawBody: 'blafff' | ||
} | ||
} | ||
] | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should return an object with the exchanges JSONified', function () { | ||
expect( | ||
new HttpConversation({ | ||
exchanges: [ | ||
{ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf'), | ||
}, | ||
{ | ||
request: new HttpRequest( | ||
'GET /foo HTTP/1.1\nFoo: Barrr\n\nblahhh' | ||
), | ||
response: new HttpResponse( | ||
'HTTP/1.1 412 Precondition Failed\nQuux: Bazzz\n\nblafff' | ||
), | ||
}, | ||
], | ||
}).toJSON(), | ||
'to equal', | ||
{ | ||
exchanges: [ | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'Bar', | ||
}, | ||
rawBody: 'blah', | ||
}, | ||
response: { | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Quux: 'Baz', | ||
}, | ||
rawBody: 'blaf', | ||
}, | ||
}, | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'Barrr', | ||
}, | ||
rawBody: 'blahhh', | ||
}, | ||
response: { | ||
statusCode: 412, | ||
statusMessage: 'Precondition Failed', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Quux: 'Bazzz', | ||
}, | ||
rawBody: 'blafff', | ||
}, | ||
}, | ||
], | ||
} | ||
); | ||
}); | ||
}); | ||
}); |
@@ -1,162 +0,169 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
HttpExchange = require('../lib/HttpExchange'), | ||
HttpRequest = require('../lib/HttpRequest'), | ||
HttpResponse = require('../lib/HttpResponse'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const HttpExchange = require('../lib/HttpExchange'); | ||
const HttpRequest = require('../lib/HttpRequest'); | ||
const HttpResponse = require('../lib/HttpResponse'); | ||
describe('HttpExchange', function () { | ||
it('should accept an object containing an HttpRequest and an HttpResponse instance', function () { | ||
var httpExchange = new HttpExchange({ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf') | ||
}); | ||
it('should accept an object containing an HttpRequest and an HttpResponse instance', function () { | ||
const httpExchange = new HttpExchange({ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf'), | ||
}); | ||
expect(httpExchange, 'to have properties', ['request', 'response']); | ||
expect(httpExchange.request, 'to be an', HttpRequest); | ||
expect(httpExchange.response, 'to be an', HttpResponse); | ||
expect( | ||
httpExchange.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
expect(httpExchange, 'to have properties', ['request', 'response']); | ||
expect(httpExchange.request, 'to be an', HttpRequest); | ||
expect(httpExchange.response, 'to be an', HttpResponse); | ||
expect( | ||
httpExchange.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
}); | ||
it('should accept an object containing request and response as strings', function () { | ||
const httpExchange = new HttpExchange({ | ||
request: 'GET / HTTP/1.1\nFoo: Bar\n\nblah', | ||
response: 'HTTP/1.1 200 OK\nQuux: Baz\n\nblaf', | ||
}); | ||
expect(httpExchange, 'to have properties', ['request', 'response']); | ||
expect(httpExchange.request, 'to be an', HttpRequest); | ||
expect(httpExchange.response, 'to be an', HttpResponse); | ||
expect( | ||
httpExchange.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
}); | ||
it('should accept an object containing request and response as strings', function () { | ||
var httpExchange = new HttpExchange({ | ||
request: 'GET / HTTP/1.1\nFoo: Bar\n\nblah', | ||
response: 'HTTP/1.1 200 OK\nQuux: Baz\n\nblaf' | ||
}); | ||
expect(httpExchange, 'to have properties', ['request', 'response']); | ||
expect(httpExchange.request, 'to be an', HttpRequest); | ||
expect(httpExchange.response, 'to be an', HttpResponse); | ||
expect( | ||
httpExchange.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nFoo: Bar\r\n\r\nblah\r\n\r\nHTTP/1.1 200 OK\r\nQuux: Baz\r\n\r\nblaf' | ||
); | ||
it('should accept an object containing HttpRequest and HttpResponse options objects', function () { | ||
const httpExchange = new HttpExchange({ | ||
request: { | ||
requestLine: { | ||
method: 'GET', | ||
protocol: 'HTTP/1.1', | ||
path: '/', | ||
}, | ||
headers: { | ||
'Content-Type': 'text/html', | ||
}, | ||
body: 'The Body', | ||
}, | ||
response: { | ||
statusLine: 'HTTP/1.1 404 Not Found', | ||
headers: 'Content-Type: application/json', | ||
body: { foo: 123 }, | ||
}, | ||
}); | ||
expect(httpExchange, 'to have properties', ['request', 'response']); | ||
expect(httpExchange.request, 'to be an', HttpRequest); | ||
expect(httpExchange.response, 'to be an', HttpResponse); | ||
expect( | ||
httpExchange.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nContent-Type: text/html\r\n\r\nThe Body\r\n\r\nHTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n\r\n{"foo":123}' | ||
); | ||
}); | ||
it('should accept an object containing HttpRequest and HttpResponse options objects', function () { | ||
var httpExchange = new HttpExchange({ | ||
request: { | ||
requestLine: { | ||
method: 'GET', | ||
protocol: 'HTTP/1.1', | ||
path: '/' | ||
}, | ||
headers: { | ||
'Content-Type': 'text/html' | ||
}, | ||
body: 'The Body' | ||
}, | ||
response: { | ||
statusLine: 'HTTP/1.1 404 Not Found', | ||
headers: 'Content-Type: application/json', | ||
body: {foo: 123} | ||
} | ||
}); | ||
expect(httpExchange, 'to have properties', ['request', 'response']); | ||
expect(httpExchange.request, 'to be an', HttpRequest); | ||
expect(httpExchange.response, 'to be an', HttpResponse); | ||
expect( | ||
httpExchange.toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\nContent-Type: text/html\r\n\r\nThe Body\r\n\r\nHTTP/1.1 404 Not Found\r\nContent-Type: application/json\r\n\r\n{"foo":123}' | ||
); | ||
it('should consider identical instances equal', function () { | ||
const httpExchange1 = new HttpExchange({ | ||
request: 'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK', | ||
}, | ||
body: 'blaf', | ||
}, | ||
}); | ||
const httpExchange2 = new HttpExchange({ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
headers: { | ||
host: 'foo.com', | ||
}, | ||
body: 'blah', | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf', | ||
}); | ||
expect(httpExchange1.equals(httpExchange2), 'to be true'); | ||
expect(httpExchange1.toString(), 'to equal', httpExchange2.toString()); | ||
}); | ||
it('should consider identical instances equal', function () { | ||
var httpExchange1 = new HttpExchange({ | ||
request: 'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK' | ||
}, | ||
body: 'blaf' | ||
} | ||
}), | ||
httpExchange2 = new HttpExchange({ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
headers: { | ||
host: 'foo.com' | ||
}, | ||
body: 'blah' | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf' | ||
}); | ||
expect(httpExchange1.equals(httpExchange2), 'to be true'); | ||
expect(httpExchange1.toString(), 'to equal', httpExchange2.toString()); | ||
it('should consider different instances unequal', function () { | ||
const httpExchange1 = new HttpExchange({ | ||
request: 'GET /foo HTTP/1.0\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK', | ||
}, | ||
body: 'blaf', | ||
}, | ||
}); | ||
const httpExchange2 = new HttpExchange({ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf', | ||
}); | ||
expect(httpExchange1.equals(httpExchange2), 'to be false'); | ||
expect(httpExchange1.toString(), 'not to equal', httpExchange2.toString()); | ||
}); | ||
it('should consider different instances unequal', function () { | ||
var httpExchange1 = new HttpExchange({ | ||
request: 'GET /foo HTTP/1.0\r\nHost: foo.com\r\n\r\nblah', | ||
response: { | ||
statusLine: { | ||
statusCode: 200, | ||
protocol: 'HTTP/1.1', | ||
statusMessage: 'OK' | ||
}, | ||
body: 'blaf' | ||
} | ||
}), | ||
httpExchange2 = new HttpExchange({ | ||
request: { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1' | ||
}, | ||
response: 'HTTP/1.1 200 OK\r\n\r\nblaf' | ||
}); | ||
expect(httpExchange1.equals(httpExchange2), 'to be false'); | ||
expect(httpExchange1.toString(), 'not to equal', httpExchange2.toString()); | ||
describe('#toJSON', function () { | ||
it('should return an object with the request and response JSONified', function () { | ||
expect( | ||
new HttpExchange({ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf'), | ||
}).toJSON(), | ||
'to equal', | ||
{ | ||
request: { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'Bar', | ||
}, | ||
rawBody: 'blah', | ||
}, | ||
response: { | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Quux: 'Baz', | ||
}, | ||
rawBody: 'blaf', | ||
}, | ||
} | ||
); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should return an object with the request and response JSONified', function () { | ||
expect(new HttpExchange({ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: new HttpResponse('HTTP/1.1 200 OK\nQuux: Baz\n\nblaf') | ||
}).toJSON(), 'to equal', { | ||
request: { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'Bar' | ||
}, | ||
rawBody: 'blah' | ||
}, | ||
response: { | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Quux: 'Baz' | ||
}, | ||
rawBody: 'blaf' | ||
} | ||
}); | ||
}); | ||
// Makes it possible to use statusLine.toJSON() as the RHS of a 'to satisfy' assertion in Unexpected | ||
// where undefined means that the property must not be present: | ||
it('should not include the keys that have undefined values', function () { | ||
const httpExchange = new HttpExchange({ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: undefined, | ||
}); | ||
// Makes it possible to use statusLine.toJSON() as the RHS of a 'to satisfy' assertion in Unexpected | ||
// where undefined means that the property must not be present: | ||
it('should not include the keys that have undefined values', function () { | ||
var httpExchange = new HttpExchange({ | ||
request: new HttpRequest('GET / HTTP/1.1\nFoo: Bar\n\nblah'), | ||
response: undefined | ||
}); | ||
httpExchange.request = undefined; | ||
httpExchange.request = undefined; | ||
expect(httpExchange.toJSON(), 'not to have keys', ['request', 'response']); | ||
}); | ||
expect(httpExchange.toJSON(), 'not to have keys', [ | ||
'request', | ||
'response', | ||
]); | ||
}); | ||
}); | ||
}); |
@@ -1,430 +0,626 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
HttpRequest = require('../lib/HttpRequest'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const HttpRequest = require('../lib/HttpRequest'); | ||
describe('HttpRequest', function () { | ||
it('should complain when receiving an unsupported property', function () { | ||
expect(function () { | ||
new HttpRequest({ contentType: 'text/css' }); | ||
}, 'to throw', 'messy.Message: Unsupported property name: contentType'); | ||
}); | ||
it('should complain when receiving an unsupported property', function () { | ||
expect( | ||
function () { | ||
// eslint-disable-next-line no-new | ||
new HttpRequest({ contentType: 'text/css' }); | ||
}, | ||
'to throw', | ||
'messy.Message: Unsupported property name: contentType' | ||
); | ||
}); | ||
it('should allow the credentials property to be passed (for fetch)', function () { | ||
new HttpRequest({ credentials: 'same-origin' }); | ||
}); | ||
it('should allow the credentials property to be passed (for fetch)', function () { | ||
// eslint-disable-next-line no-new | ||
new HttpRequest({ credentials: 'same-origin' }); | ||
}); | ||
it('should parse a standalone request line', function () { | ||
var httpRequest = new HttpRequest('GET /foo HTTP/1.1'); | ||
expect(httpRequest.method, 'to equal', 'GET'); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
expect(httpRequest.protocol, 'to equal', 'HTTP/1.1'); | ||
expect(httpRequest.toString(), 'to equal', 'GET /foo HTTP/1.1\r\n'); | ||
}); | ||
it('should parse a standalone request line', function () { | ||
const httpRequest = new HttpRequest('GET /foo HTTP/1.1'); | ||
expect(httpRequest.method, 'to equal', 'GET'); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
expect(httpRequest.protocol, 'to equal', 'HTTP/1.1'); | ||
expect(httpRequest.toString(), 'to equal', 'GET /foo HTTP/1.1\r\n'); | ||
}); | ||
it('should add a leading slash to the request url if not specified', function () { | ||
var httpRequest = new HttpRequest('GET foo'); | ||
expect(httpRequest.method, 'to equal', 'GET'); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
}); | ||
it('should add a leading slash to the request url if not specified', function () { | ||
const httpRequest = new HttpRequest('GET foo'); | ||
expect(httpRequest.method, 'to equal', 'GET'); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
}); | ||
it('should parse a request url with a query string', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET /foo?quux=baz' }); | ||
expect(httpRequest.requestLine.url, 'to equal', '/foo?quux=baz'); | ||
}); | ||
it('should parse a request url with a query string', function () { | ||
const httpRequest = new HttpRequest({ url: 'GET /foo?quux=baz' }); | ||
expect(httpRequest.requestLine.url, 'to equal', '/foo?quux=baz'); | ||
}); | ||
it('should parse a request line followed by headers', function () { | ||
var httpRequest = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n'); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
expect(httpRequest.toString(), 'to equal', 'GET /foo HTTP/1.1\r\nHost: foo.com\r\n'); | ||
}); | ||
it('should parse a request line followed by headers', function () { | ||
const httpRequest = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n' | ||
); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
expect( | ||
httpRequest.toString(), | ||
'to equal', | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n' | ||
); | ||
}); | ||
it('should parse a request line followed by headers and a body', function () { | ||
var httpRequest = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
expect(httpRequest.body, 'to equal', 'blah'); | ||
expect(httpRequest.toString(), 'to equal', 'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'); | ||
}); | ||
it('should parse a request line followed by headers and a body', function () { | ||
const httpRequest = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
expect(httpRequest.url, 'to equal', '/foo'); | ||
expect(httpRequest.body, 'to equal', 'blah'); | ||
expect( | ||
httpRequest.toString(), | ||
'to equal', | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
}); | ||
it('should accept encrypted as a parameter to the constructor', function () { | ||
expect(new HttpRequest({encrypted: true}), 'to have properties', {encrypted: true}); | ||
it('should accept encrypted as a parameter to the constructor', function () { | ||
expect(new HttpRequest({ encrypted: true }), 'to have properties', { | ||
encrypted: true, | ||
}); | ||
}); | ||
it('should accept the request line as an option to the constructor', function () { | ||
expect(new HttpRequest({requestLine: 'GET /foo HTTP/1.1'}), 'to have properties', { | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1' | ||
}); | ||
}); | ||
it('should accept the request line as an option to the constructor', function () { | ||
expect( | ||
new HttpRequest({ requestLine: 'GET /foo HTTP/1.1' }), | ||
'to have properties', | ||
{ | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
} | ||
); | ||
}); | ||
it('should parse a partial request line', function () { | ||
expect(new HttpRequest('GET /foo'), 'to have properties', { | ||
method: 'GET', | ||
url: '/foo' | ||
}); | ||
it('should parse a partial request line', function () { | ||
expect(new HttpRequest('GET /foo'), 'to have properties', { | ||
method: 'GET', | ||
url: '/foo', | ||
}); | ||
}); | ||
it('should only include CRLFCRLF when there are no headers', function () { | ||
expect(new HttpRequest({ | ||
requestLine: 'GET / HTTP/1.1', | ||
body: 'foo' | ||
}).toString(), 'to equal', 'GET / HTTP/1.1\r\n\r\nfoo'); | ||
}); | ||
it('should only include CRLFCRLF when there are no headers', function () { | ||
expect( | ||
new HttpRequest({ | ||
requestLine: 'GET / HTTP/1.1', | ||
body: 'foo', | ||
}).toString(), | ||
'to equal', | ||
'GET / HTTP/1.1\r\n\r\nfoo' | ||
); | ||
}); | ||
it('should make the request line available', function () { | ||
expect(new HttpRequest({ | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1' | ||
}).requestLine.toString(), 'to equal', 'GET /foo HTTP/1.1'); | ||
}); | ||
it('should make the request line available', function () { | ||
expect( | ||
new HttpRequest({ | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
}).requestLine.toString(), | ||
'to equal', | ||
'GET /foo HTTP/1.1' | ||
); | ||
}); | ||
it('should allow updating the request line via a setter', function () { | ||
var httpRequest = new HttpRequest({ | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1' | ||
}); | ||
httpRequest.requestLine.populateFromString('PUT /bar HTTP/1.0'); | ||
expect(httpRequest, 'to have properties', { | ||
method: 'PUT', | ||
url: '/bar', | ||
protocol: 'HTTP/1.0' | ||
}); | ||
it('should allow updating the request line via a setter', function () { | ||
const httpRequest = new HttpRequest({ | ||
method: 'GET', | ||
url: '/foo', | ||
protocol: 'HTTP/1.1', | ||
}); | ||
it('should make the protocol version available as a getter', function () { | ||
expect(new HttpRequest('GET /foo HTTP/1.1').protocolVersion, 'to equal', '1.1'); | ||
httpRequest.requestLine.populateFromString('PUT /bar HTTP/1.0'); | ||
expect(httpRequest, 'to have properties', { | ||
method: 'PUT', | ||
url: '/bar', | ||
protocol: 'HTTP/1.0', | ||
}); | ||
}); | ||
it('should make the protocol name available as a getter', function () { | ||
expect(new HttpRequest('GET /foo HTTP/1.1').protocolName, 'to equal', 'HTTP'); | ||
}); | ||
it('should make the protocol version available as a getter', function () { | ||
expect( | ||
new HttpRequest('GET /foo HTTP/1.1').protocolVersion, | ||
'to equal', | ||
'1.1' | ||
); | ||
}); | ||
it('should make the components of the request url available as individual getters', function () { | ||
expect(new HttpRequest('GET /foo?foo=bar HTTP/1.1'), 'to have properties', { | ||
path: '/foo', | ||
search: '?foo=bar', | ||
query: 'foo=bar' | ||
}); | ||
}); | ||
it('should make the protocol name available as a getter', function () { | ||
expect( | ||
new HttpRequest('GET /foo HTTP/1.1').protocolName, | ||
'to equal', | ||
'HTTP' | ||
); | ||
}); | ||
it('should make path, query, and search available as individual setters', function () { | ||
var httpRequest = new HttpRequest('GET /foo?foo=bar HTTP/1.1'); | ||
httpRequest.search = '?blabla'; | ||
httpRequest.path = '/bla'; | ||
expect(httpRequest.url, 'to equal', '/bla?blabla'); | ||
httpRequest.query = 'foobar'; | ||
expect(httpRequest.url, 'to equal', '/bla?foobar'); | ||
it('should make the components of the request url available as individual getters', function () { | ||
expect(new HttpRequest('GET /foo?foo=bar HTTP/1.1'), 'to have properties', { | ||
path: '/foo', | ||
search: '?foo=bar', | ||
query: 'foo=bar', | ||
}); | ||
}); | ||
it('should accept the individual request line fields as options to the constructor', function () { | ||
expect(new HttpRequest({ | ||
method: 'get', | ||
url: '/foo', | ||
protocol: 'http/1.1' | ||
}).requestLine.toString(), 'to equal', 'GET /foo HTTP/1.1'); | ||
}); | ||
it('should make path, query, and search available as individual setters', function () { | ||
const httpRequest = new HttpRequest('GET /foo?foo=bar HTTP/1.1'); | ||
httpRequest.search = '?blabla'; | ||
httpRequest.path = '/bla'; | ||
expect(httpRequest.url, 'to equal', '/bla?blabla'); | ||
httpRequest.query = 'foobar'; | ||
expect(httpRequest.url, 'to equal', '/bla?foobar'); | ||
}); | ||
it('should consider an identical instance equal', function () { | ||
var httpRequest1 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'), | ||
httpRequest2 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'); | ||
expect(httpRequest1.equals(httpRequest2), 'to be true'); | ||
}); | ||
it('should accept the individual request line fields as options to the constructor', function () { | ||
expect( | ||
new HttpRequest({ | ||
method: 'get', | ||
url: '/foo', | ||
protocol: 'http/1.1', | ||
}).requestLine.toString(), | ||
'to equal', | ||
'GET /foo HTTP/1.1' | ||
); | ||
}); | ||
it('should consider two instances unequal if they differ by method', function () { | ||
var httpRequest1 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'), | ||
httpRequest2 = new HttpRequest('POST /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider an identical instance equal', function () { | ||
const httpRequest1 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
const httpRequest2 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
expect(httpRequest1.equals(httpRequest2), 'to be true'); | ||
}); | ||
it('should consider two instances unequal if they differ by url', function () { | ||
var httpRequest1 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'), | ||
httpRequest2 = new HttpRequest('GET /bar HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by method', function () { | ||
const httpRequest1 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
const httpRequest2 = new HttpRequest( | ||
'POST /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by protocol', function () { | ||
var httpRequest1 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'), | ||
httpRequest2 = new HttpRequest('GET /foo HTTP/1.0\r\nHost: foo.com\r\n\r\nblah'); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by url', function () { | ||
const httpRequest1 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
const httpRequest2 = new HttpRequest( | ||
'GET /bar HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if their headers differ', function () { | ||
var httpRequest1 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'), | ||
httpRequest2 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: bar.com\r\n\r\nblah'); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by protocol', function () { | ||
const httpRequest1 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
const httpRequest2 = new HttpRequest( | ||
'GET /foo HTTP/1.0\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if their bodies differ', function () { | ||
var httpRequest1 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah'), | ||
httpRequest2 = new HttpRequest('GET /foo HTTP/1.1\r\nHost: bar.com\r\n\r\nquux'); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if their headers differ', function () { | ||
const httpRequest1 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
const httpRequest2 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: bar.com\r\n\r\nblah' | ||
); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider instances with different encrypted flags different', function () { | ||
var httpRequest1 = new HttpRequest({encrypted: true}), | ||
httpRequest2 = new HttpRequest({encrypted: false}); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
it('should consider two instances unequal if their bodies differ', function () { | ||
const httpRequest1 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: foo.com\r\n\r\nblah' | ||
); | ||
const httpRequest2 = new HttpRequest( | ||
'GET /foo HTTP/1.1\r\nHost: bar.com\r\n\r\nquux' | ||
); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should consider instances with different encrypted flags different', function () { | ||
const httpRequest1 = new HttpRequest({ encrypted: true }); | ||
const httpRequest2 = new HttpRequest({ encrypted: false }); | ||
expect(httpRequest1.equals(httpRequest2), 'to be false'); | ||
}); | ||
it('should parse a buffer', function () { | ||
const rawSrc = | ||
'POST / HTTP/1.1\r\n' + | ||
'Date: Sat, 21 Mar 2015 00:25:45 GMT\r\n' + | ||
'Connection: keep-alive\r\n' + | ||
'\r\n' + | ||
'blah'; | ||
const httpRequest = new HttpRequest(Buffer.from(rawSrc, 'ascii')); | ||
expect(httpRequest.toString(), 'to equal', rawSrc); | ||
}); | ||
describe('without a verb in the request line', function () { | ||
it('should assume GET', function () { | ||
expect(new HttpRequest('/foo'), 'to satisfy', { | ||
method: 'GET', | ||
path: '/foo', | ||
}); | ||
}); | ||
}); | ||
it('should parse a buffer', function () { | ||
var rawSrc = | ||
'POST / HTTP/1.1\r\n' + | ||
'Date: Sat, 21 Mar 2015 00:25:45 GMT\r\n' + | ||
'Connection: keep-alive\r\n' + | ||
'\r\n' + | ||
'blah'; | ||
it('should accept host and port as options', function () { | ||
expect( | ||
new HttpRequest({ host: 'foo.com', port: 987 }), | ||
'to have properties', | ||
{ | ||
host: 'foo.com', | ||
port: 987, | ||
} | ||
); | ||
}); | ||
var httpRequest = new HttpRequest(new Buffer(rawSrc, 'ascii')); | ||
it('should leave host and port as undefined when not given', function () { | ||
expect(new HttpRequest('GET /').port, 'to be undefined'); | ||
}); | ||
expect(httpRequest.toString(), 'to equal', rawSrc); | ||
describe('#url', function () { | ||
it('should include the schema, host, port, path, and search if available', function () { | ||
expect( | ||
new HttpRequest('GET https://localhost:3000/foo?bar=quux').url, | ||
'to equal', | ||
'https://localhost:3000/foo?bar=quux' | ||
); | ||
}); | ||
describe('without a verb in the request line', function () { | ||
it('should assume GET', function () { | ||
expect(new HttpRequest('/foo'), 'to satisfy', { | ||
method: 'GET', | ||
path: '/foo' | ||
}); | ||
}); | ||
it('should include the default http port explicitly', function () { | ||
expect( | ||
new HttpRequest('GET http://localhost/').url, | ||
'to equal', | ||
'http://localhost/' | ||
); | ||
}); | ||
it('should accept host and port as options', function () { | ||
expect(new HttpRequest({ host: 'foo.com', port: 987 }), 'to have properties', { | ||
host: 'foo.com', | ||
port: 987 | ||
}); | ||
it('should include the default https port explicitly', function () { | ||
expect( | ||
new HttpRequest('GET https://localhost/').url, | ||
'to equal', | ||
'https://localhost/' | ||
); | ||
}); | ||
it('should leave host and port as undefined when not given', function () { | ||
expect(new HttpRequest('GET /').port, 'to be undefined'); | ||
it('should include the username and password if available', function () { | ||
expect( | ||
new HttpRequest('GET http://foo:bar@localhost:3000/').url, | ||
'to equal', | ||
'http://foo:bar@localhost:3000/' | ||
); | ||
}); | ||
describe('#url', function () { | ||
it('should include the schema, host, port, path, and search if available', function () { | ||
expect(new HttpRequest('GET https://localhost:3000/foo?bar=quux').url, 'to equal', 'https://localhost:3000/foo?bar=quux'); | ||
}); | ||
describe('invoked as a setter', function () { | ||
it('should update the encrypted, host, port, path, and search properties', function () { | ||
const httpRequest = new HttpRequest( | ||
'GET http://foo:bar@localhost:3000/yes/?i=am' | ||
); | ||
expect(httpRequest.headers.getAll('Authorization'), 'to equal', [ | ||
'Basic Zm9vOmJhcg==', | ||
]); | ||
expect(httpRequest, 'to have property', 'encrypted', false); | ||
httpRequest.url = 'http://baz:quux@localhost:9876/no/not/?so=much'; | ||
expect( | ||
httpRequest.url, | ||
'to equal', | ||
'http://baz:quux@localhost:9876/no/not/?so=much' | ||
); | ||
expect(httpRequest.headers.getAll('Authorization'), 'to equal', [ | ||
'Basic YmF6OnF1dXg=', | ||
]); | ||
expect(httpRequest, 'to have property', 'encrypted', false); | ||
}); | ||
it('should include the default http port explicitly', function () { | ||
expect(new HttpRequest('GET http://localhost/').url, 'to equal', 'http://localhost/'); | ||
}); | ||
it('should remove an existing Authorization header if the new url does not have credentials', function () { | ||
const httpRequest = new HttpRequest( | ||
'GET http://foo:bar@localhost:3000/yes/?i=am' | ||
); | ||
httpRequest.url = 'http://localhost:9876/no/not/?so=much'; | ||
expect( | ||
httpRequest.headers.getAll('Authorization'), | ||
'to equal', | ||
undefined | ||
); | ||
}); | ||
}); | ||
}); | ||
it('should include the default https port explicitly', function () { | ||
expect(new HttpRequest('GET https://localhost/').url, 'to equal', 'https://localhost/'); | ||
}); | ||
describe('with a url passed in the request line', function () { | ||
it('should support a localhost url', function () { | ||
const httpRequest = new HttpRequest('GET http://localhost:3000/'); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'localhost:3000'); | ||
expect(httpRequest.host, 'to equal', 'localhost'); | ||
expect(httpRequest.port, 'to equal', 3000); | ||
}); | ||
it('should include the username and password if available', function () { | ||
expect(new HttpRequest('GET http://foo:bar@localhost:3000/').url, 'to equal', 'http://foo:bar@localhost:3000/'); | ||
}); | ||
it('should allow passing the same host in the host property and in the url', function () { | ||
const httpRequest = new HttpRequest({ | ||
url: 'GET http://localhost:3000/', | ||
host: 'localhost', | ||
}); | ||
expect(httpRequest.host, 'to equal', 'localhost'); | ||
}); | ||
describe('invoked as a setter', function () { | ||
it('should update the encrypted, host, port, path, and search properties', function () { | ||
var httpRequest = new HttpRequest('GET http://foo:bar@localhost:3000/yes/?i=am'); | ||
expect(httpRequest.headers.getAll('Authorization'), 'to equal', [ 'Basic Zm9vOmJhcg==' ]); | ||
expect(httpRequest, 'to have property', 'encrypted', false); | ||
httpRequest.url = 'http://baz:quux@localhost:9876/no/not/?so=much'; | ||
expect(httpRequest.url, 'to equal', 'http://baz:quux@localhost:9876/no/not/?so=much'); | ||
expect(httpRequest.headers.getAll('Authorization'), 'to equal', [ 'Basic YmF6OnF1dXg=' ]); | ||
expect(httpRequest, 'to have property', 'encrypted', false); | ||
}); | ||
it('should throw if different hosts are passed in the host property and in the url', function () { | ||
expect( | ||
function () { | ||
// eslint-disable-next-line no-new | ||
new HttpRequest({ | ||
url: 'GET http://blabla.com:3000/', | ||
host: 'localhost', | ||
}); | ||
}, | ||
'to throw', | ||
'the host property and the url specify different hosts, localhost vs. blabla.com' | ||
); | ||
}); | ||
it('should remove an existing Authorization header if the new url does not have credentials', function () { | ||
var httpRequest = new HttpRequest('GET http://foo:bar@localhost:3000/yes/?i=am'); | ||
httpRequest.url = 'http://localhost:9876/no/not/?so=much'; | ||
expect(httpRequest.headers.getAll('Authorization'), 'to equal', undefined); | ||
}); | ||
}); | ||
it('should allow passing the same port in the port property and in the url', function () { | ||
const httpRequest = new HttpRequest({ | ||
url: 'GET http://localhost:3000/', | ||
port: 3000, | ||
}); | ||
expect(httpRequest.port, 'to equal', 3000); | ||
}); | ||
describe('with a url passed in the request line', function () { | ||
it('should support a localhost url', function () { | ||
var httpRequest = new HttpRequest('GET http://localhost:3000/'); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'localhost:3000'); | ||
expect(httpRequest.host, 'to equal', 'localhost'); | ||
expect(httpRequest.port, 'to equal', 3000); | ||
}); | ||
it('should throw if different ports are passed in the port property and in the url', function () { | ||
expect( | ||
function () { | ||
// eslint-disable-next-line no-new | ||
new HttpRequest({ url: 'GET http://blabla.com:3000/', port: 3020 }); | ||
}, | ||
'to throw', | ||
'the port property and the url specify different ports, 3020 vs. 3000' | ||
); | ||
}); | ||
it('should allow passing the same host in the host property and in the url', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET http://localhost:3000/', host: 'localhost' }); | ||
expect(httpRequest.host, 'to equal', 'localhost'); | ||
}); | ||
it('should support a url with an IP address', function () { | ||
const httpRequest = new HttpRequest('GET http://99.88.77.66/'); | ||
expect(httpRequest.headers.get('Host'), 'to equal', '99.88.77.66'); | ||
expect(httpRequest.host, 'to equal', '99.88.77.66'); | ||
}); | ||
it('should throw if different hosts are passed in the host property and in the url', function () { | ||
expect(function () { | ||
new HttpRequest({ url: 'GET http://blabla.com:3000/', host: 'localhost' }); | ||
}, 'to throw', 'the host property and the url specify different hosts, localhost vs. blabla.com'); | ||
}); | ||
it('should set the Host header', function () { | ||
const httpRequest = new HttpRequest('GET http://foo.com/'); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'foo.com'); | ||
}); | ||
it('should allow passing the same port in the port property and in the url', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET http://localhost:3000/', port: 3000 }); | ||
expect(httpRequest.port, 'to equal', 3000); | ||
}); | ||
it('should set the host property', function () { | ||
expect( | ||
new HttpRequest('GET http://foo.com/').host, | ||
'to equal', | ||
'foo.com' | ||
); | ||
}); | ||
it('should throw if different ports are passed in the port property and in the url', function () { | ||
expect(function () { | ||
new HttpRequest({ url: 'GET http://blabla.com:3000/', port: 3020 }); | ||
}, 'to throw', 'the port property and the url specify different ports, 3020 vs. 3000'); | ||
}); | ||
it('should set the port property when explicitly given', function () { | ||
expect(new HttpRequest('GET http://foo.com:987/').port, 'to equal', 987); | ||
}); | ||
it('should support a url with an IP address', function () { | ||
var httpRequest = new HttpRequest('GET http://99.88.77.66/'); | ||
expect(httpRequest.headers.get('Host'), 'to equal', '99.88.77.66'); | ||
expect(httpRequest.host, 'to equal', '99.88.77.66'); | ||
}); | ||
it('should set the port property to 80 when http', function () { | ||
expect(new HttpRequest('GET http://foo.com/').port, 'to equal', 80); | ||
}); | ||
it('should set the Host header', function () { | ||
var httpRequest = new HttpRequest('GET http://foo.com/'); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'foo.com'); | ||
}); | ||
it('should set the port property to 443 when https', function () { | ||
expect(new HttpRequest('GET https://foo.com/').port, 'to equal', 443); | ||
}); | ||
it('should set the host property', function () { | ||
expect(new HttpRequest('GET http://foo.com/').host, 'to equal', 'foo.com'); | ||
}); | ||
it('should set the path', function () { | ||
expect( | ||
new HttpRequest('GET http://foo.com/heythere/you').path, | ||
'to equal', | ||
'/heythere/you' | ||
); | ||
}); | ||
it('should set the port property when explicitly given', function () { | ||
expect(new HttpRequest('GET http://foo.com:987/').port, 'to equal', 987); | ||
}); | ||
it('should set the Host header and include the port if given', function () { | ||
expect( | ||
new HttpRequest('GET http://foo.com:987/').headers.get('Host'), | ||
'to equal', | ||
'foo.com:987' | ||
); | ||
}); | ||
it('should set the port property to 80 when http', function () { | ||
expect(new HttpRequest('GET http://foo.com/').port, 'to equal', 80); | ||
}); | ||
it('should not overwrite an explicit Host header', function () { | ||
expect( | ||
new HttpRequest('GET http://foo.com/\r\nHost: bar.com').headers.getAll( | ||
'Host' | ||
), | ||
'to equal', | ||
['bar.com'] | ||
); | ||
}); | ||
it('should set the port property to 443 when https', function () { | ||
expect(new HttpRequest('GET https://foo.com/').port, 'to equal', 443); | ||
}); | ||
it('should still set the host property when there is an explicit Host header', function () { | ||
expect( | ||
new HttpRequest('GET http://foo.com/\r\nHost: bar.com'), | ||
'to have property', | ||
'host', | ||
'foo.com' | ||
); | ||
}); | ||
it('should set the path', function () { | ||
expect(new HttpRequest('GET http://foo.com/heythere/you').path, 'to equal', '/heythere/you'); | ||
}); | ||
it('should set the "encrypted" property if the protocol is https', function () { | ||
expect(new HttpRequest('GET https://foo.com/'), 'to satisfy', { | ||
encrypted: true, | ||
}); | ||
}); | ||
it('should set the Host header and include the port if given', function () { | ||
expect(new HttpRequest('GET http://foo.com:987/').headers.get('Host'), 'to equal', 'foo.com:987'); | ||
}); | ||
it('should not set the "encrypted" property if the protocol is http', function () { | ||
expect(new HttpRequest('GET http://foo.com/'), 'to satisfy', { | ||
encrypted: expect.it('to be falsy'), | ||
}); | ||
}); | ||
it('should not overwrite an explicit Host header', function () { | ||
expect(new HttpRequest('GET http://foo.com/\r\nHost: bar.com').headers.getAll('Host'), 'to equal', [ 'bar.com' ]); | ||
}); | ||
describe('with credentials passed in the url', function () { | ||
it('should set the Authorization header', function () { | ||
expect( | ||
new HttpRequest('GET https://foo:bar@foo.com/').headers.get( | ||
'Authorization' | ||
), | ||
'to equal', | ||
'Basic Zm9vOmJhcg==' | ||
); | ||
}); | ||
it('should still set the host property when there is an explicit Host header', function () { | ||
expect(new HttpRequest('GET http://foo.com/\r\nHost: bar.com'), 'to have property', 'host', 'foo.com'); | ||
}); | ||
it.skip('should not overwrite an existing Authorization header', function () { | ||
expect( | ||
new HttpRequest({ | ||
url: 'GET https://foo:bar@foo.com/', | ||
headers: { | ||
Authorization: 'foobar', | ||
}, | ||
}).headers.get('Authorization'), | ||
'to equal', | ||
'foobar' | ||
); | ||
}); | ||
it('should set the "encrypted" property if the protocol is https', function () { | ||
expect(new HttpRequest('GET https://foo.com/'), 'to satisfy', { encrypted: true }); | ||
}); | ||
it('should support percent-encoded octets, including colons, and a non-encoded colon in the password', function () { | ||
expect( | ||
new HttpRequest( | ||
'GET http://fo%C3%A6o%25bar:baz%25quux:yadda@localhost:4232/' | ||
).headers.get('Authorization'), | ||
'to equal', | ||
'Basic Zm/Dpm8lYmFyOmJheiVxdXV4OnlhZGRh' | ||
); | ||
}); | ||
it('should not set the "encrypted" property if the protocol is http', function () { | ||
expect(new HttpRequest('GET http://foo.com/'), 'to satisfy', { encrypted: expect.it('to be falsy') }); | ||
}); | ||
it('should leave all percent encoded octets in the username if one of them does not decode as UTF-8', function () { | ||
expect( | ||
new HttpRequest( | ||
'http://fo%C3%A6o%25bar%C3:baz%C3%A6quux:yadda@localhost:4232/' | ||
).headers.get('Authorization'), | ||
'to equal', | ||
'Basic Zm8lQzMlQTZvJTI1YmFyJUMzOmJhesOmcXV1eDp5YWRkYQ==' | ||
); | ||
}); | ||
}); | ||
}); | ||
describe('with credentials passed in the url', function () { | ||
it('should set the Authorization header', function () { | ||
expect( | ||
new HttpRequest('GET https://foo:bar@foo.com/').headers.get('Authorization'), | ||
'to equal', | ||
'Basic Zm9vOmJhcg==' | ||
); | ||
}); | ||
describe('with a url passed in an options object', function () { | ||
it('should set the Host header', function () { | ||
const httpRequest = new HttpRequest({ url: 'GET http://foo.com/' }); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'foo.com'); | ||
}); | ||
it.skip('should not overwrite an existing Authorization header', function () { | ||
expect( | ||
new HttpRequest({ | ||
url: 'GET https://foo:bar@foo.com/', | ||
headers: { | ||
Authorization: 'foobar' | ||
} | ||
}).headers.get('Authorization'), | ||
'to equal', | ||
'foobar' | ||
); | ||
}); | ||
it('should set the host property', function () { | ||
expect( | ||
new HttpRequest({ url: 'GET http://foo.com/' }).host, | ||
'to equal', | ||
'foo.com' | ||
); | ||
}); | ||
it('should support percent-encoded octets, including colons, and a non-encoded colon in the password', function () { | ||
expect( | ||
new HttpRequest('GET http://fo%C3%A6o%25bar:baz%25quux:yadda@localhost:4232/').headers.get('Authorization'), | ||
'to equal', | ||
'Basic Zm/Dpm8lYmFyOmJheiVxdXV4OnlhZGRh' | ||
); | ||
}); | ||
it('should set the port property when explicitly given', function () { | ||
expect( | ||
new HttpRequest({ url: 'GET http://foo.com:987/' }).port, | ||
'to equal', | ||
987 | ||
); | ||
}); | ||
it('should leave all percent encoded octets in the username if one of them does not decode as UTF-8', function () { | ||
expect( | ||
new HttpRequest('http://fo%C3%A6o%25bar%C3:baz%C3%A6quux:yadda@localhost:4232/').headers.get('Authorization'), | ||
'to equal', | ||
'Basic Zm8lQzMlQTZvJTI1YmFyJUMzOmJhesOmcXV1eDp5YWRkYQ==' | ||
); | ||
}); | ||
}); | ||
it('should set the port property to 80 when http', function () { | ||
expect( | ||
new HttpRequest({ url: 'GET http://foo.com/' }).port, | ||
'to equal', | ||
80 | ||
); | ||
}); | ||
describe('with a url passed in an options object', function () { | ||
it('should set the Host header', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET http://foo.com/' }); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'foo.com'); | ||
}); | ||
it('should set the port property to 443 when https', function () { | ||
expect( | ||
new HttpRequest({ url: 'GET https://foo.com/' }).port, | ||
'to equal', | ||
443 | ||
); | ||
}); | ||
it('should set the host property', function () { | ||
expect(new HttpRequest({ url: 'GET http://foo.com/' }).host, 'to equal', 'foo.com'); | ||
}); | ||
it('should set the path', function () { | ||
expect( | ||
new HttpRequest({ url: 'GET http://foo.com/heythere/you' }).path, | ||
'to equal', | ||
'/heythere/you' | ||
); | ||
}); | ||
it('should set the port property when explicitly given', function () { | ||
expect(new HttpRequest({ url: 'GET http://foo.com:987/' }).port, 'to equal', 987); | ||
}); | ||
it('should set the Host header and include the port if given', function () { | ||
const httpRequest = new HttpRequest({ url: 'GET http://foo.com:987/' }); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'foo.com:987'); | ||
}); | ||
it('should set the port property to 80 when http', function () { | ||
expect(new HttpRequest({ url: 'GET http://foo.com/' }).port, 'to equal', 80); | ||
}); | ||
it('should not overwrite an explicit Host header', function () { | ||
const httpRequest = new HttpRequest({ | ||
url: 'GET http://foo.com/', | ||
headers: { Host: 'bar.com' }, | ||
}); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'bar.com'); | ||
}); | ||
it('should set the port property to 443 when https', function () { | ||
expect(new HttpRequest({ url: 'GET https://foo.com/' }).port, 'to equal', 443); | ||
}); | ||
it('should set the "encrypted" property if the protocol is https', function () { | ||
expect(new HttpRequest({ url: 'GET https://foo.com/' }), 'to satisfy', { | ||
encrypted: true, | ||
}); | ||
}); | ||
it('should set the path', function () { | ||
expect(new HttpRequest({ url: 'GET http://foo.com/heythere/you' }).path, 'to equal', '/heythere/you'); | ||
}); | ||
it('should not set the "encrypted" property if the protocol is http', function () { | ||
expect(new HttpRequest({ url: 'GET http://foo.com/' }), 'to satisfy', { | ||
encrypted: expect.it('to be falsy'), | ||
}); | ||
}); | ||
it('should set the Host header and include the port if given', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET http://foo.com:987/' }); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'foo.com:987'); | ||
}); | ||
it('should set the Authorization header if credentials are passed in the url', function () { | ||
const httpRequest = new HttpRequest({ | ||
url: 'GET https://foo:bar@foo.com/', | ||
}); | ||
expect( | ||
httpRequest.headers.get('Authorization'), | ||
'to equal', | ||
'Basic Zm9vOmJhcg==' | ||
); | ||
}); | ||
it('should not overwrite an explicit Host header', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET http://foo.com/', headers: { Host: 'bar.com' } }); | ||
expect(httpRequest.headers.get('Host'), 'to equal', 'bar.com'); | ||
}); | ||
it('should set the "encrypted" property if the protocol is https', function () { | ||
expect(new HttpRequest({ url: 'GET https://foo.com/' }), 'to satisfy', { encrypted: true }); | ||
}); | ||
it('should not set the "encrypted" property if the protocol is http', function () { | ||
expect(new HttpRequest({ url: 'GET http://foo.com/' }), 'to satisfy', { encrypted: expect.it('to be falsy') }); | ||
}); | ||
it('should set the Authorization header if credentials are passed in the url', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET https://foo:bar@foo.com/' }); | ||
expect(httpRequest.headers.get('Authorization'), 'to equal', 'Basic Zm9vOmJhcg=='); | ||
}); | ||
it('should keep the Authorization header when no credentials are passed in the url', function () { | ||
var httpRequest = new HttpRequest({ url: 'GET http://localhost:36033/', headers: { Authorization: 'foobar' } } ); | ||
expect(httpRequest.headers.get('Authorization'), 'to equal', 'foobar'); | ||
}); | ||
it('should keep the Authorization header when no credentials are passed in the url', function () { | ||
const httpRequest = new HttpRequest({ | ||
url: 'GET http://localhost:36033/', | ||
headers: { Authorization: 'foobar' }, | ||
}); | ||
expect(httpRequest.headers.get('Authorization'), 'to equal', 'foobar'); | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should include the messy.Message properties and put the requestLine properties at the top level', function () { | ||
expect(new HttpRequest('GET / HTTP/1.1\r\nFoo: bar\r\n\r\nblabla').toJSON(), 'to equal', { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'bar' | ||
}, | ||
rawBody: 'blabla' | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should include the messy.Message properties and put the requestLine properties at the top level', function () { | ||
expect( | ||
new HttpRequest('GET / HTTP/1.1\r\nFoo: bar\r\n\r\nblabla').toJSON(), | ||
'to equal', | ||
{ | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
headers: { | ||
Foo: 'bar', | ||
}, | ||
rawBody: 'blabla', | ||
} | ||
); | ||
}); | ||
}); | ||
}); |
@@ -1,178 +0,251 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
HttpResponse = require('../lib/HttpResponse'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const HttpResponse = require('../lib/HttpResponse'); | ||
describe('HttpResponse', function () { | ||
it('should complain when receiving an unsupported property', function () { | ||
expect(function () { | ||
new HttpResponse({ contentType: 'text/css' }); | ||
}, 'to throw', 'messy.Message: Unsupported property name: contentType'); | ||
}); | ||
it('should complain when receiving an unsupported property', function () { | ||
expect( | ||
function () { | ||
// eslint-disable-next-line no-new | ||
new HttpResponse({ contentType: 'text/css' }); | ||
}, | ||
'to throw', | ||
'messy.Message: Unsupported property name: contentType' | ||
); | ||
}); | ||
it('should parse a standalone status line', function () { | ||
var httpResponse = new HttpResponse('HTTP/1.1 200 OK'); | ||
expect(httpResponse.protocol, 'to equal', 'HTTP/1.1'); | ||
expect(httpResponse.statusCode, 'to equal', 200); | ||
expect(httpResponse.statusMessage, 'to equal', 'OK'); | ||
expect(httpResponse.toString(), 'to equal', 'HTTP/1.1 200 OK\r\n'); | ||
}); | ||
it('should parse a standalone status line', function () { | ||
const httpResponse = new HttpResponse('HTTP/1.1 200 OK'); | ||
expect(httpResponse.protocol, 'to equal', 'HTTP/1.1'); | ||
expect(httpResponse.statusCode, 'to equal', 200); | ||
expect(httpResponse.statusMessage, 'to equal', 'OK'); | ||
expect(httpResponse.toString(), 'to equal', 'HTTP/1.1 200 OK\r\n'); | ||
}); | ||
it('should parse a status line with more than one word in the status message', function () { | ||
var httpResponse = new HttpResponse('HTTP/1.1 412 Precondition Failed'); | ||
expect(httpResponse.protocol, 'to equal', 'HTTP/1.1'); | ||
expect(httpResponse.statusCode, 'to equal', 412); | ||
expect(httpResponse.statusMessage, 'to equal', 'Precondition Failed'); | ||
expect(httpResponse.toString(), 'to equal', 'HTTP/1.1 412 Precondition Failed\r\n'); | ||
}); | ||
it('should parse a status line with more than one word in the status message', function () { | ||
const httpResponse = new HttpResponse('HTTP/1.1 412 Precondition Failed'); | ||
expect(httpResponse.protocol, 'to equal', 'HTTP/1.1'); | ||
expect(httpResponse.statusCode, 'to equal', 412); | ||
expect(httpResponse.statusMessage, 'to equal', 'Precondition Failed'); | ||
expect( | ||
httpResponse.toString(), | ||
'to equal', | ||
'HTTP/1.1 412 Precondition Failed\r\n' | ||
); | ||
}); | ||
it('should parse a status line followed by headers', function () { | ||
var httpResponse = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'); | ||
expect(httpResponse.statusCode, 'to equal', 200); | ||
expect(httpResponse.toString(), 'to equal', 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n'); | ||
}); | ||
it('should parse a status line followed by headers', function () { | ||
const httpResponse = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n' | ||
); | ||
expect(httpResponse.statusCode, 'to equal', 200); | ||
expect( | ||
httpResponse.toString(), | ||
'to equal', | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n' | ||
); | ||
}); | ||
it('should parse a status line followed by headers and a body', function () { | ||
var httpResponse = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'); | ||
expect(httpResponse.statusCode, 'to equal', 200); | ||
expect(httpResponse.body, 'to equal', 'blah'); | ||
expect(httpResponse.toString(), 'to equal', 'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'); | ||
}); | ||
it('should parse a status line followed by headers and a body', function () { | ||
const httpResponse = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
expect(httpResponse.statusCode, 'to equal', 200); | ||
expect(httpResponse.body, 'to equal', 'blah'); | ||
expect( | ||
httpResponse.toString(), | ||
'to equal', | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
}); | ||
it('should accept the status line as an option to the constructor', function () { | ||
expect(new HttpResponse({statusLine: 'HTTP/1.1 200 OK'}), 'to have properties', { | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK' | ||
}); | ||
}); | ||
it('should accept the status line as an option to the constructor', function () { | ||
expect( | ||
new HttpResponse({ statusLine: 'HTTP/1.1 200 OK' }), | ||
'to have properties', | ||
{ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
} | ||
); | ||
}); | ||
it('should accept a number and interpret it as the status code', function () { | ||
expect(new HttpResponse(404), 'to have properties', { | ||
statusCode: 404 | ||
}); | ||
it('should accept a number and interpret it as the status code', function () { | ||
expect(new HttpResponse(404), 'to have properties', { | ||
statusCode: 404, | ||
}); | ||
}); | ||
it('should parse a partial status line', function () { | ||
expect(new HttpResponse('HTTP/1.1 200'), 'to have properties', { | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200 | ||
}); | ||
it('should parse a partial status line', function () { | ||
expect(new HttpResponse('HTTP/1.1 200'), 'to have properties', { | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
}); | ||
}); | ||
it('should only include CRLFCRLF when there are no headers', function () { | ||
expect(new HttpResponse({ | ||
statusLine: 'HTTP/1.1 200 OK', | ||
body: 'foo' | ||
}).toString(), 'to equal', 'HTTP/1.1 200 OK\r\n\r\nfoo'); | ||
}); | ||
it('should only include CRLFCRLF when there are no headers', function () { | ||
expect( | ||
new HttpResponse({ | ||
statusLine: 'HTTP/1.1 200 OK', | ||
body: 'foo', | ||
}).toString(), | ||
'to equal', | ||
'HTTP/1.1 200 OK\r\n\r\nfoo' | ||
); | ||
}); | ||
it('should expose the StatusLine instance', function () { | ||
expect(new HttpResponse({ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK' | ||
}).statusLine.toString(), 'to equal', 'HTTP/1.1 200 OK'); | ||
}); | ||
it('should expose the StatusLine instance', function () { | ||
expect( | ||
new HttpResponse({ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
}).statusLine.toString(), | ||
'to equal', | ||
'HTTP/1.1 200 OK' | ||
); | ||
}); | ||
it('should allow updating the status line', function () { | ||
var httpResponse = new HttpResponse({ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK' | ||
}); | ||
httpResponse.statusLine.populateFromString('HTTP/1.0 400 Bad Request'); | ||
expect(httpResponse, 'to have properties', { | ||
protocol: 'HTTP/1.0', | ||
statusCode: 400, | ||
statusMessage: 'Bad Request' | ||
}); | ||
it('should allow updating the status line', function () { | ||
const httpResponse = new HttpResponse({ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
}); | ||
it('should make the protocol version available as a getter', function () { | ||
expect(new HttpResponse('HTTP/1.1 200 OK').protocolVersion, 'to equal', '1.1'); | ||
httpResponse.statusLine.populateFromString('HTTP/1.0 400 Bad Request'); | ||
expect(httpResponse, 'to have properties', { | ||
protocol: 'HTTP/1.0', | ||
statusCode: 400, | ||
statusMessage: 'Bad Request', | ||
}); | ||
}); | ||
it('should make the protocol name available as a getter', function () { | ||
expect(new HttpResponse('HTTP/1.0 200 OK').protocolName, 'to equal', 'HTTP'); | ||
}); | ||
it('should make the protocol version available as a getter', function () { | ||
expect( | ||
new HttpResponse('HTTP/1.1 200 OK').protocolVersion, | ||
'to equal', | ||
'1.1' | ||
); | ||
}); | ||
it('should accept the individual status line properties as options to the constructor', function () { | ||
expect(new HttpResponse({ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK' | ||
}), 'to have properties', { | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK' | ||
}); | ||
}); | ||
it('should make the protocol name available as a getter', function () { | ||
expect( | ||
new HttpResponse('HTTP/1.0 200 OK').protocolName, | ||
'to equal', | ||
'HTTP' | ||
); | ||
}); | ||
it('should consider an identical instance equal', function () { | ||
var httpResponse1 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'), | ||
httpResponse2 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'); | ||
expect(httpResponse1.equals(httpResponse2), 'to be true'); | ||
}); | ||
it('should accept the individual status line properties as options to the constructor', function () { | ||
expect( | ||
new HttpResponse({ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
}), | ||
'to have properties', | ||
{ | ||
protocol: 'HTTP/1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
} | ||
); | ||
}); | ||
it('should consider two instances unequal if they differ by protocol', function () { | ||
var httpResponse1 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'), | ||
httpResponse2 = new HttpResponse('HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\nblah'); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider an identical instance equal', function () { | ||
const httpResponse1 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
const httpResponse2 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
expect(httpResponse1.equals(httpResponse2), 'to be true'); | ||
}); | ||
it('should consider two instances unequal if they differ by status code', function () { | ||
var httpResponse1 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'), | ||
httpResponse2 = new HttpResponse('HTTP/1.1 400 OK\r\nContent-Type: text/html\r\n\r\nblah'); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by protocol', function () { | ||
const httpResponse1 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
const httpResponse2 = new HttpResponse( | ||
'HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by status message', function () { | ||
var httpResponse1 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'), | ||
httpResponse2 = new HttpResponse('HTTP/1.1 200 KO\r\nContent-Type: text/html\r\n\r\nblah'); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by status code', function () { | ||
const httpResponse1 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
const httpResponse2 = new HttpResponse( | ||
'HTTP/1.1 400 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by status message', function () { | ||
var httpResponse1 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'), | ||
httpResponse2 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nblah'); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by status message', function () { | ||
const httpResponse1 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
const httpResponse2 = new HttpResponse( | ||
'HTTP/1.1 200 KO\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by status message', function () { | ||
var httpResponse1 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah'), | ||
httpResponse2 = new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nquux'); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should consider two instances unequal if they differ by Content-Type', function () { | ||
const httpResponse1 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
const httpResponse2 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nblah' | ||
); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
it('should parse a buffer', function () { | ||
var rawSrc = | ||
'HTTP/1.1 200 OK\r\n' + | ||
'X-Powered-By: Express\r\n' + | ||
'Content-Type: text/plain\r\n' + | ||
'Content-Length: 4\r\n' + | ||
'ETag: "-836148900"\r\n' + | ||
'Date: Sat, 21 Mar 2015 00:25:45 GMT\r\n' + | ||
'Connection: keep-alive\r\n' + | ||
'\r\n' + | ||
'blah'; | ||
it('should consider two instances unequal if they differ by body', function () { | ||
const httpResponse1 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\nblah' | ||
); | ||
const httpResponse2 = new HttpResponse( | ||
'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nquux' | ||
); | ||
expect(httpResponse1.equals(httpResponse2), 'to be false'); | ||
}); | ||
var httpResponse = new HttpResponse(new Buffer(rawSrc, 'ascii')); | ||
it('should parse a buffer', function () { | ||
const rawSrc = | ||
'HTTP/1.1 200 OK\r\n' + | ||
'X-Powered-By: Express\r\n' + | ||
'Content-Type: text/plain\r\n' + | ||
'Content-Length: 4\r\n' + | ||
'ETag: "-836148900"\r\n' + | ||
'Date: Sat, 21 Mar 2015 00:25:45 GMT\r\n' + | ||
'Connection: keep-alive\r\n' + | ||
'\r\n' + | ||
'blah'; | ||
expect(httpResponse.toString(), 'to equal', rawSrc); | ||
}); | ||
const httpResponse = new HttpResponse(Buffer.from(rawSrc, 'ascii')); | ||
describe('#toJSON', function () { | ||
it('should include the messy.Message properties and put the statusLine properties at the top level', function () { | ||
expect(new HttpResponse('HTTP/1.1 200 OK\r\nFoo: bar\r\n\r\nblabla').toJSON(), 'to equal', { | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
headers: { | ||
Foo: 'bar' | ||
}, | ||
rawBody: 'blabla' | ||
}); | ||
}); | ||
expect(httpResponse.toString(), 'to equal', rawSrc); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should include the messy.Message properties and put the statusLine properties at the top level', function () { | ||
expect( | ||
new HttpResponse('HTTP/1.1 200 OK\r\nFoo: bar\r\n\r\nblabla').toJSON(), | ||
'to equal', | ||
{ | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
headers: { | ||
Foo: 'bar', | ||
}, | ||
rawBody: 'blabla', | ||
} | ||
); | ||
}); | ||
}); | ||
}); |
@@ -1,51 +0,57 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
Mail = require('../lib/Mail'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const Mail = require('../lib/Mail'); | ||
describe('Mail', function () { | ||
it('should rfc2047 decode the header values', function () { | ||
var mail = new Mail('Subject: =?iso-8859-1?Q?=A1?=Hola, se=?iso-8859-1?Q?=F1?=or!'); | ||
expect(mail.headers.get('subject'), 'to equal', '¡Hola, señor!'); | ||
}); | ||
it('should rfc2047 decode the header values', function () { | ||
const mail = new Mail( | ||
'Subject: =?iso-8859-1?Q?=A1?=Hola, se=?iso-8859-1?Q?=F1?=or!' | ||
); | ||
expect(mail.headers.get('subject'), 'to equal', '¡Hola, señor!'); | ||
}); | ||
it('should rfc2047 encode when serializing', function () { | ||
var mail = new Mail({body: 'bar'}); | ||
mail.headers.set('subject', '¡Hola, señor!'); | ||
expect(mail.toString(), 'to equal', 'Subject: =?utf-8?Q?=C2=A1Hola=2C?= =?utf-8?Q?_se=C3=B1or!?=\r\n\r\nbar'); | ||
}); | ||
it('should rfc2047 encode when serializing', function () { | ||
const mail = new Mail({ body: 'bar' }); | ||
mail.headers.set('subject', '¡Hola, señor!'); | ||
expect( | ||
mail.toString(), | ||
'to equal', | ||
'Subject: =?utf-8?Q?=C2=A1Hola=2C?= =?utf-8?Q?_se=C3=B1or!?=\r\n\r\nbar' | ||
); | ||
}); | ||
describe('#fileName', function () { | ||
describe('when invoked as a getter', function () { | ||
it('should fall back to the name property of the Content-Type header when the Content-Disposition header has no filename parameter', function () { | ||
var mail = new Mail( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Disposition: attachment\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(mail.fileName, 'to equal', 'æøå.png'); | ||
}); | ||
describe('#fileName', function () { | ||
describe('when invoked as a getter', function () { | ||
it('should fall back to the name property of the Content-Type header when the Content-Disposition header has no filename parameter', function () { | ||
const mail = new Mail( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Disposition: attachment\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(mail.fileName, 'to equal', 'æøå.png'); | ||
}); | ||
it('should fall back to the name property of the Content-Type header when there is no Content-Disposition header', function () { | ||
var mail = new Mail( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(mail.fileName, 'to equal', 'æøå.png'); | ||
}); | ||
}); | ||
it('should fall back to the name property of the Content-Type header when there is no Content-Disposition header', function () { | ||
const mail = new Mail( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(mail.fileName, 'to equal', 'æøå.png'); | ||
}); | ||
}); | ||
describe('when invoked as a setter', function () { | ||
it('should update the name property of the Content-Type header if available', function () { | ||
var mail = new Mail({headers: {'Content-Type': 'image/png'}}); | ||
mail.fileName = 'æøå.png'; | ||
expect( | ||
mail.toString(), | ||
'to equal', | ||
// TODO: Would be better to emit 'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="\r\n' + | ||
'Content-Type: image/png; =?utf-8?Q?name=3D=C3=A6=C3=B8=C3=A5?=.png\r\n' + | ||
"Content-Disposition: attachment; filename*=iso-8859-1''%E6%F8%E5.png\r\n" | ||
); | ||
}); | ||
}); | ||
describe('when invoked as a setter', function () { | ||
it('should update the name property of the Content-Type header if available', function () { | ||
const mail = new Mail({ headers: { 'Content-Type': 'image/png' } }); | ||
mail.fileName = 'æøå.png'; | ||
expect( | ||
mail.toString(), | ||
'to equal', | ||
// TODO: Would be better to emit 'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="\r\n' + | ||
'Content-Type: image/png; =?utf-8?Q?name=3D=C3=A6=C3=B8=C3=A5?=.png\r\n' + | ||
"Content-Disposition: attachment; filename*=iso-8859-1''%E6%F8%E5.png\r\n" | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
2300
test/Message.js
@@ -1,1081 +0,1491 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
Message = require('../lib/Message'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const Message = require('../lib/Message'); | ||
it.skipIf = function (condition) { | ||
(condition ? it.skip : it).apply(it, Array.prototype.slice.call(arguments, 1)); | ||
(condition ? it.skip : it).apply( | ||
it, | ||
Array.prototype.slice.call(arguments, 1) | ||
); | ||
}; | ||
describe('Message', function () { | ||
it('should accept an options object with headers and body', function () { | ||
var message = new Message({ | ||
headers: { | ||
Received: ['foo', 'bar'], | ||
Subject: 'hey' | ||
}, | ||
body: 'abc' | ||
}); | ||
expect(message.body, 'to equal', 'abc'); | ||
expect(message.headers.getAll('received'), 'to equal', ['foo', 'bar']); | ||
expect(message.headers.getAll('subject'), 'to equal', ['hey']); | ||
expect(message.toString(), 'to equal', 'Received: foo\r\nReceived: bar\r\nSubject: hey\r\n\r\nabc'); | ||
it('should accept an options object with headers and body', function () { | ||
const message = new Message({ | ||
headers: { | ||
Received: ['foo', 'bar'], | ||
Subject: 'hey', | ||
}, | ||
body: 'abc', | ||
}); | ||
expect(message.body, 'to equal', 'abc'); | ||
expect(message.headers.getAll('received'), 'to equal', ['foo', 'bar']); | ||
expect(message.headers.getAll('subject'), 'to equal', ['hey']); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Received: foo\r\nReceived: bar\r\nSubject: hey\r\n\r\nabc' | ||
); | ||
}); | ||
it('should complain when receiving an unsupported property', function () { | ||
expect(function () { | ||
new Message({ contentType: 'text/css' }); | ||
}, 'to throw', 'messy.Message: Unsupported property name: contentType'); | ||
}); | ||
it('should complain when receiving an unsupported property', function () { | ||
expect( | ||
function () { | ||
// eslint-disable-next-line no-new | ||
new Message({ contentType: 'text/css' }); | ||
}, | ||
'to throw', | ||
'messy.Message: Unsupported property name: contentType' | ||
); | ||
}); | ||
it('should parse the headers from the input', function () { | ||
var message = new Message('From: thisguy@example.com\r\nTo: thisotherguy@example.com'); | ||
expect(message.headers.getNames(), 'to equal', ['From', 'To']); | ||
expect(message.headers.getNamesLowerCase(), 'to equal', ['from', 'to']); | ||
}); | ||
it('should parse the headers from the input', function () { | ||
const message = new Message( | ||
'From: thisguy@example.com\r\nTo: thisotherguy@example.com' | ||
); | ||
expect(message.headers.getNames(), 'to equal', ['From', 'To']); | ||
expect(message.headers.getNamesLowerCase(), 'to equal', ['from', 'to']); | ||
}); | ||
it('should support reading a Buffer instance with iso-8859-1 chars', function () { | ||
var buffer = Buffer.concat([new Buffer('From: ', 'ascii'), new Buffer([0xf8]), new Buffer('foobarquux', 'ascii')]); | ||
expect(new Message(buffer).headers.get('From'), 'to equal', 'øfoobarquux'); | ||
}); | ||
it('should support reading a Buffer instance with iso-8859-1 chars', function () { | ||
const buffer = Buffer.concat([ | ||
Buffer.from('From: ', 'ascii'), | ||
Buffer.from([0xf8]), | ||
Buffer.from('foobarquux', 'ascii'), | ||
]); | ||
expect(new Message(buffer).headers.get('From'), 'to equal', 'øfoobarquux'); | ||
}); | ||
it('should handle folded lines when parsing', function () { | ||
var message = new Message('Subject: abc\r\n def'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc def'); | ||
}); | ||
it('should handle folded lines when parsing', function () { | ||
const message = new Message('Subject: abc\r\n def'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc def'); | ||
}); | ||
it('should handle folded lines with just CR when parsing', function () { | ||
var message = new Message('Subject: abc\r def'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc def'); | ||
}); | ||
it('should handle folded lines with just CR when parsing', function () { | ||
const message = new Message('Subject: abc\r def'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc def'); | ||
}); | ||
it('should handle folded lines with just LF when parsing', function () { | ||
var message = new Message('Subject: abc\n def'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc def'); | ||
}); | ||
it('should handle folded lines with just LF when parsing', function () { | ||
const message = new Message('Subject: abc\n def'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc def'); | ||
}); | ||
it('should handle folded lines + tabs when parsing', function () { | ||
// Observed on iPhone | ||
var message = new Message('Subject: abc\r\n\tdef'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc\tdef'); | ||
}); | ||
it('should handle folded lines + tabs when parsing', function () { | ||
// Observed on iPhone | ||
const message = new Message('Subject: abc\r\n\tdef'); | ||
expect(message.headers.get('subject'), 'to equal', 'abc\tdef'); | ||
}); | ||
it('should keep the buffer as a Buffer when the message is provided as a Buffer', function () { | ||
expect(new Message(Buffer.concat([ | ||
new Buffer( | ||
'From: foo@bar\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
), | ||
new Buffer([0xf8]) | ||
])), 'to have properties', { | ||
body: new Buffer([0xf8]) | ||
}); | ||
}); | ||
it('should keep the buffer as a Buffer when the message is provided as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.concat([ | ||
Buffer.from('From: foo@bar\r\n' + '\r\n', 'utf-8'), | ||
Buffer.from([0xf8]), | ||
]) | ||
), | ||
'to have properties', | ||
{ | ||
body: Buffer.from([0xf8]), | ||
} | ||
); | ||
}); | ||
it('should detect the end of the headers properly when separated by CRCR', function () { | ||
expect(new Message(Buffer.concat([ | ||
new Buffer('From: foo@bar\r\r', 'utf-8'), new Buffer([0xf8]) | ||
])), 'to have properties', { | ||
body: new Buffer([0xf8]) | ||
}); | ||
it('should detect the end of the headers properly when separated by CRCR', function () { | ||
expect( | ||
new Message( | ||
Buffer.concat([ | ||
Buffer.from('From: foo@bar\r\r', 'utf-8'), | ||
Buffer.from([0xf8]), | ||
]) | ||
), | ||
'to have properties', | ||
{ | ||
body: Buffer.from([0xf8]), | ||
} | ||
); | ||
expect(new Message(new Buffer('From: foo@bar\r\r', 'utf-8')), 'not to have property', 'body'); | ||
}); | ||
expect( | ||
new Message(Buffer.from('From: foo@bar\r\r', 'utf-8')), | ||
'not to have property', | ||
'body' | ||
); | ||
}); | ||
it('should detect the end of the headers properly when separated by LFLF', function () { | ||
expect(new Message(Buffer.concat([ | ||
new Buffer('From: foo@bar\n\n', 'utf-8'), new Buffer([0xf8]) | ||
])), 'to have properties', { | ||
body: new Buffer([0xf8]) | ||
}); | ||
it('should detect the end of the headers properly when separated by LFLF', function () { | ||
expect( | ||
new Message( | ||
Buffer.concat([ | ||
Buffer.from('From: foo@bar\n\n', 'utf-8'), | ||
Buffer.from([0xf8]), | ||
]) | ||
), | ||
'to have properties', | ||
{ | ||
body: Buffer.from([0xf8]), | ||
} | ||
); | ||
expect(new Message(new Buffer('From: foo@bar\n\n', 'utf-8')), 'not to have property', 'body'); | ||
}); | ||
expect( | ||
new Message(Buffer.from('From: foo@bar\n\n', 'utf-8')), | ||
'not to have property', | ||
'body' | ||
); | ||
}); | ||
it('should not read past CRLFCRLF when parsing', function () { | ||
var message = new Message('Subject: abc\r\n\r\nFrom: me'); | ||
expect(message.headers.get('from'), 'to be', undefined); | ||
}); | ||
it('should not read past CRLFCRLF when parsing', function () { | ||
const message = new Message('Subject: abc\r\n\r\nFrom: me'); | ||
expect(message.headers.get('from'), 'to be', undefined); | ||
}); | ||
it('should not read past CRCR when parsing', function () { | ||
var message = new Message('Subject: abc\r\rFrom: me'); | ||
expect(message.headers.get('from'), 'to be', undefined); | ||
}); | ||
it('should not read past CRCR when parsing', function () { | ||
const message = new Message('Subject: abc\r\rFrom: me'); | ||
expect(message.headers.get('from'), 'to be', undefined); | ||
}); | ||
it('should not read past LFLF when parsing', function () { | ||
var message = new Message('Subject: abc\r\rFrom: me'); | ||
expect(message.headers.get('from'), 'to be', undefined); | ||
}); | ||
it('should not read past LFLF when parsing', function () { | ||
const message = new Message('Subject: abc\r\rFrom: me'); | ||
expect(message.headers.get('from'), 'to be', undefined); | ||
}); | ||
it('should produce an empty string when handed an empty buffer', function () { | ||
expect(new Message(new Buffer(0)).toString(), 'to equal', ''); | ||
}); | ||
it('should produce an empty string when handed an empty buffer', function () { | ||
expect(new Message(Buffer.alloc(0)).toString(), 'to equal', ''); | ||
}); | ||
it('should parse Real Life message from Apple Mail', function () { | ||
var message = new Message(new Buffer([ | ||
"Content-Type: multipart/mixed;", | ||
"\tboundary=Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD", | ||
"Content-Transfer-Encoding: 7bit", | ||
"Subject: Foobar 123", | ||
"From: foo@example.com", | ||
"Message-Id: <D37F257C-4EEC-44F2-B279-690B00C4844B@example.com>", | ||
"Date: Wed, 22 May 2013 14:46:38 +0200", | ||
'To: "Unittest account" <foo@example.com>', | ||
"Mime-Version: 1.0 (1.0)", | ||
"", | ||
"", | ||
"--Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD", | ||
"..." | ||
].join('\r\n'), 'utf-8')); | ||
it('should parse Real Life message from Apple Mail', function () { | ||
const message = new Message( | ||
Buffer.from( | ||
[ | ||
'Content-Type: multipart/mixed;', | ||
'\tboundary=Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD', | ||
'Content-Transfer-Encoding: 7bit', | ||
'Subject: Foobar 123', | ||
'From: foo@example.com', | ||
'Message-Id: <D37F257C-4EEC-44F2-B279-690B00C4844B@example.com>', | ||
'Date: Wed, 22 May 2013 14:46:38 +0200', | ||
'To: "Unittest account" <foo@example.com>', | ||
'Mime-Version: 1.0 (1.0)', | ||
'', | ||
'', | ||
'--Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD', | ||
'...', | ||
].join('\r\n'), | ||
'utf-8' | ||
) | ||
); | ||
expect(message.headers.getNames(), 'to equal', ['Content-Type', 'Content-Transfer-Encoding', 'Subject', 'From', 'Message-Id', 'Date', 'To', 'Mime-Version']); | ||
expect(message.headers.getNamesLowerCase(), 'to equal', ['content-type', 'content-transfer-encoding', 'subject', 'from', 'message-id', 'date', 'to', 'mime-version']); | ||
expect(message.headers.get('Content-Type'), 'to equal', 'multipart/mixed;\tboundary=Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD'); | ||
expect(message.headers.get('content-transfer-encoding'), 'to equal', '7bit'); | ||
expect(message.headers.get('Subject'), 'to equal', 'Foobar 123'); | ||
expect(message.headers.getNames(), 'to equal', [ | ||
'Content-Type', | ||
'Content-Transfer-Encoding', | ||
'Subject', | ||
'From', | ||
'Message-Id', | ||
'Date', | ||
'To', | ||
'Mime-Version', | ||
]); | ||
expect(message.headers.getNamesLowerCase(), 'to equal', [ | ||
'content-type', | ||
'content-transfer-encoding', | ||
'subject', | ||
'from', | ||
'message-id', | ||
'date', | ||
'to', | ||
'mime-version', | ||
]); | ||
expect( | ||
message.headers.get('Content-Type'), | ||
'to equal', | ||
'multipart/mixed;\tboundary=Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD' | ||
); | ||
expect( | ||
message.headers.get('content-transfer-encoding'), | ||
'to equal', | ||
'7bit' | ||
); | ||
expect(message.headers.get('Subject'), 'to equal', 'Foobar 123'); | ||
expect(message.toString(), 'to contain', 'multipart\/mixed;\r\n\tboundary=Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD'); | ||
}); | ||
expect( | ||
message.toString(), | ||
'to contain', | ||
'multipart/mixed;\r\n\tboundary=Apple-Mail-589ECA5D-7F89-4C39-B7B7-7FD03E6333CD' | ||
); | ||
}); | ||
it('should preserve repeated headers', function () { | ||
var message = new Message('Received: foo\r\nReceived: bar\r\n'); | ||
it('should preserve repeated headers', function () { | ||
const message = new Message('Received: foo\r\nReceived: bar\r\n'); | ||
expect(message.toString(), 'to equal', 'Received: foo\r\nReceived: bar\r\n'); | ||
}); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Received: foo\r\nReceived: bar\r\n' | ||
); | ||
}); | ||
it('should preserve text after CRLFCRLF as-is', function () { | ||
const message = new Message('foo: bar\r\n\r\nthis is the:body'); | ||
it('should preserve text after CRLFCRLF as-is', function () { | ||
var message = new Message('foo: bar\r\n\r\nthis is the:body'); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
}); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
}); | ||
it('should preserve text after CRCR as-is', function () { | ||
const message = new Message('foo: bar\r\rthis is the:body'); | ||
it('should preserve text after CRCR as-is', function () { | ||
var message = new Message('foo: bar\r\rthis is the:body'); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
}); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
it('should preserve text after LFLF as-is', function () { | ||
const message = new Message('foo: bar\n\nthis is the:body'); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
}); | ||
it('should preserve text after LFCRLFCR as-is', function () { | ||
const message = new Message('foo: bar\n\r\n\rthis is the:body'); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
}); | ||
it('should read an iso-8859-1 body after LFCRLFCR into a buffer and turn it into REPLACEMENT CHARACTER U+FFFD when serializing as text', function () { | ||
const message = new Message( | ||
Buffer.concat([ | ||
Buffer.from('foo: bar\n\r\n\rthis is the:body', 'utf-8'), | ||
Buffer.from([0xf8, 0xe6]), | ||
]) | ||
); | ||
expect( | ||
message.body, | ||
'to equal', | ||
Buffer.concat([ | ||
Buffer.from('this is the:body', 'utf-8'), | ||
Buffer.from([0xf8, 0xe6]), | ||
]) | ||
); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Foo: bar\r\n\r\nthis is the:body\ufffd\ufffd' | ||
); | ||
}); | ||
it('should treat a body passed as a Buffer as the unchunked body', function () { | ||
const message = new Message({ body: Buffer.from([0xff]) }); | ||
expect(message._unchunkedBody, 'to equal', Buffer.from([0xff])); | ||
}); | ||
describe('#hasEmptyBody', function () { | ||
it('should consider a zero length Buffer to be an empty body', function () { | ||
expect( | ||
new Message({ body: Buffer.from([]) }).hasEmptyBody(), | ||
'to be true' | ||
); | ||
}); | ||
it('should preserve text after LFLF as-is', function () { | ||
var message = new Message('foo: bar\n\nthis is the:body'); | ||
it('should consider a non-zero length Buffer not to be an empty body', function () { | ||
expect( | ||
new Message({ body: Buffer.from([123]) }).hasEmptyBody(), | ||
'to be false' | ||
); | ||
}); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
it('should consider the empty string to be an empty body', function () { | ||
expect(new Message({ body: '' }).hasEmptyBody(), 'to be true'); | ||
}); | ||
it('should preserve text after LFCRLFCR as-is', function () { | ||
var message = new Message('foo: bar\n\r\n\rthis is the:body'); | ||
it('should consider a non-empty string not to be an empty body', function () { | ||
expect(new Message({ body: 'foo' }).hasEmptyBody(), 'to be false'); | ||
}); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body'); | ||
it('should consider undefined to be an empty body', function () { | ||
expect(new Message({ body: undefined }).hasEmptyBody(), 'to be true'); | ||
}); | ||
it('should read an iso-8859-1 body after LFCRLFCR into a buffer and turn it into REPLACEMENT CHARACTER U+FFFD when serializing as text', function () { | ||
var message = new Message(Buffer.concat([new Buffer('foo: bar\n\r\n\rthis is the:body', 'utf-8'), new Buffer([0xf8, 0xe6])])); | ||
expect(message.body, 'to equal', Buffer.concat([new Buffer('this is the:body', 'utf-8'), new Buffer([0xf8, 0xe6])])); | ||
expect(message.toString(), 'to equal', 'Foo: bar\r\n\r\nthis is the:body\ufffd\ufffd'); | ||
it('should consider an absent body to be empty', function () { | ||
expect(new Message().hasEmptyBody(), 'to be true'); | ||
}); | ||
it('should treat a body passed as a Buffer as the unchunked body', function () { | ||
var message = new Message({ body: new Buffer([0xff]) }); | ||
expect(message._unchunkedBody, 'to equal', new Buffer([0xff])); | ||
it('should consider an empty Object to be a non-empty body', function () { | ||
expect(new Message({ body: {} }).hasEmptyBody(), 'to be false'); | ||
}); | ||
}); | ||
describe('#hasEmptyBody', function () { | ||
it('should consider a zero length Buffer to be an empty body', function () { | ||
expect(new Message({body: new Buffer([])}).hasEmptyBody(), 'to be true'); | ||
}); | ||
describe('with a JSON body', function () { | ||
it('should provide a parsed body (and default to utf-8)', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: application/json\n\n{"foo":"æøå"}', | ||
'utf-8' | ||
) | ||
).body, | ||
'to equal', | ||
{ | ||
foo: 'æøå', | ||
} | ||
); | ||
}); | ||
it('should consider a non-zero length Buffer not to be an empty body', function () { | ||
expect(new Message({body: new Buffer([123])}).hasEmptyBody(), 'to be false'); | ||
}); | ||
it('should support a Content-Type that ends with +json', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: foobar+json; charset=UTF-8\n\n{"foo":"æøå"}', | ||
'utf-8' | ||
) | ||
).body, | ||
'to equal', | ||
{ | ||
foo: 'æøå', | ||
} | ||
); | ||
}); | ||
it('should consider the empty string to be an empty body', function () { | ||
expect(new Message({body: ''}).hasEmptyBody(), 'to be true'); | ||
}); | ||
it('should reserialize the body as utf-8', function () { | ||
const message = new Message( | ||
Buffer.from('Content-Type: application/json\n\n{"foo":"æøå"}', 'utf-8') | ||
); | ||
message.body = { foo: '☺' }; | ||
expect(message.rawBody, 'to equal', Buffer.from('{"foo":"☺"}', 'utf-8')); | ||
}); | ||
it('should consider a non-empty string not to be an empty body', function () { | ||
expect(new Message({body: 'foo'}).hasEmptyBody(), 'to be false'); | ||
}); | ||
describe('with an application/json body that does not parse as JSON', function () { | ||
const message = new Message({ | ||
headers: { 'Content-Type': 'application/json' }, | ||
unchunkedBody: '!!=!=', | ||
}); | ||
it('should consider undefined to be an empty body', function () { | ||
expect(new Message({body: undefined}).hasEmptyBody(), 'to be true'); | ||
}); | ||
it('should leave unchunkedBody as-is', function () { | ||
expect(message.unchunkedBody, 'to equal', '!!=!='); | ||
}); | ||
it('should consider an absent body to be empty', function () { | ||
expect(new Message().hasEmptyBody(), 'to be true'); | ||
}); | ||
it('should provide body as a string', function () { | ||
expect(message.body, 'to equal', '!!=!='); | ||
}); | ||
}); | ||
it('should consider an empty Object to be a non-empty body', function () { | ||
expect(new Message({body: {}}).hasEmptyBody(), 'to be false'); | ||
describe('when the body is specified as a string', function () { | ||
it('should interpret the string as already encoded JSON', function () { | ||
const msg = new Message({ | ||
headers: { 'Content-Type': 'application/json' }, | ||
body: '{"foo": 123}', | ||
}); | ||
expect(msg.unchunkedBody.toString(), 'to equal', '{"foo": 123}'); | ||
}); | ||
}); | ||
}); | ||
describe('with a JSON body', function () { | ||
it('should provide a parsed body (and default to utf-8)', function () { | ||
expect(new Message(new Buffer('Content-Type: application/json\n\n{"foo":"æøå"}', 'utf-8')).body, 'to equal', { | ||
foo: 'æøå' | ||
}); | ||
}); | ||
describe('with parts passed to the constructor', function () { | ||
it('should accept both messy.Message instances and valid constructor arguments ', function () { | ||
const message = new Message({ | ||
headers: 'Content-Type: multipart/mixed', | ||
parts: [ | ||
new Message('Content-Type: text/html\r\n\r\n<h1>Hello, world!</h1>'), | ||
{ headers: 'Content-Type: text/plain', body: 'Hello, world!' }, | ||
'Content-Type: text/foo\r\n\r\nhey', | ||
], | ||
}); | ||
expect(message.parts.length, 'to equal', 3); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/mixed\r\n' + | ||
'\r\n' + | ||
'--\r\n' + | ||
'Content-Type: text/html\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--\r\n' + | ||
'Content-Type: text/plain\r\n' + | ||
'\r\n' + | ||
'Hello, world!\r\n' + | ||
'--\r\n' + | ||
'Content-Type: text/foo\r\n' + | ||
'\r\n' + | ||
'hey\r\n' + | ||
'----\r\n' | ||
); | ||
}); | ||
it('should support a Content-Type that ends with +json', function () { | ||
expect(new Message(new Buffer('Content-Type: foobar+json; charset=UTF-8\n\n{"foo":"æøå"}', 'utf-8')).body, 'to equal', { | ||
foo: 'æøå' | ||
}); | ||
}); | ||
it('should pick up the boundary with boundary', function () { | ||
const message = new Message({ | ||
headers: 'Content-Type: multipart/mixed; boundary=foo', | ||
parts: [ | ||
new Message('Content-Type: text/html\r\n\r\n<h1>Hello, world!</h1>'), | ||
], | ||
}); | ||
expect(message.parts.length, 'to equal', 1); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
); | ||
}); | ||
it('should reserialize the body as utf-8', function () { | ||
var message = new Message(new Buffer('Content-Type: application/json\n\n{"foo":"æøå"}', 'utf-8')); | ||
message.body = { foo: '☺' }; | ||
expect(message.rawBody, 'to equal', new Buffer('{"foo":"☺"}', 'utf-8')); | ||
}); | ||
it('should allow passing a single part', function () { | ||
const message = new Message({ | ||
headers: 'Content-Type: multipart/mixed; boundary=foo', | ||
parts: new Message( | ||
'Content-Type: text/html\r\n\r\n<h1>Hello, world!</h1>' | ||
), | ||
}); | ||
expect(message.parts.length, 'to equal', 1); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
); | ||
}); | ||
}); | ||
describe('with an application/json body that does not parse as JSON', function () { | ||
var message = new Message({ | ||
headers: { 'Content-Type': 'application/json' }, | ||
unchunkedBody: '!!=!=' | ||
}); | ||
describe('with a multipart body', function () { | ||
const src = | ||
'Content-Type: multipart/form-data;\r\n' + | ||
' boundary=--------------------------231099812216460892104111\r\n' + | ||
'\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="Name "\r\n' + | ||
'\r\n' + | ||
'The name\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="email"\r\n' + | ||
'\r\n' + | ||
'the@email.com\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="Message "\r\n' + | ||
'\r\n' + | ||
'The message\r\n' + | ||
'----------------------------231099812216460892104111--\r\n'; | ||
it('should leave unchunkedBody as-is', function () { | ||
expect(message.unchunkedBody, 'to equal', '!!=!='); | ||
}); | ||
it('should decode the multipart parts when the body is passed as a Buffer', function () { | ||
const message = new Message(Buffer.from(src, 'utf-8')); | ||
it('should provide body as a string', function () { | ||
expect(message.body, 'to equal', '!!=!='); | ||
}); | ||
}); | ||
expect(message.toString(), 'to equal', src); | ||
describe('when the body is specified as a string', function () { | ||
it('should interpret the string as already encoded JSON', function () { | ||
var msg = new Message({ | ||
headers: { 'Content-Type': 'application/json' }, | ||
body: '{"foo": 123}' | ||
}); | ||
expect(msg.unchunkedBody.toString(), 'to equal', '{"foo": 123}'); | ||
}); | ||
}); | ||
expect(message.parts, 'to equal', [ | ||
new Message( | ||
Buffer.from( | ||
'Content-Disposition: form-data; name="recipient"\r\n\r\nandreas@one.com', | ||
'utf-8' | ||
) | ||
), | ||
new Message( | ||
Buffer.from( | ||
'Content-Disposition: form-data; name="Name "\r\n\r\nThe name', | ||
'utf-8' | ||
) | ||
), | ||
new Message( | ||
Buffer.from( | ||
'Content-Disposition: form-data; name="email"\r\n\r\nthe@email.com', | ||
'utf-8' | ||
) | ||
), | ||
new Message( | ||
Buffer.from( | ||
'Content-Disposition: form-data; name="Message "\r\n\r\nThe message', | ||
'utf-8' | ||
) | ||
), | ||
]); | ||
}); | ||
describe('with parts passed to the constructor', function () { | ||
it('should accept both messy.Message instances and valid constructor arguments ', function () { | ||
var message = new Message({ | ||
headers: 'Content-Type: multipart/mixed', | ||
parts: [ | ||
new Message('Content-Type: text/html\r\n\r\n<h1>Hello, world!</h1>'), | ||
{ headers: 'Content-Type: text/plain', body: 'Hello, world!' }, | ||
'Content-Type: text/foo\r\n\r\nhey' | ||
] | ||
}); | ||
expect(message.parts.length, 'to equal', 3); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/mixed\r\n' + | ||
'\r\n' + | ||
'--\r\n' + | ||
'Content-Type: text/html\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--\r\n' + | ||
'Content-Type: text/plain\r\n' + | ||
'\r\n' + | ||
'Hello, world!\r\n' + | ||
'--\r\n' + | ||
'Content-Type: text/foo\r\n' + | ||
'\r\n' + | ||
'hey\r\n' + | ||
'----\r\n' | ||
); | ||
}); | ||
it('should decode the multipart parts when the body is passed as a string', function () { | ||
const message = new Message(src); | ||
it('should pick up the boundary with boundary', function () { | ||
var message = new Message({ | ||
headers: 'Content-Type: multipart/mixed; boundary=foo', | ||
parts: [ | ||
new Message('Content-Type: text/html\r\n\r\n<h1>Hello, world!</h1>') | ||
] | ||
}); | ||
expect(message.parts.length, 'to equal', 1); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
); | ||
}); | ||
it('should allow passing a single part', function () { | ||
var message = new Message({ | ||
headers: 'Content-Type: multipart/mixed; boundary=foo', | ||
parts: new Message('Content-Type: text/html\r\n\r\n<h1>Hello, world!</h1>') | ||
}); | ||
expect(message.parts.length, 'to equal', 1); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
); | ||
}); | ||
expect(message.parts, 'to equal', [ | ||
new Message( | ||
'Content-Disposition: form-data; name="recipient"\r\n\r\nandreas@one.com' | ||
), | ||
new Message( | ||
'Content-Disposition: form-data; name="Name "\r\n\r\nThe name' | ||
), | ||
new Message( | ||
'Content-Disposition: form-data; name="email"\r\n\r\nthe@email.com' | ||
), | ||
new Message( | ||
'Content-Disposition: form-data; name="Message "\r\n\r\nThe message' | ||
), | ||
]); | ||
}); | ||
describe('with a multipart body', function () { | ||
var src = | ||
'Content-Type: multipart/form-data;\r\n' + | ||
' boundary=--------------------------231099812216460892104111\r\n' + | ||
'\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="Name "\r\n' + | ||
'\r\n' + | ||
'The name\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="email"\r\n' + | ||
'\r\n' + | ||
'the@email.com\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="Message "\r\n' + | ||
'\r\n' + | ||
'The message\r\n' + | ||
'----------------------------231099812216460892104111--\r\n'; | ||
it('#toString should serialize the (possibly mutated) decoded parts if available', function () { | ||
const message = new Message(src); | ||
it('should decode the multipart parts when the body is passed as a Buffer', function () { | ||
var message = new Message(new Buffer(src, 'utf-8')); | ||
message.parts.splice(1, 3); | ||
expect(message.toString(), 'to equal', src); | ||
message.parts[0].headers.set('Foo', 'quux'); | ||
expect(message.parts, 'to equal', [ | ||
new Message(new Buffer('Content-Disposition: form-data; name="recipient"\r\n\r\nandreas@one.com', 'utf-8')), | ||
new Message(new Buffer('Content-Disposition: form-data; name="Name "\r\n\r\nThe name', 'utf-8')), | ||
new Message(new Buffer('Content-Disposition: form-data; name="email"\r\n\r\nthe@email.com', 'utf-8')), | ||
new Message(new Buffer('Content-Disposition: form-data; name="Message "\r\n\r\nThe message', 'utf-8')) | ||
]); | ||
}); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/form-data;\r\n' + | ||
' boundary=--------------------------231099812216460892104111\r\n' + | ||
'\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'Foo: quux\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
it('should decode the multipart parts when the body is passed as a string', function () { | ||
var message = new Message(src); | ||
it('should reparse the parts if the body of the containing Message is updated', function () { | ||
const message = new Message(src); | ||
message.parts.splice(1, 3); | ||
message.body = src; | ||
expect(message.parts, 'to have length', 4); | ||
}); | ||
expect(message.parts, 'to equal', [ | ||
new Message('Content-Disposition: form-data; name="recipient"\r\n\r\nandreas@one.com'), | ||
new Message('Content-Disposition: form-data; name="Name "\r\n\r\nThe name'), | ||
new Message('Content-Disposition: form-data; name="email"\r\n\r\nthe@email.com'), | ||
new Message('Content-Disposition: form-data; name="Message "\r\n\r\nThe message') | ||
]); | ||
}); | ||
it('should support updating the parts property', function () { | ||
const message = new Message(src); | ||
message.parts = []; | ||
expect(message.parts, 'to equal', []); | ||
}); | ||
it('#toString should serialize the (possibly mutated) decoded parts if available', function () { | ||
var message = new Message(src); | ||
it('should recompute the body if the parts are updated', function () { | ||
const message = new Message(src); | ||
expect(message.parts, 'to have length', 4); | ||
message.parts.splice(1, 3); | ||
expect( | ||
message.body, | ||
'to equal', | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
message.parts.splice(1, 3); | ||
it('should recompute the body if the parts are updated, binary mode', function () { | ||
const message = new Message(src); | ||
expect(message.parts, 'to have length', 4); | ||
message.parts[0].body = Buffer.from([0]); | ||
message.parts.splice(1, 3); | ||
const body = message.body; | ||
expect(body, 'to be a', Buffer); | ||
expect( | ||
body.toString('utf-8'), | ||
'to equal', | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'\x00\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
}); | ||
message.parts[0].headers.set('Foo', 'quux'); | ||
describe('#unchunkedBody', function () { | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a string', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
).unchunkedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/form-data;\r\n' + | ||
' boundary=--------------------------231099812216460892104111\r\n' + | ||
'\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'Foo: quux\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
) | ||
).unchunkedBody, | ||
'to equal', | ||
Buffer.from('Wikipedia in\r\n\r\nchunks.', 'utf-8') | ||
); | ||
}); | ||
it('should reparse the parts if the body of the containing Message is updated', function () { | ||
var message = new Message(src); | ||
message.parts.splice(1, 3); | ||
message.body = src; | ||
expect(message.parts, 'to have length', 4); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a string', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.' | ||
).unchunkedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
it('should support updating the parts property', function () { | ||
var message = new Message(src); | ||
message.parts = []; | ||
expect(message.parts, 'to equal', []); | ||
}); | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' | ||
).unchunkedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
it('should recompute the body if the parts are updated', function () { | ||
var message = new Message(src); | ||
expect(message.parts, 'to have length', 4); | ||
message.parts.splice(1, 3); | ||
expect(message.body, 'to equal', | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111--\r\n'); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.', | ||
'utf-8' | ||
) | ||
).unchunkedBody, | ||
'to equal', | ||
Buffer.from('Wikipedia in\r\n\r\nchunks.', 'utf-8') | ||
); | ||
it('should recompute the body if the parts are updated, binary mode', function () { | ||
var message = new Message(src); | ||
expect(message.parts, 'to have length', 4); | ||
message.parts[0].body = new Buffer([0]); | ||
message.parts.splice(1, 3); | ||
var body = message.body; | ||
expect(body, 'to be a', Buffer); | ||
expect(body.toString('utf-8'), 'to equal', | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'\x00\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n', | ||
'utf-8' | ||
) | ||
).unchunkedBody, | ||
'to equal', | ||
Buffer.from('Wikipedia in\r\n\r\nchunks.', 'utf-8') | ||
); | ||
}); | ||
describe('#unchunkedBody', function () { | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a string', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
).unchunkedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
describe('when accessed as a setter', function () { | ||
it('should update rawBody and body', function () { | ||
const message = new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
); | ||
message.unchunkedBody = 'foobar'; | ||
expect( | ||
message.rawBody, | ||
'to equal', | ||
Buffer.from('6\r\nfoobar\r\n0\r\n\r\n', 'utf-8') | ||
); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a Buffer', function () { | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
) | ||
).unchunkedBody, 'to equal', new Buffer('Wikipedia in\r\n\r\nchunks.', 'utf-8')); | ||
describe('when passed to the constructor', function () { | ||
it('should populate rawBody and body', function () { | ||
const message = new Message({ | ||
headers: | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n', | ||
unchunkedBody: 'foobar', | ||
}); | ||
expect( | ||
message.rawBody, | ||
'to equal', | ||
Buffer.from('6\r\nfoobar\r\n0\r\n\r\n', 'utf-8') | ||
); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a string', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.' | ||
).unchunkedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
describe('#decodedBody', function () { | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a string', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
).decodedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' | ||
).unchunkedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
) | ||
).decodedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a Buffer', function () { | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.', | ||
'utf-8' | ||
) | ||
).unchunkedBody, 'to equal', new Buffer('Wikipedia in\r\n\r\nchunks.', 'utf-8')); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a string', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.' | ||
).decodedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n', | ||
'utf-8' | ||
) | ||
).unchunkedBody, 'to equal', new Buffer('Wikipedia in\r\n\r\nchunks.', 'utf-8')); | ||
}); | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' | ||
).decodedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
describe('when accessed as a setter', function () { | ||
it('should update rawBody and body', function () { | ||
var message = new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
); | ||
message.unchunkedBody = 'foobar'; | ||
expect(message.rawBody, 'to equal', new Buffer('6\r\nfoobar\r\n0\r\n\r\n', 'utf-8')); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.', | ||
'utf-8' | ||
) | ||
).decodedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
describe('when passed to the constructor', function () { | ||
it('should populate rawBody and body', function () { | ||
var message = new Message({ | ||
headers: | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n', | ||
unchunkedBody: 'foobar' | ||
}); | ||
expect(message.rawBody, 'to equal', new Buffer('6\r\nfoobar\r\n0\r\n\r\n', 'utf-8')); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n', | ||
'utf-8' | ||
) | ||
).decodedBody, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
describe('#decodedBody', function () { | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a string', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
).decodedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
describe('when accessed as a setter', function () { | ||
it('should update rawBody and body', function () { | ||
const message = new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
); | ||
message.decodedBody = 'foobar'; | ||
expect( | ||
message.rawBody, | ||
'to equal', | ||
Buffer.from('6\r\nfoobar\r\n0\r\n\r\n') | ||
); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a Buffer', function () { | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
) | ||
).decodedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
describe('when passed to the constructor', function () { | ||
it('should populate rawBody and body', function () { | ||
const message = new Message({ | ||
headers: | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n', | ||
decodedBody: 'foobar', | ||
}); | ||
expect( | ||
message.rawBody, | ||
'to equal', | ||
Buffer.from('6\r\nfoobar\r\n0\r\n\r\n', 'utf-8') | ||
); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a string', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.' | ||
).decodedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
describe('#body', function () { | ||
it('should decode the body as text when the Content-Type contains +xml', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: foobar+xml\r\n' + | ||
'\r\n' + | ||
'<?xml version="1.0" encoding="utf-8"?><foo></foo>\r\n' | ||
).body, | ||
'to equal', | ||
'<?xml version="1.0" encoding="utf-8"?><foo></foo>\r\n' | ||
); | ||
}); | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' | ||
).decodedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
it('should decode a base64 body to a string when the Content-Transfer-Encoding is base64 and the Content-Type is textual and the body is stored as a string', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).body, | ||
'to equal', | ||
'foo' | ||
); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when a partial body is provided as a Buffer', function () { | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.', | ||
'utf-8' | ||
) | ||
).decodedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
it('should decode a base64 body to a string when the Content-Transfer-Encoding is base64 and the Content-Type is textual and the body is stored as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
) | ||
).body, | ||
'to equal', | ||
'foo' | ||
); | ||
}); | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n', | ||
'utf-8' | ||
) | ||
).decodedBody, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
it('should decode a base64 body to a Buffer when the Content-Transfer-Encoding is base64 and the Content-Type is not textual', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: image/png; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).body, | ||
'to equal', | ||
Buffer.from('foo') | ||
); | ||
}); | ||
describe('when accessed as a setter', function () { | ||
it('should update rawBody and body', function () { | ||
var message = new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
); | ||
message.decodedBody = 'foobar'; | ||
expect(message.rawBody, 'to equal', new Buffer('6\r\nfoobar\r\n0\r\n\r\n')); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
it('should decode quoted-printable when the Content-Transfer-Encoding header says so', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: quoted-printable\r\n' + | ||
'\r\n' + | ||
'Abc =C3=A6=C3=B8=C3=A5\r\n' | ||
).body, | ||
'to equal', | ||
'Abc æøå\r\n' | ||
); | ||
}); | ||
describe('when passed to the constructor', function () { | ||
it('should populate rawBody and body', function () { | ||
var message = new Message({ | ||
headers: | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n', | ||
decodedBody: 'foobar' | ||
}); | ||
expect(message.rawBody, 'to equal', new Buffer('6\r\nfoobar\r\n0\r\n\r\n', 'utf-8')); | ||
expect(message.body, 'to equal', 'foobar'); | ||
}); | ||
}); | ||
it('should not break if the Content-Transfer-Encoding is unsupported', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: image/png; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: foo\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).body, | ||
'to equal', | ||
'Zm9v\r\n' | ||
); | ||
}); | ||
describe('#body', function () { | ||
it('should decode the body as text when the Content-Type contains +xml', function () { | ||
expect(new Message( | ||
'Content-Type: foobar+xml\r\n' + | ||
'\r\n' + | ||
'<?xml version="1.0" encoding="utf-8"?><foo></foo>\r\n' | ||
).body, 'to equal', '<?xml version="1.0" encoding="utf-8"?><foo></foo>\r\n'); | ||
}); | ||
it('should support quoted-printable with iso-8859-1', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\n' + | ||
'Content-Transfer-Encoding: quoted-printable\r\n' + | ||
'\r\n' + | ||
'Abc =F8\r\n' | ||
).body, | ||
'to equal', | ||
'Abc ø\r\n' | ||
); | ||
}); | ||
it('should decode a base64 body to a string when the Content-Transfer-Encoding is base64 and the Content-Type is textual and the body is stored as a string', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).body, 'to equal', 'foo'); | ||
}); | ||
it('should provide a decoded body when the body is already given as a string with no Content-Transfer-Encoding, even when a charset is defined', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + '\r\n' + 'Abcdef\r\n' | ||
).body, | ||
'to equal', | ||
'Abcdef\r\n' | ||
); | ||
}); | ||
it('should decode a base64 body to a string when the Content-Transfer-Encoding is base64 and the Content-Type is textual and the body is stored as a Buffer', function () { | ||
expect(new Message(new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
it('should support quoted-printable with no Content-Transfer-Encoding', function () { | ||
expect( | ||
new Message( | ||
Buffer.concat([ | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n') | ||
).body, 'to equal', 'foo'); | ||
}); | ||
'Abc ' | ||
), | ||
Buffer.from([0xf8]), | ||
Buffer.from('\r\n'), | ||
]) | ||
).body, | ||
'to equal', | ||
'Abc ø\r\n' | ||
); | ||
}); | ||
it('should decode a base64 body to a Buffer when the Content-Transfer-Encoding is base64 and the Content-Type is not textual', function () { | ||
expect(new Message( | ||
'Content-Type: image/png; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).body, 'to equal', new Buffer('foo')); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a string', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
).body, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
it('should decode quoted-printable when the Content-Transfer-Encoding header says so', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: quoted-printable\r\n' + | ||
'\r\n' + | ||
'Abc =C3=A6=C3=B8=C3=A5\r\n' | ||
).body, 'to equal', 'Abc æøå\r\n'); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a Buffer', function () { | ||
expect( | ||
new Message( | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
) | ||
).body, | ||
'to equal', | ||
'Wikipedia in\r\n\r\nchunks.' | ||
); | ||
}); | ||
it('should not break if the Content-Transfer-Encoding is unsupported', function () { | ||
expect(new Message( | ||
'Content-Type: image/png; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: foo\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).body, 'to equal', 'Zm9v\r\n'); | ||
}); | ||
it('should support quoted-printable with iso-8859-1', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\n' + | ||
'Content-Transfer-Encoding: quoted-printable\r\n' + | ||
'\r\n' + | ||
'Abc =F8\r\n' | ||
).body, 'to equal', 'Abc ø\r\n'); | ||
}); | ||
it('should provide a decoded body when the body is already given as a string with no Content-Transfer-Encoding, even when a charset is defined', function () { | ||
expect(new Message( | ||
it.skipIf( | ||
!require('zlib').gunzipSync, | ||
'should decode Content-Encoding:gzip', | ||
function () { | ||
expect( | ||
new Message( | ||
Buffer.concat([ | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'Abcdef\r\n' | ||
).body, 'to equal', 'Abcdef\r\n'); | ||
}); | ||
'Content-Encoding: gzip\r\n' + | ||
'\r\n', | ||
'ascii' | ||
), | ||
Buffer.from([ | ||
0x1f, | ||
0x8b, | ||
0x08, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x03, | ||
0x4b, | ||
0xcb, | ||
0xcf, | ||
0x4f, | ||
0x4a, | ||
0x2c, | ||
0x02, | ||
0x00, | ||
0x95, | ||
0x1f, | ||
0xf6, | ||
0x9e, | ||
0x06, | ||
0x00, | ||
0x00, | ||
0x00, | ||
]), | ||
]) | ||
).body, | ||
'to equal', | ||
'foobar' | ||
); | ||
} | ||
); | ||
it('should support quoted-printable with no Content-Transfer-Encoding', function () { | ||
expect(new Message( | ||
Buffer.concat([ | ||
new Buffer( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\n' + | ||
'\r\n' + | ||
'Abc '), | ||
new Buffer([0xf8]), | ||
new Buffer('\r\n') | ||
]) | ||
).body, 'to equal', 'Abc ø\r\n'); | ||
}); | ||
it.skipIf( | ||
!require('zlib').gunzipSync, | ||
'should decode Content-Encoding:gzip when the message has been instantiated from an object', | ||
function () { | ||
expect( | ||
new Message({ | ||
headers: { | ||
'Content-Type': 'text/plain', | ||
'Content-Encoding': 'gzip', | ||
}, | ||
unchunkedBody: require('zlib').gzipSync('foobarquux'), | ||
}).body, | ||
'to equal', | ||
'foobarquux' | ||
); | ||
} | ||
); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a string', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
).body, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
it('should decode application/x-www-form-urlencoded as text', function () { | ||
const src = Buffer.from( | ||
'Content-Type: application/x-www-form-urlencoded\n\nfoo=bar&data=%5B%22foo.txt%22%5D', | ||
'ascii' | ||
); | ||
expect( | ||
new Message(src).body, | ||
'to equal', | ||
'foo=bar&data=%5B%22foo.txt%22%5D' | ||
); | ||
}); | ||
}); | ||
it('should decode Transfer-Encoding:chunked when the body is provided as a Buffer', function () { | ||
expect(new Message( | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'5\r\n' + | ||
'pedia\r\n' + | ||
'e\r\n' + | ||
' in\r\n\r\nchunks.\r\n' + | ||
'0\r\n' + | ||
'\r\n', | ||
'utf-8' | ||
) | ||
).body, 'to equal', 'Wikipedia in\r\n\r\nchunks.'); | ||
}); | ||
describe('#rawBody', function () { | ||
it('should be populated when instantiating a Message from a string', function () { | ||
const rawBody = 'Foo: bar\r\n\r\nquux'; | ||
expect(new Message(rawBody).rawBody, 'to equal', 'quux'); | ||
}); | ||
it.skipIf(!require('zlib').gunzipSync, 'should decode Content-Encoding:gzip', function () { | ||
expect(new Message(Buffer.concat([ | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Encoding: gzip\r\n' + | ||
'\r\n', 'ascii' | ||
), | ||
new Buffer([0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4b, 0xcb, 0xcf, 0x4f, 0x4a, 0x2c, 0x02, 0x00, 0x95, 0x1f, 0xf6, 0x9e, 0x06, 0x00, 0x00, 0x00]) | ||
])).body, 'to equal', 'foobar'); | ||
}); | ||
it('should be populated when instantiating a Message from a Buffer', function () { | ||
const rawBody = Buffer.from('Foo: bar\r\n\r\nquux', 'utf-8'); | ||
expect( | ||
new Message(rawBody).rawBody, | ||
'to equal', | ||
Buffer.from('quux', 'utf-8') | ||
); | ||
}); | ||
it.skipIf(!require('zlib').gunzipSync, 'should decode Content-Encoding:gzip when the message has been instantiated from an object', function () { | ||
expect(new Message({ | ||
headers: { | ||
'Content-Type': 'text/plain', | ||
'Content-Encoding': 'gzip' | ||
}, | ||
unchunkedBody: require('zlib').gzipSync('foobarquux') | ||
}).body, 'to equal', 'foobarquux'); | ||
}); | ||
it('should be recomputed from the body if updated, with Content-Transfer-Encoding', function () { | ||
const message = new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: base64\r\n\r\nZm9vYmFy' | ||
); | ||
expect(message.rawBody, 'to equal', 'Zm9vYmFy'); | ||
expect(message.body, 'to equal', 'foobar'); | ||
message.body = 'quux'; | ||
expect(message.rawBody, 'to equal', 'cXV1eA=='); | ||
}); | ||
it('should decode application/x-www-form-urlencoded as text', function () { | ||
var src = new Buffer('Content-Type: application/x-www-form-urlencoded\n\nfoo=bar&data=%5B%22foo.txt%22%5D', 'ascii'); | ||
expect(new Message(src).body, 'to equal', 'foo=bar&data=%5B%22foo.txt%22%5D'); | ||
}); | ||
it('should be recomputed from the body if updated, with quoted-printable and a charset', function () { | ||
const message = new Message( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nF=E6' | ||
); | ||
expect(message.rawBody, 'to equal', 'F=E6'); | ||
expect(message.body, 'to equal', 'Fæ'); | ||
message.body = 'øh'; | ||
expect(message.rawBody, 'to equal', '=F8h'); | ||
}); | ||
describe('#rawBody', function () { | ||
it('should be populated when instantiating a Message from a string', function () { | ||
var rawBody = 'Foo: bar\r\n\r\nquux'; | ||
expect(new Message(rawBody).rawBody, 'to equal', 'quux'); | ||
}); | ||
it('should be recomputed from the body if updated, with transfer-encoding: chunked', function () { | ||
const message = new Message( | ||
'Content-Type: text/plain; charset=utf-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
); | ||
expect(message.rawBody, 'to equal', '4\r\nWiki\r\n0\r\n\r\n'); | ||
expect(message.body, 'to equal', 'Wiki'); | ||
message.body = 'sarbarbarbab'; | ||
expect( | ||
message.rawBody, | ||
'to equal', | ||
Buffer.from('c\r\nsarbarbarbab\r\n0\r\n\r\n') | ||
); | ||
expect(message.body, 'to equal', 'sarbarbarbab'); | ||
}); | ||
it('should be populated when instantiating a Message from a Buffer', function () { | ||
var rawBody = new Buffer('Foo: bar\r\n\r\nquux', 'utf-8'); | ||
expect(new Message(rawBody).rawBody, 'to equal', new Buffer('quux', 'utf-8')); | ||
}); | ||
it.skipIf( | ||
!require('zlib').gzipSync, | ||
'should be recomputed from the body if updated, with Content-Encoding:gzip', | ||
function () { | ||
const message = new Message( | ||
Buffer.concat([ | ||
Buffer.from( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Encoding: gzip\r\n' + | ||
'\r\n', | ||
'ascii' | ||
), | ||
Buffer.from([ | ||
0x1f, | ||
0x8b, | ||
0x08, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x03, | ||
0x4b, | ||
0xcb, | ||
0xcf, | ||
0x4f, | ||
0x4a, | ||
0x2c, | ||
0x02, | ||
0x00, | ||
0x95, | ||
0x1f, | ||
0xf6, | ||
0x9e, | ||
0x06, | ||
0x00, | ||
0x00, | ||
0x00, | ||
]), | ||
]) | ||
); | ||
expect(message.body, 'to equal', 'foobar'); | ||
message.body = 'barfoo'; | ||
expect( | ||
message.rawBody, | ||
'to equal', | ||
Buffer.from([ | ||
0x1f, | ||
0x8b, | ||
0x08, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x00, | ||
0x03, | ||
0x4b, | ||
0x4a, | ||
0x2c, | ||
0x4a, | ||
0xcb, | ||
0xcf, | ||
0x07, | ||
0x00, | ||
0x2b, | ||
0x85, | ||
0xa8, | ||
0xe2, | ||
0x06, | ||
0x00, | ||
0x00, | ||
0x00, | ||
]) | ||
); | ||
expect(message.body, 'to equal', 'barfoo'); | ||
} | ||
); | ||
}); | ||
it('should be recomputed from the body if updated, with Content-Transfer-Encoding', function () { | ||
var message = new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\nContent-Transfer-Encoding: base64\r\n\r\nZm9vYmFy' | ||
); | ||
expect(message.rawBody, 'to equal', 'Zm9vYmFy'); | ||
expect(message.body, 'to equal', 'foobar'); | ||
message.body = 'quux'; | ||
expect(message.rawBody, 'to equal', 'cXV1eA=='); | ||
}); | ||
describe('#fileName', function () { | ||
describe('when invoked as a getter', function () { | ||
it('should decode the Content-Disposition filename', function () { | ||
const message = new Message( | ||
'Content-Disposition: attachment;\r\n' + | ||
" filename*0*=utf-8''%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n" + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA' | ||
); | ||
expect( | ||
message.fileName, | ||
'to equal', | ||
'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺' | ||
); | ||
}); | ||
it('should be recomputed from the body if updated, with quoted-printable and a charset', function () { | ||
var message = new Message( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nF=E6' | ||
); | ||
expect(message.rawBody, 'to equal', 'F=E6'); | ||
expect(message.body, 'to equal', 'Fæ'); | ||
message.body = 'øh'; | ||
expect(message.rawBody, 'to equal', '=F8h'); | ||
}); | ||
it('should not fall back to the name property of the Content-Type header when the Content-Disposition header has no filename parameter', function () { | ||
const message = new Message( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Disposition: attachment\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(message.fileName, 'to equal', undefined); | ||
}); | ||
it('should be recomputed from the body if updated, with transfer-encoding: chunked', function () { | ||
var message = new Message( | ||
'Content-Type: text/plain; charset=utf-8\r\n' + | ||
'Transfer-Encoding: chunked\r\n\r\n' + | ||
'4\r\n' + | ||
'Wiki\r\n' + | ||
'0\r\n' + | ||
'\r\n' | ||
); | ||
expect(message.rawBody, 'to equal', '4\r\nWiki\r\n0\r\n\r\n'); | ||
expect(message.body, 'to equal', 'Wiki'); | ||
message.body = 'sarbarbarbab'; | ||
expect(message.rawBody, 'to equal', new Buffer('c\r\nsarbarbarbab\r\n0\r\n\r\n')); | ||
expect(message.body, 'to equal', 'sarbarbarbab'); | ||
}); | ||
it.skipIf(!require('zlib').gzipSync, 'should be recomputed from the body if updated, with Content-Encoding:gzip', function () { | ||
var message = new Message(Buffer.concat([ | ||
new Buffer( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Encoding: gzip\r\n' + | ||
'\r\n', 'ascii' | ||
), | ||
new Buffer([0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4b, 0xcb, 0xcf, 0x4f, 0x4a, 0x2c, 0x02, 0x00, 0x95, 0x1f, 0xf6, 0x9e, 0x06, 0x00, 0x00, 0x00]) | ||
])); | ||
expect(message.body, 'to equal', 'foobar'); | ||
message.body = 'barfoo'; | ||
expect(message.rawBody, 'to equal', new Buffer([0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4b, 0x4a, 0x2c, 0x4a, 0xcb, 0xcf, 0x07, 0x00, 0x2b, 0x85, 0xa8, 0xe2, 0x06, 0x00, 0x00, 0x00])); | ||
expect(message.body, 'to equal', 'barfoo'); | ||
}); | ||
it('should not fall back to the name property of the Content-Type header when there is no Content-Disposition header', function () { | ||
const message = new Message( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(message.fileName, 'to equal', undefined); | ||
}); | ||
}); | ||
describe('#fileName', function () { | ||
describe('when invoked as a getter', function () { | ||
it('should decode the Content-Disposition filename', function () { | ||
var message = new Message( | ||
'Content-Disposition: attachment;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA'); | ||
expect(message.fileName, 'to equal', 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'); | ||
}); | ||
describe('when invoked as a setter', function () { | ||
it('should support a fileName setter which updates the Content-Disposition filename with the rfc2231 encoded representation', function () { | ||
const message = new Message({ body: 'bar' }); | ||
message.fileName = | ||
'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Disposition: attachment;\r\n' + | ||
" filename*0*=utf-8''%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n" + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' + | ||
'\r\n' + | ||
'bar' | ||
); | ||
}); | ||
it('should not fall back to the name property of the Content-Type header when the Content-Disposition header has no filename parameter', function () { | ||
var message = new Message( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Disposition: attachment\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(message.fileName, 'to equal', undefined); | ||
}); | ||
it('should not fall back to the name property of the Content-Type header when there is no Content-Disposition header', function () { | ||
var message = new Message( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(message.fileName, 'to equal', undefined); | ||
}); | ||
it('should not update the name property of the Content-Type header, even if available', function () { | ||
const message = new Message({ | ||
headers: { 'Content-Type': 'image/png' }, | ||
}); | ||
message.fileName = 'æøå.png'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: image/png\r\n' + | ||
"Content-Disposition: attachment; filename*=iso-8859-1''%E6%F8%E5.png\r\n" | ||
); | ||
}); | ||
}); | ||
}); | ||
describe('when invoked as a setter', function () { | ||
it('should support a fileName setter which updates the Content-Disposition filename with the rfc2231 encoded representation', function () { | ||
var message = new Message({body: 'bar'}); | ||
message.fileName = 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Disposition: attachment;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' + | ||
'\r\n' + | ||
'bar' | ||
); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should include headers if any are present', function () { | ||
expect(new Message('Foo: bar').toJSON(), 'to equal', { | ||
headers: { | ||
Foo: 'bar', | ||
}, | ||
}); | ||
}); | ||
it('should not update the name property of the Content-Type header, even if available', function () { | ||
var message = new Message({headers: {'Content-Type': 'image/png'}}); | ||
message.fileName = 'æøå.png'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: image/png\r\n' + | ||
"Content-Disposition: attachment; filename*=iso-8859-1''%E6%F8%E5.png\r\n" | ||
); | ||
}); | ||
}); | ||
it('should not include an empty headers object', function () { | ||
expect(new Message('\r\nhey').toJSON(), 'to equal', { | ||
rawBody: 'hey', | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should include headers if any are present', function () { | ||
expect(new Message('Foo: bar').toJSON(), 'to equal', { | ||
headers: { | ||
Foo: 'bar' | ||
} | ||
}); | ||
}); | ||
it('should include the parsed representation of the body if it has been touched', function () { | ||
const message = new Message( | ||
'Content-type: application/json\r\n\r\n{"foo":"bar"}' | ||
); | ||
message.body.blah = 123; | ||
expect(message.toJSON(), 'to equal', { | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: { | ||
foo: 'bar', | ||
blah: 123, | ||
}, | ||
}); | ||
}); | ||
it('should not include an empty headers object', function () { | ||
expect(new Message('\r\nhey').toJSON(), 'to equal', { | ||
rawBody: 'hey' | ||
}); | ||
}); | ||
describe('with a multipart message', function () { | ||
it('should not include the parts if they have not been touched', function () { | ||
expect( | ||
new Message( | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
).toJSON(), | ||
'to equal', | ||
{ | ||
headers: { | ||
'Content-Type': 'multipart/mixed; boundary=foo', | ||
}, | ||
rawBody: | ||
'--foo\r\n' + | ||
'Content-Type: text/html; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n', | ||
} | ||
); | ||
}); | ||
}); | ||
it('should include the parsed representation of the body if it has been touched', function () { | ||
var message = new Message('Content-type: application/json\r\n\r\n{"foo":"bar"}'); | ||
message.body.blah = 123; | ||
expect(message.toJSON(), 'to equal', { | ||
headers: { | ||
'Content-Type': 'application/json' | ||
}, | ||
body: { | ||
foo: 'bar', | ||
blah: 123 | ||
} | ||
}); | ||
}); | ||
it('should include parts if they have been touched', function () { | ||
const message = new Message( | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
); | ||
describe('with a multipart message', function () { | ||
it('should not include the parts if they have not been touched', function () { | ||
expect(new Message( | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
).toJSON(), 'to equal', { | ||
headers: { | ||
'Content-Type': 'multipart/mixed; boundary=foo' | ||
}, | ||
rawBody: | ||
'--foo\r\n' + | ||
'Content-Type: text/html; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
}); | ||
}); | ||
}); | ||
message.parts[0].headers.set('Hey', 'there'); | ||
it('should include parts if they have been touched', function () { | ||
var message = new Message( | ||
'Content-Type: multipart/mixed; boundary=foo\r\n' + | ||
'\r\n' + | ||
'--foo\r\n' + | ||
'Content-Type: text/html; charset=UTF-8\r\n' + | ||
'\r\n' + | ||
'<h1>Hello, world!</h1>\r\n' + | ||
'--foo--\r\n' | ||
); | ||
message.parts[0].headers.set('Hey', 'there'); | ||
expect(message.toJSON(), 'to equal', { | ||
headers: { | ||
'Content-Type': 'multipart/mixed; boundary=foo' | ||
}, | ||
parts: [ | ||
{ | ||
headers: { | ||
'Content-Type': 'text/html; charset=UTF-8', | ||
Hey: 'there' | ||
}, | ||
rawBody: '<h1>Hello, world!</h1>' | ||
} | ||
] | ||
}); | ||
}); | ||
}); | ||
describe('with a JSON body provided as an object despite a non-JSON Content-Type', function () { | ||
var message = new Message({ | ||
expect(message.toJSON(), 'to equal', { | ||
headers: { | ||
'Content-Type': 'multipart/mixed; boundary=foo', | ||
}, | ||
parts: [ | ||
{ | ||
headers: { | ||
'Content-Type': 'application/octet-stream' | ||
'Content-Type': 'text/html; charset=UTF-8', | ||
Hey: 'there', | ||
}, | ||
body: { | ||
foo: 123 | ||
} | ||
}); | ||
rawBody: '<h1>Hello, world!</h1>', | ||
}, | ||
], | ||
}); | ||
}); | ||
}); | ||
it('should stringify correctly', function () { | ||
expect(message.toString(), 'to equal', | ||
'Content-Type: application/octet-stream\r\n' + | ||
'\r\n' + | ||
'{"foo":123}' | ||
); | ||
}); | ||
describe('with a JSON body provided as an object despite a non-JSON Content-Type', function () { | ||
const message = new Message({ | ||
headers: { | ||
'Content-Type': 'application/octet-stream', | ||
}, | ||
body: { | ||
foo: 123, | ||
}, | ||
}); | ||
it('should have the correct unchunkedBody', function () { | ||
expect(message.unchunkedBody.toString('utf-8'), 'to equal', '{"foo":123}'); | ||
}); | ||
it('should stringify correctly', function () { | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: application/octet-stream\r\n' + '\r\n' + '{"foo":123}' | ||
); | ||
}); | ||
it('should have the correct unchunkedBody', function () { | ||
expect( | ||
message.unchunkedBody.toString('utf-8'), | ||
'to equal', | ||
'{"foo":123}' | ||
); | ||
}); | ||
}); | ||
}); |
@@ -1,32 +0,36 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
RequestLine = require('../lib/RequestLine'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const RequestLine = require('../lib/RequestLine'); | ||
describe('RequestLine', function () { | ||
it('should add a leading slash to the url if not specified', function () { | ||
expect(new RequestLine('GET foo').url, 'to equal', '/foo'); | ||
it('should add a leading slash to the url if not specified', function () { | ||
expect(new RequestLine('GET foo').url, 'to equal', '/foo'); | ||
}); | ||
describe('#toString', function () { | ||
it('should omit an undefined protocol', function () { | ||
expect(new RequestLine('GET /').toString(), 'to equal', 'GET /'); | ||
}); | ||
}); | ||
describe('#toString', function () { | ||
it('should omit an undefined protocol', function () { | ||
expect(new RequestLine('GET /').toString(), 'to equal', 'GET /'); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should return non-computed properties', function () { | ||
expect(new RequestLine('GET / HTTP/1.1').toJSON(), 'to equal', { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should return non-computed properties', function () { | ||
expect(new RequestLine('GET / HTTP/1.1').toJSON(), 'to equal', { | ||
method: 'GET', | ||
url: '/', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1' | ||
}); | ||
}); | ||
// Makes it possible to use statusLine.toJSON() as the RHS of a 'to satisfy' assertion in Unexpected | ||
// where undefined means that the property must not be present: | ||
it('should not include the keys that have undefined values', function () { | ||
expect(new RequestLine('GET').toJSON(), 'not to have keys', ['path', 'protocolName', 'protocolVersion']); | ||
}); | ||
// Makes it possible to use statusLine.toJSON() as the RHS of a 'to satisfy' assertion in Unexpected | ||
// where undefined means that the property must not be present: | ||
it('should not include the keys that have undefined values', function () { | ||
expect(new RequestLine('GET').toJSON(), 'not to have keys', [ | ||
'path', | ||
'protocolName', | ||
'protocolVersion', | ||
]); | ||
}); | ||
}); | ||
}); |
@@ -1,22 +0,26 @@ | ||
/*global describe, it*/ | ||
var expect = require('unexpected'), | ||
StatusLine = require('../lib/StatusLine'); | ||
/* global describe, it */ | ||
const expect = require('unexpected'); | ||
const StatusLine = require('../lib/StatusLine'); | ||
describe('StatusLine', function () { | ||
describe('#toJSON', function () { | ||
it('should return non-computed properties', function () { | ||
expect(new StatusLine('HTTP/1.1 200 OK').toJSON(), 'to equal', { | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1' | ||
}); | ||
}); | ||
describe('#toJSON', function () { | ||
it('should return non-computed properties', function () { | ||
expect(new StatusLine('HTTP/1.1 200 OK').toJSON(), 'to equal', { | ||
statusCode: 200, | ||
statusMessage: 'OK', | ||
protocolName: 'HTTP', | ||
protocolVersion: '1.1', | ||
}); | ||
}); | ||
// Makes it possible to use statusLine.toJSON() as the RHS of a 'to satisfy' assertion in Unexpected | ||
// where undefined means that the property must not be present: | ||
it('should not include the keys that have undefined values', function () { | ||
expect(new StatusLine('HTTP/1.1').toJSON(), 'not to have key', 'statusCode'); | ||
}); | ||
// Makes it possible to use statusLine.toJSON() as the RHS of a 'to satisfy' assertion in Unexpected | ||
// where undefined means that the property must not be present: | ||
it('should not include the keys that have undefined values', function () { | ||
expect( | ||
new StatusLine('HTTP/1.1').toJSON(), | ||
'not to have key', | ||
'statusCode' | ||
); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
35
5043
173216
13
13
+ Addedlodash.omit@^4.5.0
+ Addediconv-lite@0.6.3(transitive)
+ Addedlodash.omit@4.5.0(transitive)
+ Addedquoted-printable@1.0.1(transitive)
+ Addedrfc2047@3.0.1(transitive)
- Removedunderscore@^1.6.0
- Removediconv-lite@0.4.24(transitive)
- Removedquoted-printable@1.0.0(transitive)
- Removedrfc2047@2.0.0(transitive)
- Removedunderscore@1.13.7(transitive)
Updatediconv-lite@^0.6.0
Updatedquoted-printable@^1.0.1
Updatedrfc2047@^3.0.1
Updatedrfc2231@^1.3.0