Skip to content

Commit 639cdfc

Browse files
committed
Move request step definition materialization into the impl classes
This allows us to more easily control this process for cases that need to instantiate impl-only fields etc. It also helps demarcate the two step process: first we turn data into definition (for remote clients) then we turn definition into implementation.
1 parent fbaf20e commit 639cdfc

File tree

3 files changed

+47
-14
lines changed

3 files changed

+47
-14
lines changed

src/rules/requests/request-rule.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,21 @@ export class RequestRule implements RequestRule {
5555
this.matchers = data.matchers;
5656
this.completionChecker = data.completionChecker;
5757

58-
this.steps = data.steps.map((stepDefinition, i) => {
59-
const step = Object.assign(
60-
Object.create(StepLookup[stepDefinition.type].prototype),
61-
stepDefinition
62-
) as RequestStepImpl;
58+
this.steps = data.steps.map(<S extends RequestStepDefinition>(stepDefinition: S, i: number) => {
59+
const StepImplClass = StepLookup[stepDefinition.type];
6360

64-
if (StepLookup[step.type].isFinal && i !== data.steps.length - 1) {
61+
if (StepImplClass.isFinal && i !== data.steps.length - 1) {
6562
throw new Error(
6663
`Cannot create a rule with a final step before the last position ("${
67-
step.explain()
64+
stepDefinition.explain()
6865
}" in position ${i + 1} of ${data.steps.length})`
6966
);
7067
}
7168

72-
return step;
69+
// All step impls have a fromDefinition static method that turns a definition into a
70+
// full impl (copying data and initializing any impl-only fields). Note that for remote clients,
71+
// the definition itself has already been deserialized by impl.deserialize(data) beforehand.
72+
return StepImplClass.fromDefinition(stepDefinition as any) as RequestStepImpl;
7373
});
7474
}
7575

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

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -755,11 +755,6 @@ export class PassThroughStep extends Serializable implements RequestStepDefiniti
755755

756756
public readonly simulateConnectionErrors: boolean;
757757

758-
// Used in subclass - awkwardly needs to be initialized here to ensure that its set when using a
759-
// step built from a definition. In future, we could improve this (compose instead of inheritance
760-
// to better control step construction?) but this will do for now.
761-
protected outgoingSockets = new Set<net.Socket>();
762-
763758
constructor(options: PassThroughStepOptions = {}) {
764759
super();
765760

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

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,19 @@ export interface RequestStepImpl extends RequestStepDefinition {
165165
>;
166166
}
167167

168+
const copyDefinitionToImpl = (defn: RequestStepDefinition): RequestStepImpl =>
169+
Object.assign(Object.create(StepLookup[defn.type].prototype), defn);
170+
168171
export interface RequestStepOptions {
169172
emitEventCallback?: (type: string, event: unknown) => void;
170173
keyLogStream?: Writable;
171174
debug: boolean;
172175
}
173176

174177
export class FixedResponseStepImpl extends FixedResponseStep {
178+
179+
static readonly fromDefinition = copyDefinitionToImpl;
180+
175181
async handle(_request: OngoingRequest, response: OngoingResponse) {
176182
if (this.headers) dropDefaultHeaders(response);
177183
writeHead(response, this.status, this.statusMessage, this.headers);
@@ -231,6 +237,8 @@ async function writeResponseFromCallback(
231237

232238
export class CallbackStepImpl extends CallbackStep {
233239

240+
static readonly fromDefinition = copyDefinitionToImpl;
241+
234242
async handle(request: OngoingRequest, response: OngoingResponse) {
235243
let req = await waitForCompletedRequest(request);
236244

@@ -285,6 +293,8 @@ export class CallbackStepImpl extends CallbackStep {
285293

286294
export class StreamStepImpl extends StreamStep {
287295

296+
static readonly fromDefinition = copyDefinitionToImpl;
297+
288298
async handle(request: OngoingRequest, response: OngoingResponse) {
289299
if (!this.stream.done) {
290300
if (this.headers) dropDefaultHeaders(response);
@@ -366,6 +376,9 @@ export class StreamStepImpl extends StreamStep {
366376
}
367377

368378
export class FileStepImpl extends FileStep {
379+
380+
static readonly fromDefinition = copyDefinitionToImpl;
381+
369382
async handle(_request: OngoingRequest, response: OngoingResponse) {
370383
// Read the file first, to ensure we error cleanly if it's unavailable
371384
const fileContents = await fs.readFile(this.filePath);
@@ -423,6 +436,11 @@ const h2ProtocolQueue = new Map();
423436

424437
export class PassThroughStepImpl extends PassThroughStep {
425438

439+
// In this case, we actually use the constructor, to ensure we initialize fields as normal:
440+
static readonly fromDefinition = (defn: PassThroughStep) => Object.assign(new PassThroughStepImpl(), defn);
441+
442+
protected outgoingSockets = new Set<net.Socket>();
443+
426444
private _trustedCACertificates: MaybePromise<Array<string> | undefined>;
427445
private async trustedCACertificates(): Promise<Array<string> | undefined> {
428446
if (!this.extraCACertificates.length) return undefined;
@@ -1379,6 +1397,9 @@ export class PassThroughStepImpl extends PassThroughStep {
13791397
}
13801398

13811399
export class CloseConnectionStepImpl extends CloseConnectionStep {
1400+
1401+
static readonly fromDefinition = () => new CloseConnectionStepImpl();
1402+
13821403
async handle(request: OngoingRequest) {
13831404
const socket: net.Socket = (request as any).socket;
13841405
socket.end();
@@ -1387,6 +1408,9 @@ export class CloseConnectionStepImpl extends CloseConnectionStep {
13871408
}
13881409

13891410
export class ResetConnectionStepImpl extends ResetConnectionStep {
1411+
1412+
static readonly fromDefinition = () => new ResetConnectionStepImpl();
1413+
13901414
constructor() {
13911415
super();
13921416
requireSocketResetSupport();
@@ -1408,13 +1432,19 @@ export class ResetConnectionStepImpl extends ResetConnectionStep {
14081432
}
14091433

14101434
export class TimeoutStepImpl extends TimeoutStep {
1435+
1436+
static readonly fromDefinition = () => new TimeoutStepImpl();
1437+
14111438
async handle() {
14121439
// Do nothing, leaving the socket open but never sending a response.
14131440
return new Promise<void>(() => {});
14141441
}
14151442
}
14161443

14171444
export class JsonRpcResponseStepImpl extends JsonRpcResponseStep {
1445+
1446+
static readonly fromDefinition = copyDefinitionToImpl;
1447+
14181448
async handle(request: OngoingRequest, response: OngoingResponse) {
14191449
const data: any = await request.body.asJson()
14201450
.catch(() => {}); // Handle parsing errors with the check below
@@ -1436,13 +1466,19 @@ export class JsonRpcResponseStepImpl extends JsonRpcResponseStep {
14361466
}
14371467

14381468
export class DelayStepImpl extends DelayStep {
1469+
1470+
static readonly fromDefinition = copyDefinitionToImpl;
1471+
14391472
async handle(): Promise<{ continue: true }> {
14401473
await delay(this.delayMs);
14411474
return { continue: true };
14421475
}
14431476
}
14441477

14451478
export class WaitForRequestBodyStepImpl extends WaitForRequestBodyStep {
1479+
1480+
static readonly fromDefinition = () => new WaitForRequestBodyStepImpl();
1481+
14461482
async handle(request: OngoingRequest): Promise<{ continue: true }> {
14471483
await request.body.asBuffer();
14481484
return { continue: true };
@@ -1458,6 +1494,8 @@ const encodeWebhookBody = (body: Buffer) => {
14581494

14591495
export class WebhookStepImpl extends WebhookStep {
14601496

1497+
static readonly fromDefinition = copyDefinitionToImpl;
1498+
14611499
private sendEvent(data: {
14621500
eventType: string;
14631501
eventData: {};
@@ -1530,7 +1568,7 @@ export class WebhookStepImpl extends WebhookStep {
15301568
}
15311569
}
15321570

1533-
export const StepLookup: typeof StepDefinitionLookup = {
1571+
export const StepLookup = {
15341572
'simple': FixedResponseStepImpl,
15351573
'callback': CallbackStepImpl,
15361574
'stream': StreamStepImpl,

0 commit comments

Comments
 (0)