Skip to content

Commit

Permalink
perf(repeater): add option to bypass decompression (#503)
Browse files Browse the repository at this point in the history
closes #502
  • Loading branch information
derevnjuk authored Jan 16, 2024
1 parent e7c1da2 commit 0037d62
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 8 deletions.
23 changes: 23 additions & 0 deletions src/RequestExecutor/HttpRequestExecutor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,29 @@ describe('HttpRequestExecutor', () => {
expect(response.body).toEqual(expected);
});

it('should prevent decoding response body if decompress option is disabled', async () => {
when(spiedExecutorOptions.maxContentLength).thenReturn(1);
when(spiedExecutorOptions.whitelistMimes).thenReturn(['text/plain']);
const { request, requestOptions } = createRequest({
decompress: false,
encoding: 'base64'
});
const expected = 'x'.repeat(100);
const body = await promisify(gzip)(expected, {
flush: constants.Z_SYNC_FLUSH,
finishFlush: constants.Z_SYNC_FLUSH
});
nock(requestOptions.url).get('/').reply(200, body, {
'content-type': 'text/plain',
'content-encoding': 'gzip'
});

const response = await executor.execute(request);

expect(response.body).toEqual(body.toString('base64'));
expect(response.headers).toMatchObject({ 'content-encoding': 'gzip' });
});

it('should decode response body if content-encoding is gzip', async () => {
when(spiedExecutorOptions.maxContentLength).thenReturn(1);
when(spiedExecutorOptions.whitelistMimes).thenReturn(['text/plain']);
Expand Down
32 changes: 24 additions & 8 deletions src/RequestExecutor/HttpRequestExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,26 +191,37 @@ export class HttpRequestExecutor implements RequestExecutor {
);
}

private async truncateResponse(req: Request, res: IncomingMessage) {
private async truncateResponse(
{ decompress, encoding, maxContentSize }: Request,
res: IncomingMessage
) {
if (this.responseHasNoBody(res)) {
logger.debug('The response does not contain any body.');

return { res, body: '' };
}

const { type, encoding } = this.parseContentType(res);
const contentType = this.parseContentType(res);
const { type } = contentType;
const requiresTruncating = !this.options.whitelistMimes?.some(
(mime: string) => type.startsWith(mime)
);

const maxBodySize =
(req.maxContentSize ?? this.options.maxContentLength) * 1024;
const body = await this.parseBody(res, { maxBodySize, requiresTruncating });
(maxContentSize ?? this.options.maxContentLength) * 1024;
const body = await this.parseBody(res, {
maxBodySize,
requiresTruncating,
decompress
});

res.headers['content-length'] = body.byteLength.toFixed();
delete res.headers['content-encoding'];

return { res, body: iconv.decode(body, req.encoding ?? encoding) };
if (decompress) {
delete res.headers['content-encoding'];
}

return { res, body: iconv.decode(body, encoding ?? contentType.encoding) };
}

private parseContentType(res: IncomingMessage): {
Expand Down Expand Up @@ -276,11 +287,16 @@ export class HttpRequestExecutor implements RequestExecutor {

private async parseBody(
res: IncomingMessage,
options: { maxBodySize: number; requiresTruncating: boolean }
options: {
maxBodySize: number;
requiresTruncating: boolean;
decompress: boolean;
}
): Promise<Buffer> {
const chunks: Buffer[] = [];
const stream = options.decompress ? this.unzipBody(res) : res;

for await (const chuck of this.unzipBody(res)) {
for await (const chuck of stream) {
chunks.push(chuck);
}

Expand Down
4 changes: 4 additions & 0 deletions src/RequestExecutor/Request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface RequestOptions {
encoding?: 'base64';
maxContentSize?: number;
timeout?: number;
decompress?: boolean;
}

export interface Cert {
Expand Down Expand Up @@ -51,6 +52,7 @@ export class Request {
public readonly correlationIdRegex?: RegExp;
public readonly encoding?: 'base64';
public readonly maxContentSize?: number;
public readonly decompress?: boolean;
public readonly timeout?: number;

private _method: string;
Expand Down Expand Up @@ -99,6 +101,7 @@ export class Request {
correlationIdRegex,
maxContentSize,
encoding,
decompress = true,
headers = {}
}: RequestOptions) {
this.protocol = protocol;
Expand Down Expand Up @@ -127,6 +130,7 @@ export class Request {
this.encoding = encoding;
this.timeout = timeout;
this.maxContentSize = maxContentSize;
this.decompress = !!decompress;
}

public setHeaders(headers: Record<string, string | string[]>): void {
Expand Down

0 comments on commit 0037d62

Please sign in to comment.