HTTP client
This page details HTTP client.
Dependency
<dependency>
<groupId>io.yupiik.fusion</groupId>
<artifactId>fusion-httpclient</artifactId>
<version>${fusion.version}</version>
</dependency>
Core
Fusion HTTP Client is based on java.net.http.HttpClient. It is configured with ExtendedHttpClientConfiguration which enables to:
-
optionally provide a configured
HttpClientinstance - otherwise the default JVM one is used, -
optionally provide a set of
RequestListeners which listen for requests.
Request Listeners
Request listener is a callback triggered before and after each request. It enables to store data before the requests in the caller context and execute some callback once the request is finished. To pass data between both steps (since the request can be asynchronous or not) it uses a State which can hold any data the listener needs.
|
TIP
|
if you write custom listeners (to add OpenTracing capabilities for example, see tracing
), you can make them implement |
Default Listeners
DefaultTimeout
This listener is pretty straightforward, if the request does not have a timeout, it sets it to the default one configured in the listener. It enables to enforce a global timeout to all requests.
SetUserAgent
This listener enforces a custom user-agent value. It defaults to the chrome one.
ExchangeLogger
This listener enables to force all exchanges to be logged.
CurlLoggerListener
This listener is a sibling of ExchangeLogger but instead of a free-form message it logs each exchange as a ready-to-replay curl command (method, URI, headers and body when payload logging is enabled). When payload logging is enabled the command uses -i and is followed by the raw response formatted like curl -i output (status line, headers, body), errors being logged like curl: <message>.
new CurlLoggerListener(Logger.getLogger(getClass().getName()), false)
|
TIP
|
since the listener must read the request body to build the command, it replaces the request publisher with the captured bytes so the request is still sent normally. |
FilteringListener
This listener wraps another listener to filter the calls to before/after callbacks either based on the request or on the response. Can be useful to ignore some data (for example to only capture errors in HARDumperListener).
|
TIP
|
for |
HARDumperListener
This listener enables to capture a HAR dump of all exchanges which went through the client. It can be very useful to generate some test data you replay with a HAR server in a test or a demo environment.
It comes with its companion HARHttpClient which enables to replay a HAR without actually doing the requests.
|
TIP
|
a sibling listener called |
|
IMPORTANT
|
the |
Sample usage
final var conf = new ExtendedHttpClientConfiguration();
// (optional) force a custom SSL context
conf.setDelegate(
HttpClient.newBuilder()
.sslContext(getSSLContext())
.build());
// (optional) force custom listeners
conf.setListeners(List.of(
new AutoTimeoutSetter(Duration.ofSeconds(3600)),
new AutoUserAgentSetter(),
new ExchangeLogger(
Logger.getLogger(getClass().getName()),
Clock.systemUTC(),
false)));
Routing, throttling and retries
-
RoutingHttpClientroutes requests to several backend clients based on a predicate (for example by host name), -
ThrottledHttpClient(and itsRateLimitedClient/RateLimitercounterparts) throttles outgoing requests to respect a rate limit, -
redirects and automatic retries are handled by the underlying
java.net.http.HttpClientand can be configured on the delegate.
These decorators are combined by wrapping the client: new ThrottledHttpClient(new RoutingHttpClient(delegate)).
Closing
By default (no closeTimeout set) ExtendedHttpClient.close() only closes the registered hooks and listeners: the underlying transport is left as-is, whatever the JDK version. This preserves the historical behavior and keeps the caller in control of the transport lifecycle.
Set a positive closeTimeout (a java.time.Duration) on the configuration to also close the transport when the ExtendedHttpClient created it itself (no delegate was provided):
final var conf = new ExtendedHttpClientConfiguration().setCloseTimeout(Duration.ofSeconds(2));
close() then waits at most closeTimeout for the client to terminate:
-
on Java 21+, it calls
HttpClient.shutdown(), thenHttpClient.awaitTermination(closeTimeout); if the timeout elapses without termination,HttpClient.shutdownNow()force-closes the remaining operations, -
on Java 17, the JDK client exposes no shutdown/termination API, so the close is attempted on the common pool and the wait is abandoned (cancelled) when the timeout elapses. This is a best effort: the JDK selector threads are daemons and stop once the client becomes unreachable.
A null, zero or negative closeTimeout leaves the transport untouched.
When a delegate is provided through setDelegate, ExtendedHttpClient.close() never closes it: the transport lifecycle belongs to the caller (this applies with or without closeTimeout).
|
TIP
|
the underlying |
Tracing
See the tracing page
for the TracingListener enabling Zipkin/OpenTelemetry propagation on outgoing requests.