CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-io-helidon-webserver--helidon-webserver

High-performance HTTP server implementation built on Java 21 Virtual Threads for Helidon microservices framework

Pending
Overview
Eval results
Files

request-response.mddocs/

Request and Response Handling

Complete HTTP request and response interfaces with entity handling, headers, parameters, and content negotiation for comprehensive HTTP communication.

Capabilities

Handler Interface

The core interface for processing HTTP requests and generating responses.

/**
 * Interface for handling HTTP requests.
 */
interface Handler {
    /**
     * Handle HTTP request and generate response.
     * @param req server request
     * @param res server response
     */
    void handle(ServerRequest req, ServerResponse res);
    
    /**
     * Create handler from consumer function.
     * @param handler consumer that processes the request
     * @return new handler instance
     */
    static Handler create(Consumer<ServerRequest> handler);
    
    /**
     * Create handler from function that returns response data.
     * @param handler function that processes request and returns response data
     * @return new handler instance
     */
    static Handler create(Function<ServerRequest, ?> handler);
    
    /**
     * Create handler from supplier that returns response data.
     * @param handler supplier that returns response data
     * @return new handler instance
     */
    static Handler create(Supplier<?> handler);
}

Usage Examples:

import io.helidon.webserver.http.Handler;

// Lambda handler
Handler lambdaHandler = (req, res) -> {
    res.send("Hello from lambda");
};

// Consumer-based handler
Handler consumerHandler = Handler.create(req -> {
    System.out.println("Processing request: " + req.method());
});

// Function-based handler
Handler functionHandler = Handler.create(req -> {
    return "Response for " + req.path().path();
});

// Supplier-based handler
Handler supplierHandler = Handler.create(() -> {
    return "Fixed response";
});

ServerRequest Interface

Interface for accessing HTTP request data, headers, parameters, and body content.

/**
 * HTTP server request interface.
 */
interface ServerRequest extends HttpRequest {
    /**
     * Get request path information.
     * @return path information with parameters and matchers
     */
    ServerRequestPath path();
    
    /**
     * Get request query parameters.
     * @return query parameters
     */
    Parameters query();
    
    /**
     * Get request headers.
     * @return request headers
     */
    ServerRequestHeaders headers();
    
    /**
     * Get request entity (body).
     * @return request entity
     */
    ServerRequestEntity entity();
    
    /**
     * Get HTTP method.
     * @return HTTP method
     */
    Method method();
    
    /**
     * Get request URI.
     * @return request URI
     */
    UriInfo requestedUri();
    
    /**
     * Get local address of the connection.
     * @return local address
     */
    String localAddress();
    
    /**
     * Get local port of the connection.
     * @return local port
     */
    int localPort();
    
    /**
     * Get remote address of the connection.
     * @return remote address
     */
    String remoteAddress();
    
    /**
     * Get remote port of the connection.
     * @return remote port
     */
    int remotePort();
    
    /**
     * Check if connection is secure (TLS).
     * @return true if secure connection
     */
    boolean isSecure();
    
    /**
     * Get server request context.
     * @return request context
     */
    Context context();
}

Usage Examples:

Handler requestInfoHandler = (req, res) -> {
    // Access request information
    String method = req.method().text();
    String path = req.path().path();
    String remoteAddr = req.remoteAddress();
    boolean secure = req.isSecure();
    
    // Access path parameters
    Map<String, String> pathParams = req.path().pathParameters().toMap();
    String userId = pathParams.get("id");
    
    // Access query parameters  
    Optional<String> filter = req.query().first("filter");
    List<String> tags = req.query().all("tag");
    
    // Access headers
    Optional<String> contentType = req.headers().contentType()
        .map(MediaType::text);
    Optional<String> userAgent = req.headers().first("User-Agent");
    
    // Build response
    String info = String.format(
        "Method: %s, Path: %s, Remote: %s, Secure: %s",
        method, path, remoteAddr, secure
    );
    
    res.send(info);
};

ServerResponse Interface

Interface for generating HTTP responses with status codes, headers, and body content.

/**
 * HTTP server response interface.
 */
interface ServerResponse {
    /**
     * Set response status code.
     * @param status HTTP status
     * @return this response for chaining
     */
    ServerResponse status(Status status);
    
    /**
     * Set response status code.
     * @param statusCode HTTP status code
     * @return this response for chaining
     */
    ServerResponse status(int statusCode);
    
    /**
     * Add response header.
     * @param name header name
     * @param value header value
     * @return this response for chaining
     */
    ServerResponse header(String name, String value);
    
    /**
     * Add response header with multiple values.
     * @param name header name
     * @param values header values
     * @return this response for chaining
     */
    ServerResponse header(String name, String... values);
    
    /**
     * Set response headers.
     * @param headers headers to set
     * @return this response for chaining
     */
    ServerResponse headers(WritableHeaders<?> headers);
    
    /**
     * Set content type.
     * @param contentType media type
     * @return this response for chaining
     */
    ServerResponse contentType(MediaType contentType);
    
    /**
     * Send response with string content.
     * @param entity response content
     */
    void send(String entity);
    
    /**
     * Send response with byte array content.
     * @param entity response content
     */
    void send(byte[] entity);
    
    /**
     * Send response with input stream content.
     * @param entity response content
     */
    void send(InputStream entity);
    
    /**
     * Send response with object content (uses media support for serialization).
     * @param entity response object
     */
    void send(Object entity);
    
    /**
     * Send empty response.
     */
    void send();
    
    /**
     * Re-route request to different path.
     * @param path new path
     */
    void reroute(String path);
    
    /**
     * Re-route request to different path with query parameters.
     * @param path new path
     * @param queryParams query parameters
     */
    void reroute(String path, UriQuery queryParams);
    
    /**
     * Get response headers for modification.
     * @return response headers
     */
    ServerResponseHeaders headers();
    
    /**
     * Check if response headers have been sent.
     * @return true if headers sent
     */
    boolean headersSent();
    
    /**
     * Get server response context.
     * @return response context
     */
    Context context();
}

Usage Examples:

import io.helidon.http.Status;
import io.helidon.http.MediaType;

// Basic response handling
Handler basicHandler = (req, res) -> {
    res.status(Status.OK_200)
       .contentType(MediaType.TEXT_PLAIN)
       .send("Hello World");
};

// JSON response
Handler jsonHandler = (req, res) -> {
    Map<String, Object> data = Map.of(
        "message", "Hello",
        "timestamp", System.currentTimeMillis()
    );
    
    res.status(Status.OK_200)
       .contentType(MediaType.APPLICATION_JSON)
       .send(data); // Automatically serialized to JSON
};

// Custom headers
Handler customHeadersHandler = (req, res) -> {
    res.status(Status.OK_200)
       .header("X-Custom-Header", "CustomValue")
       .header("Cache-Control", "no-cache", "no-store")
       .contentType(MediaType.TEXT_HTML)
       .send("<h1>Custom Response</h1>");
};

// File download
Handler downloadHandler = (req, res) -> {
    try {
        Path filePath = Paths.get("data.pdf");
        byte[] fileContent = Files.readAllBytes(filePath);
        
        res.status(Status.OK_200)
           .contentType(MediaType.APPLICATION_OCTET_STREAM)
           .header("Content-Disposition", "attachment; filename=data.pdf")
           .send(fileContent);
    } catch (IOException e) {
        res.status(Status.INTERNAL_SERVER_ERROR_500)
           .send("File not found");
    }
};

// Streaming response
Handler streamHandler = (req, res) -> {
    try {
        InputStream stream = new FileInputStream("large-file.txt");
        res.status(Status.OK_200)
           .contentType(MediaType.TEXT_PLAIN)
           .send(stream); // Automatically streams content
    } catch (FileNotFoundException e) {
        res.status(Status.NOT_FOUND_404).send("File not found");
    }
};

ServerRequestEntity Interface

Interface for accessing request body content with various content types.

/**
 * Entity part of server request for accessing request body.
 */
interface ServerRequestEntity {
    /**
     * Get entity as string.
     * @return entity content as string
     */
    String as(Class<String> type);
    
    /**
     * Get entity as byte array.
     * @return entity content as byte array
     */
    byte[] as(Class<byte[]> type);
    
    /**
     * Get entity as input stream.
     * @return entity content as input stream
     */
    InputStream as(Class<InputStream> type);
    
    /**
     * Get entity as specific type using media support.
     * @param type target type
     * @param <T> type parameter
     * @return entity converted to target type
     */
    <T> T as(Class<T> type);
    
    /**
     * Get entity as generic type using media support.
     * @param type generic type token
     * @param <T> type parameter
     * @return entity converted to target type
     */
    <T> T as(GenericType<T> type);
    
    /**
     * Check if entity is available.
     * @return true if entity is present
     */
    boolean hasEntity();
    
    /**
     * Get entity content type.
     * @return media type of entity
     */
    Optional<MediaType> contentType();
    
    /**
     * Get entity content length.
     * @return content length if known
     */
    OptionalLong contentLength();
}

Usage Examples:

// Handle different content types
Handler entityHandler = (req, res) -> {
    ServerRequestEntity entity = req.entity();
    
    if (!entity.hasEntity()) {
        res.status(Status.BAD_REQUEST_400).send("Request body required");
        return;
    }
    
    Optional<MediaType> contentType = entity.contentType();
    
    if (contentType.isPresent()) {
        if (MediaType.APPLICATION_JSON.test(contentType.get())) {
            // Handle JSON
            Map<String, Object> jsonData = entity.as(Map.class);
            res.send("Received JSON: " + jsonData);
            
        } else if (MediaType.TEXT_PLAIN.test(contentType.get())) {
            // Handle text
            String textData = entity.as(String.class);
            res.send("Received text: " + textData);
            
        } else if (MediaType.APPLICATION_OCTET_STREAM.test(contentType.get())) {
            // Handle binary data
            byte[] binaryData = entity.as(byte[].class);
            res.send("Received " + binaryData.length + " bytes");
            
        } else {
            res.status(Status.UNSUPPORTED_MEDIA_TYPE_415)
               .send("Unsupported content type");
        }
    } else {
        // Default handling
        String content = entity.as(String.class);
        res.send("Received: " + content);
    }
};

// Custom object deserialization
Handler userHandler = (req, res) -> {
    try {
        User user = req.entity().as(User.class); // Auto-deserialize JSON to User
        // Process user object
        res.status(Status.CREATED_201).send("User created: " + user.getName());
    } catch (Exception e) {
        res.status(Status.BAD_REQUEST_400).send("Invalid user data");
    }
};

ServerRequestPath Interface

Interface for accessing path information, parameters, and matching details.

/**
 * Server request path information.
 */
interface ServerRequestPath {
    /**
     * Get absolute path of the request.
     * @return absolute path
     */
    String path();
    
    /**
     * Get path parameters extracted from path pattern.
     * @return path parameters
     */
    Parameters pathParameters();
    
    /**
     * Get matched path pattern.
     * @return path pattern that matched this request
     */
    Optional<String> pathPattern();
    
    /**
     * Get path matcher used for matching.
     * @return path matcher
     */
    Optional<PathMatcher> pathMatcher();
    
    /**
     * Get remaining path after matching.
     * @return remaining unmatched path
     */
    String remainingPath();
}

Parameters Interface

Interface for accessing query and path parameters with type conversion.

/**
 * Interface for accessing parameters with type conversion.
 */
interface Parameters {
    /**
     * Get first parameter value.
     * @param name parameter name
     * @return optional parameter value
     */
    Optional<String> first(String name);
    
    /**
     * Get all parameter values.
     * @param name parameter name
     * @return list of parameter values
     */
    List<String> all(String name);
    
    /**
     * Get parameter value or default.
     * @param name parameter name
     * @param defaultValue default value if parameter not found
     * @return parameter value or default
     */
    String value(String name, String defaultValue);
    
    /**
     * Check if parameter exists.
     * @param name parameter name
     * @return true if parameter exists
     */
    boolean contains(String name);
    
    /**
     * Get all parameter names.
     * @return set of parameter names
     */
    Set<String> names();
    
    /**
     * Convert to map.
     * @return map of parameter names to first values
     */
    Map<String, String> toMap();
    
    /**
     * Convert to multi-value map.
     * @return map of parameter names to all values
     */
    Map<String, List<String>> toMultiMap();
}

Usage Examples:

Handler parameterHandler = (req, res) -> {
    // Path parameters (from /users/{id}/posts/{postId})
    Parameters pathParams = req.path().pathParameters();
    String userId = pathParams.first("id").orElse("unknown");
    String postId = pathParams.first("postId").orElse("unknown");
    
    // Query parameters (?filter=active&tag=java&tag=web)
    Parameters queryParams = req.query();
    String filter = queryParams.value("filter", "all");
    List<String> tags = queryParams.all("tag");
    boolean hasSearch = queryParams.contains("search");
    
    // Convert to maps for easier processing
    Map<String, String> pathMap = pathParams.toMap();
    Map<String, List<String>> queryMap = queryParams.toMultiMap();
    
    String response = String.format(
        "User: %s, Post: %s, Filter: %s, Tags: %s",
        userId, postId, filter, tags
    );
    
    res.send(response);
};

Advanced Request/Response Patterns

Content Negotiation

Handler contentNegotiationHandler = (req, res) -> {
    Optional<String> acceptHeader = req.headers().first("Accept");
    
    Map<String, Object> data = Map.of(
        "message", "Hello World",
        "timestamp", System.currentTimeMillis()
    );
    
    if (acceptHeader.isPresent()) {
        String accept = acceptHeader.get();
        
        if (accept.contains("application/json")) {
            res.contentType(MediaType.APPLICATION_JSON).send(data);
        } else if (accept.contains("application/xml")) {
            res.contentType(MediaType.APPLICATION_XML).send(toXml(data));
        } else if (accept.contains("text/html")) {
            res.contentType(MediaType.TEXT_HTML)
               .send("<h1>Hello World</h1><p>Timestamp: " + data.get("timestamp") + "</p>");
        } else {
            res.contentType(MediaType.TEXT_PLAIN)
               .send("Hello World at " + data.get("timestamp"));
        }
    } else {
        // Default to JSON
        res.contentType(MediaType.APPLICATION_JSON).send(data);
    }
};

Request Validation

Handler validationHandler = (req, res) -> {
    // Validate required headers
    if (!req.headers().contains("Authorization")) {
        res.status(Status.UNAUTHORIZED_401)
           .send("Authorization header required");
        return;
    }
    
    // Validate path parameters
    String id = req.path().pathParameters().first("id").orElse("");
    if (id.isEmpty() || !id.matches("\\d+")) {
        res.status(Status.BAD_REQUEST_400)
           .send("Invalid ID parameter");
        return;
    }
    
    // Validate query parameters
    Optional<String> limit = req.query().first("limit");
    if (limit.isPresent()) {
        try {
            int limitValue = Integer.parseInt(limit.get());
            if (limitValue < 1 || limitValue > 100) {
                res.status(Status.BAD_REQUEST_400)
                   .send("Limit must be between 1 and 100");
                return;
            }
        } catch (NumberFormatException e) {
            res.status(Status.BAD_REQUEST_400)
               .send("Invalid limit parameter");
            return;
        }
    }
    
    // Process valid request
    res.send("Valid request processed");
};

Response Streaming

Handler streamingHandler = (req, res) -> {
    res.status(Status.OK_200)
       .contentType(MediaType.TEXT_PLAIN)
       .header("Transfer-Encoding", "chunked");
    
    // Stream large dataset
    try (BufferedWriter writer = new BufferedWriter(
             new OutputStreamWriter(res.outputStream()))) {
        
        for (int i = 0; i < 1000; i++) {
            writer.write("Data chunk " + i + "\n");
            writer.flush();
            
            // Simulate processing delay
            Thread.sleep(10);
        }
    } catch (Exception e) {
        // Handle streaming errors
        res.status(Status.INTERNAL_SERVER_ERROR_500)
           .send("Streaming error");
    }
};

Request Context and State

Handler contextHandler = (req, res) -> {
    Context requestContext = req.context();
    
    // Store data in request context
    requestContext.register("startTime", System.currentTimeMillis());
    requestContext.register("requestId", UUID.randomUUID().toString());
    
    // Access context data
    String requestId = requestContext.get("requestId", String.class)
        .orElse("unknown");
    
    res.header("X-Request-ID", requestId)
       .send("Request processed with ID: " + requestId);
};

Install with Tessl CLI

npx tessl i tessl/maven-io-helidon-webserver--helidon-webserver

docs

configuration.md

http-routing.md

http-services.md

http1-protocol.md

index.md

request-response.md

server-management.md

spi.md

tile.json