Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added variable to track the downloaded content size in response #1362

Merged
merged 9 commits into from
Jul 29, 2024
2 changes: 2 additions & 0 deletions CHANGELOG.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
unreleased:
fixed bugs:
- GH-1366 Fixed a bug where $randomAvatarImage was returning an invalid URL
chores:
- GH-1362 Updated `Request~size` to handle `downloadedBytes` property

4.4.0:
date: 2024-02-28
Expand Down
68 changes: 35 additions & 33 deletions lib/collection/response.js
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ _.assign(Response.prototype, /** @lends Response.prototype */ {
update (options) {
// options.stream accepts Buffer, Buffer.toJSON() or base64 string
// @todo this temporarily doubles the memory footprint (options.stream + generated buffer).
var stream = normalizeStream(options.stream);
const stream = normalizeStream(options.stream);

_.mergeDefined((this._details = _.clone(httpReasons.lookup(options.code))), {
name: _.choose(options.reason, options.status),
Expand Down Expand Up @@ -255,7 +255,13 @@ _.assign(Response.prototype, /** @lends Response.prototype */ {
* @private
* @type {Number}
*/
responseSize: stream && stream.byteLength
responseSize: stream && stream.byteLength,

/**
* @private
* @type {Number}
*/
downloadedBytes: options.downloadedBytes
});
}
});
Expand Down Expand Up @@ -397,61 +403,57 @@ _.assign(Response.prototype, /** @lends Response.prototype */ {
},

/**
* Get the response size by computing the same from content length header or using the actual response body.
* @typedef Response.sizeInfo
* @property {Number} body - size of the response body in bytes
* @property {Number} header - size of the response header in bytes
* @property {Number} total - total size of the response body and header in bytes
*/
/**
* Get the response size by computing the same from content length header or
* using the actual response body.
*
* @returns {Number}
* @todo write unit tests
* @returns {Response.sizeInfo} - Response size object
*/
size: function () {
var sizeInfo = {
const sizeInfo = {
body: 0,
header: 0,
total: 0
},
contentLength = this.headers.get(CONTENT_LENGTH);

contentEncoding = this.headers.get(CONTENT_ENCODING),
contentLength = this.headers.get(CONTENT_LENGTH),
isCompressed = false,
byteLength;

// 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);
// Set body size from downloadedBytes if available
if (util.isNumeric(this.downloadedBytes)) {
sizeInfo.body = this.downloadedBytes;
}

// 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)) {
// Rely on content-length header
else if (contentLength && util.isNumeric(contentLength)) {
sizeInfo.body = _.parseInt(contentLength, 10);
}
// if there is a stream defined which looks like buffer, use it's data and move on
else if (this.stream) {
byteLength = this.stream.byteLength;
sizeInfo.body = util.isNumeric(byteLength) ? byteLength :
/* istanbul ignore next */
0;
// Fall back to stream if available
else if (this.stream && util.isNumeric(this.stream.byteLength)) {
sizeInfo.body = this.stream.byteLength;
}
// otherwise, if body is defined, we try get the true length of the body
// Or, calculate size from body string
else if (!_.isNil(this.body)) {
sizeInfo.body = supportsBuffer ? Buffer.byteLength(this.body.toString()) :
sizeInfo.body = supportsBuffer ?
Buffer.byteLength(this.body.toString()) :
/* istanbul ignore next */
this.body.toString().length;
}

// size of header is added
// Header size is calculated as per the HTTP message format
// https://tools.ietf.org/html/rfc7230#section-3
// HTTP-message = start-line (request-line / status-line)
// *( header-field CRLF )
// CRLF
// [ message-body ]
// status-line = HTTP-version SP status-code SP reason-phrase CRLF
sizeInfo.header = (HTTP_X_X + SP + this.code + SP + this.reason() + CRLF + CRLF).length +
this.headers.contentSize();
sizeInfo.header = (
HTTP_X_X + SP + this.code + SP + this.reason() + CRLF + CRLF
).length + this.headers.contentSize();

// compute the approximate total body size by adding size of header and body
// Compute the approximate total body size by adding size of header and body
sizeInfo.total = (sizeInfo.body || 0) + (sizeInfo.header);

return sizeInfo;
Expand Down
48 changes: 47 additions & 1 deletion test/unit/response.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,20 @@ 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 });
});

it('should handle string responses correctly', function () {
var response = new Response({ body: 'random' });

expect(response.size()).to.eql({ body: 6, header: 32, total: 38 });
});

it('should report downloaded size correctly', function () {
var response = new Response({ body: 'random', downloadedBytes: 6 });

expect(response.size()).to.eql({
body: 0, header: 32, total: 32
body: 6, header: 32, total: 38
});
});
});
Expand Down Expand Up @@ -477,6 +489,17 @@ describe('Response', function () {
expect(response.size().body).to.equal(20);
});

it('must match the content-length of the response if brotli encoded', function () {
var rawResponse = {
code: 200,
body: 'gzipped content xyzxyzxyzxyzxyzxyz',
header: 'Content-Encoding: br\nContent-Length: 20'
},
response = new Response(rawResponse);

expect(response.size().body).to.equal(20);
});

it('must use byteLength from buffer if provided', function () {
var rawResponse = {
code: 200,
Expand All @@ -487,6 +510,29 @@ describe('Response', function () {

expect(response.size().body).to.equal(14);
});

it('must use downloaded size if provided', function () {
var rawResponse = {
code: 200,
body: 'something nice',
header: 'Transfer-Encoding: chunked\nContent-Length: 20',
downloadedBytes: 10
},
response = new Response(rawResponse);

expect(response.size().body).to.equal(10);
});

it('must use content length of stream if header is provided and downloaded bytes is not present', function () {
var rawResponse = {
code: 200,
stream: Buffer.from('something nice'),
header: 'Transfer-Encoding: chunked\nContent-Length: 20\nContent-Encoding: gzip'
},
response = new Response(rawResponse);

expect(response.size().body).to.equal(20);
});
});

describe('toJSON', function () {
Expand Down
18 changes: 15 additions & 3 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Type definitions for postman-collection 4.3.0
// Type definitions for postman-collection 4.4.0
// Project: https://github.com/postmanlabs/postman-collection
// Definitions by: PostmanLabs
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
Expand Down Expand Up @@ -2005,6 +2005,16 @@ declare module "postman-collection" {
stream?: Buffer | ArrayBuffer;
responseTime: number;
};
/**
* @property body - size of the response body in bytes
* @property header - size of the response header in bytes
* @property total - total size of the response body and header in bytes
*/
type sizeInfo = {
body: number;
header: number;
total: number;
};
/**
* Returns the durations of each request phase in milliseconds
* @example
Expand Down Expand Up @@ -2129,9 +2139,11 @@ declare module "postman-collection" {
*/
dataURI(): string;
/**
* Get the response size by computing the same from content length header or using the actual response body.
* Get the response size by computing the same from content length header or
* using the actual response body.
* @returns - Response size object
*/
size(): number;
size(): Response.sizeInfo;
/**
* Check whether an object is an instance of ItemGroup.
* @param obj - -
Expand Down
Loading