Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8296355

Lightweight HTTP Server Blends Requests Leading to Failures to Parse Headers

XMLWordPrintable

    • generic
    • generic

      A DESCRIPTION OF THE PROBLEM :
      While handling concurrent requests, the lightweight HTTP server sometimes fails to correctly parse request headers. Log statements from the JDK Lightweight HTTP Server show that the server fails to parse a request line because the URI is interrupted by header data (presumably from another, concurrent request).

      The probability of reproducing this error increases with the size of the HTTP request body.

      This issue does not occur on newer versions of OpenJDK (I tested versions 11 and 17). I am able to reproduce this problem on Java 8 builds from Eclipse Temurin, Oracle, and IBM. The latest build I tested with is Eclipse Temurin 1.8.0_345-b01.

      REGRESSION : Last worked in version 11

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Run a basic Lightweight HTTP Server such as the one included in the source code. Use a load generator to send concurrent requests to the server. Use large request bodies (~4MB) for the POST requests, and also send small GET requests.

      Enable FINEST logging for the HTTP Server to help detect the error condition.

      com.sun.net.httpserver.level = FINEST
      java.util.logging.ConsoleHandler.level = FINEST

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      Lightweight HTTP Server parses the valid HTTP request headers and handles the request.
      ACTUAL -
      Oct 24, 2022 3:24:48 PM sun.net.httpserver.ServerImpl logReply
      FINE: y HTTP/1.1 [400 Bad Request] (Bad request line)
      Oct 24, 2022 3:24:48 PM sun.net.httpserver.ServerImpl logReply
      FINE: POST /ecHost: test-applications.contsec.com [404 Not Found] (No context found for request)

      ---------- BEGIN SOURCE ----------
      public class FailsParseHeaders {

        public static void main(final String[] args) throws IOException {
          final HttpServer server = HttpServer.create();
          final InetSocketAddress address = new InetSocketAddress("localhost", 8080);
          server.setExecutor(Executors.newCachedThreadPool());
          server.bind(address, 0);

          // register routes
          server.createContext(
              "/ping",
              exchange -> {
                exchange.sendResponseHeaders(204, 0);
                exchange.getResponseBody().close();
              });
          server.createContext(
              "/echo",
              exchange -> {
                final int length =
                    exchange.getRequestHeaders().get("content-length").stream()
                        .findFirst()
                        .map(Integer::parseInt)
                        .orElse(0);
                if (length > 0) {
                  exchange.sendResponseHeaders(200, length);
                  try (InputStream request = exchange.getRequestBody();
                      OutputStream response = exchange.getResponseBody()) {
                    byte[] buffer = new byte[1024];
                    int read;
                    while ((read = request.read(buffer, 0, buffer.length)) >= 0) {
                      response.write(buffer, 0, read);
                    }
                  }
                } else {
                  discardInputStream(exchange);
                  exchange.sendResponseHeaders(204, 0);
                }
              });

          server.start();
        }

        static void discardInputStream(final HttpExchange exchange) throws IOException {
          try (InputStream body = exchange.getRequestBody()) {
            while (body.read() >= 0) {}
          }
        }
      }
      ---------- END SOURCE ----------

      FREQUENCY : rarely


            michaelm Michael McMahon
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated: