HTTP Client in Java 9

Introduction to the New HTTP Client in Java 9

In this post, we will explore the new HTTP client in Java 9.This new feature is still in the incubating state which means that final features are subjected to change.

 

Introduction

While working on the HTTP resources with Java, we widely used third-party libraries like Apache HttpClient, Jetty etc.Java provide support using HttpURLConnection API which is not known as feature rich and user-friendly as compared to these third-party libraries.

Java 9 introduced a brand new HTTP client with following features

  • Support for HTTP/2
  • Comprehensive and sleek API design.
  • Feature rich and flexible API.

 

1. Why HTTP/2

Java 9’s HttpClient API module bring first-class support for HTTP/2.HTTP/2 brings a number of exciting features to help in an overall performance boost.

  • Support for stateful connections.
  • Long-running connections.
  • Multiplexing. Multiple requests are allowed at the same time, on the same connection.
  • Binary format. More compact.
  • Single Connection to the server reduces the number of round trips needed to set up multiple TCP connections.
  • Bidirectional communication using push requests
  • Data compression of HTTP headers.

For more information on HTTP/2 read HTTP/2 FAQ

 

2. Setup

The HttpClient module is bundled as an incubator module in Java 9.To start, we need to define a new module using module-info.java.This module will indicate the required module needed to run our application.

module com.javadevjournal.httpclient {
    requires jdk.incubator.httpclient;
}

 

3. Introduction to HttpClient API

The new HTTP client introduced in Java 9 supports HTTP/1.1 and HTTP/2, both synchronous and asynchronous programming models.HttpClients are immutable and based on the builder pattern.Here’s a basic request that prints the response body as a String.

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
     .uri(URI.create("https://postman-echo.com/get")).GET()
     .build();
        
client.sendAsync(request, asString())
    .thenApply(HttpResponse::body)
     .thenAccept(System.out::println)
     .join();

API consists of following core classes.

  • HttpClient – A container for configuration information common to multiple HttpRequests.
  • HttpRequest – HTTP Request.
  • HttpResponse – HTTP response.

 

4. HttpClient

HttpClient is responsible for processing all requests using this API.We can create HttpClient instance using HttpClient.newBuilder() method or by calling HttpClient.newHttpClient().We have the options to set following additional information while creating HttpClient

  • The protocol version ( HTTP/1.1 or HTTP/2).
  • Proxy setting
  • Redirect configurations.
  • Authenticator

 

4.1. Setting Protocol Version

To define preferred protocol version ( HTTP/1.1 or HTTP/2 ) for the HttpClient, we may use version() method while building the client.

HttpRequest request1 = HttpClient.newBuilder().version(HttpClient.Version.HTTP_2).build();

The JDK HTTP Client supports both HTTP/1.1 and HTTP/2. By default, the client will send requests using HTTP/2. Requests sent to servers that do not yet support HTTP/2 will automatically be downgraded to HTTP/1.1

 

4.2. Setting a Proxy

To set proxy for the client request, call proxy() method on the Builder instance.

HttpClient client = HttpClient.newBuilder()
                .proxy(ProxySelector.getDefault()).build();

HttpClient clien1 = HttpClient.newBuilder()
                .proxy(ProxySelector.of(new InetSocketAddress("www-proxy.com", 8080)))
                .build();

In the first example, we used the default system proxy by setting ProxySelector.getDefault().

 

4.3. Setting Redirect Policy

HttpClient provides options to redirect the request to the new URI automatically by setting redirect option using followRedirects() method in the Builder instance.

HttpClient client = HttpClient.newBuilder()
            .followRedirects(HttpClient.Redirect.SAME_PROTOCOL).build();

 

4.4. Setting Authenticator

An authenticator used for the HTTP authentication for a connection.Authenticator provides different authentication schemes which can be used while connecting with resources over the HTTP.

HttpClient client = HttpClient.newBuilder()
        .authenticator(Authenticator.getDefault())
                .build();
//Password Authentication
 HttpClient client = HttpClient.newBuilder()
  .authenticator(new Authenticator() {
   @Override
   protected PasswordAuthentication getPasswordAuthentication() {
       return new PasswordAuthentication("admin","password".toCharArray());
   }
}).build();

 

4.5. CookieManager

It’s easy to set cookies with new HttpClient using CookieManager.We may use builder method cookieManager() method to set cookies.

HttpClient client1 = HttpClient.newBuilder().cookieManager(new CookieManager(null, CookiePolicy.ACCEPT_ALL)).build()

 

4.6. Sync vs. Async Request

The new HttpClient provides 2 options to for sending a request to the server.

  • Synchronously (client will wait until receiving a response)
  • Asynchronously (non-blocking, not wait for a response)

To send the request in synchronous mode, we may use send() method provided by the client.This method waits for a response.

HttpRequest request = HttpRequest.newBuilder()
     .uri(URI.create("https://postman-echo.com/get")).GET()
     .build();
HttpResponse response = HttpClient.newBuilder().build().send(request,HttpResponse.BodyHandler.asString());

To send the request asynchronously, we may use sendAsync() method.

HttpClient client = HttpClient.newHttpClient();
 HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://postman-echo.com/get")).GET()
      .build();
CompletableFuture<HttpResponse> response= client.sendAsync(request, HttpResponse.BodyHandler.asString());

 

5. HttpRequest

HttpRequest class represent the request sent to the customer the server.Use HttpRequest.newBuilder to create a new instance of the HttpRequest.

HttpRequest request = HttpRequest.newBuilder()
       .uri(URI.create("https://javadevjournal.com/"))
       .build();

Request builder used to set

  • URI
  • Http methods (GET, PUT, POST etc.)
  • Request body
  • Timeout
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://postman-echo.com/get"))
    .timeout(Duration.ofMinutes(1))
    .header("Content-Type", "application/json")
    .GET(BodyPublisher.fromFile(Paths.get("file.json")))
    .build();

 

5.1.  Specifying the HTTP Method

We may pass HTTP method to the HttpRequest by calling method provided by the builder.Let’s create simple GET request.

HttpRequest request = HttpRequest.newBuilder()
   .uri(URI.create("https://postman-echo.com/get")).GET()
   .build();

 

5.2.  Setting HTTP Protocol

HttpClient by default use Http/2 protocol, however, it provides options to set Http protocol using version() method in the builder.

HttpRequest request = HttpRequest.newBuilder()
   .uri(URI.create("https://postman-echo.com/get")).GET()
   .version(HttpClient.Version.HTTP_2)
   .build();

 

5.3.  Setting HTTP Headers

To send Http headers in the request, we useheader() method provided in the Builder.To set all headers as key-value pairs we can use headers() method

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://postman-echo.com/get"))
  .headers("Content-Type", "application/json","Header1","Value 1")
  .build();

 

5.4.  Setting Timeout

Use timeout() method under the builder to set the timeout for the HttpRequest.API will throw HttpTimeoutException exception.

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://postman-echo.com/get"))
  .timeout(Duration.ofMinutes(1))
  .build();

[pullquote align=”normal”]The default timeout is set to infinity [/pullquote]

 

5.3.  Http Request body

To set the body of the HttpRequest, use Http method provided by the builder by passing BodyProcessor.

  • POST(BodyProcessor body);
  • DELETE(BodyProcessor body);

There are numbers of out of the box BodyProcessors provided by the JDK.Here is the list

  • FileProcessor – A request body processor that takes data from the contents of a File.
  • StringProcessor – Returns a request body processor whose body is the given String, converted using the UTF_8 character set. 
  • ByteArrayProcessor – Read body from a byte array.

For complete list read.

HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://postman-echo.com/post"))
  .POST(HttpRequest.noBody())
  .build();

Let’s take a look at some of the BodyProcessor available out of the box in the JDK.

 

5.4.  ByteArrayProcessor

byte[] requestBody ="This is expected to be sent back as part of response body".getBytes();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://postman-echo.com/post"))
  .POST(HttpRequest.BodyProcessor.fromByteArray(requestBody))
  .build();

 

5.5. StringProcessor

String requestBody ="This is expected to be sent back as part of response body".getBytes();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
  .uri(URI.create("https://postman-echo.com/post"))
  .POST(HttpRequest.BodyProcessor.fromString(requestBody))
  .build();

 

6. HttpResponse

HttpResponse object represents the response from the server for the given request.On a high-level HttpResponse provide following important information.

  • statusCode()  – Returns the status code for this response.
  • body() – Returns the response body.
  • headers() -Response headers.
  • uri() – Returns the URI that the response was received from.
  • version() – Returns the HTTP protocol version used for this response.

 

6.1. Response Status Code

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://postman-echo.com/post"))
    .POST(HttpRequest.BodyProcessor.fromString("This is expected to be sent back as part of response body"))
    .build();

HttpResponse response= client.send(request, HttpResponse.BodyHandler.asString());
if(response.statusCode() == 200){
    //process business logic
}

 

6.2. HTTP Response Headers

To obtain headers from the response, call header() method on the response object.

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://postman-echo.com/post"))
    .POST(HttpRequest.BodyProcessor.fromString("This is expected to be sent back as part of response body"))
    .build();

HttpResponse response= client.send(request, HttpResponse.BodyHandler.asString());
if(response.statusCode() == 200){
    HttpHeaders header = response.headers();
}

 

6.2. HTTP Response Version

The version() method will help us to determine what HTTP protocol used for server communication.This is really helpful as it will give us low-level information about the communication.

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://postman-echo.com/post"))
    .POST(HttpRequest.BodyProcessor.fromString("This is expected to be sent back as part of response body"))
    .build();

HttpResponse response= client.send(request, HttpResponse.BodyHandler.asString());
if(response.statusCode() == 200){
  System.out.println(response.version());
}

For above method, we got following response back from the server

HTTP_1_1

Since HTTP/2 is the default preferred protocol, and the implementation seamlessly fallbacks to HTTP/1.1 where necessary.In this case, the server was not supporting HTTP/2 protocol and HTTPClient fallbacks to HTTP/1.1.

 

7. HTTPClient with Java 10

Java 10 brought a number of enhancements to the HTTP Client in Java 9.Below is the list of the enhancement.

  • Additional convenience request body publishers.
  • New response body subscribers added.
  • API is fully asynchronous in Java 10.

Read more 

 

Summary

In this article, we discussed the new HTTP Client in Java 9.We discussed different feature introduced in this new API and how to use these features to build HTTP/2 powered API.