Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

messy

Package Overview
Dependencies
Maintainers
4
Versions
73
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

messy - npm Package Compare versions

Comparing version 6.17.0 to 7.0.0

.editorconfig

176

lib/decodeChunkedTransferEncoding.js
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"
);
});
});
});
});

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc