or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-config.mdcontent-handling.mdfiltering.mdhttp-client.mdindex.mdrequest-response.mdrouting.mdwebsocket.md

filtering.mddocs/

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

```