0
# Utility Handlers
1
2
Utility handlers provide specialized functionality for common web server needs including GZIP compression, CORS support, statistics collection, error handling, and request processing utilities.
3
4
## GZIP Compression
5
6
### GzipHandler
7
8
Compresses response content using GZIP encoding to reduce bandwidth usage.
9
10
```java { .api }
11
public class GzipHandler extends Handler.Wrapper implements GzipFactory {
12
// Constructors
13
public GzipHandler();
14
15
// Compression configuration
16
public int getMinGzipSize();
17
public void setMinGzipSize(int minGzipSize);
18
public int getCompressionLevel();
19
public void setCompressionLevel(int compressionLevel);
20
21
// Content type filtering
22
public Set<String> getIncludedMimeTypes();
23
public void setIncludedMimeTypes(String... types);
24
public Set<String> getExcludedMimeTypes();
25
public void setExcludedMimeTypes(String... types);
26
public Set<String> getIncludedMethods();
27
public void setIncludedMethods(String... methods);
28
public Set<String> getExcludedMethods();
29
public void setExcludedMethods(String... methods);
30
31
// Path filtering
32
public PathMappings<Boolean> getIncludedPaths();
33
public void addIncludedPaths(String... pathspecs);
34
public PathMappings<Boolean> getExcludedPaths();
35
public void addExcludedPaths(String... pathspecs);
36
37
// User agent filtering
38
public Set<String> getIncludedAgentPatterns();
39
public void setIncludedAgentPatterns(String... patterns);
40
public Set<String> getExcludedAgentPatterns();
41
public void setExcludedAgentPatterns(String... patterns);
42
43
// Deflater configuration
44
public boolean isCheckGzExists();
45
public void setCheckGzExists(boolean checkGzExists);
46
public boolean isSyncFlush();
47
public void setSyncFlush(boolean syncFlush);
48
public int getInflateBufferSize();
49
public void setInflateBufferSize(int size);
50
}
51
```
52
53
### Basic GZIP Configuration
54
55
```java
56
public class GzipConfiguration {
57
58
public void setupGzipHandler(Server server) {
59
// Create GZIP handler
60
GzipHandler gzipHandler = new GzipHandler();
61
62
// Configure minimum size for compression (1KB)
63
gzipHandler.setMinGzipSize(1024);
64
65
// Set compression level (6 = default, good balance of speed/compression)
66
gzipHandler.setCompressionLevel(6);
67
68
// Include text-based content types
69
gzipHandler.setIncludedMimeTypes(
70
"text/html",
71
"text/plain",
72
"text/xml",
73
"text/css",
74
"application/javascript",
75
"application/json",
76
"application/xml"
77
);
78
79
// Exclude already compressed content
80
gzipHandler.setExcludedMimeTypes(
81
"image/jpeg",
82
"image/png",
83
"image/gif",
84
"application/zip",
85
"application/gzip"
86
);
87
88
// Only compress GET and POST requests
89
gzipHandler.setIncludedMethods("GET", "POST");
90
91
// Exclude paths that shouldn't be compressed
92
gzipHandler.addExcludedPaths("/api/binary/*", "/downloads/*");
93
94
// Set application handler as child
95
gzipHandler.setHandler(new ApplicationHandler());
96
97
server.setHandler(gzipHandler);
98
}
99
}
100
```
101
102
## CORS Support
103
104
### CrossOriginHandler
105
106
Handles Cross-Origin Resource Sharing (CORS) for browser security.
107
108
```java { .api }
109
public class CrossOriginHandler extends Handler.Wrapper {
110
// Origin configuration
111
public Set<String> getAllowedOriginPatterns();
112
public void setAllowedOriginPatterns(Set<String> allowedOriginPatterns);
113
public void addAllowedOrigin(String origin);
114
115
// Method configuration
116
public Set<String> getAllowedMethods();
117
public void setAllowedMethods(Set<String> allowedMethods);
118
public void addAllowedMethod(String method);
119
120
// Header configuration
121
public Set<String> getAllowedHeaders();
122
public void setAllowedHeaders(Set<String> allowedHeaders);
123
public void addAllowedHeader(String header);
124
public Set<String> getExposedHeaders();
125
public void setExposedHeaders(Set<String> exposedHeaders);
126
public void addExposedHeader(String header);
127
128
// Preflight configuration
129
public boolean isAllowCredentials();
130
public void setAllowCredentials(boolean allowCredentials);
131
public int getPreflightMaxAge();
132
public void setPreflightMaxAge(int preflightMaxAge);
133
134
// Chain configuration
135
public boolean isChainPreflight();
136
public void setChainPreflight(boolean chainPreflight);
137
}
138
```
139
140
### CORS Configuration Example
141
142
```java
143
public class CORSConfiguration {
144
145
public void setupCORSHandler(Server server) {
146
CrossOriginHandler corsHandler = new CrossOriginHandler();
147
148
// Allow specific origins
149
corsHandler.setAllowedOriginPatterns(Set.of(
150
"https://example.com",
151
"https://*.example.com",
152
"http://localhost:*"
153
));
154
155
// Allow specific HTTP methods
156
corsHandler.setAllowedMethods(Set.of(
157
"GET", "POST", "PUT", "DELETE", "OPTIONS", "HEAD"
158
));
159
160
// Allow specific headers
161
corsHandler.setAllowedHeaders(Set.of(
162
"Content-Type",
163
"Authorization",
164
"X-Requested-With",
165
"Accept",
166
"Origin"
167
));
168
169
// Expose custom headers to client
170
corsHandler.setExposedHeaders(Set.of(
171
"X-Total-Count",
172
"X-Page-Count"
173
));
174
175
// Allow credentials (cookies, authorization headers)
176
corsHandler.setAllowCredentials(true);
177
178
// Cache preflight requests for 1 hour
179
corsHandler.setPreflightMaxAge(3600);
180
181
corsHandler.setHandler(new ApiHandler());
182
server.setHandler(corsHandler);
183
}
184
185
public void setupDevelopmentCORS(Server server) {
186
// Permissive CORS for development
187
CrossOriginHandler corsHandler = new CrossOriginHandler();
188
189
corsHandler.setAllowedOriginPatterns(Set.of("*"));
190
corsHandler.setAllowedMethods(Set.of("*"));
191
corsHandler.setAllowedHeaders(Set.of("*"));
192
corsHandler.setAllowCredentials(false); // Can't use * origins with credentials
193
194
corsHandler.setHandler(new DevelopmentHandler());
195
server.setHandler(corsHandler);
196
}
197
}
198
```
199
200
## Statistics Collection
201
202
### StatisticsHandler
203
204
Collects comprehensive request and response statistics.
205
206
```java { .api }
207
public class StatisticsHandler extends EventsHandler {
208
// Request statistics
209
public int getRequests();
210
public int getRequestsActive();
211
public int getRequestsActiveMax();
212
213
// Timing statistics
214
public long getStatsOnMs();
215
public long getRequestTimeTotal();
216
public long getRequestTimeMax();
217
public long getRequestTimeMean();
218
public long getRequestTimeStdDev();
219
220
// Response statistics by status
221
public int getResponses1xx();
222
public int getResponses2xx();
223
public int getResponses3xx();
224
public int getResponses4xx();
225
public int getResponses5xx();
226
227
// Byte statistics
228
public long getBytesReceived();
229
public long getBytesSent();
230
231
// Error statistics
232
public int getErrors();
233
public int getTimeouts();
234
235
// Async statistics
236
public int getAsyncRequests();
237
public int getAsyncRequestsWaiting();
238
public int getAsyncRequestsWaitingMax();
239
240
// Connection statistics
241
public int getConnections();
242
public int getConnectionsOpen();
243
public int getConnectionsOpenMax();
244
public Duration getConnectionsOpenMax();
245
246
// Statistics control
247
public void statsReset();
248
public String toStatsHTML();
249
}
250
```
251
252
### Statistics Usage Example
253
254
```java
255
public class StatisticsConfiguration {
256
257
public void setupStatisticsHandler(Server server) {
258
StatisticsHandler statsHandler = new StatisticsHandler();
259
260
// Set up statistics collection
261
statsHandler.setHandler(new ApplicationHandler());
262
263
// Add statistics reporting endpoint
264
Handler.Sequence rootHandler = new Handler.Sequence();
265
rootHandler.addHandler(new StatsReportingHandler(statsHandler));
266
rootHandler.addHandler(statsHandler);
267
268
server.setHandler(rootHandler);
269
270
// Start periodic statistics logging
271
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
272
scheduler.scheduleAtFixedRate(() -> {
273
logStatistics(statsHandler);
274
}, 60, 60, TimeUnit.SECONDS);
275
}
276
277
private void logStatistics(StatisticsHandler stats) {
278
System.out.println("=== Server Statistics ===");
279
System.out.println("Active Requests: " + stats.getRequestsActive());
280
System.out.println("Total Requests: " + stats.getRequests());
281
System.out.println("2xx Responses: " + stats.getResponses2xx());
282
System.out.println("4xx Responses: " + stats.getResponses4xx());
283
System.out.println("5xx Responses: " + stats.getResponses5xx());
284
System.out.println("Bytes Sent: " + stats.getBytesSent());
285
System.out.println("Mean Response Time: " + stats.getRequestTimeMean() + "ms");
286
System.out.println("Errors: " + stats.getErrors());
287
}
288
}
289
290
// Handler to expose statistics via HTTP
291
public class StatsReportingHandler extends Handler.Abstract {
292
private final StatisticsHandler statisticsHandler;
293
294
public StatsReportingHandler(StatisticsHandler statisticsHandler) {
295
this.statisticsHandler = statisticsHandler;
296
}
297
298
@Override
299
public boolean handle(Request request, Response response, Callback callback)
300
throws Exception {
301
302
if ("/stats".equals(request.getHttpURI().getPath())) {
303
response.setStatus(200);
304
response.getHeaders().put("Content-Type", "application/json");
305
306
String statsJson = createStatsJson(statisticsHandler);
307
response.write(true, ByteBuffer.wrap(statsJson.getBytes()), callback);
308
return true;
309
}
310
311
return false; // Not handled
312
}
313
314
private String createStatsJson(StatisticsHandler stats) {
315
Map<String, Object> statsMap = new HashMap<>();
316
statsMap.put("requests", stats.getRequests());
317
statsMap.put("requestsActive", stats.getRequestsActive());
318
statsMap.put("responses2xx", stats.getResponses2xx());
319
statsMap.put("responses4xx", stats.getResponses4xx());
320
statsMap.put("responses5xx", stats.getResponses5xx());
321
statsMap.put("bytesSent", stats.getBytesSent());
322
statsMap.put("bytesReceived", stats.getBytesReceived());
323
statsMap.put("meanResponseTime", stats.getRequestTimeMean());
324
statsMap.put("maxResponseTime", stats.getRequestTimeMax());
325
statsMap.put("errors", stats.getErrors());
326
statsMap.put("uptime", stats.getStatsOnMs());
327
328
// Convert to JSON (simplified)
329
return toJson(statsMap);
330
}
331
}
332
```
333
334
## Quality of Service (QoS)
335
336
### QoSHandler
337
338
Implements quality of service controls with request limiting and prioritization.
339
340
```java { .api }
341
public class QoSHandler extends ConditionalHandler.Abstract {
342
// Request limiting
343
public int getMaxRequests();
344
public void setMaxRequests(int maxRequests);
345
346
// Suspend configuration
347
public long getMaxSuspend();
348
public void setMaxSuspend(long maxSuspend);
349
350
// Current state
351
public int getRequests();
352
public int getSuspended();
353
354
// Priority configuration
355
public int getPriority(Request request);
356
public void setPriority(Request request, int priority);
357
}
358
```
359
360
### ThreadLimitHandler
361
362
Limits the number of threads processing requests.
363
364
```java { .api }
365
public class ThreadLimitHandler extends ConditionalHandler.Abstract {
366
// Thread limiting
367
public int getThreadLimit();
368
public void setThreadLimit(int threadLimit);
369
370
// Current state
371
public int getThreads();
372
public boolean isForwardedIdleTimeout();
373
public void setForwardedIdleTimeout(boolean forwardedIdleTimeout);
374
}
375
```
376
377
### QoS Configuration Example
378
379
```java
380
public class QoSConfiguration {
381
382
public void setupQoSHandlers(Server server) {
383
// Thread limiting handler
384
ThreadLimitHandler threadLimit = new ThreadLimitHandler();
385
threadLimit.setThreadLimit(50); // Max 50 concurrent processing threads
386
387
// Request limiting handler
388
QoSHandler qosHandler = new QoSHandler() {
389
@Override
390
protected boolean shouldHandle(Request request) {
391
// Apply QoS only to API requests
392
return request.getHttpURI().getPath().startsWith("/api/");
393
}
394
};
395
qosHandler.setMaxRequests(100); // Max 100 concurrent API requests
396
qosHandler.setMaxSuspend(30000); // Wait up to 30 seconds
397
398
// Chain handlers
399
threadLimit.setHandler(qosHandler);
400
qosHandler.setHandler(new ApplicationHandler());
401
402
server.setHandler(threadLimit);
403
}
404
405
public void setupPriorityQoS(Server server) {
406
QoSHandler priorityQoS = new QoSHandler() {
407
@Override
408
public int getPriority(Request request) {
409
// Prioritize authenticated users
410
if (request.getHeaders().get("Authorization") != null) {
411
return 0; // High priority
412
}
413
414
// Lower priority for anonymous users
415
return 1;
416
}
417
};
418
419
priorityQoS.setMaxRequests(50);
420
priorityQoS.setHandler(new ApplicationHandler());
421
server.setHandler(priorityQoS);
422
}
423
}
424
```
425
426
## Error Handling
427
428
### ErrorHandler
429
430
Generates error pages for HTTP error responses.
431
432
```java { .api }
433
public class ErrorHandler implements Request.Handler {
434
// Error page generation
435
public void handle(Request request, Response response, Callback callback) throws Exception;
436
public String getErrorPage(Request request, int code, String message);
437
public void writeErrorPage(Request request, Response response, Callback callback,
438
int code, String message, boolean showStacks);
439
440
// Configuration
441
public boolean isShowStacks();
442
public void setShowStacks(boolean showStacks);
443
public boolean isShowMessageInTitle();
444
public void setShowMessageInTitle(boolean showMessageInTitle);
445
446
// Caching
447
public String getCacheControl();
448
public void setCacheControl(String cacheControl);
449
}
450
```
451
452
### Custom Error Handler
453
454
```java
455
public class CustomErrorHandler extends ErrorHandler {
456
457
@Override
458
public void writeErrorPage(Request request, Response response, Callback callback,
459
int code, String message, boolean showStacks) {
460
461
response.getHeaders().put("Content-Type", "text/html; charset=utf-8");
462
463
String errorPage = generateCustomErrorPage(code, message, request);
464
response.write(true, ByteBuffer.wrap(errorPage.getBytes()), callback);
465
}
466
467
private String generateCustomErrorPage(int code, String message, Request request) {
468
StringBuilder html = new StringBuilder();
469
html.append("<!DOCTYPE html>\n");
470
html.append("<html>\n<head>\n");
471
html.append("<title>Error ").append(code).append("</title>\n");
472
html.append("<style>\n");
473
html.append("body { font-family: Arial, sans-serif; margin: 40px; }\n");
474
html.append(".error-container { max-width: 600px; margin: 0 auto; }\n");
475
html.append(".error-code { font-size: 72px; color: #dc3545; margin: 0; }\n");
476
html.append(".error-message { font-size: 24px; color: #6c757d; margin: 10px 0; }\n");
477
html.append("</style>\n");
478
html.append("</head>\n<body>\n");
479
html.append("<div class='error-container'>\n");
480
html.append("<h1 class='error-code'>").append(code).append("</h1>\n");
481
html.append("<p class='error-message'>").append(escapeHtml(message)).append("</p>\n");
482
483
if (code == 404) {
484
html.append("<p>The requested resource was not found on this server.</p>\n");
485
} else if (code >= 500) {
486
html.append("<p>An internal server error occurred. Please try again later.</p>\n");
487
}
488
489
html.append("<hr>\n");
490
html.append("<p><small>Request ID: ").append(request.getId()).append("</small></p>\n");
491
html.append("</div>\n</body>\n</html>");
492
493
return html.toString();
494
}
495
496
private String escapeHtml(String text) {
497
return text.replace("&", "&")
498
.replace("<", "<")
499
.replace(">", ">")
500
.replace("\"", """)
501
.replace("'", "'");
502
}
503
}
504
```
505
506
## Size Limiting
507
508
### SizeLimitHandler
509
510
Limits request and response sizes to prevent resource exhaustion.
511
512
```java { .api }
513
public class SizeLimitHandler extends Handler.Wrapper {
514
// Request size limiting
515
public long getRequestLimit();
516
public void setRequestLimit(long requestLimit);
517
518
// Response size limiting
519
public long getResponseLimit();
520
public void setResponseLimit(long responseLimit);
521
522
// Limit exceeded behavior
523
public String getLimitExceededMessage();
524
public void setLimitExceededMessage(String limitExceededMessage);
525
}
526
```
527
528
### Usage Example
529
530
```java
531
public class SizeLimitConfiguration {
532
533
public void setupSizeLimits(Server server) {
534
SizeLimitHandler sizeLimitHandler = new SizeLimitHandler();
535
536
// Limit request size to 10MB
537
sizeLimitHandler.setRequestLimit(10 * 1024 * 1024);
538
539
// Limit response size to 50MB
540
sizeLimitHandler.setResponseLimit(50 * 1024 * 1024);
541
542
// Custom error message
543
sizeLimitHandler.setLimitExceededMessage("Content size limit exceeded");
544
545
sizeLimitHandler.setHandler(new ApplicationHandler());
546
server.setHandler(sizeLimitHandler);
547
}
548
}
549
```
550
551
## Timeout Handling
552
553
### IdleTimeoutHandler
554
555
Sets idle timeout for individual requests.
556
557
```java { .api }
558
public class IdleTimeoutHandler extends Handler.Wrapper {
559
// Timeout configuration
560
public long getIdleTimeout();
561
public void setIdleTimeout(long idleTimeout);
562
563
// Apply timeout conditions
564
public boolean apply(String pathInContext, Request request, Response response);
565
}
566
```
567
568
### DelayedHandler
569
570
Adds artificial delay to request processing for testing or rate limiting.
571
572
```java { .api }
573
public class DelayedHandler extends Handler.Wrapper {
574
// Delay configuration
575
public long getDelayMs();
576
public void setDelayMs(long delayMs);
577
}
578
```
579
580
## Complete Utility Handler Chain
581
582
```java
583
public class UtilityHandlerChain {
584
585
public Handler createUtilityChain() {
586
// Statistics collection (outermost)
587
StatisticsHandler statsHandler = new StatisticsHandler();
588
589
// GZIP compression
590
GzipHandler gzipHandler = new GzipHandler();
591
gzipHandler.setMinGzipSize(1024);
592
gzipHandler.setIncludedMimeTypes("text/html", "text/css", "application/javascript", "application/json");
593
594
// CORS support
595
CrossOriginHandler corsHandler = new CrossOriginHandler();
596
corsHandler.setAllowedOriginPatterns(Set.of("https://*.example.com"));
597
corsHandler.setAllowedMethods(Set.of("GET", "POST", "PUT", "DELETE"));
598
599
// Size limiting
600
SizeLimitHandler sizeLimitHandler = new SizeLimitHandler();
601
sizeLimitHandler.setRequestLimit(10 * 1024 * 1024); // 10MB
602
603
// QoS limiting
604
QoSHandler qosHandler = new QoSHandler();
605
qosHandler.setMaxRequests(200);
606
607
// Application handler
608
Handler applicationHandler = new ApplicationHandler();
609
610
// Chain them together
611
statsHandler.setHandler(gzipHandler);
612
gzipHandler.setHandler(corsHandler);
613
corsHandler.setHandler(sizeLimitHandler);
614
sizeLimitHandler.setHandler(qosHandler);
615
qosHandler.setHandler(applicationHandler);
616
617
return statsHandler;
618
}
619
}
620
```