From 71cb1e38e621137db91db18e134abe3b8b1de593 Mon Sep 17 00:00:00 2001 From: Parth Verma Date: Tue, 25 Jun 2024 17:12:55 -0700 Subject: [PATCH] Added attributes to size function to track transfer size and resource size --- lib/collection/response.js | 74 +++++++++++++++++++++++--------------- test/unit/response.test.js | 38 +++++++++++++------- 2 files changed, 71 insertions(+), 41 deletions(-) diff --git a/lib/collection/response.js b/lib/collection/response.js index e8fd367e8..4b7f81937 100644 --- a/lib/collection/response.js +++ b/lib/collection/response.js @@ -288,6 +288,7 @@ _.assign(Response.prototype, /** @lends Response.prototype */ { var response = PropertyBase.toJSON(this); response._details && (delete response._details); + delete response.downloadedBytes; return response; }, @@ -409,46 +410,61 @@ _.assign(Response.prototype, /** @lends Response.prototype */ { */ size: function () { var sizeInfo = { - body: this.downloadedBytes || 0, + body: 0, header: 0, - total: 0 + total: 0, + transferedBytes: this.downloadedBytes || 0, + resourceBytes: 0 }, contentEncoding = this.headers.get(CONTENT_ENCODING), contentLength = this.headers.get(CONTENT_LENGTH), isCompressed = false, byteLength; - if (!this.downloadedBytes) { - if (_.isString(contentEncoding)) { - // desensitise case of content encoding - contentEncoding = contentEncoding.toLowerCase(); - // eslint-disable-next-line lodash/prefer-includes - isCompressed = (contentEncoding.indexOf('gzip') > -1) || (contentEncoding.indexOf('deflate') > -1); - } - // If there is a stream defined which looks like buffer, use it's data to calculate the body - if (this.stream) { - byteLength = this.stream.byteLength; - sizeInfo.body = util.isNumeric(byteLength) ? byteLength : - /* istanbul ignore next */ - 0; + // if server sent encoded data, we should first try deriving length from headers + if (_.isString(contentEncoding)) { + // desensitise case of content encoding + contentEncoding = contentEncoding.toLowerCase(); + // eslint-disable-next-line lodash/prefer-includes + isCompressed = (contentEncoding.indexOf('gzip') > -1) || (contentEncoding.indexOf('deflate') > -1); + } + + // if 'Content-Length' header is present and encoding is of type gzip/deflate, we take body as declared by + // server. else we need to compute the same. + if (contentLength && isCompressed && util.isNumeric(contentLength)) { + sizeInfo.body = _.parseInt(contentLength, 10); + } + // If there is a stream defined which looks like buffer, use it's data to calculate the body + + if (this.stream) { + byteLength = this.stream.byteLength; + const streamLength = util.isNumeric(byteLength) ? byteLength : + /* istanbul ignore next */ + 0; + + sizeInfo.resourceBytes = streamLength; + + if (!sizeInfo.body) { + sizeInfo.body = streamLength; } - // otherwise, if body is defined, we try get the true length of the body - else if (!_.isNil(this.body)) { - sizeInfo.body = supportsBuffer ? Buffer.byteLength(this.body.toString()) : - /* istanbul ignore next */ - this.body.toString().length; + } + // otherwise, if body is defined, we try get the true length of the body + else if (!_.isNil(this.body)) { + const bodyLength = supportsBuffer ? Buffer.byteLength(this.body.toString()) : + /* istanbul ignore next */ + this.body.toString().length; + + sizeInfo.resourceBytes = bodyLength; + if (!sizeInfo.body) { + sizeInfo.body = bodyLength; } + } - if (!sizeInfo.downloadedBytes) { - if (contentLength && isCompressed && util.isNumeric(contentLength)) { - sizeInfo.downloadedBytes = _.parseInt(contentLength, 10); - } - else { - sizeInfo.downloadedBytes = sizeInfo.body; - } - } + if (!sizeInfo.transferedBytes) { + sizeInfo.transferedBytes = sizeInfo.resourceBytes; } + // size of header is added // https://tools.ietf.org/html/rfc7230#section-3 // HTTP-message = start-line (request-line / status-line) @@ -460,7 +476,7 @@ _.assign(Response.prototype, /** @lends Response.prototype */ { this.headers.contentSize(); // compute the approximate total body size by adding size of header and body - sizeInfo.total = (sizeInfo.body || 0) + (sizeInfo.header); + sizeInfo.total = (sizeInfo.transferedBytes || 0) + (sizeInfo.header); return sizeInfo; }, diff --git a/test/unit/response.test.js b/test/unit/response.test.js index 20d009ff3..3b9ff7063 100644 --- a/test/unit/response.test.js +++ b/test/unit/response.test.js @@ -271,16 +271,14 @@ describe('Response', function () { it('should handle blank responses correctly', function () { var response = new Response(); - expect(response.size()).to.eql({ - body: 0, header: 32, total: 32, downloadedBytes: 0 - }); + expect(response.size()).to.eql({ body: 0, header: 32, total: 32, transferedBytes: 0, resourceBytes: 0 }); }); it('should report downloaded size correctly', function () { - var response = new Response({ body: 'random', downloadedBytes: 6 }); + var response = new Response({ body: 'random', transferedBytes: 6 }); expect(response.size()).to.eql({ - body: 6, header: 32, total: 38 + body: 6, header: 32, total: 38, transferedBytes: 6, resourceBytes: 6 }); }); }); @@ -459,8 +457,8 @@ describe('Response', function () { size1 = response1.size(), size2 = response2.size(); - expect(size1.downloadedBytes + size1.header).to.eql(rawResponse1.header.length + rawResponse1.body.length); - expect(size2.downloadedBytes + size2.header).to.eql(rawResponse1.header.length + rawResponse1.body.length); + expect(size1.body + size1.header).to.eql(rawResponse1.header.length + rawResponse1.body.length); + expect(size2.body + size2.header).to.eql(rawResponse1.header.length + rawResponse1.body.length); }); it('must match the content-length of the response if gzip encoded', function () { @@ -471,7 +469,7 @@ describe('Response', function () { }, response = new Response(rawResponse); - expect(response.size().downloadedBytes).to.equal(10); + expect(response.size().body).to.equal(10); }); it('must match the content-length of the response if deflate encoded', function () { @@ -482,7 +480,7 @@ describe('Response', function () { }, response = new Response(rawResponse); - expect(response.size().downloadedBytes).to.equal(20); + expect(response.size().body).to.equal(20); }); it('must use byteLength from buffer if provided', function () { @@ -496,16 +494,32 @@ describe('Response', function () { expect(response.size().body).to.equal(14); }); - it('must use body size if no downloaded bytes are provided', function () { + it('must use downloaded size if provided', function () { var rawResponse = { code: 200, body: 'something nice', - header: 'Transfer-Encoding: chunked' + header: 'Transfer-Encoding: chunked\nContent-Length: 20', + downloadedBytes: 10 }, response = new Response(rawResponse); - expect(response.size().downloadedBytes).to.equal(14); expect(response.size().body).to.equal(14); + expect(response.size().transferedBytes).to.equal(10); + expect(response.size().resourceBytes).to.equal(14); + }); + + it('must use content length of stream if header is provided', function () { + var rawResponse = { + code: 200, + stream: Buffer.from('something nice'), + header: 'Transfer-Encoding: chunked\nContent-Length: 20\nContent-Encoding: gzip', + downloadedBytes: 10 + }, + response = new Response(rawResponse); + + expect(response.size().body).to.equal(20); + expect(response.size().transferedBytes).to.equal(10); + expect(response.size().resourceBytes).to.equal(14); }); });