or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

fixture-management.mdhost-configuration.mdindex.mdmode-management.mdproxy-system.md

proxy-system.mddocs/

0

# Proxy System

1

2

Replay's extensible proxy system allows custom request processing through a configurable chain of handlers. The system processes HTTP requests through a series of proxies until one provides a response.

3

4

## Capabilities

5

6

### Use Method

7

8

Add custom proxy handlers to the processing chain.

9

10

```javascript { .api }

11

/**

12

* Add a proxy handler to the beginning of the processing chain

13

* @param {ProxyHandler} proxy - Handler function to process requests

14

* @returns {Replay} The Replay instance for chaining

15

*/

16

Replay.use(proxy: ProxyHandler): Replay;

17

18

/**

19

* Proxy handler function type

20

* @typedef {Function} ProxyHandler

21

* @param {any} request - The HTTP request object

22

* @param {Function} callback - Callback function to call with response or error

23

*/

24

type ProxyHandler = (request: any, callback: (error?: Error, response?: any) => void) => void;

25

```

26

27

**Usage Examples:**

28

29

```javascript

30

const Replay = require('replay');

31

32

// Custom authentication proxy

33

Replay.use(function authProxy(request, callback) {

34

if (request.url.hostname === 'api.example.com') {

35

// Add authentication headers

36

request.headers['Authorization'] = 'Bearer ' + process.env.API_TOKEN;

37

}

38

// Pass to next proxy in chain

39

callback();

40

});

41

42

// Custom response modification proxy

43

Replay.use(function responseModifier(request, callback) {

44

if (request.url.pathname === '/api/time') {

45

// Provide mock response

46

callback(null, {

47

statusCode: 200,

48

headers: { 'Content-Type': 'application/json' },

49

body: JSON.stringify({ timestamp: Date.now() })

50

});

51

return;

52

}

53

// Pass to next proxy

54

callback();

55

});

56

57

// Chain multiple proxies

58

Replay

59

.use(customLoggingProxy)

60

.use(authenticationProxy)

61

.use(mockDataProxy);

62

```

63

64

### Built-in Logger Proxy

65

66

Replay provides a built-in logging proxy for debugging HTTP requests.

67

68

```javascript { .api }

69

/**

70

* Create a logging proxy that logs HTTP requests when DEBUG=replay

71

* @returns {ProxyHandler} Logging proxy handler function

72

*/

73

Replay.logger(): ProxyHandler;

74

```

75

76

**Usage Examples:**

77

78

```javascript

79

const Replay = require('replay');

80

81

// Add logging to the proxy chain

82

Replay.use(Replay.logger());

83

84

// Logger output when DEBUG=replay is set:

85

// Requesting GET http://api.example.com:80/users

86

// Requesting GET https://cdn.example.com:443/assets/style.css

87

```

88

89

**Environment Configuration:**

90

91

```bash

92

# Enable debug logging

93

DEBUG=replay node app.js

94

95

# Logger will output request URLs:

96

# Requesting GET http://api.example.com:80/data

97

# Requesting GET https://auth.example.com:443/login

98

```

99

100

### Chain API

101

102

Direct access to the internal proxy chain for advanced management.

103

104

```javascript { .api }

105

/**

106

* Append a handler to the end of the proxy chain (executed last)

107

* @param {ProxyHandler} handler - Handler function to append

108

* @returns {Chain} The Chain instance for chaining

109

*/

110

Replay.chain.append(handler: ProxyHandler): Chain;

111

112

/**

113

* Prepend a handler to the beginning of the proxy chain (executed first)

114

* @param {ProxyHandler} handler - Handler function to prepend

115

* @returns {Chain} The Chain instance for chaining

116

*/

117

Replay.chain.prepend(handler: ProxyHandler): Chain;

118

119

/**

120

* Clear all handlers from the proxy chain

121

*/

122

Replay.chain.clear(): void;

123

124

/**

125

* Get the first handler in the proxy chain

126

* @type {ProxyHandler | null}

127

*/

128

Replay.chain.start: ProxyHandler | null;

129

```

130

131

**Usage Examples:**

132

133

```javascript

134

const Replay = require('replay');

135

136

// Direct chain manipulation

137

Replay.chain.prepend(function firstHandler(request, callback) {

138

console.log('First handler - always executes first');

139

callback(); // Pass to next handler

140

});

141

142

Replay.chain.append(function lastHandler(request, callback) {

143

console.log('Last handler - executes if no other handler provides response');

144

callback(); // Pass to next handler

145

});

146

147

// Get the first handler

148

const firstHandler = Replay.chain.start;

149

if (firstHandler) {

150

console.log('Chain has handlers');

151

}

152

153

// Clear all handlers (advanced usage - removes built-in functionality)

154

// Note: This will disable all replay functionality including recording/playback

155

Replay.chain.clear();

156

157

// Chain operations can be chained together

158

Replay.chain

159

.prepend(authHandler)

160

.prepend(loggingHandler)

161

.append(fallbackHandler);

162

```

163

164

**Note:** Direct chain manipulation is advanced functionality. The `Replay.use()` method is recommended for most use cases as it maintains the proper order with built-in handlers.

165

166

## Proxy Chain Architecture

167

168

### Processing Flow

169

170

1. **Request Capture**: HTTP requests are intercepted by Replay's patched HTTP modules

171

2. **Chain Execution**: Request passes through proxy chain from first to last

172

3. **Response Handling**: First proxy to provide a response terminates the chain

173

4. **Fallback**: If no proxy provides a response, request fails or passes through based on mode

174

175

### Default Proxy Chain

176

177

Replay sets up a default proxy chain:

178

179

```javascript

180

// Default chain (from first to last):

181

Replay

182

.use(passThrough(passWhenBloodyOrCheat)) // Pass-through for bloody/cheat modes

183

.use(recorder(replay)) // Record/replay functionality

184

.use(logger(replay)) // Debug logging (when enabled)

185

.use(passThrough(passToLocalhost)); // Pass-through for localhost

186

```

187

188

### Custom Proxy Implementation

189

190

```javascript

191

/**

192

* Example custom proxy implementation

193

*/

194

function customProxy(request, callback) {

195

// Check if this proxy should handle the request

196

if (shouldHandle(request)) {

197

// Process the request

198

processRequest(request, function(error, response) {

199

if (error) {

200

// Pass error to callback

201

callback(error);

202

} else {

203

// Provide response (terminates chain)

204

callback(null, response);

205

}

206

});

207

} else {

208

// Pass to next proxy in chain (no arguments = continue)

209

callback();

210

}

211

}

212

213

function shouldHandle(request) {

214

return request.url.hostname === 'mock.example.com';

215

}

216

217

function processRequest(request, callback) {

218

// Custom request processing logic

219

const response = {

220

statusCode: 200,

221

headers: { 'Content-Type': 'application/json' },

222

body: JSON.stringify({ mocked: true })

223

};

224

callback(null, response);

225

}

226

227

// Add to proxy chain

228

Replay.use(customProxy);

229

```

230

231

## Advanced Proxy Patterns

232

233

### Conditional Processing

234

235

```javascript

236

// Proxy that only handles specific hosts

237

Replay.use(function conditionalProxy(request, callback) {

238

const hostname = request.url.hostname;

239

240

if (hostname === 'api.example.com') {

241

// Handle API requests specially

242

handleApiRequest(request, callback);

243

} else if (hostname.endsWith('.amazonaws.com')) {

244

// Handle AWS requests

245

handleAwsRequest(request, callback);

246

} else {

247

// Pass to next proxy

248

callback();

249

}

250

});

251

```

252

253

### Response Transformation

254

255

```javascript

256

// Proxy that transforms responses

257

Replay.use(function responseTransformer(request, callback) {

258

if (request.url.pathname.startsWith('/api/v1/')) {

259

// Let other proxies handle the request first

260

callback();

261

} else {

262

callback();

263

}

264

});

265

266

// Note: Response transformation typically requires wrapping other proxies

267

// or intercepting at a different level in the HTTP stack

268

```

269

270

### Error Handling

271

272

```javascript

273

// Proxy with comprehensive error handling

274

Replay.use(function errorHandlingProxy(request, callback) {

275

try {

276

if (request.url.hostname === 'flaky-api.example.com') {

277

// Simulate flaky service

278

if (Math.random() < 0.3) {

279

const error = new Error('Service temporarily unavailable');

280

error.code = 'ECONNREFUSED';

281

callback(error);

282

return;

283

}

284

}

285

286

// Pass to next proxy

287

callback();

288

} catch (error) {

289

callback(error);

290

}

291

});

292

```

293

294

### Request Modification

295

296

```javascript

297

// Proxy that modifies requests

298

Replay.use(function requestModifier(request, callback) {

299

// Add custom headers

300

request.headers['X-Client-Version'] = '1.2.3';

301

request.headers['X-Request-ID'] = generateRequestId();

302

303

// Modify URL for testing

304

if (process.env.NODE_ENV === 'test') {

305

request.url.hostname = request.url.hostname.replace('.com', '.test');

306

}

307

308

// Pass modified request to next proxy

309

callback();

310

});

311

312

function generateRequestId() {

313

return Math.random().toString(36).substr(2, 9);

314

}

315

```

316

317

## Proxy Chain Management

318

319

### Order Importance

320

321

Proxies are processed in the order they are added with `use()`. Later proxies are added to the beginning of the chain:

322

323

```javascript

324

Replay.use(proxyA); // Will execute third

325

Replay.use(proxyB); // Will execute second

326

Replay.use(proxyC); // Will execute first

327

328

// Execution order: proxyC -> proxyB -> proxyA -> default chain

329

```

330

331

### Chaining Support

332

333

The `use()` method returns the Replay instance for method chaining:

334

335

```javascript

336

Replay

337

.use(authenticationProxy)

338

.use(loggingProxy)

339

.use(mockDataProxy)

340

.passThrough('*.amazonaws.com')

341

.drop('*.doubleclick.net');

342

```

343

344

### Integration with Built-in Proxies

345

346

Custom proxies work alongside Replay's built-in functionality:

347

348

```javascript

349

// Custom proxy + built-in features

350

Replay

351

.use(customAuthProxy) // Custom authentication

352

.use(Replay.logger()) // Built-in logging

353

.passThrough('cdn.example.com') // Built-in pass-through

354

.localhost('*.local'); // Built-in localhost routing

355

```