Skip to content

Commit f80a2de

Browse files
committed
WIP: skip trackResponse overhead when there are no listeners
1 parent 1589ca9 commit f80a2de

File tree

3 files changed

+52
-18
lines changed

3 files changed

+52
-18
lines changed

src/rules/requests/request-rule.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export class RequestRule implements RequestRule {
4444

4545
public id: string;
4646
public readonly priority: number;
47+
public readonly needsResponseTracking: boolean;
4748
public requests: Promise<CompletedRequest>[] = [];
4849
public requestCount = 0;
4950

@@ -54,6 +55,7 @@ export class RequestRule implements RequestRule {
5455
this.priority = data.priority ?? RulePriority.DEFAULT;
5556
this.matchers = data.matchers;
5657
this.completionChecker = data.completionChecker;
58+
this.needsResponseTracking = data.steps.some(s => s.needsResponseTracking);
5759

5860
this.steps = data.steps.map(<S extends RequestStepDefinition>(stepDefinition: S, i: number) => {
5961
const StepImplClass = StepLookup[stepDefinition.type];

src/rules/requests/request-step-definitions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ the serialized output from the serialize() methods defined here and creates a wo
6868
*/
6969
export interface RequestStepDefinition extends Explainable, Serializable {
7070
type: keyof typeof StepDefinitionLookup;
71+
readonly needsResponseTracking?: boolean;
7172
}
7273

7374
export type SerializedBuffer = { type: 'Buffer', data: number[] };
@@ -1093,6 +1094,10 @@ export class WebhookStep extends Serializable implements RequestStepDefinition {
10931094
readonly type = 'webhook';
10941095
static readonly isFinal = false;
10951096

1097+
get needsResponseTracking() {
1098+
return this.events.includes('response');
1099+
}
1100+
10961101
constructor(
10971102
public readonly url: string,
10981103
public readonly events: RequestWebhookEvents[]

src/server/mockttp-server.ts

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
113113

114114
private requestRuleSets: { [priority: number]: RequestRule[] } = {};
115115
private webSocketRuleSets: { [priority: number]: WebSocketRule[] } = {};
116+
private rulesNeedResponseTracking: boolean = false;
116117

117118
private httpsOptions: MockttpHttpsOptions | undefined;
118119
private isHttp2Enabled: boolean | 'fallback';
@@ -287,6 +288,7 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
287288

288289
const rules = ruleData.map((ruleDatum) => new RequestRule(ruleDatum));
289290
this.requestRuleSets = _.groupBy(rules, r => r.priority);
291+
this.rulesNeedResponseTracking = rules.some(r => r.needsResponseTracking);
290292

291293
return Promise.resolve(rules.map(r => new ServerMockedEndpoint(r)));
292294
}
@@ -295,6 +297,7 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
295297
return Promise.resolve(ruleData.map((ruleDatum) => {
296298
const rule = new RequestRule(ruleDatum);
297299
this.addToRuleSets(this.requestRuleSets, rule);
300+
if (rule.needsResponseTracking) this.rulesNeedResponseTracking = true;
298301
return new ServerMockedEndpoint(rule);
299302
}));
300303
}
@@ -384,6 +387,47 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
384387
});
385388
}
386389

390+
private prepareResponse(
391+
rawResponse: http.ServerResponse,
392+
request: OngoingRequest
393+
): { response: OngoingResponse, hasResponseListener: boolean } {
394+
const hasResponseListener = this.eventEmitter.listenerCount('response') > 0;
395+
const needsResponseTracking = hasResponseListener
396+
|| this.eventEmitter.listenerCount('response-initiated') > 0
397+
|| this.eventEmitter.listenerCount('response-body-data') > 0
398+
|| this.eventEmitter.listenerCount('abort') > 0 // Abort events include response timing data
399+
|| this.rulesNeedResponseTracking;
400+
401+
let response: OngoingResponse;
402+
if (needsResponseTracking) {
403+
response = trackResponse(
404+
rawResponse,
405+
request.timingEvents,
406+
request.tags,
407+
{
408+
maxSize: this.maxBodySize,
409+
onWriteHead: () => this.announceInitialResponseAsync(response),
410+
onBodyData: this.eventEmitter.listenerCount('response-body-data') > 0
411+
? this.announceBodyDataAsync.bind(this, 'response')
412+
: undefined
413+
}
414+
);
415+
if (hasResponseListener) {
416+
// Start buffering response body if there's somebody who
417+
// might want to hear about it later
418+
response.body.asBuffer().catch(() => {});
419+
}
420+
} else {
421+
// When no response-related events are registered, skip the tracking overhead
422+
// (PassThrough stream creation, write/writeHead/end interception)
423+
response = rawResponse as OngoingResponse;
424+
response.timingEvents = request.timingEvents;
425+
response.tags = request.tags;
426+
}
427+
428+
return { response, hasResponseListener };
429+
}
430+
387431
private announceInitialRequestAsync(request: OngoingRequest) {
388432
if (this.eventEmitter.listenerCount('request-initiated') === 0) return;
389433

@@ -698,24 +742,7 @@ export class MockttpServer extends AbstractMockttp implements Mockttp {
698742

699743
this.announceInitialRequestAsync(request);
700744

701-
const response = trackResponse(
702-
rawResponse,
703-
request.timingEvents,
704-
request.tags,
705-
{
706-
maxSize: this.maxBodySize,
707-
onWriteHead: () => this.announceInitialResponseAsync(response),
708-
onBodyData: this.eventEmitter.listenerCount('response-body-data') > 0
709-
? this.announceBodyDataAsync.bind(this, 'response')
710-
: undefined
711-
}
712-
);
713-
const hasResponseListener = this.eventEmitter.listenerCount('response') > 0;
714-
if (hasResponseListener) {
715-
// Start buffering response body if there's somebody who
716-
// might want to hear about it later
717-
response.body.asBuffer().catch(() => {});
718-
}
745+
const { response, hasResponseListener } = this.prepareResponse(rawResponse, request);
719746

720747
response.id = request.id;
721748
response.on('error', (error) => {

0 commit comments

Comments
 (0)