This is an automated email from the ASF dual-hosted git repository.
abernal pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-website.git
The following commit(s) were added to refs/heads/master by this push:
new bb99baa Add HttpClient SSE module docs (overview, guide, examples,
javadocs links)
bb99baa is described below
commit bb99baa372e97666bb4624452afef77cba20f8fa
Author: Arturo Bernal <[email protected]>
AuthorDate: Tue Dec 23 10:10:10 2025 +0100
Add HttpClient SSE module docs (overview, guide, examples, javadocs links)
---
.../httpcomponents-client-5.6.x/examples-sse.md | 191 ++++++++++++++++++++
.../markdown/httpcomponents-client-5.6.x/index.md | 6 +
.../markdown/httpcomponents-client-5.6.x/sse.md | 192 +++++++++++++++++++++
3 files changed, 389 insertions(+)
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/examples-sse.md
b/src/site/markdown/httpcomponents-client-5.6.x/examples-sse.md
new file mode 100644
index 0000000..6977ca6
--- /dev/null
+++ b/src/site/markdown/httpcomponents-client-5.6.x/examples-sse.md
@@ -0,0 +1,191 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+HttpClient SSE examples
+=======================
+
+This page shows complete examples for the optional SSE module
(`httpclient5-sse`).
+
+Example 1: public stream (Wikimedia recent changes)
+---------------------------------------------------
+
+This example is based on
`org.apache.hc.client5.http.sse.example.ClientSseExample` from the
+`httpclient5-sse` module tests.
+
+It demonstrates:
+
+- Custom async client with HTTP/2 negotiation and stream multiplexing.
+- Dedicated scheduler for reconnect backoff.
+- `SseParser.BYTE` for reduced allocations.
+- A simple listener with lifecycle handling.
+
+```java
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+import org.apache.hc.client5.http.config.TlsConfig;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
+import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
+import
org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
+import org.apache.hc.client5.http.sse.EventSource;
+import org.apache.hc.client5.http.sse.EventSourceConfig;
+import org.apache.hc.client5.http.sse.EventSourceListener;
+import org.apache.hc.client5.http.sse.SseExecutor;
+import org.apache.hc.client5.http.sse.impl.ExponentialJitterBackoff;
+import org.apache.hc.client5.http.sse.impl.SseParser;
+import org.apache.hc.core5.concurrent.DefaultThreadFactory;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.config.H2Config;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.util.TimeValue;
+
+public final class ClientSseExample {
+
+ public static void main(final String[] args) throws Exception {
+ final URI uri = URI.create(args.length > 0
+ ? args[0]
+ : "https://stream.wikimedia.org/v2/stream/recentchange");
+
+ final IOReactorConfig ioCfg = IOReactorConfig.custom()
+ .setIoThreadCount(Math.max(2,
Runtime.getRuntime().availableProcessors()))
+ .setSoKeepAlive(true)
+ .setTcpNoDelay(true)
+ .build();
+
+ final PoolingAsyncClientConnectionManager connMgr =
+ PoolingAsyncClientConnectionManagerBuilder.create()
+ .useSystemProperties()
+ .setMessageMultiplexing(true)
+ .setMaxConnPerRoute(32)
+ .setMaxConnTotal(256)
+ .setDefaultTlsConfig(
+ TlsConfig.custom()
+
.setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+ .build())
+ .build();
+
+ final CloseableHttpAsyncClient httpClient =
HttpAsyncClientBuilder.create()
+ .setIOReactorConfig(ioCfg)
+ .setConnectionManager(connMgr)
+ .setH2Config(H2Config.custom()
+ .setPushEnabled(false)
+ .setMaxConcurrentStreams(256)
+ .build())
+ .useSystemProperties()
+ .evictExpiredConnections()
+ .evictIdleConnections(TimeValue.ofMinutes(1))
+ .build();
+
+ final ScheduledThreadPoolExecutor scheduler =
+ new ScheduledThreadPoolExecutor(4, new
DefaultThreadFactory("sse-backoff", true));
+ scheduler.setRemoveOnCancelPolicy(true);
+
+ final Executor callbacks = Runnable::run;
+
+ final EventSourceConfig cfg = EventSourceConfig.builder()
+ .backoff(new ExponentialJitterBackoff(500L, 30_000L, 2.0,
250L))
+ .maxReconnects(-1)
+ .build();
+
+ final Map<String, String> defaultHeaders = new HashMap<>();
+ defaultHeaders.put("User-Agent", "Apache-HttpClient-SSE/5.x");
+ defaultHeaders.put("Accept-Language", "en");
+
+ final SseExecutor exec = SseExecutor.custom()
+ .setHttpClient(httpClient)
+ .setScheduler(scheduler)
+ .setCallbackExecutor(callbacks)
+ .setEventSourceConfig(cfg)
+ .setDefaultHeaders(defaultHeaders)
+ .setParserStrategy(SseParser.BYTE)
+ .build();
+
+ final CountDownLatch done = new CountDownLatch(1);
+
+ final EventSourceListener listener = new EventSourceListener() {
+ @Override
+ public void onOpen() {
+ System.out.println("[SSE] open: " + uri);
+ }
+
+ @Override
+ public void onEvent(final String id, final String type, final
String data) {
+ final String shortData = data.length() > 120 ? data.substring(0, 120) +
"…" : data;
+ System.out.printf(Locale.ROOT, "[SSE] %s id=%s %s%n",
+ type != null ? type : "message", id, shortData);
+ }
+
+ @Override
+ public void onFailure(final Throwable t, final boolean
willReconnect) {
+ System.err.println("[SSE] failure: " + t + " willReconnect=" +
willReconnect);
+ if (!willReconnect) {
+ done.countDown();
+ }
+ }
+
+ @Override
+ public void onClosed() {
+ System.out.println("[SSE] closed");
+ done.countDown();
+ }
+ };
+
+ final EventSource es = exec.open(uri, listener);
+
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ try {
+ es.cancel();
+ } catch (final Exception ignore) {
+ }
+ try {
+ exec.close();
+ } catch (final Exception ignore) {
+ }
+ try {
+ scheduler.shutdownNow();
+ } catch (final Exception ignore) {
+ }
+ }, "sse-shutdown"));
+
+ es.start();
+ done.await();
+
+ es.cancel();
+ exec.close();
+ scheduler.shutdownNow();
+ }
+}
+```
+
+Example 2: perf harness
+----------------------
+
+The `httpclient5-sse` tests also include a small performance harness:
+
+- `org.apache.hc.client5.http.sse.example.performance.SsePerfServer` – local
SSE server with configurable rate/payload.
+- `org.apache.hc.client5.http.sse.example.performance.SsePerfClient` – many
connections, ramp-up batching, simple
+ latency histogram.
+
+Those classes are intended for development / benchmarking (not as a public
API).
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/index.md
b/src/site/markdown/httpcomponents-client-5.6.x/index.md
index dd05275..311d5e1 100644
--- a/src/site/markdown/httpcomponents-client-5.6.x/index.md
+++ b/src/site/markdown/httpcomponents-client-5.6.x/index.md
@@ -53,6 +53,8 @@ Documentation
* [Observation](observation.md)
* [SCRAM-SHA-256](scram-sha-256.md)
* [SPKI pinning TLS strategy](spki-pinning.md)
+ * [Server-Sent Events (SSE)](sse.md)
+
6. Examples demonstrating some common as well as more complex use cases
@@ -60,6 +62,7 @@ Documentation
* [HttpClient (async APIs)](examples-async.md)
* [HttpClient (reactive APIs)](examples-reactive.md)
* [HttpClient (observation APIs)](examples-observation.md)
+ * [HttpClient (SSE APIs)](examples-sse.md)
1. Javadocs
@@ -67,6 +70,7 @@ Documentation
* [HC Fluent](./current/httpclient5-fluent/apidocs/)
* [HttpClient Cache](./current/httpclient5-cache/apidocs/)
* [HttpClient Observation](./current/httpclient5-observation/apidocs/)
+ * [HttpClient SSE](./current/httpclient5-sse/apidocs/)
1. API compatibility reports
@@ -99,6 +103,8 @@ Features
- Optional Server-Sent Events (SSE) module for consuming long-lived event
streams over HTTP/1.1 and HTTP/2 using the async transport.
- Source code is freely available under the Apache License.
+- Optional Server-Sent Events (SSE) module for consuming long-lived event
+ streams over HTTP/1.1 and HTTP/2 using the async transport.
Standards Compliance
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/sse.md
b/src/site/markdown/httpcomponents-client-5.6.x/sse.md
new file mode 100644
index 0000000..748770d
--- /dev/null
+++ b/src/site/markdown/httpcomponents-client-5.6.x/sse.md
@@ -0,0 +1,192 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+Server-Sent Events (SSE)
+========================
+
+Server-Sent Events (SSE) is a simple, server → client streaming protocol built
on top of HTTP. The server keeps an
+HTTP response open and sends events as lines in the `text/event-stream` format.
+
+HttpClient provides an optional SSE module (`httpclient5-sse`) that consumes
long-lived event streams over HTTP/1.1 and
+HTTP/2 using the async transport.
+
+Requirements
+------------
+
+- Java 8+
+- Apache HttpClient 5.7+ (the SSE module is `@since 5.7`)
+
+Dependency
+----------
+
+Maven:
+
+```xml
+<dependency>
+ <groupId>org.apache.httpcomponents.client5</groupId>
+ <artifactId>httpclient5-sse</artifactId>
+ <version>${httpclient.version}</version>
+</dependency>
+```
+
+Gradle:
+
+```gradle
+implementation("org.apache.httpcomponents.client5:httpclient5-sse:${httpclientVersion}")
+```
+
+Quick start
+-----------
+
+The SSE API is intentionally small:
+
+- `SseExecutor` is the entry point.
+- `EventSource` represents a single SSE stream.
+- `EventSourceListener` receives events and lifecycle callbacks.
+
+Minimal example (shared async client):
+
+```java
+import java.net.URI;
+
+import org.apache.hc.client5.http.sse.EventSource;
+import org.apache.hc.client5.http.sse.EventSourceListener;
+import org.apache.hc.client5.http.sse.SseExecutor;
+
+public final class MinimalSse {
+
+ public static void main(final String[] args) throws Exception {
+ final URI uri =
URI.create("https://stream.wikimedia.org/v2/stream/recentchange");
+
+ final SseExecutor exec = SseExecutor.newInstance();
+ final EventSource es = exec.open(uri, new EventSourceListener() {
+ @Override
+ public void onOpen() {
+ System.out.println("[SSE] open");
+ }
+
+ @Override
+ public void onEvent(final String id, final String type, final
String data) {
+ System.out.println("[SSE] " + (type != null ? type : "message") +
": " + data);
+ }
+
+ @Override
+ public void onFailure(final Throwable t, final boolean
willReconnect) {
+ System.err.println("[SSE] failure=" + t + " willReconnect=" +
willReconnect);
+ }
+
+ @Override
+ public void onClosed() {
+ System.out.println("[SSE] closed");
+ }
+ });
+
+ es.start();
+
+ // ... do other work ...
+
+ // Stop the stream (no further reconnects) when you are done.
+ es.cancel();
+
+ // When using the shared client, exec.close() is a no-op.
+ // Call SseExecutor.closeSharedClient() if you want to explicitly shut
it down.
+ }
+}
+```
+
+Configuration
+-------------
+
+Reconnect / backoff
+~~~~~~~~~~~~~~~~~~~
+
+Reconnects are controlled by `EventSourceConfig` and `BackoffStrategy`.
+
+- `maxReconnects = -1` means “try forever”.
+- The default backoff is `ExponentialJitterBackoff` and it honors server hints:
+ - the SSE `retry:` field (milliseconds)
+ - the HTTP `Retry-After` header
+
+Example:
+
+```java
+import org.apache.hc.client5.http.sse.EventSourceConfig;
+import org.apache.hc.client5.http.sse.impl.ExponentialJitterBackoff;
+
+final EventSourceConfig cfg = EventSourceConfig.builder()
+ .backoff(new ExponentialJitterBackoff(500L, 30_000L, 2.0, 250L))
+ .maxReconnects(-1)
+ .build();
+```
+
+Threading
+~~~~~~~~~
+
+Listener callbacks must be non-blocking.
+
+- If you do heavy work in `onEvent(...)`, provide a callback executor so the
I/O threads stay responsive.
+- Reconnect scheduling can use an application scheduler (recommended for large
fan-out).
+
+The builder makes those defaults explicit:
+
+```java
+import java.util.concurrent.Executor;
+import java.util.concurrent.ScheduledExecutorService;
+
+import org.apache.hc.client5.http.sse.SseExecutor;
+
+final ScheduledExecutorService scheduler = /* your scheduler */;
+final Executor callbacks = /* your callback executor */;
+
+final SseExecutor exec = SseExecutor.custom()
+ .setScheduler(scheduler)
+ .setCallbackExecutor(callbacks)
+ .build();
+```
+
+HTTP/2 multiplexing
+~~~~~~~~~~~~~~~~~~~
+
+SSE works over HTTP/1.1 and HTTP/2.
+
+- The default shared client created by `SseExecutor.newInstance()` uses a
pooling async connection manager with message
+ multiplexing enabled.
+- If you provide your own async client, enable message multiplexing if you
plan to run many streams over a small number
+ of HTTP/2 connections.
+
+Request headers and resumption
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- The implementation always sends `Accept: text/event-stream`.
+- The most recent non-null event id is tracked and used as `Last-Event-ID` on
reconnect.
+- You can set and remove headers on the `EventSource` to affect subsequent
reconnects.
+
+```java
+es.setHeader("Authorization", "Bearer ...");
+es.removeHeader("Authorization");
+
+final String last = es.lastEventId();
+es.setLastEventId(last); // override / resume manually
+```
+
+See also
+--------
+
+- [SSE examples](examples-sse.md)
+- Module javadocs: `./current/httpclient5-sse/apidocs/`