Skip to content

Commit 08976df

Browse files
committed
feat(session-state): record mitm resource states
1 parent 85af0fc commit 08976df

27 files changed

+325
-141
lines changed

commons/interfaces/IHttpResourceLoadDetails.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default interface IHttpResourceLoadDetails {
2020
firstRedirectingUrl?: string; // track back to first redirection
2121
redirectedToUrl?: string;
2222
clientAlpn: string;
23-
23+
dnsResolvedIp?: string;
2424
url: URL;
2525
method: string;
2626
requestTime: Date;

core/lib/Session.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import RequestSession, {
99
IRequestSessionHttpErrorEvent,
1010
IRequestSessionRequestEvent,
1111
IRequestSessionResponseEvent,
12+
IResourceStateChangeEvent,
1213
} from '@secret-agent/mitm/handlers/RequestSession';
1314
import * as Os from 'os';
1415
import IPuppetContext, {
@@ -105,7 +106,8 @@ export default class Session {
105106
const requestSession = this.mitmRequestSession;
106107
requestSession.on('request', this.onMitmRequest.bind(this));
107108
requestSession.on('response', this.onMitmResponse.bind(this));
108-
requestSession.on('httpError', this.onMitmError.bind(this));
109+
requestSession.on('http-error', this.onMitmError.bind(this));
110+
requestSession.on('resource-state', this.onResourceStates.bind(this));
109111
}
110112

111113
public async createTab() {
@@ -174,6 +176,10 @@ export default class Session {
174176
this.sessionState.captureResourceError(tabId, event.request, event.error);
175177
}
176178

179+
private onResourceStates(event: IResourceStateChangeEvent) {
180+
this.sessionState.captureResourceState(event.context.id, event.context.stateChanges);
181+
}
182+
177183
private async onNewTab(
178184
parentTab: Tab,
179185
page: IPuppetPage,

injected-scripts/scripts/jsPath.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,10 @@ class ObjectAtPath {
252252
if (!element) return false;
253253
const { top, right, left, bottom } = element.getBoundingClientRect();
254254
// adjust coordinates to get more accurate results
255-
if (document.elementFromPoint(left + 1, top + 1) !== element) return true;
256-
if (document.elementFromPoint(left + 1, bottom - 1) !== element) return true;
257-
if (document.elementFromPoint(right - 1, top + 1) !== element) return true;
258-
if (document.elementFromPoint(right - 1, bottom - 1) !== element) return true;
255+
if (!element.contains(document.elementFromPoint(left + 1, top + 1))) return true;
256+
if (!element.contains(document.elementFromPoint(left + 1, bottom - 1))) return true;
257+
if (!element.contains(document.elementFromPoint(right - 1, top + 1))) return true;
258+
if (!element.contains(document.elementFromPoint(right - 1, bottom - 1))) return true;
259259
return false;
260260
}
261261

mitm/handlers/BaseHttpHandler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import HeadersHandler from './HeadersHandler';
55
import CookieHandler from './CookieHandler';
66
import MitmRequestContext from '../lib/MitmRequestContext';
77
import HttpResponseCache from '../lib/HttpResponseCache';
8+
import ResourceState from '../interfaces/ResourceState';
89

910
const { log } = Log(module);
1011

@@ -39,9 +40,10 @@ export default abstract class BaseHttpHandler {
3940
method: context.method,
4041
ws: context.isUpgrade,
4142
});
42-
if (session.isClosing) return;
43+
if (session.isClosing) return context.setState(ResourceState.SessionClosed);
4344

4445
if (BlockHandler.shouldBlockRequest(context)) {
46+
context.setState(ResourceState.Blocked);
4547
log.info(`Http.RequestBlocked`, {
4648
sessionId: session.sessionId,
4749
url: context.url.href,
@@ -58,7 +60,7 @@ export default abstract class BaseHttpHandler {
5860
context.cacheHandler.onRequest();
5961

6062
// do one more check on the session before doing a connect
61-
if (session.isClosing) return;
63+
if (session.isClosing) return context.setState(ResourceState.SessionClosed);
6264

6365
this.context.proxyToServerRequest = await session.requestAgent.request(context);
6466
this.context.proxyToServerRequest.on(

mitm/handlers/BlockHandler.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import IMitmRequestContext from '../interfaces/IMitmRequestContext';
1+
import IMitmRequestContext from "../interfaces/IMitmRequestContext";
2+
import ResourceState from "../interfaces/ResourceState";
23

34
export default class BlockHandler {
45
public static shouldBlockRequest(ctx: IMitmRequestContext) {
6+
ctx.setState(ResourceState.BlockHandler);
57
const session = ctx.requestSession;
68
if (!session) return false;
79
if (session.isClosing) return true;
810

11+
912
const shouldBlock =
1013
(ctx.resourceType && session.blockedResources?.types?.includes(ctx.resourceType)) ||
1114
session.shouldBlockRequest(ctx.url.href);

mitm/handlers/CacheHandler.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import HttpResponseCache from '../lib/HttpResponseCache';
22
import IMitmRequestContext from '../interfaces/IMitmRequestContext';
3+
import ResourceState from "../interfaces/ResourceState";
34

45
export default class CacheHandler {
56
public didProposeCachedResource = false;
@@ -19,6 +20,7 @@ export default class CacheHandler {
1920

2021
public onRequest() {
2122
const ctx = this.ctx;
23+
ctx.setState(ResourceState.CheckCacheOnRequest);
2224
// only cache get (don't do preflight, post, etc)
2325
if (ctx.method === 'GET' && !ctx.requestLowerHeaders['if-none-match']) {
2426
const cache = this.responseCache?.get(ctx.url.href);
@@ -32,6 +34,7 @@ export default class CacheHandler {
3234
}
3335

3436
public onHttp2PushStream() {
37+
this.ctx.setState(ResourceState.CheckCacheOnRequest);
3538
if (this.ctx.method === 'GET') {
3639
const cached = this.responseCache?.get(this.ctx.url.href);
3740
if (cached) {
@@ -60,6 +63,7 @@ export default class CacheHandler {
6063

6164
public onResponseEnd() {
6265
const ctx = this.ctx;
66+
ctx.setState(ResourceState.CheckCacheOnResponseEnd);
6367
if (
6468
ctx.method === 'GET' &&
6569
!this.didProposeCachedResource &&

mitm/handlers/CookieHandler.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import Log from '@secret-agent/commons/Logger';
22
import IResourceHeaders from '@secret-agent/core-interfaces/IResourceHeaders';
33
import IMitmRequestContext from '../interfaces/IMitmRequestContext';
4+
import ResourceState from "../interfaces/ResourceState";
45

56
const { log } = Log(module);
67

78
export default class CookieHandler {
89
public static async setProxyToServerCookies(ctx: IMitmRequestContext) {
10+
ctx.setState(ResourceState.SetCookieHeader);
911
const session = ctx.requestSession;
1012
if (!session || !session.delegate?.getCookieHeader) return;
1113
// never send cookies to preflight requests
@@ -16,6 +18,7 @@ export default class CookieHandler {
1618
}
1719

1820
public static async readServerResponseCookies(ctx: IMitmRequestContext) {
21+
ctx.setState(ResourceState.ReadResponseCookies);
1922
const session = ctx.requestSession;
2023
if (!session || !session.delegate?.setCookie) return;
2124

mitm/handlers/HeadersHandler.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import * as http from 'http';
66
import http2 from 'http2';
77
import { parseRawHeaders } from '../lib/Utils';
88
import IMitmRequestContext from '../interfaces/IMitmRequestContext';
9+
import ResourceState from '../interfaces/ResourceState';
910

1011
const { log } = Log(module);
1112
export default class HeadersHandler {
1213
public static async waitForBrowserRequest(ctx: IMitmRequestContext) {
14+
ctx.setState(ResourceState.WaitForBrowserRequest);
1315
const session = ctx.requestSession;
1416

1517
const { method, requestHeaders } = ctx;
@@ -46,6 +48,7 @@ export default class HeadersHandler {
4648
}
4749

4850
public static modifyHeaders(ctx: IMitmRequestContext) {
51+
ctx.setState(ResourceState.ModifyHeaders);
4952
const session = ctx.requestSession;
5053
if (session.delegate?.modifyHeadersBeforeSend) {
5154
const updatedHeaders = session.delegate.modifyHeadersBeforeSend({

mitm/handlers/HttpRequestHandler.ts

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import MitmRequestContext from '../lib/MitmRequestContext';
99
import { parseRawHeaders } from '../lib/Utils';
1010
import BaseHttpHandler from './BaseHttpHandler';
1111
import HttpResponseCache from '../lib/HttpResponseCache';
12+
import ResourceState from '../interfaces/ResourceState';
1213

1314
const { log } = Log(module);
1415
const redirectCodes = new Set([300, 301, 302, 303, 305, 307, 308]);
@@ -23,23 +24,25 @@ export default class HttpRequestHandler extends BaseHttpHandler {
2324
>,
2425
) {
2526
super(request, false, HttpRequestHandler.responseCache);
27+
this.context.setState(ResourceState.ClientToProxyRequest);
28+
29+
// register error listeners first
30+
const { clientToProxyRequest, proxyToClientResponse } = request;
31+
clientToProxyRequest.on('error', this.onError.bind(this, 'ClientToProxy.RequestError'));
32+
if (clientToProxyRequest instanceof Http2ServerRequest) {
33+
const http2Session = clientToProxyRequest.stream.session;
34+
if (!http2Session.listenerCount('error')) {
35+
http2Session.on('error', this.onError.bind(this, 'ClientToProxy.Http2SessionError'));
36+
}
37+
}
38+
proxyToClientResponse.on('error', this.onError.bind(this, 'ProxyToClient.ResponseError'));
2639
}
2740

2841
public async onRequest() {
29-
const { clientToProxyRequest, proxyToClientResponse } = this.context;
42+
const { clientToProxyRequest } = this.context;
3043

3144
try {
3245
clientToProxyRequest.pause();
33-
clientToProxyRequest.on('error', this.onError.bind(this, 'ClientToProxy.RequestError'));
34-
if (clientToProxyRequest instanceof Http2ServerRequest) {
35-
if (clientToProxyRequest.stream.session.listenerCount('error') === 0) {
36-
clientToProxyRequest.stream.session.on(
37-
'error',
38-
this.onError.bind(this, 'ClientToProxy.SessionError'),
39-
);
40-
}
41-
}
42-
proxyToClientResponse.on('error', this.onError.bind(this, 'ProxyToClient.ResponseError'));
4346

4447
const proxyToServerRequest = await this.createProxyToServerRequest();
4548
if (!proxyToServerRequest) return;
@@ -61,6 +64,8 @@ export default class HttpRequestHandler extends BaseHttpHandler {
6164
rawHeaders?: string[],
6265
) {
6366
const context = this.context;
67+
context.setState(ResourceState.ServerToProxyOnResponse);
68+
6469
if (response instanceof http.IncomingMessage) {
6570
MitmRequestContext.readHttp1Response(context, response);
6671
} else {
@@ -91,6 +96,8 @@ export default class HttpRequestHandler extends BaseHttpHandler {
9196

9297
await this.writeResponse();
9398

99+
context.setState(ResourceState.End);
100+
94101
process.nextTick(agent => agent.freeSocket(context), context.requestSession.requestAgent);
95102
}
96103

@@ -99,7 +106,8 @@ export default class HttpRequestHandler extends BaseHttpHandler {
99106
const { method, requestSession, proxyToClientResponse } = this.context;
100107
const sessionId = requestSession.sessionId;
101108

102-
requestSession.emit('httpError', {
109+
this.context.setState(ResourceState.Error);
110+
requestSession.emit('http-error', {
103111
request: MitmRequestContext.toEmittedResource(this.context),
104112
error,
105113
});
@@ -120,6 +128,7 @@ export default class HttpRequestHandler extends BaseHttpHandler {
120128
}
121129

122130
private async writeRequest() {
131+
this.context.setState(ResourceState.WriteProxyToServerRequestBody);
123132
const { proxyToServerRequest, clientToProxyRequest } = this.context;
124133

125134
const onWriteError = error => {
@@ -142,6 +151,7 @@ export default class HttpRequestHandler extends BaseHttpHandler {
142151
log.warn('Error.NoProxyToClientResponse', {
143152
sessionId: context.requestSession.sessionId,
144153
});
154+
context.setState(ResourceState.PrematurelyClosed);
145155
return;
146156
}
147157

@@ -157,6 +167,7 @@ export default class HttpRequestHandler extends BaseHttpHandler {
157167
context.responseTrailers = headers;
158168
});
159169

170+
context.setState(ResourceState.WriteProxyToClientResponseBody);
160171
for await (const chunk of serverToProxyResponse) {
161172
const data = context.cacheHandler.onResponseData(chunk as Buffer);
162173
if (data && !(proxyToClientResponse as Http2ServerResponse).stream?.destroyed) {

mitm/handlers/HttpUpgradeHandler.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import MitmRequestContext from '../lib/MitmRequestContext';
55
import CookieHandler from './CookieHandler';
66
import BaseHttpHandler from './BaseHttpHandler';
77
import IMitmRequestContext from '../interfaces/IMitmRequestContext';
8+
import ResourceState from '../interfaces/ResourceState';
89

910
const { log } = Log(module);
1011

@@ -15,12 +16,12 @@ export default class HttpUpgradeHandler extends BaseHttpHandler {
1516
readonly clientHead: Buffer,
1617
) {
1718
super(request, true, null);
19+
this.context.setState(ResourceState.ClientToProxyRequest);
20+
this.clientSocket.on('error', this.onError.bind(this, 'ClientToProxy.UpgradeSocketError'));
1821
}
1922

2023
public async onUpgrade() {
2124
try {
22-
this.clientSocket.on('error', this.onError.bind(this, 'ClientToProxy.UpgradeSocketError'));
23-
2425
const proxyToServerRequest = await this.createProxyToServerRequest();
2526
if (!proxyToServerRequest) return;
2627

@@ -37,7 +38,9 @@ export default class HttpUpgradeHandler extends BaseHttpHandler {
3738
const url = context.url.href;
3839
const session = context.requestSession;
3940
const sessionId = session.sessionId;
40-
session.emit('httpError', { request: MitmRequestContext.toEmittedResource(context), error });
41+
context.setState(ResourceState.Error);
42+
43+
session.emit('http-error', { request: MitmRequestContext.toEmittedResource(context), error });
4144

4245
if (!(error as any)?.isLogged) {
4346
log.error(`MitmWebSocket.${errorType}`, {
@@ -54,6 +57,7 @@ export default class HttpUpgradeHandler extends BaseHttpHandler {
5457
serverSocket: net.Socket,
5558
serverHead: Buffer,
5659
) {
60+
this.context.setState(ResourceState.ServerToProxyOnResponse);
5761
serverSocket.pause();
5862
MitmRequestContext.readHttp1Response(this.context, serverResponse);
5963
this.context.serverToProxyResponse = serverResponse;
@@ -65,6 +69,7 @@ export default class HttpUpgradeHandler extends BaseHttpHandler {
6569
clientSocket.on('end', () => proxyToServerMitmSocket.close());
6670
serverSocket.on('end', () => proxyToServerMitmSocket.close());
6771
proxyToServerMitmSocket.on('close', () => {
72+
this.context.setState(ResourceState.End);
6873
// don't try to write again
6974
clientSocket.destroy();
7075
serverSocket.destroy();
@@ -77,11 +82,13 @@ export default class HttpUpgradeHandler extends BaseHttpHandler {
7782
}
7883
await CookieHandler.readServerResponseCookies(this.context);
7984

85+
this.context.setState(ResourceState.WriteProxyToClientResponseBody);
8086
clientSocket.write(`${responseMessage}\r\n`, error => {
8187
if (error) this.onError('ProxyToClient.UpgradeWriteError', error);
8288
});
8389

8490
if (!serverSocket.readable || !serverSocket.writable) {
91+
this.context.setState(ResourceState.PrematurelyClosed);
8592
return serverSocket.destroy();
8693
}
8794
serverSocket.on('error', this.onError.bind(this, 'ServerToProxy.UpgradeSocketError'));
@@ -104,6 +111,7 @@ export default class HttpUpgradeHandler extends BaseHttpHandler {
104111

105112
const formattedResponse = MitmRequestContext.toEmittedResource(this.context);
106113
this.context.requestSession.emit('response', formattedResponse);
114+
// don't log close since this stays open...
107115
}
108116

109117
public static async onUpgrade(

0 commit comments

Comments
 (0)