New convenience HttpHandler

Pavel Rappo pavel.rappo at gmail.com
Sun Apr 27 15:11:06 UTC 2025


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


More information about the net-dev mailing list