You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@hc.apache.org by Oleg Kalnichevski <ol...@apache.org> on 2025/12/23 09:40:21 UTC

Re: (httpcomponents-website) branch master updated: Add HttpClient SSE module docs (overview, guide, examples, javadocs links)

Why are those changes in the 5.6 and not in 5.7? SSE module in is master 
and is not in the 5.6.x branch.

Oleg

https://github.com/apache/httpcomponents-website/tree/master/src/site/markdown/httpcomponents-client-5.7.x

On 12/23/2025 10:10, abernal@apache.org wrote:
> 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 <ab...@apache.org>
> 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/`
> 


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@hc.apache.org
For additional commands, e-mail: dev-help@hc.apache.org


Re: (httpcomponents-website) branch master updated: Add HttpClient SSE module docs (overview, guide, examples, javadocs links)

Posted by Arturo Bernal <ab...@apache.org>.
Hi,

Yes. my bad. I just remove the commit.


Arturo


On Tue, Dec 23, 2025 at 10:40 AM Oleg Kalnichevski <ol...@apache.org> wrote:

> Why are those changes in the 5.6 and not in 5.7? SSE module in is master
> and is not in the 5.6.x branch.
>
> Oleg
>
>
> https://github.com/apache/httpcomponents-website/tree/master/src/site/markdown/httpcomponents-client-5.7.x
>
> On 12/23/2025 10:10, abernal@apache.org wrote:
> > 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 <ab...@apache.org>
> > 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/`
> >
>
>