DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Designing a Java Connector for Software Integrations
  • Jakarta NoSQL 1.0: A Way To Bring Java and NoSQL Together
  • How to Get Word Document Form Values Using Java
  • Build a REST API With Just 2 Classes in Java and Quarkus

Trending

  • From Zero to Production: Best Practices for Scaling LLMs in the Enterprise
  • Beyond ChatGPT, AI Reasoning 2.0: Engineering AI Models With Human-Like Reasoning
  • DGS GraphQL and Spring Boot
  • How to Configure and Customize the Go SDK for Azure Cosmos DB
  1. DZone
  2. Data Engineering
  3. Databases
  4. HTTP/2 Server Push Via Java 11 HTTP Client API

HTTP/2 Server Push Via Java 11 HTTP Client API

Do you remember HttpUrlConnection? Well, JDK 11 has reinvented this server push using the HTTP Client API. Check it out!

By 
Anghel Leonard user avatar
Anghel Leonard
DZone Core CORE ·
Oct. 23, 19 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
22.2K Views

Join the DZone community and get the full member experience.

Join For Free

Java 11 HTTP CLient API

Do you remember HttpUrlConnection? Well, JDK 11 has reinvented this API as the HTTP Client API. 

Do you remember HttpUrlConnection? Well, JDK 11 comes up with the HTTP Client API as a reinvention of HttpUrlConnection. The HTTP Client API is easy to use and supports HTTP/2 (default) and HTTP/1.1. For backward compatibility, the HTTP Client API will automatically downgrade from HTTP/2 to HTTP 1.1 when the server doesn't support HTTP/2.

Moreover, the HTTP Client API supports synchronous and asynchronous programming models and relies on streams to transfer data (reactive streams). It also supports the WebSocket protocol, which is used in real-time web applications to provide client-server communication with low message overhead.

You may also like: Java 11: Standardized HTTP Client API

Besides multiplexing, another powerful feature of HTTP/2 is its server push capability. Mainly, in the traditional approach (HTTP/1.1), a browser triggers a request for getting an HTML page and parses the received markup to identify the referenced resources (for example, JS, CSS, images, and so on).

To fetch these resources, the browser sends additional requests (one request for each referenced resource). On the other hand, HTTP/2 sends the HTML page and the referenced resources without explicit requests from the browser. So, the browser requests the HTML page and receives the page and everything else that's needed for displaying the page. The HTTP Client API supports this HTTP/2 feature via the PushPromiseHandlerinterface.

The implementation of this interface must be given as the third argument of the send()or sendAsync()method. PushPromiseHandlerrelies on three coordinates, as follows:

  • The initiating client send request (initiatingRequest)

  • The synthetic push request (pushPromiseRequest)

  • The acceptor function, which must be successfully invoked to accept the push promise (acceptor)

A push promise is accepted by invoking the given acceptor function. The acceptor function must be passed a non-null BodyHandler, which is used to handle the promise's response body. The acceptor function will return a CompletableFuture instance that completes the promise's response.

Based on this information, let's look at an implementation of PushPromiseHandler :

private static final List<CompletableFuture<Void>>
   asyncPushRequests = new CopyOnWriteArrayList<>();
...
private static HttpResponse.PushPromiseHandler<String> pushPromiseHandler() {

   return (HttpRequest initiatingRequest,
      HttpRequest pushPromiseRequest,
      Function<HttpResponse.BodyHandler<String> ,
      CompletableFuture<HttpResponse<String>>> acceptor) -> {
      CompletableFuture<Void> pushcf =
         acceptor.apply(HttpResponse.BodyHandlers.ofString())
         .thenApply(HttpResponse::body)
         .thenAccept((b) -> System.out.println(
            "\nPushed resource body:\n " + b));

         asyncPushRequests.add(pushcf);

         System.out.println("\nJust got promise push number: " +
            asyncPushRequests.size());
         System.out.println("\nInitial push request: " +
            initiatingRequest.uri());
         System.out.println("Initial push headers: " +
            initiatingRequest.headers());
         System.out.println("Promise push request: " +
            pushPromiseRequest.uri());
         System.out.println("Promise push headers: " +
            pushPromiseRequest.headers());
   };
}


Now, let's trigger a request and pass this PushPromiseHandler  to sendAsync():

HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
   .uri(URI.create("https://http2.golang.org/serverpush"))
   .build();

client.sendAsync(request,
   HttpResponse.BodyHandlers.ofString(), pushPromiseHandler())
      .thenApply(HttpResponse::body)
      .thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
      .join();

asyncPushRequests.forEach(CompletableFuture::join);

System.out.println("\nFetched a total of " +
   asyncPushRequests.size() + " push requests");


The complete source code is available on GitHub.

If we want to return a push promise handler that accumulates push promises, and their responses, into the given map, then we can rely on the PushPromiseHandler.of()method, as follows:

private static final ConcurrentMap<HttpRequest,
   CompletableFuture<HttpResponse<String>>> promisesMap
      = new ConcurrentHashMap<>();

private static final Function<HttpRequest,
   HttpResponse.BodyHandler<String>> promiseHandler
      = (HttpRequest req) -> HttpResponse.BodyHandlers.ofString();

public static void main(String[] args)
         throws IOException, InterruptedException {

   HttpClient client = HttpClient.newHttpClient();
   HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://http2.golang.org/serverpush"))
      .build();

   client.sendAsync(request,
      HttpResponse.BodyHandlers.ofString(), pushPromiseHandler())
         .thenApply(HttpResponse::body)
         .thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
         .join();

   System.out.println("\nPush promises map size: " +
      promisesMap.size() + "\n");

   promisesMap.entrySet().forEach((entry) -> {
      System.out.println("Request = " + entry.getKey() +
          ", \nResponse = " + entry.getValue().join().body());
   });
}

private static HttpResponse.PushPromiseHandler<String> pushPromiseHandler() {
   return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}


The complete source code is available on GitHub.

In both solutions of the preceding solutions, we have used a BodyHandlerof the Stringtype via ofString(). This is not very useful if the server pushes binary data as well (for example, images). So, if we are dealing with binary data, we need to switch toBodyHandlerof thebyte[]type via ofByteArray(). Alternatively, we can send the pushed resources to disk viaofFile(), as shown in the following solution, which is an adapted version of the preceding solution:

private static final ConcurrentMap<HttpRequest,
   CompletableFuture<HttpResponse<Path>>>
      promisesMap = new ConcurrentHashMap<>();

private static final Function<HttpRequest,
   HttpResponse.BodyHandler<Path>> promiseHandler
      = (HttpRequest req) -> HttpResponse.BodyHandlers.ofFile(  
        Paths.get(req.uri().getPath()).getFileName());

public static void main(String[] args)
                  throws IOException, InterruptedException {

   HttpClient client = HttpClient.newHttpClient();
   HttpRequest request = HttpRequest.newBuilder()
      .uri(URI.create("https://http2.golang.org/serverpush"))
      .build();

   client.sendAsync(request, HttpResponse.BodyHandlers.ofFile(
      Path.of("index.html")), pushPromiseHandler())
         .thenApply(HttpResponse::body)
         .thenAccept((b) -> System.out.println("\nMain resource:\n" + b))
         .join();

   System.out.println("\nPush promises map size: " +
      promisesMap.size() + "\n");

   promisesMap.entrySet().forEach((entry) -> {
      System.out.println("Request = " + entry.getKey() +
         ", \nResponse = " + entry.getValue().join().body());
   });
}

private static HttpResponse.PushPromiseHandler<Path> pushPromiseHandler() {
   return HttpResponse.PushPromiseHandler.of(promiseHandler, promisesMap);
}


The preceding code should save the pushed resources in the application classpath, as
shown in the following screenshot:

Image title

The complete source code is available on GitHub.

If you enjoyed this article, then I'm sure you will love my book, Java Coding Problems, which has an entire chapter dedicated to HTTP Client API. Check it out!

Further Reading

Java 11: Standardized HTTP Client API

Benefits of REST APIs With HTTP/2

API push Java (programming language)

Opinions expressed by DZone contributors are their own.

Related

  • Designing a Java Connector for Software Integrations
  • Jakarta NoSQL 1.0: A Way To Bring Java and NoSQL Together
  • How to Get Word Document Form Values Using Java
  • Build a REST API With Just 2 Classes in Java and Quarkus

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!