Eclipse Jetty HTTP Client - A lightweight, asynchronous HTTP client library that supports HTTP/1.1, HTTP/2, WebSocket, and various authentication mechanisms, proxy configurations, and connection pooling strategies.
—
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.
The core interface for accessing HTTP response information.
public interface Response {
// Request association
Request getRequest();
// Status information
HttpVersion getVersion();
int getStatus();
String getReason();
// Headers and trailers
HttpFields getHeaders();
HttpFields getTrailers();
// Listener interfaces for event-driven processing
interface Listener extends EventListener {
default void onBegin(Response response) {}
default void onHeader(Response response, HttpField field) {}
default void onHeaders(Response response) {}
default void onContent(Response response, ByteBuffer content) {}
default void onSuccess(Response response) {}
default void onFailure(Response response, Throwable failure) {}
default void onComplete(Result result) {}
}
interface CompleteListener extends Listener {
void onComplete(Result result);
}
interface BeginListener extends EventListener {
void onBegin(Response response);
}
interface HeaderListener extends EventListener {
void onHeader(Response response, HttpField field);
}
interface HeadersListener extends EventListener {
void onHeaders(Response response);
}
interface ContentListener extends EventListener {
void onContent(Response response, ByteBuffer content);
}
interface DemandedContentListener extends EventListener {
void onContent(Response response, LongConsumer demand, ByteBuffer content);
}
interface SuccessListener extends EventListener {
void onSuccess(Response response);
}
interface FailureListener extends EventListener {
void onFailure(Response response, Throwable failure);
}
}Extended response interface that provides access to buffered response content.
public interface ContentResponse extends Response {
// Content access
String getMediaType();
String getEncoding();
byte[] getContent();
String getContentAsString();
String getContentAsString(String encoding);
// Factory method
static ContentResponse from(Response response, byte[] content, String mediaType, String encoding);
}ContentResponse response = client.GET("https://api.example.com/users");
// Status information
int status = response.getStatus();
String reason = response.getReason();
HttpVersion version = response.getVersion();
System.out.println("Status: " + status + " " + reason);
System.out.println("HTTP Version: " + version);
// Content access
byte[] contentBytes = response.getContent();
String contentString = response.getContentAsString();
String mediaType = response.getMediaType();
String encoding = response.getEncoding();
System.out.println("Content Type: " + mediaType);
System.out.println("Content Length: " + contentBytes.length);
System.out.println("Content: " + contentString);ContentResponse response = client.GET("https://api.example.com/data");
HttpFields headers = response.getHeaders();
// Access specific headers
String contentType = headers.get("Content-Type");
String serverHeader = headers.get("Server");
String cacheControl = headers.get("Cache-Control");
// Iterate through all headers
for (HttpField field : headers) {
System.out.println(field.getName() + ": " + field.getValue());
}
// Check for header existence
if (headers.contains("X-Rate-Limit-Remaining")) {
String rateLimit = headers.get("X-Rate-Limit-Remaining");
System.out.println("Remaining requests: " + rateLimit);
}Process responses as they arrive without blocking:
client.newRequest("https://api.example.com/stream")
.send(new Response.Listener() {
@Override
public void onBegin(Response response) {
System.out.println("Response begun: " + response.getStatus());
}
@Override
public void onHeaders(Response response) {
HttpFields headers = response.getHeaders();
String contentType = headers.get("Content-Type");
System.out.println("Content-Type: " + contentType);
}
@Override
public void onContent(Response response, ByteBuffer content) {
// Process content as it arrives
byte[] bytes = new byte[content.remaining()];
content.get(bytes);
System.out.println("Received chunk: " + bytes.length + " bytes");
}
@Override
public void onSuccess(Response response) {
System.out.println("Response completed successfully");
}
@Override
public void onFailure(Response response, Throwable failure) {
System.err.println("Response failed: " + failure.getMessage());
}
});Simplified listener for complete response handling:
client.newRequest("https://api.example.com/data")
.send(result -> {
if (result.isSucceeded()) {
Response response = result.getResponse();
System.out.println("Success: " + response.getStatus());
// Access request that generated this response
Request request = result.getRequest();
System.out.println("Original URL: " + request.getURI());
} else {
Throwable failure = result.getFailure();
System.err.println("Request failed: " + failure.getMessage());
}
});Automatically buffers response content in memory:
public class BufferingResponseListener extends Response.Listener.Adapter {
public BufferingResponseListener();
public BufferingResponseListener(int maxLength);
public byte[] getContent();
public String getContentAsString();
public String getContentAsString(String encoding);
public String getMediaType();
public String getEncoding();
}BufferingResponseListener listener = new BufferingResponseListener();
client.newRequest("https://api.example.com/data")
.send(listener);
// Wait for completion and access buffered content
Response response = listener.get(5, TimeUnit.SECONDS);
byte[] content = listener.getContent();
String contentString = listener.getContentAsString();Provides InputStream access to response content:
public class InputStreamResponseListener extends Response.Listener.Adapter {
public InputStreamResponseListener();
public InputStream getInputStream();
public Response get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException, ExecutionException;
}InputStreamResponseListener listener = new InputStreamResponseListener();
client.newRequest("https://api.example.com/large-file")
.send(listener);
// Get the InputStream to read content
try (InputStream inputStream = listener.getInputStream()) {
// Process stream as data arrives
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
// Process buffer content
processData(buffer, 0, bytesRead);
}
}Saves response content directly to a file:
public class PathResponseListener extends Response.Listener.Adapter {
public PathResponseListener(Path file);
public PathResponseListener(Path file, boolean overwrite);
public Path getPath();
}Path downloadPath = Paths.get("/tmp/downloaded-file.zip");
PathResponseListener listener = new PathResponseListener(downloadPath, true);
client.newRequest("https://api.example.com/download/file.zip")
.send(listener);
// Wait for download completion
Response response = listener.get(30, TimeUnit.SECONDS);
if (response.getStatus() == 200) {
System.out.println("File downloaded to: " + downloadPath);
}CompletableFuture-based response handling:
public class FutureResponseListener extends BufferingResponseListener implements Future<ContentResponse> {
public FutureResponseListener(Request request);
public FutureResponseListener(Request request, int maxLength);
}Request request = client.newRequest("https://api.example.com/data");
FutureResponseListener listener = new FutureResponseListener(request);
request.send(listener);
// Use as Future
ContentResponse response = listener.get(10, TimeUnit.SECONDS);
String content = response.getContentAsString();The Result interface provides access to the complete request/response cycle:
public interface Result {
Request getRequest();
Response getResponse();
Throwable getRequestFailure();
Throwable getResponseFailure();
boolean isSucceeded();
boolean isFailed();
}client.newRequest("https://api.example.com/data")
.send(result -> {
Request request = result.getRequest();
System.out.println("Request URL: " + request.getURI());
if (result.isSucceeded()) {
Response response = result.getResponse();
System.out.println("Response status: " + response.getStatus());
} else {
// Check for request vs response failures
Throwable requestFailure = result.getRequestFailure();
Throwable responseFailure = result.getResponseFailure();
if (requestFailure != null) {
System.err.println("Request failed: " + requestFailure.getMessage());
}
if (responseFailure != null) {
System.err.println("Response failed: " + responseFailure.getMessage());
}
}
});ContentResponse response = client.GET("https://api.example.com/resource");
switch (response.getStatus()) {
case 200:
// OK - process successful response
String content = response.getContentAsString();
break;
case 201:
// Created - resource created successfully
String location = response.getHeaders().get("Location");
break;
case 204:
// No Content - successful operation with no response body
break;
case 301:
case 302:
// Redirects - typically handled automatically by client
String location = response.getHeaders().get("Location");
break;
case 400:
// Bad Request - client error
String errorDetails = response.getContentAsString();
throw new IllegalArgumentException("Bad request: " + errorDetails);
case 401:
// Unauthorized - authentication required
throw new SecurityException("Authentication required");
case 403:
// Forbidden - access denied
throw new SecurityException("Access forbidden");
case 404:
// Not Found - resource doesn't exist
throw new IllegalArgumentException("Resource not found");
case 429:
// Too Many Requests - rate limiting
String retryAfter = response.getHeaders().get("Retry-After");
throw new RuntimeException("Rate limited. Retry after: " + retryAfter);
case 500:
// Internal Server Error
throw new RuntimeException("Server error: " + response.getReason());
default:
if (response.getStatus() >= 400) {
throw new RuntimeException("HTTP error: " + response.getStatus() + " " + response.getReason());
}
}ContentResponse response = client.GET("https://api.example.com/data");
int status = response.getStatus();
if (status >= 200 && status < 300) {
// Success responses
handleSuccess(response);
} else if (status >= 300 && status < 400) {
// Redirection responses (usually handled automatically)
handleRedirect(response);
} else if (status >= 400 && status < 500) {
// Client error responses
handleClientError(response);
} else if (status >= 500) {
// Server error responses
handleServerError(response);
}ContentResponse response = client.GET("https://api.example.com/data");
String contentType = response.getMediaType();
String encoding = response.getEncoding();
if ("application/json".equals(contentType)) {
String json = response.getContentAsString();
// Parse JSON content
} else if ("application/xml".equals(contentType)) {
String xml = response.getContentAsString();
// Parse XML content
} else if (contentType != null && contentType.startsWith("text/")) {
String text = response.getContentAsString(encoding);
// Process text content
} else {
byte[] binaryData = response.getContent();
// Process binary content
}client.newRequest("https://api.example.com/large-dataset")
.send(new Response.Listener() {
private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@Override
public void onContent(Response response, ByteBuffer content) {
// Stream content to avoid memory issues
byte[] bytes = new byte[content.remaining()];
content.get(bytes);
try {
buffer.write(bytes);
// Process buffer when it reaches a certain size
if (buffer.size() > 64 * 1024) { // 64KB chunks
processChunk(buffer.toByteArray());
buffer.reset();
}
} catch (IOException e) {
throw new RuntimeException("Failed to process content", e);
}
}
@Override
public void onSuccess(Response response) {
// Process any remaining content
if (buffer.size() > 0) {
processChunk(buffer.toByteArray());
}
}
});public class ValidatingResponseListener extends Response.Listener.Adapter {
@Override
public void onHeaders(Response response) {
// Validate response headers
HttpFields headers = response.getHeaders();
String contentType = headers.get("Content-Type");
if (contentType == null || !contentType.startsWith("application/json")) {
throw new IllegalStateException("Expected JSON response, got: " + contentType);
}
String contentLength = headers.get("Content-Length");
if (contentLength != null) {
long length = Long.parseLong(contentLength);
if (length > MAX_RESPONSE_SIZE) {
throw new IllegalStateException("Response too large: " + length);
}
}
}
@Override
public void onSuccess(Response response) {
if (response.getStatus() != 200) {
throw new IllegalStateException("Expected 200 OK, got: " + response.getStatus());
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-client