Core server component of Eclipse Jetty web server providing HTTP server functionality, request handling, and connection management
—
The request and response APIs provide the core interfaces for handling HTTP requests and generating responses, including content processing, header management, and lifecycle callbacks.
The Request interface represents an HTTP request with content, headers, and context.
public interface Request extends Attributes, Content.Source {
// Request identification and metadata
String getId();
Components getComponents();
ConnectionMetaData getConnectionMetaData();
// HTTP method and URI
String getMethod();
HttpURI getHttpURI();
// Request context
Context getContext();
// Headers and cookies
HttpFields getHeaders();
List<HttpCookie> getCookies();
// Session management
Session getSession(boolean create);
// Content handling (from Content.Source)
Content.Chunk read();
void demand(Runnable demandCallback);
void fail(Throwable failure);
// Nested interfaces
interface Handler {
boolean handle(Request request, Response response, Callback callback) throws Exception;
}
interface Wrapper extends Request {
Request getWrapped();
}
}The Response interface represents an HTTP response with status, headers, and content writing capabilities.
public interface Response extends Content.Sink {
// Request association
Request getRequest();
// Status management
int getStatus();
void setStatus(int code);
// Header management
HttpFields.Mutable getHeaders();
// Content writing (from Content.Sink)
void write(boolean last, ByteBuffer byteBuffer, Callback callback);
// Error handling
Runnable writeError(Request request, Response response, Callback callback,
int code, String message);
// Response state
boolean isCommitted();
boolean isCompleted();
long getBytesWritten();
// Nested classes
class Wrapper implements Response {
public Response getWrapped();
}
}public class SimpleHandler extends Handler.Abstract {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
// Get request information
String method = request.getMethod();
String path = request.getHttpURI().getPath();
String userAgent = request.getHeaders().get("User-Agent");
System.out.println("Request: " + method + " " + path);
System.out.println("User-Agent: " + userAgent);
// Set response
response.setStatus(200);
response.getHeaders().put("Content-Type", "text/html");
String html = "<html><body><h1>Hello from Jetty!</h1>" +
"<p>Method: " + method + "</p>" +
"<p>Path: " + path + "</p></body></html>";
response.write(true, ByteBuffer.wrap(html.getBytes()), callback);
return true;
}
}public class ContentHandler extends Handler.Abstract {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
// Check if request has content
String contentType = request.getHeaders().get("Content-Type");
if (contentType != null && contentType.startsWith("application/json")) {
readJsonContent(request, response, callback);
} else {
handleNoContent(response, callback);
}
return true;
}
private void readJsonContent(Request request, Response response, Callback callback) {
StringBuilder content = new StringBuilder();
// Read content chunks asynchronously
request.demand(() -> {
Content.Chunk chunk;
while ((chunk = request.read()) != null) {
if (chunk.hasRemaining()) {
// Process content chunk
ByteBuffer buffer = chunk.getByteBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
content.append(new String(bytes, StandardCharsets.UTF_8));
}
chunk.release();
if (chunk.isLast()) {
// All content read, process and respond
processJsonAndRespond(content.toString(), response, callback);
return;
}
}
// Need more content, demand again
request.demand(() -> readJsonContent(request, response, callback));
});
}
private void processJsonAndRespond(String json, Response response, Callback callback) {
response.setStatus(200);
response.getHeaders().put("Content-Type", "application/json");
String responseJson = "{\"received\": " + json + ", \"status\": \"processed\"}";
response.write(true, ByteBuffer.wrap(responseJson.getBytes()), callback);
}
private void handleNoContent(Response response, Callback callback) {
response.setStatus(400);
response.getHeaders().put("Content-Type", "text/plain");
response.write(true, ByteBuffer.wrap("No content provided".getBytes()), callback);
}
}public class SessionHandler extends Handler.Abstract {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
// Get or create session
Session session = request.getSession(true);
String sessionId = session.getId();
// Read session data
Integer visitCount = (Integer) session.getAttribute("visitCount");
if (visitCount == null) {
visitCount = 0;
}
visitCount++;
session.setAttribute("visitCount", visitCount);
// Read cookies
List<HttpCookie> cookies = request.getCookies();
String preferredLang = null;
for (HttpCookie cookie : cookies) {
if ("lang".equals(cookie.getName())) {
preferredLang = cookie.getValue();
break;
}
}
// Set response cookie
if (preferredLang == null) {
preferredLang = "en";
HttpCookie langCookie = HttpCookie.build("lang", preferredLang)
.path("/")
.maxAge(Duration.ofDays(30))
.httpOnly(true)
.build();
response.getHeaders().addCookie(langCookie);
}
// Generate response
response.setStatus(200);
response.getHeaders().put("Content-Type", "text/html");
String html = String.format(
"<html><body>" +
"<h1>Session Demo</h1>" +
"<p>Session ID: %s</p>" +
"<p>Visit count: %d</p>" +
"<p>Language: %s</p>" +
"</body></html>",
sessionId, visitCount, preferredLang);
response.write(true, ByteBuffer.wrap(html.getBytes()), callback);
return true;
}
}Parse form data from request content.
public class FormFields extends ContentSourceCompletableFuture<Fields> {
public static CompletableFuture<Fields> from(Request request);
public static CompletableFuture<Fields> from(Request request, Charset charset, int maxFields, int maxLength);
}Usage:
public class FormHandler extends Handler.Abstract {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws Exception {
String contentType = request.getHeaders().get("Content-Type");
if ("application/x-www-form-urlencoded".equals(contentType)) {
FormFields.from(request).thenAccept(fields -> {
// Process form fields
String name = fields.getValue("name");
String email = fields.getValue("email");
// Generate response
response.setStatus(200);
response.getHeaders().put("Content-Type", "text/plain");
String responseText = "Received: name=" + name + ", email=" + email;
response.write(true, ByteBuffer.wrap(responseText.getBytes()), callback);
}).exceptionally(throwable -> {
// Handle parsing error
response.setStatus(400);
response.getHeaders().put("Content-Type", "text/plain");
response.write(true, ByteBuffer.wrap("Invalid form data".getBytes()), callback);
return null;
});
return true;
}
return false; // Not handled
}
}public interface HttpURI {
String getScheme();
String getHost();
int getPort();
String getPath();
String getQuery();
String getFragment();
String getParam(String key);
Map<String, List<String>> getQueryParameters();
boolean isAbsolute();
}Usage:
public boolean handle(Request request, Response response, Callback callback) throws Exception {
HttpURI uri = request.getHttpURI();
String scheme = uri.getScheme(); // "http" or "https"
String host = uri.getHost(); // "localhost"
int port = uri.getPort(); // 8080
String path = uri.getPath(); // "/api/users"
String query = uri.getQuery(); // "page=1&size=10"
// Parse query parameters
Map<String, List<String>> queryParams = uri.getQueryParameters();
String page = queryParams.containsKey("page") ?
queryParams.get("page").get(0) : "1";
String size = queryParams.containsKey("size") ?
queryParams.get("size").get(0) : "20";
// Generate response based on parameters
response.setStatus(200);
response.getHeaders().put("Content-Type", "application/json");
String json = String.format(
"{\"path\": \"%s\", \"page\": %s, \"size\": %s}",
path, page, size);
response.write(true, ByteBuffer.wrap(json.getBytes()), callback);
return true;
}public interface HttpFields extends Iterable<HttpField> {
// Field access
HttpField getField(String name);
String get(String name);
List<String> getValuesList(String name);
int size();
boolean contains(String name);
boolean contains(String name, String value);
// Iteration
Iterator<HttpField> iterator();
Stream<HttpField> stream();
// Mutable version for responses
interface Mutable extends HttpFields {
HttpField put(String name, String value);
HttpField add(String name, String value);
boolean remove(String name);
void clear();
void addCookie(HttpCookie cookie);
}
}public interface ConnectionMetaData extends Attributes {
String getId();
HttpConfiguration getHttpConfiguration();
String getProtocol();
SocketAddress getLocalSocketAddress();
SocketAddress getRemoteSocketAddress();
boolean isSecure();
X509Certificate[] getPeerCertificates();
}Usage:
public boolean handle(Request request, Response response, Callback callback) throws Exception {
ConnectionMetaData connMeta = request.getConnectionMetaData();
String protocol = connMeta.getProtocol(); // "HTTP/1.1", "HTTP/2", etc.
boolean isSecure = connMeta.isSecure(); // true for HTTPS
SocketAddress remoteAddr = connMeta.getRemoteSocketAddress();
// Client certificate info for mutual TLS
X509Certificate[] clientCerts = connMeta.getPeerCertificates();
String clientDN = null;
if (clientCerts != null && clientCerts.length > 0) {
clientDN = clientCerts[0].getSubjectX500Principal().getName();
}
// Generate response with connection info
response.setStatus(200);
response.getHeaders().put("Content-Type", "application/json");
String json = String.format(
"{\"protocol\": \"%s\", \"secure\": %b, \"remote\": \"%s\", \"clientDN\": \"%s\"}",
protocol, isSecure, remoteAddr, clientDN);
response.write(true, ByteBuffer.wrap(json.getBytes()), callback);
return true;
}Base class for wrapping and modifying requests.
public static class Request.Wrapper implements Request {
public Request getWrapped();
// All Request methods delegate to wrapped instance
// Override specific methods to modify behavior
}Usage:
public class TimingRequestWrapper extends Request.Wrapper {
private final long startTime;
public TimingRequestWrapper(Request wrapped) {
super(wrapped);
this.startTime = System.nanoTime();
}
public long getProcessingTime() {
return System.nanoTime() - startTime;
}
}
// In handler
TimingRequestWrapper timedRequest = new TimingRequestWrapper(request);
// Process request...
long processingTime = timedRequest.getProcessingTime();Base class for wrapping and modifying responses.
public static class Response.Wrapper implements Response {
public Response getWrapped();
// Override methods to intercept/modify response behavior
}Usage:
public class CompressingResponseWrapper extends Response.Wrapper {
private final GZIPOutputStream gzipOut;
public CompressingResponseWrapper(Response wrapped) throws IOException {
super(wrapped);
this.gzipOut = new GZIPOutputStream(new ResponseOutputStream(wrapped));
}
@Override
public void write(boolean last, ByteBuffer content, Callback callback) {
try {
// Compress content before writing
byte[] bytes = new byte[content.remaining()];
content.get(bytes);
gzipOut.write(bytes);
if (last) {
gzipOut.close();
}
callback.succeeded();
} catch (IOException e) {
callback.failed(e);
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-eclipse-jetty--jetty-server