0
# Event Monitoring
1
2
Comprehensive event system for monitoring all HTTP/WebSocket traffic, TLS connections, errors, and custom rule events to enable detailed testing and debugging.
3
4
## Capabilities
5
6
### HTTP Request/Response Events
7
8
Monitor the lifecycle of HTTP requests and responses.
9
10
```typescript { .api }
11
interface Mockttp {
12
/**
13
* Subscribe to request initiation events (headers received, body may still be streaming).
14
* Fires as soon as request method, path, and headers are available.
15
*/
16
on(event: 'request-initiated', callback: (req: InitiatedRequest) => void): Promise<void>;
17
18
/**
19
* Subscribe to completed request events (full request including body received).
20
* Fires when the complete request has been received and processed.
21
*/
22
on(event: 'request', callback: (req: CompletedRequest) => void): Promise<void>;
23
24
/**
25
* Subscribe to response completion events.
26
* Fires when a response has been fully sent to the client.
27
*/
28
on(event: 'response', callback: (res: CompletedResponse) => void): Promise<void>;
29
30
/**
31
* Subscribe to request abortion events.
32
* Fires when requests are aborted before completion (client disconnect, timeout, etc.).
33
*/
34
on(event: 'abort', callback: (req: AbortedRequest) => void): Promise<void>;
35
}
36
37
interface InitiatedRequest {
38
id: string;
39
protocol: string;
40
httpVersion: string;
41
method: string;
42
url: string;
43
path: string;
44
headers: Headers;
45
timingEvents: TimingEvents;
46
tags: string[];
47
// Note: No body property - body may still be streaming
48
}
49
50
interface CompletedRequest extends InitiatedRequest {
51
matchedRuleId?: string;
52
body: CompletedBody;
53
rawTrailers: RawTrailers;
54
trailers: Trailers;
55
}
56
57
interface CompletedResponse {
58
id: string;
59
statusCode: number;
60
statusMessage: string;
61
headers: Headers;
62
rawHeaders: RawHeaders;
63
body: CompletedBody;
64
rawTrailers: RawTrailers;
65
trailers: Trailers;
66
timingEvents: TimingEvents;
67
tags: string[];
68
}
69
70
interface AbortedRequest extends InitiatedRequest {
71
error?: {
72
name?: string;
73
code?: string;
74
message?: string;
75
stack?: string;
76
};
77
}
78
```
79
80
**Usage Examples:**
81
82
```typescript
83
import { getLocal } from "mockttp";
84
85
const mockServer = getLocal();
86
await mockServer.start();
87
88
// Monitor all HTTP traffic
89
await mockServer.on('request-initiated', (req) => {
90
console.log(`${req.method} ${req.path} - Started`);
91
});
92
93
await mockServer.on('request', (req) => {
94
console.log(`${req.method} ${req.path} - Body: ${req.body.buffer.length} bytes`);
95
});
96
97
await mockServer.on('response', (res) => {
98
console.log(`Response ${res.statusCode} - ${res.body.buffer.length} bytes`);
99
});
100
101
await mockServer.on('abort', (req) => {
102
console.log(`Request aborted: ${req.method} ${req.path}`);
103
if (req.error) {
104
console.log(`Error: ${req.error.message}`);
105
}
106
});
107
108
// Set up some mock rules
109
await mockServer.forGet("/api/test").thenReply(200, "OK");
110
```
111
112
### WebSocket Events
113
114
Monitor WebSocket connection lifecycle and message traffic.
115
116
```typescript { .api }
117
interface Mockttp {
118
/**
119
* Subscribe to WebSocket upgrade requests.
120
* Fires when a WebSocket upgrade is requested, before acceptance/rejection decision.
121
*/
122
on(event: 'websocket-request', callback: (req: CompletedRequest) => void): Promise<void>;
123
124
/**
125
* Subscribe to WebSocket upgrade acceptance events.
126
* Fires when a WebSocket upgrade is successfully accepted.
127
*/
128
on(event: 'websocket-accepted', callback: (res: CompletedResponse) => void): Promise<void>;
129
130
/**
131
* Subscribe to WebSocket messages received from clients.
132
* Fires for every message received from WebSocket clients.
133
*/
134
on(event: 'websocket-message-received', callback: (msg: WebSocketMessage) => void): Promise<void>;
135
136
/**
137
* Subscribe to WebSocket messages sent to clients.
138
* Fires for every message sent to WebSocket clients.
139
*/
140
on(event: 'websocket-message-sent', callback: (msg: WebSocketMessage) => void): Promise<void>;
141
142
/**
143
* Subscribe to WebSocket connection closure events.
144
* Fires when WebSocket connections are cleanly closed with close frames.
145
*/
146
on(event: 'websocket-close', callback: (close: WebSocketClose) => void): Promise<void>;
147
}
148
149
interface WebSocketMessage {
150
streamId: string;
151
direction: 'sent' | 'received';
152
content: Uint8Array;
153
isBinary: boolean;
154
eventTimestamp: number;
155
timingEvents: TimingEvents;
156
tags: string[];
157
}
158
159
interface WebSocketClose {
160
streamId: string;
161
closeCode: number | undefined;
162
closeReason: string;
163
timingEvents: TimingEvents;
164
tags: string[];
165
}
166
```
167
168
**Usage Examples:**
169
170
```typescript
171
import { getLocal } from "mockttp";
172
173
const mockServer = getLocal();
174
await mockServer.start();
175
176
// Track WebSocket connections
177
const connections = new Map();
178
179
await mockServer.on('websocket-request', (req) => {
180
console.log(`WebSocket requested: ${req.url} by ${req.headers['user-agent']}`);
181
});
182
183
await mockServer.on('websocket-accepted', (res) => {
184
connections.set(res.id, {
185
connectedAt: Date.now(),
186
messageCount: 0
187
});
188
console.log(`WebSocket ${res.id} connected`);
189
});
190
191
await mockServer.on('websocket-message-received', (msg) => {
192
const conn = connections.get(msg.streamId);
193
if (conn) conn.messageCount++;
194
195
const text = msg.isBinary ?
196
`[Binary: ${msg.content.length} bytes]` :
197
new TextDecoder().decode(msg.content);
198
199
console.log(`${msg.streamId} received: ${text}`);
200
});
201
202
await mockServer.on('websocket-message-sent', (msg) => {
203
const text = msg.isBinary ?
204
`[Binary: ${msg.content.length} bytes]` :
205
new TextDecoder().decode(msg.content);
206
207
console.log(`${msg.streamId} sent: ${text}`);
208
});
209
210
await mockServer.on('websocket-close', (close) => {
211
const conn = connections.get(close.streamId);
212
if (conn) {
213
const duration = Date.now() - conn.connectedAt;
214
console.log(`WebSocket ${close.streamId} closed after ${duration}ms, ${conn.messageCount} messages`);
215
}
216
connections.delete(close.streamId);
217
});
218
219
// Set up WebSocket mock
220
await mockServer.forAnyWebSocket().thenEcho();
221
```
222
223
### TLS and Connection Events
224
225
Monitor TLS handshakes, client errors, and connection-level events.
226
227
```typescript { .api }
228
interface Mockttp {
229
/**
230
* Subscribe to TLS handshake failure events.
231
* Fires when TLS connections fail to complete handshake.
232
*/
233
on(event: 'tls-client-error', callback: (error: TlsHandshakeFailure) => void): Promise<void>;
234
235
/**
236
* Subscribe to client error events.
237
* Fires when requests fail before completion due to client errors.
238
*/
239
on(event: 'client-error', callback: (error: ClientError) => void): Promise<void>;
240
241
/**
242
* Subscribe to TLS passthrough connection events.
243
* Fires when TLS connections are passed through without interception.
244
*/
245
on(event: 'tls-passthrough-opened', callback: (event: TlsPassthroughEvent) => void): Promise<void>;
246
247
/**
248
* Subscribe to TLS passthrough closure events.
249
*/
250
on(event: 'tls-passthrough-closed', callback: (event: TlsPassthroughEvent) => void): Promise<void>;
251
252
/**
253
* Subscribe to raw protocol passthrough events.
254
* Fires when non-HTTP protocols are passed through.
255
*/
256
on(event: 'raw-passthrough-opened', callback: (event: RawPassthroughEvent) => void): Promise<void>;
257
258
/**
259
* Subscribe to raw protocol passthrough closure events.
260
*/
261
on(event: 'raw-passthrough-closed', callback: (event: RawPassthroughEvent) => void): Promise<void>;
262
263
/**
264
* Subscribe to raw passthrough data events.
265
* Fires for each chunk of data in raw passthrough tunnels.
266
*/
267
on(event: 'raw-passthrough-data', callback: (event: RawPassthroughDataEvent) => void): Promise<void>;
268
}
269
270
interface TlsHandshakeFailure {
271
failureCause: 'closed' | 'reset' | 'cert-rejected' | 'no-shared-cipher' | 'handshake-timeout' | 'unknown';
272
remoteIpAddress?: string;
273
remotePort?: number;
274
timingEvents: TlsFailureTimingEvents;
275
tags: string[];
276
destination?: Destination;
277
tlsMetadata: TlsSocketMetadata;
278
}
279
280
interface ClientError {
281
errorCode?: string;
282
request: Partial<CompletedRequest>;
283
response: CompletedResponse | 'aborted';
284
}
285
286
interface TlsPassthroughEvent {
287
id: string;
288
destination: Destination;
289
remoteIpAddress: string;
290
remotePort: number;
291
tags: string[];
292
timingEvents: TlsTimingEvents;
293
tlsMetadata: TlsSocketMetadata;
294
}
295
296
interface RawPassthroughEvent {
297
id: string;
298
destination: Destination;
299
remoteIpAddress: string;
300
remotePort: number;
301
tags: string[];
302
timingEvents: ConnectionTimingEvents;
303
}
304
305
interface RawPassthroughDataEvent {
306
id: string;
307
direction: 'sent' | 'received';
308
content: Uint8Array;
309
eventTimestamp: number;
310
}
311
312
interface TlsSocketMetadata {
313
sniHostname?: string;
314
clientAlpn?: string[];
315
ja3Fingerprint?: string;
316
ja4Fingerprint?: string;
317
}
318
```
319
320
**Usage Examples:**
321
322
```typescript
323
import { getLocal } from "mockttp";
324
325
const mockServer = getLocal({
326
https: {
327
keyLength: 2048
328
}
329
});
330
await mockServer.start();
331
332
// Monitor TLS issues
333
await mockServer.on('tls-client-error', (error) => {
334
console.log(`TLS error: ${error.failureCause}`);
335
if (error.tlsMetadata.sniHostname) {
336
console.log(`SNI hostname: ${error.tlsMetadata.sniHostname}`);
337
}
338
if (error.tlsMetadata.ja3Fingerprint) {
339
console.log(`JA3 fingerprint: ${error.tlsMetadata.ja3Fingerprint}`);
340
}
341
});
342
343
// Monitor client errors
344
await mockServer.on('client-error', (error) => {
345
console.log(`Client error: ${error.errorCode}`);
346
console.log(`Request: ${error.request.method} ${error.request.url}`);
347
if (error.response !== 'aborted') {
348
console.log(`Response: ${error.response.statusCode}`);
349
}
350
});
351
352
// Monitor TLS passthrough (if configured)
353
await mockServer.on('tls-passthrough-opened', (event) => {
354
console.log(`TLS passthrough opened to ${event.destination.hostname}:${event.destination.port}`);
355
});
356
357
await mockServer.on('tls-passthrough-closed', (event) => {
358
const duration = event.timingEvents.disconnectTimestamp! - event.timingEvents.connectTimestamp;
359
console.log(`TLS passthrough closed after ${duration}ms`);
360
});
361
```
362
363
### Custom Rule Events
364
365
Monitor custom events emitted by rules during request processing.
366
367
```typescript { .api }
368
interface Mockttp {
369
/**
370
* Subscribe to custom rule events.
371
* Rules may emit events with metadata about their processing.
372
*/
373
on<T = unknown>(event: 'rule-event', callback: (event: RuleEvent<T>) => void): Promise<void>;
374
}
375
376
interface RuleEvent<T = unknown> {
377
/**
378
* ID of the request being processed.
379
*/
380
requestId: string;
381
382
/**
383
* ID of the rule that emitted the event.
384
*/
385
ruleId: string;
386
387
/**
388
* Type of event (rule-specific).
389
*/
390
eventType: string;
391
392
/**
393
* Event data (rule-specific).
394
*/
395
eventData: T;
396
}
397
```
398
399
**Usage Examples:**
400
401
```typescript
402
import { getLocal } from "mockttp";
403
404
const mockServer = getLocal();
405
await mockServer.start();
406
407
// Monitor all rule events
408
await mockServer.on('rule-event', (event) => {
409
console.log(`Rule ${event.ruleId} emitted ${event.eventType} for request ${event.requestId}`);
410
console.log('Event data:', event.eventData);
411
});
412
413
// Rules that emit events include passthrough rules
414
await mockServer.forGet("/api/proxy")
415
.thenPassThrough();
416
417
// When requests hit this rule, it may emit events about upstream interactions
418
```
419
420
### Event Timing Information
421
422
All events include detailed timing information for performance analysis.
423
424
```typescript { .api }
425
interface TimingEvents {
426
/**
427
* When the request/connection started (milliseconds since Unix epoch).
428
*/
429
startTime: number;
430
431
/**
432
* High-precision start timestamp (monotonic, for duration calculations).
433
*/
434
startTimestamp: number;
435
436
/**
437
* When the request body was fully received.
438
*/
439
bodyReceivedTimestamp?: number;
440
441
/**
442
* When response headers were sent.
443
*/
444
headersSentTimestamp?: number;
445
446
/**
447
* When the response was fully completed.
448
*/
449
responseSentTimestamp?: number;
450
451
/**
452
* When WebSocket was accepted (for WebSocket requests).
453
*/
454
wsAcceptedTimestamp?: number;
455
456
/**
457
* When WebSocket was closed (for WebSocket requests).
458
*/
459
wsClosedTimestamp?: number;
460
461
/**
462
* When the request/connection was aborted.
463
*/
464
abortedTimestamp?: number;
465
}
466
467
interface ConnectionTimingEvents {
468
startTime: number;
469
connectTimestamp: number;
470
tunnelTimestamp?: number;
471
disconnectTimestamp?: number;
472
}
473
474
interface TlsTimingEvents extends ConnectionTimingEvents {
475
handshakeTimestamp?: number;
476
}
477
478
interface TlsFailureTimingEvents extends TlsTimingEvents {
479
failureTimestamp: number;
480
}
481
```
482
483
### Event-Based Testing Patterns
484
485
Common patterns for using events in testing:
486
487
**Traffic Verification**: Use events to verify that expected requests and responses occurred
488
**Performance Monitoring**: Use timing events to measure request/response latencies
489
**Error Testing**: Monitor abort and error events to test error handling
490
**WebSocket Testing**: Track WebSocket message flow and connection lifecycle
491
**TLS Debugging**: Monitor TLS events to debug certificate and handshake issues
492
**Custom Analytics**: Collect custom metrics from rule events
493
494
**Example Test Pattern:**
495
496
```typescript
497
import { getLocal } from "mockttp";
498
499
const mockServer = getLocal();
500
await mockServer.start();
501
502
const requestLog: string[] = [];
503
504
// Collect request log
505
await mockServer.on('request', (req) => {
506
requestLog.push(`${req.method} ${req.path}`);
507
});
508
509
// Set up mocks
510
await mockServer.forGet("/api/test").thenReply(200, "OK");
511
512
// Run your application tests...
513
514
// Verify expected requests occurred
515
expect(requestLog).toContain('GET /api/test');
516
```