I'm using HttpServer to implement an HTTP probe [^1] that provides application state at the time of probing. I find that convenience handlers provided by HttpHandlers are insufficient for my use case. I also find that implementing a custom HttpHandler is tricky without the help of documentation.
Perhaps my use case is typical enough so that HttpHandlers could provide a new convenience handler. That handler would send a dynamically supplied string, rather than a static string. If you also find it useful, I'd appreciate it if we could discuss it. Before I create a JBS issue, please have a look at this crude patch to see if it makes sense to you in principle. Thanks. [^1]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/ diff --git a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpHandlers.java b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpHandlers.java index 03642033914..987de0ede5d 100644 --- a/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpHandlers.java +++ b/src/jdk.httpserver/share/classes/com/sun/net/httpserver/HttpHandlers.java @@ -25,9 +25,11 @@ package com.sun.net.httpserver; +import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Objects; import java.util.function.Predicate; +import java.util.function.Supplier; /** * Implementations of {@link com.sun.net.httpserver.HttpHandler HttpHandler} @@ -140,28 +142,60 @@ public static HttpHandler handleOrElse(Predicate<Request> handlerTest, * @throws NullPointerException if headers or body are null */ public static HttpHandler of(int statusCode, Headers headers, String body) { + Objects.requireNonNull(body); + return of(statusCode, headers, () -> body); + } + + /** + * Returns an {@code HttpHandler} that sends a response comprising the given + * {@code statusCode}, {@code headers}, and {@code body}. + * + * <p> This method creates a handler that reads and discards the request + * body before it sets the response state and sends the response. + * + * <p> {@code headers} are the effective headers of the response. The + * response <i>body bytes</i> are a {@code UTF-8} encoded byte sequence of + * a string, which is supplied by {@code bodySupplier} immediately after the request body is read. The response headers + * {@linkplain HttpExchange#sendResponseHeaders(int, long) are sent} with + * the given {@code statusCode} and the body bytes' length (or {@code -1} + * if the body is empty). The body bytes are then sent as response body, + * unless the body is empty, in which case no response body is sent. + * + * @param statusCode a response status code + * @param headers a headers + * @param bodySupplier a supplier for the response body string + * @return a handler + * @throws IllegalArgumentException if statusCode is not a positive 3-digit + * integer, as per rfc2616, section 6.1.1 + * @throws NullPointerException if headers or body are null + */ + public static HttpHandler of(int statusCode, Headers headers, Supplier<String> bodySupplier) { if (statusCode < 100 || statusCode > 999) throw new IllegalArgumentException("statusCode must be 3-digit: " + statusCode); Objects.requireNonNull(headers); - Objects.requireNonNull(body); + Objects.requireNonNull(bodySupplier); final var headersCopy = Headers.of(headers); - final var bytes = body.getBytes(StandardCharsets.UTF_8); return exchange -> { try (exchange) { - exchange.getRequestBody().readAllBytes(); + exchange.getRequestBody().transferTo(OutputStream.nullOutputStream()); // discard exchange.getResponseHeaders().putAll(headersCopy); - if (exchange.getRequestMethod().equals("HEAD")) { - exchange.getResponseHeaders().set("Content-Length", Integer.toString(bytes.length)); - exchange.sendResponseHeaders(statusCode, -1); - } - else if (bytes.length == 0) { - exchange.sendResponseHeaders(statusCode, -1); + var body = bodySupplier.get(); + if (body == null) { + exchange.sendResponseHeaders(500, -1); // Internal Server Error } else { - exchange.sendResponseHeaders(statusCode, bytes.length); - exchange.getResponseBody().write(bytes); + final var bytes = body.getBytes(StandardCharsets.UTF_8); + if (exchange.getRequestMethod().equals("HEAD")) { + exchange.getResponseHeaders().set("Content-Length", Integer.toString(bytes.length)); + exchange.sendResponseHeaders(statusCode, -1); + } else if (bytes.length == 0) { + exchange.sendResponseHeaders(statusCode, -1); + } else { + exchange.sendResponseHeaders(statusCode, bytes.length); + exchange.getResponseBody().write(bytes); + } } } }; diff --git a/test/jdk/com/sun/net/httpserver/simpleserver/HttpHandlersTest.java b/test/jdk/com/sun/net/httpserver/simpleserver/HttpHandlersTest.java index 85d271e44fa..d64fa03740f 100644 --- a/test/jdk/com/sun/net/httpserver/simpleserver/HttpHandlersTest.java +++ b/test/jdk/com/sun/net/httpserver/simpleserver/HttpHandlersTest.java @@ -81,7 +81,7 @@ public void testNull() { final var headers = new Headers(); final var body = ""; assertThrows(NPE, () -> HttpHandlers.of(200, null, body)); - assertThrows(NPE, () -> HttpHandlers.of(200, headers, null)); + assertThrows(NPE, () -> HttpHandlers.of(200, headers, (String) null)); } @Test