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
```