JDK-8326418 : Malformed header key when using HttpClient
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.net
  • Affected Version: 11,17,21,22
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: generic
  • CPU: generic
  • Submitted: 2024-02-21
  • Updated: 2024-02-21
  • Resolved: 2024-02-21
Description
ADDITIONAL SYSTEM INFORMATION :
Edition: Windows 10 Enterprise 
Version: 22H2
JDK-Version: 21.0.2
Used IDE: IntelliJ IDE 2023.3.3 (Ultimate Edition)

A DESCRIPTION OF THE PROBLEM :
When using the HttpClient from java.net.http creating a request and catching the response we receive headers as key-value-tupples (HttpHeaders) where the key is `:status`. 
This only happens when using the HttpClient - When we switch to URLConnection (java.net) the same header field looks different - Key = null

Respecting the general specification of proper header field syntax, the colon should be used as a seperator between key(field name) and value (field value). But for this particular header field it's part of the field name.

Please find attached an example which shows the difference.

HttpClient: ":status" => [200]
URLConnection: null => [HTTP/1.1 200 OK]

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Execute the attached source code and check the console output - There you can find the headers from both mentioned classes

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
HttpClient headers should respect the header field name specifiaction and use colon as a seperator (In the described case the colon should not be part of the `key`)

---------- BEGIN SOURCE ----------
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.util.List;
import java.util.Map;

public class RestClientTesting {

    public static void main(String[] args) throws IOException, InterruptedException {
        HttpClient jdkClient = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder(URI.create("https://www.google.com")).build();
        HttpResponse<Void> response = jdkClient.send(request, BodyHandlers.discarding());
        java.net.http.HttpHeaders httpClientHeaders = response.headers();

        System.out.println("\n \n ======= HTTP Client Headers =======\n \n");
        httpClientHeaders.map().forEach((k,v) -> System.out.println("\t" + k + ":\t" + v));


        URL url = new URL("https://www.google.com");
        URLConnection urlConnection = url.openConnection();
        urlConnection.connect();
        Map<String, List<String>> urlConnectionHeaders = urlConnection.getHeaderFields();

        System.out.println("\n \n ======= HttpURLConnection Headers =======\n \n");
        urlConnectionHeaders.forEach((k,v) -> System.out.println("\t" + k + ":\t" + v));
    }

}
---------- END SOURCE ----------

FREQUENCY : always



Comments
:status is a standard pseudo header for HTTP/2 see RFC 9113, section 8.3.2: Response Pseudo-Header Fields [1] The difference is that the HttpClient preferred version is HTTP/2, while HttpURLConnection is HTTP/1.1 only. This can be seen by printing the HttpResponse::version(). You will see that in cases where :status is present HTTP/2 was used. [1] https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.2
21-02-2024

The presence of the semi-colon in key field like ':status' for HttpClient can be observed from JDK 11 onwards. Observation on Windows 11 --------------------------------------- JDK 11.0.20 - Same JDK 17.0.2 - Same JDK 21.0.1 - Same JDK 21.0.3ea+2 -Same JDK 22ea+9 - Same
21-02-2024