or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

certificate-management.mdevent-monitoring.mdhttp-request-mocking.mdindex.mdmock-server-setup.mdresponse-actions.mdwebsocket-mocking.md

event-monitoring.mddocs/

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

```