Concrete | Introdução ao Java 9 – HTTP2
O módulo que mais gostei no Java 9 foi o de HTTP2. Por mais que seja um módulo não oficial (incubação) há diversas utilidades: é menos verboso para criar requisições, tem mais recursos para as respostas da requisição e o mais interessante, a requisição assíncrona. Em um pequeno resumo do HTTP2, os headers são binários e comprimidos usando Hpack, o que diminui o volume de dados trafegados. Há dezenas de recursos na web comum hoje como imagens, arquivos, js etc e cada recurso é um request.
Para complicar mais ainda, o HTTP 1.1 é um protocolo sequencial que enfileira requisições. Para resolver o problema, no HTTP2, requisições e respostas são paralelas em uma única conexão automaticamente. O protocolo na sua versão 1.1 passa ser stateful que mantém o estado conversacional, mas a sua estética permanece stateless, não tem nenhuma informação das requisições anteriores.
Para utilizar uma API em fase de incubação você vai precisar passar a opção –add-modules com o seu namespace que, no caso do Http/2 Client, é o jdk.incubator.httpclient. Essa opção funciona da mesma forma caso você opte por utilizar o jshell, ferramenta de linha de comando do JDK. Para saber mais sobre jshell tem o artigo do Bruno aqui. Um dos problemas de requisições java da forma antiga é a sua verbosidade e seu uso bloqueante (enfileira requisições) era uma thread por requisição e resposta.
Requests HTTP de forma antiga no Java
<pre class="lang:default decode:true "> URLConnection urlConnection = url.openConnection(); BufferedReader reader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); String line; while((line = reader.readLine()) != null){ System.out.println(line); } reader.close(); </pre>
No novo módulo HTTP/2
A principal informação que você tem que saber sobre essa mudança são as classes abstratas nas quais a nova API lida com as conexões HttpClient, HttpRequest e HttpResponse. Sendo a principal, a HttpClient, como a responsável por criar e enviar requisições.
HttpClient -> HttpRequest -> HttpResponse -> HttpClient
Segue um código de exemplo abaixo:
<pre class="lang:default decode:true "> HttpClient httpClient = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandler.asString()); System.out.println("http version: " + response.version() + "status code: " + response.statusCode() + "content: " + response.body()); </pre> Linha1: HttpClient.newHttpClient() : cria uma nova conexão. Linha 2: HttpRequest.newBuilder().uri("passa o endereço").GET().build(): Devolve HttpRequest a necessidade principal é passar uma url e o verbo http, no exemplo é GET Linha 3: httpClient.send() : faz o envio da requisição.
HttpRequest request = HttpRequest.newBuilder().uri(uri).GET().build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandler.asString());
System.out.println("http version: " + response.version() + "status code: " + response.statusCode() + "content: " + response.body());
Linha1: HttpClient.newHttpClient() : cria uma nova conexão.
Linha 2: HttpRequest.newBuilder().uri("passa o endereço").GET().build(): Devolve HttpRequest a necessidade principal é passar uma url e o verbo http, no exemplo é GET
Linha 3: httpClient.send() : faz o envio da requisição.
No exemplo acima, utilizamos o método httpClient.send(…) que funciona de forma síncrona. Na nova API você consegue enviar requisições assíncronas, facilmente. O processo fica executando em background, enquanto a thread principal fica livre para executar outras tarefas.
Para fazer o uso, no lugar do método send, utilizamos sendAsync: httpClient.sendAsync(request, ….). O que difere entre a requisição assíncrona é o seu retorno, pois é retornado um CompletableFuture que vai nos informar quando essa resposta vai estar pronta para o uso. Com o CompletableFuture, podemos verificar se a requisição está pronta com o isDone, imprimindo a saída da resposta ou cancelando a requisição.
<pre class="lang:default decode:true "> if(response.isDone()){ System.out.println(response.get().body()); }else{ System.out.println("Cancelando a requisição"); response.cancel(true); } </pre>
if(response.isDone()){
System.out.println("Cancelando a requisição");
Outra forma de trabalhar com o CompletableFuture seria adicionando um callBack e quando a requisição ficar pronta imprimir a saída.
<pre class="lang:default decode:true "> response.whenComplete((r,t) -> System.out.println(r.body())) </pre>
response.whenComplete((r,t) -> System.out.println(r.body()))
Exemplo de requisição Assíncrona:
<pre class="lang:default decode:true "> HttpClient.newHttpClient().sendAsync(HttpRequest.newBuilder() .uri(new URI("https://google.com.br")).GET().build(), HttpResponse.BodyHandler.asString())) .whenComplete((r,t) -> System.out.println("arquivo salvo em: " + r.body().toAbsolutePath())); </pre>
HttpClient.newHttpClient().sendAsync(HttpRequest.newBuilder()
.uri(new URI("https://google.com.br")).GET().build(),
HttpResponse.BodyHandler.asString()))
.whenComplete((r,t) ->
System.out.println("arquivo salvo em: " +
r.body().toAbsolutePath()));
O módulo HTTP/2 Client do Java 9 é grande, tem muitos detalhes que podem ser lidos na documentação. Neste artigo eu destaquei pontos principais da utilização.