HTTP/2 – l’API HTTP Client de Java 11

Java 11 propose l’API HTTP Client pour faciliter l’utilisation côté client du protocole HTTP.
Cette API doit remplacer la classe historique HttpURLConnection introduite dans le JDK 1.1. Cette classe possède de nombreux inconvénients notamment :

  • son âge et des difficultés à la maintenir
  • pas facile à utiliser
  • ne fonctionne qu’en mode synchrone

L’API HTTP Client a été introduite dans Java 9 sous la forme d’un module incubator : elle était dans la package jdk.incubator.http contenu dans le module jdk.incubator.httpclient.
Elle a connu quelques évolutions pour maintenant être incluse en standard dans un module de Java 11.

Cet article fait partie de la série HTTP/2, contenant les articles suivants :

  1. HTTP/2 : introduction
  2. HTTP/2 : les détails
  3. HTTP/2 : les anciennes pratiques à éviter maintenant
  4. HTTP/2 : l’API HTTP Client de Java 11
  5. HTTP/2 : Push serveur
  6. HTTP/2 : Push serveur avec Java EE 8

 

Présentation de l’API HTTP Client

La nouvelle API HTTP Client propose un support des versions 1.1 et 2 du protocole HTTP ainsi que les WebSockets côté client.
L’API est fournie en standard dans Java 11 : son but n’est pas de fournir une implémentation complète des protocoles mais d’être un compromis entre fonctionnalités et consommation de ressources.
Cette API offre une certaine modernité en termes de conception et de mise en oeuvre :

  • utilisation du design pattern builder
  • utilisation de fabriques pour obtenir des instances des builder mais aussi d’implémentations de certaines interfaces pour des usages courants
  • utilisation de l’API Flow (reactive streams) pour fournir les données du body d’une requête (Flow.Publisher) et consommer le body d’une réponse (Flow.Subscriber)

L’API est contenu dans le package java.net.http du module java.net.http. Elle contient plusieurs types (interfaces, classes, énumération, exceptions) dont les principaux sont :

Classe/InterfaceRôle
HttpClientInstance immuable qui permet l’envoi d’une requête et la réception de la réponse correspondante en mode synchrone ou asynchrone
HttpClient.BuilderBuilder pour configurer et obtenir une instance de type HttpClient
HttpRequestInstance immuable qui encapsule une requête (URI, verbe (GET, POST, PUT, DELETE, …), headers, …)
HttpRequest.BuilderBuilder pour configurer et obtenir une instance de type HttpRequest
HttpRequest.BodyPublisherFournit le contenu du body à la requête si elle en a besoin (requêtes de type POST et PUT) à partir de différentes sources
HttpRequest.BodyPublishersFabrique pour obtenir des instances de type BodyPublishers pour des usages courants (chaîne de caractères, fichier, …)
HttpResponseEncapsule la réponse obtenue à une requête envoyée (code statut, headers, body, …)
HttpResponse.ResponseInfoEncapsule le code statut et les headers de la réponse
HttpResponse.BodySubscriberConsomme les octets du body d’une réponse pour les convertir en objet Java (chaîne de caractères, fichier, …)
HttpResponse.BodySubscribersFabrique pour obtenir des instances de type BodyHandler pour des usages courants
HttpResponse.BodyHandlerInterface fonctionnelle qui permet de gérer une réponse à partir des informations d’une ResponseInfo. Elle renvoie un BodyPublisher pour traiter le body de la réponse
HttpResponse.BodyHandlersPropose des fabriques pour des BodyHandler courants

 

La classe HttpClient

L’obtention d’une instance de type HttpClient peut se faire de deux manières :

  • l’utilisation de la fabrique newHttpClient() de la classe HttpClient pour obtenir une instance avec la configuration par défaut
HttpClient client = HttpClient.newHttpClient();
  • l’utilisation de l’interface HttpClient.Builder qui est un builder pour configurer l’instance obtenue. Une instance du Builder est obtenue en utilisant la fabrique newBuilder() de la classe HttpClient
        HttpClient httpClient = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_1_1)
    .build();

La classe HttpClient utilise par défaut la version 2 du protocole HTTP.
Il est possible de préciser un proxy grâce à la méthode proxy() en lui passant en paramètre une instance de type ProxySelector

    HttpClient httpClient = HttpClient.newBuilder()
                                      .version(HttpClient.Version.HTTP_1_1)
                                      .proxy(ProxySelector.of(new InetSocketAddress("proxy.www.oxiane.com", 80)))
                                      .build();

 

La classe HttpRequest

Elle encapsule une requête à envoyer à un serveur.
Une instance de type HttpRequest est obtenue en utilisant un builder de type HttpRequest.Builder. Une instance de l’interface HttpRequest.Builder est obtenue en utilisant la fabrique newBuilder() de la classe HttpRequest.
Plusieurs méthodes permettent de configurer les éléments de la requête : l’uri, le verbe HTTP, les headers, un timeout.

    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("http://www.www.oxiane.com/"))
        .GET()
        .build();

La méthode build() permet d’obtenir l’instance.
Pour les requêtes de type POST et PUT, il est possible de fournir le contenu du body en utilisant un BodyPublisher. Un BodyPublisher permet de fournir les données qui seront contenues dans le body à partir d’une source.
La classe BodyPublishers propose des fabriques pour obtenir des instances de type BodyPublisher pour des besoins standards

    HttpRequest requetePost = HttpRequest.newBuilder()
        .uri(URI.create("http://www.www.oxiane.com/api/formations"))
        .setHeader("Content-Type", "application/json")
        .POST(BodyPublishers.ofString("{\"cle1\":\"valeur1\", \"cle2\":\"valeur2\"}"))
        .build();

 

L’envoi de la requête et la réception de la réponse

Ils peuvent se faire de deux manières :

  • synchrone : l’envoi de la requête bloque les traitements jusqu’à la réception de la réponse
  • asynchrone : la requête est envoyée et le traitement de la réponse est effectué par un CompletableFuture

 

L’envoi en mode synchrone

Il se fait en utilisant la méthode send() de la classe HttpClient. Elle attend en paramètres :

  • la requête
  • une instance de type HttpResponse.BodyHandler qui permet de traiter le body selon la réponse

La classe HttpBodyHandlers propose des fabriques pour obtenir des instances de BodyHandler pour des usages courants.

    HttpResponse response;
    try {
      response = httpClient.send(requete, BodyHandlers.ofString());
      System.out.println("Status  : " + response.statusCode());
      System.out.println("Headers : " + response.headers());
      System.out.println("Body    : " + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }

 

L’envoi en mode asynchrone

Il se fait en utilisant la méthode sendAsync() de la classe HttpClient. Elle attend en paramètres :

  • la requête
  • une instance de type HttpResponse.BodyHandler qui permet de traiter le body selon la réponse

    Elle renvoie un CompletableFuture qui permet de définir les traitements à exécuter à la réception de la réponse.
    httpClient.sendAsync(requete, BodyHandlers.ofString()).thenAccept(response -> {
      System.out.println("Status  : " + response.statusCode());
      System.out.println("Headers : " + response.headers());
      System.out.println("Body    : " + response.body());
    });

 

Conclusion

La nouvelle API HTTP Client de Java 11 propose en standard des facilités pour envoyer des requêtes HTTP et traiter leurs réponses avec une API moderne. Le support de HTTP/2 et des WebSockets permet la mise en œuvre de clients sans avoir recours à des bibliothèques tierces.
Le prochain article de cette série détaille le fonctionnement du mode Push d’HTTP/2.