Skip to content

Using JDK Http Client - 1

When we want sending some simple RESTFUL http requests, we might turn to OKHttpClient or Jetty client. It's fine, but actually we have another choice - JDK http client. It's ship with JDK and we don't need extra dependencies.

We are using mu-server as the http server, You can check out the documentation here.

Here is a quick guide with working example.

Creating JDK http client instance

The trustAll means whether to verify the https certificate on SSL handshake. We need to integrate with the public key if we want to verify target server's SSL key.

java
public static HttpClient client(boolean trustAll) {
    HttpClient.Builder builder = HttpClient.newBuilder();
    if (trustAll) {
        try {
            final TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(
                            X509Certificate[] chain, 
                            String authType) {
                        }

                        @Override
                        public void checkServerTrusted(
                            X509Certificate[] chain, 
                            String authType) {
                        }

                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[0];
                        }
                    }
            };
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
            builder.sslContext(sslContext);

        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            throw new RuntimeException(e);
        }
    }
    return builder.build();
}

Simple GET/POST request

java
@Test
public void testGet() throws IOException, InterruptedException {
    MuServer server = MuServerBuilder.httpsServer()
            .addHandler(Method.GET, "/something", (request, response, map) -> {
                response.write("Hello, world!");
            })
            .start();

    HttpResponse<String> resp = client.send(HttpRequest.newBuilder()
            .uri(server.uri().resolve("/something"))
            .header("user-agent", "jdk-httpclient")
            .build(), HttpResponse.BodyHandlers.ofString());

    assertThat(resp.statusCode()).isEqualTo(200);
    assertThat(resp.body()).isEqualTo("Hello, world!");
}

@Test
void testPost() throws IOException, InterruptedException {

    MuServer server = MuServerBuilder.httpsServer()
            .addHandler(Method.POST, "/something", (request, response, map) -> {
                String requestBody = request.readBodyAsString();
                response.write(requestBody);
            })
            .start();

    String body = randomAsciiStringOfLength(10000);
    HttpResponse<String> resp = client.send(HttpRequest.newBuilder()
            .method("POST", HttpRequest.BodyPublishers.ofString(body))
            .uri(server.uri().resolve("/something"))
            .header("user-agent", "jdk-httpclient")
            .build(), HttpResponse.BodyHandlers.ofString());

    assertThat(resp.statusCode()).isEqualTo(200);
    assertThat(resp.body()).isEqualTo(body);
}

Streaming request body

Here we use HttpRequest.BodyPublishers interface for streaming the request body chunk bit by bit.

java
 @Test
void testPostRequestByBodyPublisher() throws IOException, InterruptedException {
    MuServer server = MuServerBuilder.httpsServer()
            .addHandler(Method.POST, "/something", (request, response, map) -> {
                String requestBody = request.readBodyAsString();
                response.write(requestBody);
            })
            .start();

    HttpResponse<String> resp = client.send(HttpRequest.newBuilder()
            .method("POST", HttpRequest.BodyPublishers.fromPublisher(subscriber -> {
                AtomicInteger counter = new AtomicInteger();
                subscriber.onSubscribe(new Flow.Subscription() {
                    @Override
                    public void request(long n) {
                        if (counter.incrementAndGet() <= 9) {
                            subscriber.onNext(ByteBuffer.wrap(
                                String.valueOf(counter.get())
                                .getBytes(StandardCharsets.UTF_8)));
                        } else {
                            subscriber.onComplete();
                        }

                    }

                    @Override
                    public void cancel() {
                    }
                });
            }))
            .uri(server.uri().resolve("/something"))
            .header("user-agent", "jdk-httpclient")
            .build(), HttpResponse.BodyHandlers.ofString());

    assertThat(resp.statusCode()).isEqualTo(200);
    assertThat(resp.body()).isEqualTo("123456789");
}

And here is another example by using the InputStream

java
    InputStream inputStream = JdkHttpClientTest.class.getClassLoader()
        .getResourceAsStream("test-file.txt");

    HttpResponse<String> resp = client.send(HttpRequest.newBuilder()
            .method("POST", HttpRequest.BodyPublishers.ofInputStream(() -> inputStream))
            .uri(server.uri().resolve("/something"))
            .header("user-agent", "jdk-httpclient")
            .build(), HttpResponse.BodyHandlers.ofString());

Stream receiving response body

Here we use HttpResponse.BodyHandler interface for receiving data bit by bit. Do remember to call subscription.request(1) when ready to take next byte, otherwise the callback will hang there without receive anything.

In the onNext() callback, we can do some sleep if we want to simulate slow client.

java
@Test
void testReadResponseBodyByBodyHandler() throws IOException, InterruptedException {

    String body = randomAsciiStringOfLength(10 * 1024 * 1024);
    MuServer server = MuServerBuilder.httpsServer()
            .addHandler(Method.GET, "/something", (request, response, map) -> {
                response.write(body);
            })
            .start();

    CountDownLatch latch = new CountDownLatch(1);
    StringBuilder bodyBuilder = new StringBuilder();
    AtomicInteger status = new AtomicInteger(0);

    HttpRequest request = HttpRequest.newBuilder()
            .uri(server.uri().resolve("/something"))
            .header("user-agent", "jdk-httpclient")
            .build();

    HttpResponse.BodyHandler<Void> bodyHandler = responseInfo -> {
        status.set(responseInfo.statusCode());
        return HttpResponse.BodySubscribers.fromSubscriber(new Flow.Subscriber<>() {
            private Flow.Subscription subscription;

            @Override
            public void onSubscribe(Flow.Subscription subscription) {
                this.subscription = subscription;
                this.subscription.request(1);
            }

            @Override
            public void onNext(List<ByteBuffer> items) {
                for (ByteBuffer item : items) {
                    bodyBuilder.append(StandardCharsets.UTF_8.decode(item));
                }
                // we can sleep here to simulate slow client
                // try {
                //     Thread.sleep(1L);
                // } catch (InterruptedException e) {
                //     throw new RuntimeException(e);
                // }
                this.subscription.request(1);
            }

            @Override
            public void onError(Throwable throwable) {

            }

            @Override
            public void onComplete() {
                latch.countDown();
            }
        });
    };

    client.sendAsync(request, bodyHandler);

    latch.await(1, TimeUnit.MINUTES);
    assertThat(status.get()).isEqualTo(200);
    assertThat(bodyBuilder.toString()).isEqualTo(body);
}

Summary

This is a quick guide for using JDK http client to send simple GET/POST request, and also some complex case for streaming request body, stream receiving response body. Please read the next article for working with SSE and websocket.

Reference

https://docs.oracle.com/en/java/javase/17/docs/api/java.net.http/java/net/http/HttpClient.html