The new HTTP Client API - Sip of Java

Need to send or receive data from a remote service over HTTP? The HTTP Client added in JDK 11 improves upon and replaces the HttpUrlConnection API. In this article, we will explore how to use the new HttpClient and some new functionality it offers that wasn’t available with HttpUrlConnection.

HttpClient Overview

The HTTP Client was added in JDK 11 after first being introduced as an incubator feature in JDK 9. The goal of the HTTP Client was to replace HttpUrlConnection, which, along with having a difficult-to-use API, didn’t offer support for protocols like HTTP/2 and WebSocket.

The HTTP Client addresses these shortcomings by offering support for the HTTP/2 and WebSocket, having an easier-to-use API, and adding new functionality like support for asynchronous calls and reactive streams.

Creating a Client

A new instance of HttpClient is created through its builder API:

HttpClient client = HttpClient.newBuilder()
	.version(Version.HTTP_1_1)
	.followRedirects(Redirect.NORMAL)
	.connectTimeout(Duration.ofSeconds(20))
	.proxy(ProxySelector.of(...))
	.authenticator(Authenticator.getDefault())
	.build();

newBuilder() has methods for setting properties like; HTTP version, redirect behavior, proxies, timeout, an authenticator, and more. Once created, an HttpClient is immutable.

Creating a Request

Like HttpClient, an HttpRequest is also created through its builder API:

HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://openjdk.org/"))
      .timeout(Duration.ofSeconds(10))
      .header("Content-Type",
      	"application/json")
      .GET()
      .build()

The newBuilder() provides methods for setting properties like; URI, timeout, headers, HTTP method, and others. Like HttpClient, an HttpRequest is immutable; however, a request can be used multiple times.

##Sending Requests

Requests can be sent either as a synchronous blocking call or asynchronous non-block call.

Synchronous Requests

HttpResponse<String> response =
      client.send(request, 
      	BodyHandlers.ofString());
System.out.println(response.statusCode());
System.out.println(response.body());

Asynchronous Requests

client.sendAsync(request, 
	BodyHandlers.ofString())
      .thenApply(response -> 
      	{ System.out.println
      		(response.statusCode());
               return response; 
       } )
      .thenApply(HttpResponse::body)
      .thenAccept(System.out::println);

Reactive Streams

Asynchronous requests can also be used as a reactive stream. As a reactive stream, the HttpRequest acts as the publisher and the HttpResponse as the subscriber, with the corresponding methods in their classes for handling the stream:

public abstract class HttpRequest {
    ...
    public interface BodyPublisher
        extends Flow.Publisher<ByteBuffer> 
        	{ ... }
}

public abstract class HttpResponse<T> {
    ...
    public interface BodyHandler<T> {
        BodySubscriber<T> 
        	apply(int statusCode, 
        	HttpHeaders responseHeaders);
    }

    public interface BodySubscriber<T>
        extends 
        	Flow.Subscriber<List<ByteBuffer>> 
        { ... }
}

Additional Reading

Happy coding!