Skip to content

Commit

Permalink
FormidableLabs#64 Pass proxy fn in event
Browse files Browse the repository at this point in the history
  • Loading branch information
ianwsperber committed Mar 29, 2020
1 parent d2127d1 commit 4f0a50c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 66 deletions.
115 changes: 56 additions & 59 deletions src/interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,27 @@ interface ProxyRequestOptions extends http.RequestOptions {
proxying?: boolean;
}

/**
* Event emitted whenever we intercept an HTTP request
*/
export interface IInterceptEvent {
/**
* The client request which initiated the HTTP request
*/
clientRequest: http.ClientRequest;
comparatorFn?: ComparatorFn;
/**
* Request arriving to our MITM proxy
*/
interceptedRequest: http.IncomingMessage;
/**
* Response from our MITM proxy
*/
interceptedResponse: http.ServerResponse;
/**
* Proxy the intercepted request to its original destination
*/
proxy: () => void;
requestNumber: number;
requestSerializer: RequestSerializer;
}
Expand Down Expand Up @@ -61,30 +77,12 @@ interface IInterceptEvents {
*/
export default class Interceptor extends EventEmitter implements IInterceptEvents {
public requestNumber: number = 0;
/**
* Whether or not to send requests to their original destination
*/
private shouldProxy: boolean = true;
private clientRequests: ClientRequestTracker = {};
private comparatorFn?: ComparatorFn;
private mitm?: Mitm.Mitm;
private origOnSocket?: (socket: Socket) => void;
private ignorePorts: number[] = [];

constructor(options?: { shouldProxy: boolean }) {
super();
const { shouldProxy = true } = options || {};
this.shouldProxy = shouldProxy;
}

/**
* Enable/disable proxying. If proxying, requests are sent to their original destination.
* @param shouldProxy Whether or not to proxy
*/
public proxy(shouldProxy: boolean): void {
this.shouldProxy = shouldProxy;
}

/**
* Enables intercepting all outbound HTTP requests.
* @param options Intercept options
Expand Down Expand Up @@ -205,56 +203,55 @@ export default class Interceptor extends EventEmitter implements IInterceptEvent

debugReq('Emitting "intercept"');

const proxy = () => {
const request = isHttps ? https.request : http.request;
const proxiedRequest: http.ClientRequest = request({
...clientOptions,
headers: _.omit(clientRequest.getHeaders(), YESNO_INTERNAL_HTTP_HEADER),
path: (clientRequest as ClientRequestFull).path,
proxying: true,
} as ProxyRequestOptions);

(readable as any).pipeline(interceptedRequest, requestSerializer, proxiedRequest);

interceptedRequest.on('error', (e: any) => debugReq('Error on intercepted request:', e));
interceptedRequest.on('aborted', () => {
debugReq('Intercepted request aborted');
proxiedRequest.abort();
});

proxiedRequest.on('timeout', (e: any) => debugReq('Proxied request timeout', e));
proxiedRequest.on('error', (e: any) => debugReq('Proxied request error', e));
proxiedRequest.on('aborted', () => debugReq('Proxied request aborted'));
proxiedRequest.on('response', (proxiedResponse: http.IncomingMessage) => {
const responseSerializer = new ResponseSerializer(proxiedResponse);
debugReq('proxied response (%d)', proxiedResponse.statusCode);
if (proxiedResponse.statusCode) {
interceptedResponse.writeHead(proxiedResponse.statusCode, proxiedResponse.headers);
}
(readable as any).pipeline(proxiedResponse, responseSerializer, interceptedResponse);

proxiedResponse.on('end', () => {
debugReq('Emitting "proxied"');
this.emit('proxied', {
requestNumber,
requestSerializer,
responseSerializer,
});
});
});
};

// YesNo will listen for this event to mock the response
this.emit('intercept', {
clientRequest,
comparatorFn: this.comparatorFn,
interceptedRequest,
interceptedResponse,
proxy,
requestNumber,
requestSerializer,
} as IInterceptEvent);

if (!this.shouldProxy) {
return;
}

const request = isHttps ? https.request : http.request;
const proxiedRequest: http.ClientRequest = request({
...clientOptions,
headers: _.omit(clientRequest.getHeaders(), YESNO_INTERNAL_HTTP_HEADER),
path: (clientRequest as ClientRequestFull).path,
proxying: true,
} as ProxyRequestOptions);

(readable as any).pipeline(interceptedRequest, requestSerializer, proxiedRequest);

interceptedRequest.on('error', (e: any) => debugReq('Error on intercepted request:', e));
interceptedRequest.on('aborted', () => {
debugReq('Intercepted request aborted');
proxiedRequest.abort();
});

proxiedRequest.on('timeout', (e: any) => debugReq('Proxied request timeout', e));
proxiedRequest.on('error', (e: any) => debugReq('Proxied request error', e));
proxiedRequest.on('aborted', () => debugReq('Proxied request aborted'));
proxiedRequest.on('response', (proxiedResponse: http.IncomingMessage) => {
const responseSerializer = new ResponseSerializer(proxiedResponse);
debugReq('proxied response (%d)', proxiedResponse.statusCode);
if (proxiedResponse.statusCode) {
interceptedResponse.writeHead(proxiedResponse.statusCode, proxiedResponse.headers);
}
(readable as any).pipeline(proxiedResponse, responseSerializer, interceptedResponse);

proxiedResponse.on('end', () => {
debugReq('Emitting "proxied"');
this.emit('proxied', {
requestNumber,
requestSerializer,
responseSerializer,
});
});
});
}

private trackSocketAndClientOptions(
Expand Down
10 changes: 3 additions & 7 deletions src/yesno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ export class YesNo implements IFiltered {
}

private createInterceptor() {
const interceptor = new Interceptor({ shouldProxy: !this.isMode(Mode.Mock) });
const interceptor = new Interceptor();

interceptor.on('intercept', this.onIntercept.bind(this));
interceptor.on('proxied', this.onProxied.bind(this));
Expand All @@ -266,7 +266,8 @@ export class YesNo implements IFiltered {
this.recordRequest(event.requestSerializer, event.requestNumber);

if (!this.ctx.hasResponsesDefinedForMatchers() && !this.isMode(Mode.Mock)) {
return;
// No need to mock, send event to its original destination
return event.proxy();
}

try {
Expand All @@ -291,7 +292,6 @@ export class YesNo implements IFiltered {
}

private onProxied({ requestSerializer, responseSerializer, requestNumber }: IProxiedEvent): void {
this.recordRequest(requestSerializer, requestNumber);
this.recordResponse(
requestSerializer.serialize(),
responseSerializer.serialize(),
Expand All @@ -301,10 +301,6 @@ export class YesNo implements IFiltered {

private setMode(mode: Mode) {
this.ctx.mode = mode;

if (this.interceptor) {
this.interceptor.proxy(!this.isMode(Mode.Mock));
}
}

private getCollection(
Expand Down

0 comments on commit 4f0a50c

Please sign in to comment.