From 4d4d63e8bf40a03a7203c883ab8b1e55f4afad70 Mon Sep 17 00:00:00 2001 From: Peter Hedenskog Date: Sat, 12 Dec 2020 13:52:51 +0100 Subject: [PATCH] A better header structure easier to work with (#98) --- lib/collect.js | 13 ++++++------- lib/headers.js | 38 +++++++++++++++++++++++++------------- test/headersTest.js | 38 +++++++++++++++++++++----------------- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/lib/collect.js b/lib/collect.js index f047eae..0e57e0f 100644 --- a/lib/collect.js +++ b/lib/collect.js @@ -44,7 +44,6 @@ module.exports = { const response = entry.response; const request = entry.request; const contentType = util.getContentType(response.content.mimeType); - const responseHeadersFlatten = headers.flatten(response.headers); const content = response.content && response.content.text ? response.content.text : ''; const timings = { @@ -64,6 +63,8 @@ module.exports = { timings.wait + timings.receive; + const responseHeaders = headers.flatten(response.headers); + const requestHeaders = headers.flatten(request.headers); return { type: contentType, url: request.url, @@ -71,11 +72,9 @@ module.exports = { contentSize: response.content.size < 0 ? response.bodySize : response.content.size, headerSize: response.headersSize, - expires: headers.getExpires(responseHeadersFlatten), + expires: headers.getExpires(responseHeaders), status: response.status, - timeSinceLastModified: headers.getTimeSinceLastModified( - responseHeadersFlatten - ), + timeSinceLastModified: headers.getTimeSinceLastModified(responseHeaders), cookieNames: headers.getCookieNames(response.headers), cookieNamesThirdParties: headers.getThirdPartyCookieNames( response.headers, @@ -83,8 +82,8 @@ module.exports = { ), httpVersion: util.getHTTPVersion(response.httpVersion), headers: { - request: request.headers, - response: response.headers + request: requestHeaders, + response: responseHeaders }, totalTime, timings, diff --git a/lib/headers.js b/lib/headers.js index 8d68d1f..9cb1399 100644 --- a/lib/headers.js +++ b/lib/headers.js @@ -26,7 +26,11 @@ module.exports = { */ flatten: headers => { const theHeaders = headers.reduce((result, header) => { - result[header.name.toLowerCase()] = header.value; + if (!result[header.name.toLowerCase()]) { + result[header.name.toLowerCase()] = [header.value]; + } else { + result[header.name.toLowerCase()].push(header.value); + } return result; }, {}); return theHeaders; @@ -45,17 +49,17 @@ module.exports = { if (responseHeaders['cache-control']) { if ( - responseHeaders['cache-control'].indexOf('no-cache') !== -1 || - responseHeaders['cache-control'].indexOf('no-store') !== -1 + responseHeaders['cache-control'][0].indexOf('no-cache') !== -1 || + responseHeaders['cache-control'][0].indexOf('no-store') !== -1 ) { return 0; } - const matches = responseHeaders['cache-control'].match(maxAgeRegExp); + const matches = responseHeaders['cache-control'][0].match(maxAgeRegExp); if (matches) { return parseInt(matches[1], 10); } } else if (responseHeaders.expires) { - const expiresMillis = parseHttpDate(responseHeaders.expires); + const expiresMillis = parseHttpDate(responseHeaders.expires[0]); if (isFinite(expiresMillis)) expireSeconds = (expiresMillis - Date.now()) / 1000; @@ -69,17 +73,25 @@ module.exports = { * @returns {int} the time since the asset was last modified in seconds. */ getTimeSinceLastModified: headers => { - const lastModifiedMillis = parseHttpDate(headers['last-modified']); + if (headers['last-modified']) { + const lastModifiedMillis = parseHttpDate(headers['last-modified'][0]); + + if (!isFinite(lastModifiedMillis)) { + return -1; + } + if (headers.date) { + let dateMillis = parseHttpDate(headers.date[0]); + if (!isFinite(dateMillis)) { + dateMillis = Date.now(); + } - if (!isFinite(lastModifiedMillis)) { + return (dateMillis - lastModifiedMillis) / 1000; + } else { + return (Date.now() - lastModifiedMillis) / 1000; + } + } else { return -1; } - let dateMillis = parseHttpDate(headers.date); - if (!isFinite(dateMillis)) { - dateMillis = Date.now(); - } - - return (dateMillis - lastModifiedMillis) / 1000; }, getCookieNames: headers => { const cookies = headers.filter(h => h.name.match(/^set-cookie$/i)); diff --git a/test/headersTest.js b/test/headersTest.js index 008c493..c3ff76b 100644 --- a/test/headersTest.js +++ b/test/headersTest.js @@ -16,11 +16,15 @@ describe('headers', function() { }, { name: 'HEADER3', value: 'value3' + }, + { + name: 'HEADER3', + value: 'value4' }]; var expected = { - 'header1': 'value1', - 'header2': 'value2', - 'header3': 'value3' + 'header1': ['value1'], + 'header2': ['value2'], + 'header3': ['value3','value4'] }; var flattenedHeaders = headers.flatten(harHeaders); @@ -36,14 +40,14 @@ describe('headers', function() { }); it('should return -1 for invalid last-modified header', () => { - const requestHeaders = {'last-modified': 'xyz'}; + const requestHeaders = {'last-modified': ['xyz']}; const timeSinceLastModified = headers.getTimeSinceLastModified(requestHeaders); assert.equal(timeSinceLastModified, -1); }); it('should return positive time with only last-modified header', () => { const requestHeaders = { - 'last-modified': 'Wed, 26 Aug 2015 12:37:50 GMT' + 'last-modified': ['Wed, 26 Aug 2015 12:37:50 GMT'] }; const timeSinceLastModified = headers.getTimeSinceLastModified(requestHeaders); assert.isTrue(timeSinceLastModified > 0); @@ -51,8 +55,8 @@ describe('headers', function() { it('should handle invalid date header', () => { const requestHeaders = { - 'last-modified': 'Wed, 26 Aug 2025 12:37:50 GMT', - 'date': 'Wedding' + 'last-modified': ['Wed, 26 Aug 2025 12:37:50 GMT'], + 'date': ['Wedding'] }; const timeSinceLastModified = headers.getTimeSinceLastModified(requestHeaders); assert.isTrue(timeSinceLastModified < 0); @@ -60,8 +64,8 @@ describe('headers', function() { it('should calculate diff between last-modified and date headers', () => { const requestHeaders = { - 'last-modified': 'Wed, 26 Aug 2015 12:37:50 GMT', - 'date': 'Wed, 26 Aug 2015 12:37:51 GMT' + 'last-modified': ['Wed, 26 Aug 2015 12:37:50 GMT'], + 'date': ['Wed, 26 Aug 2015 12:37:51 GMT'] }; const timeSinceLastModified = headers.getTimeSinceLastModified(requestHeaders); assert.equal(timeSinceLastModified, 1); @@ -75,44 +79,44 @@ describe('headers', function() { assert.equal(expires, 0); }); it('should return 0 for when cache-control is no-cache', () => { - const responseHeaders = {'cache-control': 'no-cache'}; + const responseHeaders = {'cache-control': ['no-cache']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 0); }); it('should return 0 for when cache-control is no-store', () => { - const responseHeaders = {'cache-control': 'no-store'}; + const responseHeaders = {'cache-control': ['no-store']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 0); }); it('should parse max-age from cache-control', () => { - const responseHeaders = {'cache-control': 'max-age=42'}; + const responseHeaders = {'cache-control': ['max-age=42']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 42); }); it('should handle invalid max-age from cache-control', () => { - const responseHeaders = {'cache-control': 'max-age=xyz42'}; + const responseHeaders = {'cache-control': ['max-age=xyz42']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 0); }); it('should handle invalid expires header', () => { - const responseHeaders = {'expires': 'xyz'}; + const responseHeaders = {'expires': ['xyz']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 0); }); it('should handle invalid expires header', () => { // this example is from nytimes.com - const responseHeaders = {'expires': 'Wed Sep 15 09:14:42 MDT 2010\nThu Sep 16 15:24:47 MDT 2010'}; + const responseHeaders = {'expires': ['Wed Sep 15 09:14:42 MDT 2010\nThu Sep 16 15:24:47 MDT 2010']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 0); }); it('should handle numeric expires header', () => { // this example is from nytimes.com - const responseHeaders = {'expires': '0'}; + const responseHeaders = {'expires': ['0']}; const expires = headers.getExpires(responseHeaders); assert.equal(expires, 0); }); it('should parse valid expires header', () => { - const responseHeaders = {'expires': 'Wed, 26 Aug 2015 12:37:50 GMT'}; + const responseHeaders = {'expires': ['Wed, 26 Aug 2015 12:37:50 GMT']}; const expires = headers.getExpires(responseHeaders); assert.isTrue(expires < 0); });