Skip to content

Commit 1135bcc

Browse files
Add docs for new HttpClient 5.6 features (#36)
* Add docs for new HttpClient 5.6 features - Async content compression & decompression (async) - SCRAM-SHA-256 authentication - RFC 8297 103 Early Hints support - SPKI pinning TLS strategy - Caffeine-based cache backend * Add SSE client docs to overview
1 parent 92b3338 commit 1135bcc

9 files changed

Lines changed: 1281 additions & 1 deletion

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
Async content compression & decompression
21+
=========================================
22+
23+
Starting with HttpClient 5.6 the async transport can transparently **compress**
24+
request bodies and **decompress** response bodies. The logic lives in the
25+
message-exchange layer, so application code keeps working with plain (already
26+
decoded) entities.
27+
28+
Capabilities
29+
------------
30+
31+
- Automatic `Accept-Encoding` negotiation when content compression is enabled
32+
and the request does not already carry this header.
33+
- Transparent response decompression for `Content-Encoding` values
34+
`deflate`, `gzip`, and `x-gzip`.
35+
- Optional support for `zstd` and `br` (Brotli) encodings when the
36+
corresponding libraries are present on the classpath.
37+
- Streaming request compression via `DeflatingAsyncEntityProducer` and the
38+
codec-specific wrappers (for example `DeflatingGzipEntityProducer`).
39+
40+
Supported codecs and dependencies
41+
---------------------------------
42+
43+
Deflate and GZIP are available out of the box.
44+
45+
Zstandard and Brotli are only wired in when their optional dependencies are
46+
present:
47+
48+
- **Zstandard**`com.github.luben:zstd-jni`
49+
- **Brotli**`com.aayushatharva.brotli4j:brotli4j` (plus a matching
50+
`brotli4j-native` artefact for the target platform)
51+
52+
At runtime the async client checks the classpath and registers the extra codecs
53+
without creating a hard dependency on either library.
54+
55+
Basic usage – transparent response decompression
56+
------------------------------------------------
57+
58+
By default async clients send an `Accept-Encoding` header and automatically
59+
decode compressed responses before handing them to the application.
60+
No extra code is required:
61+
62+
```java
63+
try (final CloseableHttpAsyncClient client = HttpAsyncClients.createDefault()) {
64+
client.start();
65+
66+
final SimpleHttpRequest request = SimpleRequestBuilder.get("https://httpbin.org/gzip")
67+
.build();
68+
69+
final Future<Message<HttpResponse, String>> future = client.execute(
70+
SimpleRequestProducer.create(request),
71+
new BasicResponseConsumer<>(new StringAsyncEntityConsumer()),
72+
null);
73+
74+
final Message<HttpResponse, String> message = future.get();
75+
System.out.println("status=" + message.getHead().getCode());
76+
System.out.println("body:\n" + message.getBody());
77+
}
78+
```
79+
80+
Mapping codecs to examples
81+
--------------------------
82+
83+
The async examples in the `org.apache.hc.client5.http.examples` package exercise
84+
the same codecs that are registered in `ContentCompressionAsyncExec`:
85+
86+
```java
87+
.register(ContentCoding.GZIP.token(), InflatingGzipDataConsumer::new)
88+
.register(ContentCoding.X_GZIP.token(), InflatingGzipDataConsumer::new)
89+
.register(ContentCoding.DEFLATE.token(), d -> new InflatingAsyncDataConsumer(d, null))
90+
91+
// Optional – only when the libraries are present
92+
.register(ContentCoding.ZSTD.token(), InflatingZstdDataConsumer::new)
93+
.register(ContentCoding.BROTLI.token(), InflatingBrotliDataConsumer::new);
94+
```
95+
96+
The sections below link each `ContentCoding` token to a concrete runnable
97+
example, in the same spirit as the Observation examples page.
98+
99+
GZIP (`gzip`, `x-gzip`)
100+
-----------------------
101+
102+
- [AsyncClientGzipCompressionExample](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientGzipCompressionExample.java)
103+
104+
Demonstrates streaming a request body compressed with **GZIP** using
105+
`DeflatingGzipEntityProducer`. The client sends a compressed POST while the
106+
async pipeline adds `Content-Encoding: gzip` automatically.
107+
108+
- [AsyncClientGzipDecompressionExample](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientGzipDecompressionExample.java)
109+
110+
Demonstrates **transparent GZIP response decompression**. The server returns
111+
`Content-Encoding: gzip`, the async chain wraps the downstream consumer with
112+
`InflatingGzipDataConsumer`, and application code only sees the plain body.
113+
114+
These examples correspond directly to:
115+
116+
```java
117+
.register(ContentCoding.GZIP.token(), InflatingGzipDataConsumer::new)
118+
.register(ContentCoding.X_GZIP.token(), InflatingGzipDataConsumer::new)
119+
```
120+
121+
DEFLATE (`deflate`)
122+
-------------------
123+
124+
- [AsyncClientDeflateCompressionExample](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientDeflateCompressionExample.java)
125+
126+
Demonstrates streaming a request body compressed with **deflate** using
127+
`DeflatingAsyncEntityProducer` wrapped around an arbitrary `AsyncEntityProducer`.
128+
129+
- [AsyncClientInflateDecompressionExample](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientInflateDecompressionExample.java)
130+
131+
Demonstrates **transparent deflate response decompression**. When the server
132+
responds with `Content-Encoding: deflate`, the async execution chain wraps
133+
the downstream consumer in `InflatingAsyncDataConsumer`.
134+
135+
These examples correspond to:
136+
137+
```java
138+
.register(ContentCoding.DEFLATE.token(), d -> new InflatingAsyncDataConsumer(d, null))
139+
```
140+
141+
Zstandard (`zstd`)
142+
------------------
143+
144+
- [AsyncClientZstdCompressionExample](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientZstdCompressionExample.java)
145+
146+
Shows streaming a request body compressed with **Zstandard** using
147+
`DeflatingZstdEntityProducer`, backed by the `zstd-jni` library.
148+
149+
- [AsyncClientServerZstdExample](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientServerZstdExample.java)
150+
151+
End-to-end client/server demo where the classic server always responds with
152+
`Content-Encoding: zstd` and the async client transparently decodes it via
153+
`InflatingZstdDataConsumer`.
154+
155+
These examples correspond to the optional Zstandard decoder:
156+
157+
```java
158+
.register(ContentCoding.ZSTD.token(), InflatingZstdDataConsumer::new)
159+
```
160+
161+
Brotli (`br`)
162+
-------------
163+
164+
- [AsyncClientServerBrotliRoundTrip](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientServerBrotliRoundTrip.java)
165+
166+
Async client/server demo using **Brotli** in both directions:
167+
the client sends a Brotli-compressed request and the server responds with a
168+
Brotli-compressed body. The example uses `brotli4j` on the client side and
169+
Commons Compress for server-side decoding/encoding.
170+
171+
This example matches the optional Brotli decoder registration (and the extra
172+
`Accept-Encoding: br` token):
173+
174+
```java
175+
.register(ContentCoding.BROTLI.token(), InflatingBrotliDataConsumer::new);
176+
// ...
177+
tokens.add(ContentCoding.BROTLI.token());
178+
```
179+
180+
Together, these examples show how each `ContentEncoding` registered in
181+
`ContentCompressionAsyncExec` maps to a concrete usage pattern in the async
182+
client.
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one
3+
or more contributor license agreements. See the NOTICE file
4+
distributed with this work for additional information
5+
regarding copyright ownership. The ASF licenses this file
6+
to you under the Apache License, Version 2.0 (the
7+
"License"); you may not use this file except in compliance
8+
with the License. You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing,
13+
software distributed under the License is distributed on an
14+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
KIND, either express or implied. See the License for the
16+
specific language governing permissions and limitations
17+
under the License.
18+
-->
19+
20+
RFC 8297 – 103 Early Hints (async)
21+
==================================
22+
23+
HttpClient 5.6 adds async support for **RFC 8297 – 103 Early Hints** via a
24+
callback interface:
25+
26+
- `EarlyHintsListener` – functional interface invoked for each `103` response.
27+
- `HttpAsyncClientBuilder#setEarlyHintsListener(...)` – registers a global
28+
listener on the async client.
29+
- `EarlyHintsAsyncExec` – internal exec-chain handler that taps informational
30+
responses without changing final response processing.
31+
32+
This allows applications to observe Early Hints (for example, `Link` headers
33+
with `rel=preload` / `rel=preconnect`) while leaving the normal response path
34+
untouched.
35+
36+
When is the listener called?
37+
----------------------------
38+
39+
- Called **once per `103`** informational response.
40+
- Never called for the final non-1xx response.
41+
- May be called **multiple times** for a single request if the server sends
42+
multiple `103` responses.
43+
- Runs on the I/O thread, so implementations must be **fast and non-blocking**;
44+
offload heavier work to your own executor.
45+
46+
API surface
47+
-----------
48+
49+
The public entry point is the listener:
50+
51+
```java
52+
@FunctionalInterface
53+
public interface EarlyHintsListener {
54+
55+
void onEarlyHints(HttpResponse hints, HttpContext context)
56+
throws HttpException, IOException;
57+
}
58+
```
59+
60+
You register it on the async client builder:
61+
62+
```java
63+
final EarlyHintsListener listener = (hints, ctx) -> {
64+
// Only 103s will be forwarded here
65+
System.out.println("Early Hints: " + hints.getCode());
66+
for (final Header h : hints.getHeaders("Link")) {
67+
System.out.println(" Link: " + h.getValue());
68+
}
69+
};
70+
71+
final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
72+
.setEarlyHintsListener(listener)
73+
.build();
74+
```
75+
76+
If the listener is `null` no extra handler is added to the exec chain.
77+
78+
Minimal usage example
79+
---------------------
80+
81+
This is a cut-down version of the `AsyncClientEarlyHintsEndToEnd` example. It
82+
shows how to register the listener and consume the final response as usual.
83+
84+
```java
85+
import java.util.concurrent.Future;
86+
import java.util.concurrent.TimeUnit;
87+
88+
import org.apache.hc.client5.http.EarlyHintsListener;
89+
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
90+
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
91+
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
92+
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
93+
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
94+
import org.apache.hc.core5.http.Header;
95+
96+
public class AsyncClientEarlyHintsExample {
97+
98+
public static void main(final String[] args) throws Exception {
99+
final EarlyHintsListener hintsListener = (hints, ctx) -> {
100+
System.out.println("[client] 103 Early Hints:");
101+
for (final Header h : hints.getHeaders("Link")) {
102+
System.out.println(" " + h.getValue());
103+
}
104+
};
105+
106+
try (final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
107+
.setEarlyHintsListener(hintsListener)
108+
.build()) {
109+
110+
client.start();
111+
112+
final SimpleHttpRequest req = SimpleRequestBuilder
113+
.get("https://example.com/with-early-hints")
114+
.build();
115+
116+
final Future<SimpleHttpResponse> f = client.execute(req, null);
117+
final SimpleHttpResponse resp = f.get(10, TimeUnit.SECONDS);
118+
119+
System.out.println("[client] final: " + resp.getCode() + " " + resp.getReasonPhrase());
120+
System.out.println("[client] body: " + resp.getBodyText());
121+
}
122+
}
123+
}
124+
```
125+
126+
Notes:
127+
128+
- The **final response** (e.g. `200 OK`) is handled by the normal async flow.
129+
- Early Hints are purely **side-band** notifications.
130+
131+
End-to-end example
132+
------------------
133+
134+
The full end-to-end demo lives in the examples package:
135+
136+
- [AsyncClientEarlyHintsEndToEnd](https://github.com/apache/httpcomponents-client/tree/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientEarlyHintsEndToEnd.java)
137+
138+
This example:
139+
140+
1. Starts a local async HTTP server that:
141+
- sends one `103 Early Hints` with two `Link` headers, then
142+
- completes the exchange with `200 OK` and a short body.
143+
2. Builds an async client with `setEarlyHintsListener(...)`.
144+
3. Prints:
145+
- all `Link` headers from `103` responses, and
146+
- the final status and body for the main response.
147+
148+
This is the canonical reference for wiring Early Hints support end-to-end.
149+
150+
Interaction with the exec chain
151+
-------------------------------
152+
153+
When `setEarlyHintsListener(...)` is used, the builder inserts an internal
154+
`EarlyHintsAsyncExec` handler **before** the protocol handler:
155+
156+
```java
157+
if (earlyHintsListener != null) {
158+
addExecInterceptorBefore(
159+
ChainElement.PROTOCOL.name(),
160+
"early-hints",
161+
new EarlyHintsAsyncExec(earlyHintsListener));
162+
}
163+
```
164+
165+
This handler:
166+
167+
- intercepts `handleInformationResponse(...)` callbacks,
168+
- forwards each `103` to the listener,
169+
- delegates all other informational responses and the final response unchanged.
170+
171+
If you never register a listener, no extra handler is added and the async exec
172+
chain behaves as before.
173+
174+
Summary
175+
-------
176+
177+
- Use `EarlyHintsListener` + `setEarlyHintsListener(...)` to observe `103` responses.
178+
- Normal response processing is unaffected; Early Hints are side-band.
179+
- Keep listener logic lightweight; bounce heavy work to your own executor.
180+
- Use `AsyncClientEarlyHintsEndToEnd` as a reference for testing and wiring.

0 commit comments

Comments
 (0)