0
# Filtering System
1
2
HTTP request/response filtering system using decorator pattern with built-in filters for user agents, logging, and automatic retries, enabling request/response interception and modification.
3
4
## Capabilities
5
6
### Filter Interface
7
8
Functional interface for HTTP request/response filtering using decorator pattern, allowing filters to be chained together for complex processing pipelines.
9
10
```java { .api }
11
/**
12
* Functional interface for HTTP request/response filtering
13
* Uses decorator pattern to wrap HttpHandler instances
14
* Extends Function<HttpHandler, HttpHandler> for functional composition
15
*/
16
@FunctionalInterface
17
public interface Filter extends Function<HttpHandler, HttpHandler> {
18
/**
19
* Chains this filter with another filter
20
* Creates composite filter where this filter runs first, then next filter
21
* @param next Filter to chain after this filter
22
* @return Combined filter that applies both filters in sequence
23
*/
24
Filter andThen(Filter next);
25
26
/**
27
* Terminates filter chain with HttpHandler
28
* Creates final HttpHandler with all filters applied
29
* @param end Final HttpHandler to handle filtered requests
30
* @return HttpHandler with filter chain applied
31
*/
32
HttpHandler andFinally(HttpHandler end);
33
34
/**
35
* Terminates filter chain with Routable
36
* Creates final Routable with all filters applied
37
* @param end Final Routable to handle filtered requests
38
* @return Routable with filter chain applied
39
*/
40
Routable andFinally(Routable end);
41
}
42
```
43
44
**Usage Examples:**
45
46
```java
47
import org.openqa.selenium.remote.http.*;
48
49
// Create individual filters
50
Filter userAgentFilter = new AddSeleniumUserAgent();
51
Filter loggingFilter = new DumpHttpExchangeFilter();
52
Filter retryFilter = new RetryRequest();
53
54
// Chain filters together
55
Filter combinedFilter = userAgentFilter
56
.andThen(loggingFilter)
57
.andThen(retryFilter);
58
59
// Apply filter chain to handler
60
HttpHandler baseHandler = request -> {
61
// Base request handling logic
62
return new HttpResponse().setStatus(200);
63
};
64
65
HttpHandler filteredHandler = combinedFilter.andFinally(baseHandler);
66
67
// Use filtered handler
68
HttpRequest request = new HttpRequest(HttpMethod.GET, "/api/test");
69
HttpResponse response = filteredHandler.execute(request);
70
71
// Apply filters to routable
72
Route route = Route.get("/users/{id}").to(() -> new UserHandler());
73
Routable filteredRoute = combinedFilter.andFinally(route);
74
75
// Custom filter implementation
76
Filter customFilter = next -> request -> {
77
// Pre-processing
78
System.out.println("Processing request: " + request.getUri());
79
request.addHeader("X-Request-ID", UUID.randomUUID().toString());
80
81
// Execute next handler in chain
82
HttpResponse response = next.execute(request);
83
84
// Post-processing
85
response.addHeader("X-Processed-By", "CustomFilter");
86
System.out.println("Response status: " + response.getStatus());
87
88
return response;
89
};
90
91
// Use custom filter
92
HttpHandler customHandler = customFilter.andFinally(baseHandler);
93
```
94
95
### AddSeleniumUserAgent Filter
96
97
Built-in filter that adds Selenium user agent header to HTTP requests, automatically identifying requests as coming from Selenium WebDriver.
98
99
```java { .api }
100
/**
101
* Filter that adds Selenium user agent header to requests
102
* Automatically applied in default ClientConfig
103
*/
104
public class AddSeleniumUserAgent implements Filter {
105
/**
106
* Static user agent string containing Selenium version and platform info
107
* Format: "selenium/{version} ({platform})"
108
* Example: "selenium/4.33.0 (java mac)"
109
*/
110
public static final String USER_AGENT;
111
112
/**
113
* Applies user agent filter to handler
114
* Adds User-Agent header if not already present
115
* @param next Next handler in filter chain
116
* @return HttpHandler that adds user agent header
117
*/
118
public HttpHandler apply(HttpHandler next);
119
}
120
```
121
122
**Usage Examples:**
123
124
```java
125
import org.openqa.selenium.remote.http.*;
126
127
// Filter is automatically included in default client config
128
ClientConfig defaultConfig = ClientConfig.defaultConfig();
129
// User-Agent header will be automatically added
130
131
// Manual usage of the filter
132
Filter userAgentFilter = new AddSeleniumUserAgent();
133
HttpHandler handler = userAgentFilter.andFinally(request -> {
134
// Check if User-Agent was added
135
String userAgent = request.getHeader("User-Agent");
136
System.out.println("User-Agent: " + userAgent);
137
return new HttpResponse().setStatus(200);
138
});
139
140
// Check the user agent string
141
System.out.println("Selenium User-Agent: " + AddSeleniumUserAgent.USER_AGENT);
142
143
// Combine with other filters
144
Filter combinedFilter = new AddSeleniumUserAgent()
145
.andThen(new DumpHttpExchangeFilter());
146
147
// User agent is not added if already present
148
HttpRequest request = new HttpRequest(HttpMethod.GET, "/api/test");
149
request.addHeader("User-Agent", "Custom User Agent");
150
151
HttpHandler filteredHandler = userAgentFilter.andFinally(req -> {
152
// Will still have "Custom User Agent", not overwritten
153
return new HttpResponse().setStatus(200);
154
});
155
156
HttpResponse response = filteredHandler.execute(request);
157
```
158
159
### RetryRequest Filter
160
161
Filter implementing automatic retry logic for failed requests with exponential backoff strategy for handling transient network failures and server errors.
162
163
```java { .api }
164
/**
165
* Filter that implements automatic retry logic for failed requests
166
* Retries connection failures and server errors (5xx status codes)
167
*/
168
public class RetryRequest implements Filter {
169
/**
170
* Applies retry logic to handler
171
* Automatically retries on ConnectionFailedException and 5xx responses
172
* Uses exponential backoff strategy with maximum retry attempts
173
* @param next Next handler in filter chain
174
* @return HttpHandler with retry logic applied
175
*/
176
public HttpHandler apply(HttpHandler next);
177
}
178
```
179
180
**Usage Examples:**
181
182
```java
183
import org.openqa.selenium.remote.http.*;
184
185
// Enable retries in client configuration
186
ClientConfig configWithRetries = ClientConfig.defaultConfig()
187
.baseUrl(new URL("https://unreliable-api.example.com"))
188
.withRetries();
189
190
HttpClient client = HttpClient.Factory.createDefault().createClient(configWithRetries);
191
192
// Manual usage of retry filter
193
Filter retryFilter = new RetryRequest();
194
HttpHandler unreliableHandler = request -> {
195
// Simulate unreliable service
196
if (Math.random() < 0.7) {
197
throw new ConnectionFailedException("Connection timeout");
198
}
199
if (Math.random() < 0.5) {
200
return new HttpResponse().setStatus(503); // Service unavailable
201
}
202
return new HttpResponse().setStatus(200);
203
};
204
205
HttpHandler retryHandler = retryFilter.andFinally(unreliableHandler);
206
207
// Execute request with retries
208
HttpRequest request = new HttpRequest(HttpMethod.GET, "/api/data");
209
try {
210
HttpResponse response = retryHandler.execute(request);
211
System.out.println("Success after retries: " + response.getStatus());
212
} catch (ConnectionFailedException e) {
213
System.out.println("Failed after all retry attempts");
214
}
215
216
// Combine with other filters
217
Filter robustFilter = new AddSeleniumUserAgent()
218
.andThen(new DumpHttpExchangeFilter())
219
.andThen(new RetryRequest());
220
221
// Retry behavior:
222
// - Retries ConnectionFailedException
223
// - Retries 5xx HTTP status codes
224
// - Uses exponential backoff
225
// - Has maximum retry limit
226
// - Does not retry 4xx client errors
227
```
228
229
### DumpHttpExchangeFilter Filter
230
231
Filter for logging HTTP request/response exchanges with configurable log levels for debugging and monitoring HTTP traffic.
232
233
```java { .api }
234
/**
235
* Filter for logging HTTP request/response exchanges
236
* Useful for debugging and monitoring HTTP traffic
237
*/
238
public class DumpHttpExchangeFilter implements Filter {
239
/**
240
* Public logger instance for HTTP exchange logging
241
*/
242
public static final Logger LOG;
243
244
/**
245
* Creates logging filter with FINER log level
246
*/
247
public DumpHttpExchangeFilter();
248
249
/**
250
* Creates logging filter with specified log level
251
* @param logLevel Logging level for HTTP exchanges
252
*/
253
public DumpHttpExchangeFilter(Level logLevel);
254
255
/**
256
* Applies logging filter to handler
257
* Logs request details before execution and response details after
258
* @param next Next handler in filter chain
259
* @return HttpHandler with logging applied
260
*/
261
public HttpHandler apply(HttpHandler next);
262
}
263
```
264
265
**Usage Examples:**
266
267
```java
268
import org.openqa.selenium.remote.http.*;
269
import java.util.logging.Level;
270
import java.util.logging.Logger;
271
272
// Basic logging filter
273
Filter loggingFilter = new DumpHttpExchangeFilter();
274
275
// Logging filter with custom level
276
Filter infoLoggingFilter = new DumpHttpExchangeFilter(Level.INFO);
277
Filter warningLoggingFilter = new DumpHttpExchangeFilter(Level.WARNING);
278
279
// Configure logger level to see output
280
Logger httpLogger = DumpHttpExchangeFilter.LOG;
281
httpLogger.setLevel(Level.FINER);
282
283
// Add console handler to see logs
284
ConsoleHandler consoleHandler = new ConsoleHandler();
285
consoleHandler.setLevel(Level.FINER);
286
httpLogger.addHandler(consoleHandler);
287
288
// Apply logging filter
289
HttpHandler handler = loggingFilter.andFinally(request -> {
290
return new HttpResponse()
291
.setStatus(200)
292
.setContent(Contents.utf8String("Response data"));
293
});
294
295
// Execute request - will log request and response details
296
HttpRequest request = new HttpRequest(HttpMethod.POST, "/api/users");
297
request.addHeader("Content-Type", "application/json");
298
request.setContent(Contents.asJson(Map.of("name", "John Doe")));
299
300
HttpResponse response = handler.execute(request);
301
302
// Combine with other filters for comprehensive logging
303
Filter debugFilter = new AddSeleniumUserAgent()
304
.andThen(new DumpHttpExchangeFilter(Level.INFO))
305
.andThen(new RetryRequest());
306
307
// Use in client configuration
308
ClientConfig debugConfig = ClientConfig.defaultConfig()
309
.baseUrl(new URL("https://api.example.com"))
310
.withFilter(new DumpHttpExchangeFilter(Level.INFO));
311
312
HttpClient debugClient = HttpClient.Factory.createDefault().createClient(debugConfig);
313
314
// All requests will be logged with full details:
315
// - Request method, URI, headers, and content
316
// - Response status, headers, and content
317
// - Timing information
318
```
319
320
## Custom Filter Implementation
321
322
### Creating Custom Filters
323
324
```java
325
import org.openqa.selenium.remote.http.*;
326
import java.time.Instant;
327
import java.util.UUID;
328
329
// Authentication filter
330
public class AuthenticationFilter implements Filter {
331
private final String authToken;
332
333
public AuthenticationFilter(String authToken) {
334
this.authToken = authToken;
335
}
336
337
@Override
338
public HttpHandler apply(HttpHandler next) {
339
return request -> {
340
// Add authentication header
341
request.addHeader("Authorization", "Bearer " + authToken);
342
return next.execute(request);
343
};
344
}
345
}
346
347
// Request timing filter
348
public class TimingFilter implements Filter {
349
@Override
350
public HttpHandler apply(HttpHandler next) {
351
return request -> {
352
long startTime = System.currentTimeMillis();
353
354
try {
355
HttpResponse response = next.execute(request);
356
357
long duration = System.currentTimeMillis() - startTime;
358
response.setAttribute("request.duration", duration);
359
System.out.println("Request to " + request.getUri() +
360
" took " + duration + "ms");
361
362
return response;
363
} catch (Exception e) {
364
long duration = System.currentTimeMillis() - startTime;
365
System.err.println("Request to " + request.getUri() +
366
" failed after " + duration + "ms: " + e.getMessage());
367
throw e;
368
}
369
};
370
}
371
}
372
373
// Request ID filter
374
public class RequestIdFilter implements Filter {
375
@Override
376
public HttpHandler apply(HttpHandler next) {
377
return request -> {
378
String requestId = UUID.randomUUID().toString();
379
request.addHeader("X-Request-ID", requestId);
380
request.setAttribute("request.id", requestId);
381
382
HttpResponse response = next.execute(request);
383
response.addHeader("X-Request-ID", requestId);
384
385
return response;
386
};
387
}
388
}
389
390
// Rate limiting filter
391
public class RateLimitFilter implements Filter {
392
private final long minIntervalMs;
393
private volatile long lastRequestTime = 0;
394
395
public RateLimitFilter(long minIntervalMs) {
396
this.minIntervalMs = minIntervalMs;
397
}
398
399
@Override
400
public HttpHandler apply(HttpHandler next) {
401
return request -> {
402
synchronized (this) {
403
long now = System.currentTimeMillis();
404
long timeSinceLastRequest = now - lastRequestTime;
405
406
if (timeSinceLastRequest < minIntervalMs) {
407
long sleepTime = minIntervalMs - timeSinceLastRequest;
408
try {
409
Thread.sleep(sleepTime);
410
} catch (InterruptedException e) {
411
Thread.currentThread().interrupt();
412
throw new RuntimeException("Interrupted during rate limiting", e);
413
}
414
}
415
416
lastRequestTime = System.currentTimeMillis();
417
}
418
419
return next.execute(request);
420
};
421
}
422
}
423
```
424
425
### Using Custom Filters
426
427
```java
428
import org.openqa.selenium.remote.http.*;
429
import java.net.URL;
430
431
// Combine custom filters
432
Filter authFilter = new AuthenticationFilter("secret-token-123");
433
Filter timingFilter = new TimingFilter();
434
Filter rateLimitFilter = new RateLimitFilter(1000); // 1 second between requests
435
Filter requestIdFilter = new RequestIdFilter();
436
437
// Create comprehensive filter chain
438
Filter comprehensiveFilter = authFilter
439
.andThen(requestIdFilter)
440
.andThen(timingFilter)
441
.andThen(rateLimitFilter)
442
.andThen(new DumpHttpExchangeFilter(Level.INFO))
443
.andThen(new RetryRequest());
444
445
// Use in client configuration
446
ClientConfig advancedConfig = ClientConfig.defaultConfig()
447
.baseUrl(new URL("https://api.example.com"))
448
.withFilter(comprehensiveFilter);
449
450
HttpClient client = HttpClient.Factory.createDefault().createClient(advancedConfig);
451
452
// All requests will have:
453
// - Authentication header
454
// - Unique request ID
455
// - Timing measurement
456
// - Rate limiting
457
// - Request/response logging
458
// - Automatic retries
459
460
HttpRequest request = new HttpRequest(HttpMethod.GET, "/users");
461
HttpResponse response = client.execute(request);
462
463
System.out.println("Request ID: " + response.getHeader("X-Request-ID"));
464
System.out.println("Duration: " + response.getAttribute("request.duration") + "ms");
465
```
466
467
## Filter Order and Composition
468
469
The order of filters in the chain matters. Filters are applied in the order they are chained:
470
471
```java
472
// Filter execution order
473
Filter filterChain = filterA // Runs first (outermost)
474
.andThen(filterB) // Runs second
475
.andThen(filterC); // Runs third (innermost)
476
477
HttpHandler handler = filterChain.andFinally(baseHandler);
478
479
/*
480
* Request flow:
481
* Request -> FilterA -> FilterB -> FilterC -> BaseHandler
482
*
483
* Response flow:
484
* BaseHandler -> FilterC -> FilterB -> FilterA -> Response
485
*/
486
487
// Example with specific filters
488
Filter orderedChain = new RequestIdFilter() // 1. Add request ID
489
.andThen(new AuthenticationFilter("token")) // 2. Add auth header
490
.andThen(new TimingFilter()) // 3. Start timing
491
.andThen(new DumpHttpExchangeFilter()) // 4. Log request
492
.andThen(new RateLimitFilter(1000)) // 5. Rate limit
493
.andThen(new RetryRequest()); // 6. Handle retries
494
495
// This ensures proper layering:
496
// - Request ID is added before logging
497
// - Authentication is added before rate limiting
498
// - Timing includes all processing time
499
// - Retries happen at the innermost level
500
```