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.
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
@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.
@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
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.
@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