0
# Response Processing
1
2
The response processing capability provides comprehensive response handling including status codes, headers, content processing, and streaming capabilities with multiple listener patterns for both synchronous and asynchronous response handling.
3
4
## Response Interface
5
6
The core interface for accessing HTTP response information.
7
8
```java { .api }
9
public interface Response {
10
// Request association
11
Request getRequest();
12
13
// Status information
14
HttpVersion getVersion();
15
int getStatus();
16
String getReason();
17
18
// Headers and trailers
19
HttpFields getHeaders();
20
HttpFields getTrailers();
21
22
// Listener interfaces for event-driven processing
23
interface Listener extends EventListener {
24
default void onBegin(Response response) {}
25
default void onHeader(Response response, HttpField field) {}
26
default void onHeaders(Response response) {}
27
default void onContent(Response response, ByteBuffer content) {}
28
default void onSuccess(Response response) {}
29
default void onFailure(Response response, Throwable failure) {}
30
default void onComplete(Result result) {}
31
}
32
33
interface CompleteListener extends Listener {
34
void onComplete(Result result);
35
}
36
37
interface BeginListener extends EventListener {
38
void onBegin(Response response);
39
}
40
41
interface HeaderListener extends EventListener {
42
void onHeader(Response response, HttpField field);
43
}
44
45
interface HeadersListener extends EventListener {
46
void onHeaders(Response response);
47
}
48
49
interface ContentListener extends EventListener {
50
void onContent(Response response, ByteBuffer content);
51
}
52
53
interface DemandedContentListener extends EventListener {
54
void onContent(Response response, LongConsumer demand, ByteBuffer content);
55
}
56
57
interface SuccessListener extends EventListener {
58
void onSuccess(Response response);
59
}
60
61
interface FailureListener extends EventListener {
62
void onFailure(Response response, Throwable failure);
63
}
64
}
65
```
66
67
## ContentResponse Interface
68
69
Extended response interface that provides access to buffered response content.
70
71
```java { .api }
72
public interface ContentResponse extends Response {
73
// Content access
74
String getMediaType();
75
String getEncoding();
76
byte[] getContent();
77
String getContentAsString();
78
String getContentAsString(String encoding);
79
80
// Factory method
81
static ContentResponse from(Response response, byte[] content, String mediaType, String encoding);
82
}
83
```
84
85
## Basic Response Access
86
87
### Synchronous Response Handling
88
89
```java
90
ContentResponse response = client.GET("https://api.example.com/users");
91
92
// Status information
93
int status = response.getStatus();
94
String reason = response.getReason();
95
HttpVersion version = response.getVersion();
96
97
System.out.println("Status: " + status + " " + reason);
98
System.out.println("HTTP Version: " + version);
99
100
// Content access
101
byte[] contentBytes = response.getContent();
102
String contentString = response.getContentAsString();
103
String mediaType = response.getMediaType();
104
String encoding = response.getEncoding();
105
106
System.out.println("Content Type: " + mediaType);
107
System.out.println("Content Length: " + contentBytes.length);
108
System.out.println("Content: " + contentString);
109
```
110
111
### Response Headers
112
113
```java
114
ContentResponse response = client.GET("https://api.example.com/data");
115
116
HttpFields headers = response.getHeaders();
117
118
// Access specific headers
119
String contentType = headers.get("Content-Type");
120
String serverHeader = headers.get("Server");
121
String cacheControl = headers.get("Cache-Control");
122
123
// Iterate through all headers
124
for (HttpField field : headers) {
125
System.out.println(field.getName() + ": " + field.getValue());
126
}
127
128
// Check for header existence
129
if (headers.contains("X-Rate-Limit-Remaining")) {
130
String rateLimit = headers.get("X-Rate-Limit-Remaining");
131
System.out.println("Remaining requests: " + rateLimit);
132
}
133
```
134
135
## Asynchronous Response Processing
136
137
### Using Response Listeners
138
139
Process responses as they arrive without blocking:
140
141
```java
142
client.newRequest("https://api.example.com/stream")
143
.send(new Response.Listener() {
144
@Override
145
public void onBegin(Response response) {
146
System.out.println("Response begun: " + response.getStatus());
147
}
148
149
@Override
150
public void onHeaders(Response response) {
151
HttpFields headers = response.getHeaders();
152
String contentType = headers.get("Content-Type");
153
System.out.println("Content-Type: " + contentType);
154
}
155
156
@Override
157
public void onContent(Response response, ByteBuffer content) {
158
// Process content as it arrives
159
byte[] bytes = new byte[content.remaining()];
160
content.get(bytes);
161
System.out.println("Received chunk: " + bytes.length + " bytes");
162
}
163
164
@Override
165
public void onSuccess(Response response) {
166
System.out.println("Response completed successfully");
167
}
168
169
@Override
170
public void onFailure(Response response, Throwable failure) {
171
System.err.println("Response failed: " + failure.getMessage());
172
}
173
});
174
```
175
176
### Using Complete Listener
177
178
Simplified listener for complete response handling:
179
180
```java
181
client.newRequest("https://api.example.com/data")
182
.send(result -> {
183
if (result.isSucceeded()) {
184
Response response = result.getResponse();
185
System.out.println("Success: " + response.getStatus());
186
187
// Access request that generated this response
188
Request request = result.getRequest();
189
System.out.println("Original URL: " + request.getURI());
190
} else {
191
Throwable failure = result.getFailure();
192
System.err.println("Request failed: " + failure.getMessage());
193
}
194
});
195
```
196
197
## Specialized Response Listeners
198
199
### BufferingResponseListener
200
201
Automatically buffers response content in memory:
202
203
```java { .api }
204
public class BufferingResponseListener extends Response.Listener.Adapter {
205
public BufferingResponseListener();
206
public BufferingResponseListener(int maxLength);
207
208
public byte[] getContent();
209
public String getContentAsString();
210
public String getContentAsString(String encoding);
211
public String getMediaType();
212
public String getEncoding();
213
}
214
```
215
216
```java
217
BufferingResponseListener listener = new BufferingResponseListener();
218
219
client.newRequest("https://api.example.com/data")
220
.send(listener);
221
222
// Wait for completion and access buffered content
223
Response response = listener.get(5, TimeUnit.SECONDS);
224
byte[] content = listener.getContent();
225
String contentString = listener.getContentAsString();
226
```
227
228
### InputStreamResponseListener
229
230
Provides InputStream access to response content:
231
232
```java { .api }
233
public class InputStreamResponseListener extends Response.Listener.Adapter {
234
public InputStreamResponseListener();
235
236
public InputStream getInputStream();
237
public Response get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException;
238
}
239
```
240
241
```java
242
InputStreamResponseListener listener = new InputStreamResponseListener();
243
244
client.newRequest("https://api.example.com/large-file")
245
.send(listener);
246
247
// Get the InputStream to read content
248
try (InputStream inputStream = listener.getInputStream()) {
249
// Process stream as data arrives
250
byte[] buffer = new byte[8192];
251
int bytesRead;
252
while ((bytesRead = inputStream.read(buffer)) != -1) {
253
// Process buffer content
254
processData(buffer, 0, bytesRead);
255
}
256
}
257
```
258
259
### PathResponseListener
260
261
Saves response content directly to a file:
262
263
```java { .api }
264
public class PathResponseListener extends Response.Listener.Adapter {
265
public PathResponseListener(Path file);
266
public PathResponseListener(Path file, boolean overwrite);
267
268
public Path getPath();
269
}
270
```
271
272
```java
273
Path downloadPath = Paths.get("/tmp/downloaded-file.zip");
274
PathResponseListener listener = new PathResponseListener(downloadPath, true);
275
276
client.newRequest("https://api.example.com/download/file.zip")
277
.send(listener);
278
279
// Wait for download completion
280
Response response = listener.get(30, TimeUnit.SECONDS);
281
if (response.getStatus() == 200) {
282
System.out.println("File downloaded to: " + downloadPath);
283
}
284
```
285
286
### FutureResponseListener
287
288
CompletableFuture-based response handling:
289
290
```java { .api }
291
public class FutureResponseListener extends BufferingResponseListener implements Future<ContentResponse> {
292
public FutureResponseListener(Request request);
293
public FutureResponseListener(Request request, int maxLength);
294
}
295
```
296
297
```java
298
Request request = client.newRequest("https://api.example.com/data");
299
FutureResponseListener listener = new FutureResponseListener(request);
300
301
request.send(listener);
302
303
// Use as Future
304
ContentResponse response = listener.get(10, TimeUnit.SECONDS);
305
String content = response.getContentAsString();
306
```
307
308
## Result Interface
309
310
The Result interface provides access to the complete request/response cycle:
311
312
```java { .api }
313
public interface Result {
314
Request getRequest();
315
Response getResponse();
316
Throwable getRequestFailure();
317
Throwable getResponseFailure();
318
boolean isSucceeded();
319
boolean isFailed();
320
}
321
```
322
323
### Result Processing
324
325
```java
326
client.newRequest("https://api.example.com/data")
327
.send(result -> {
328
Request request = result.getRequest();
329
System.out.println("Request URL: " + request.getURI());
330
331
if (result.isSucceeded()) {
332
Response response = result.getResponse();
333
System.out.println("Response status: " + response.getStatus());
334
} else {
335
// Check for request vs response failures
336
Throwable requestFailure = result.getRequestFailure();
337
Throwable responseFailure = result.getResponseFailure();
338
339
if (requestFailure != null) {
340
System.err.println("Request failed: " + requestFailure.getMessage());
341
}
342
if (responseFailure != null) {
343
System.err.println("Response failed: " + responseFailure.getMessage());
344
}
345
}
346
});
347
```
348
349
## Status Code Handling
350
351
### Common Status Code Patterns
352
353
```java
354
ContentResponse response = client.GET("https://api.example.com/resource");
355
356
switch (response.getStatus()) {
357
case 200:
358
// OK - process successful response
359
String content = response.getContentAsString();
360
break;
361
362
case 201:
363
// Created - resource created successfully
364
String location = response.getHeaders().get("Location");
365
break;
366
367
case 204:
368
// No Content - successful operation with no response body
369
break;
370
371
case 301:
372
case 302:
373
// Redirects - typically handled automatically by client
374
String location = response.getHeaders().get("Location");
375
break;
376
377
case 400:
378
// Bad Request - client error
379
String errorDetails = response.getContentAsString();
380
throw new IllegalArgumentException("Bad request: " + errorDetails);
381
382
case 401:
383
// Unauthorized - authentication required
384
throw new SecurityException("Authentication required");
385
386
case 403:
387
// Forbidden - access denied
388
throw new SecurityException("Access forbidden");
389
390
case 404:
391
// Not Found - resource doesn't exist
392
throw new IllegalArgumentException("Resource not found");
393
394
case 429:
395
// Too Many Requests - rate limiting
396
String retryAfter = response.getHeaders().get("Retry-After");
397
throw new RuntimeException("Rate limited. Retry after: " + retryAfter);
398
399
case 500:
400
// Internal Server Error
401
throw new RuntimeException("Server error: " + response.getReason());
402
403
default:
404
if (response.getStatus() >= 400) {
405
throw new RuntimeException("HTTP error: " + response.getStatus() + " " + response.getReason());
406
}
407
}
408
```
409
410
### Status Code Categories
411
412
```java
413
ContentResponse response = client.GET("https://api.example.com/data");
414
int status = response.getStatus();
415
416
if (status >= 200 && status < 300) {
417
// Success responses
418
handleSuccess(response);
419
} else if (status >= 300 && status < 400) {
420
// Redirection responses (usually handled automatically)
421
handleRedirect(response);
422
} else if (status >= 400 && status < 500) {
423
// Client error responses
424
handleClientError(response);
425
} else if (status >= 500) {
426
// Server error responses
427
handleServerError(response);
428
}
429
```
430
431
## Advanced Response Processing
432
433
### Content Type Processing
434
435
```java
436
ContentResponse response = client.GET("https://api.example.com/data");
437
438
String contentType = response.getMediaType();
439
String encoding = response.getEncoding();
440
441
if ("application/json".equals(contentType)) {
442
String json = response.getContentAsString();
443
// Parse JSON content
444
} else if ("application/xml".equals(contentType)) {
445
String xml = response.getContentAsString();
446
// Parse XML content
447
} else if (contentType != null && contentType.startsWith("text/")) {
448
String text = response.getContentAsString(encoding);
449
// Process text content
450
} else {
451
byte[] binaryData = response.getContent();
452
// Process binary content
453
}
454
```
455
456
### Large Response Streaming
457
458
```java
459
client.newRequest("https://api.example.com/large-dataset")
460
.send(new Response.Listener() {
461
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
462
463
@Override
464
public void onContent(Response response, ByteBuffer content) {
465
// Stream content to avoid memory issues
466
byte[] bytes = new byte[content.remaining()];
467
content.get(bytes);
468
469
try {
470
buffer.write(bytes);
471
472
// Process buffer when it reaches a certain size
473
if (buffer.size() > 64 * 1024) { // 64KB chunks
474
processChunk(buffer.toByteArray());
475
buffer.reset();
476
}
477
} catch (IOException e) {
478
throw new RuntimeException("Failed to process content", e);
479
}
480
}
481
482
@Override
483
public void onSuccess(Response response) {
484
// Process any remaining content
485
if (buffer.size() > 0) {
486
processChunk(buffer.toByteArray());
487
}
488
}
489
});
490
```
491
492
### Response Validation
493
494
```java
495
public class ValidatingResponseListener extends Response.Listener.Adapter {
496
@Override
497
public void onHeaders(Response response) {
498
// Validate response headers
499
HttpFields headers = response.getHeaders();
500
501
String contentType = headers.get("Content-Type");
502
if (contentType == null || !contentType.startsWith("application/json")) {
503
throw new IllegalStateException("Expected JSON response, got: " + contentType);
504
}
505
506
String contentLength = headers.get("Content-Length");
507
if (contentLength != null) {
508
long length = Long.parseLong(contentLength);
509
if (length > MAX_RESPONSE_SIZE) {
510
throw new IllegalStateException("Response too large: " + length);
511
}
512
}
513
}
514
515
@Override
516
public void onSuccess(Response response) {
517
if (response.getStatus() != 200) {
518
throw new IllegalStateException("Expected 200 OK, got: " + response.getStatus());
519
}
520
}
521
}
522
```