Files
cpp-httplib/docs-src/pages/en/cookbook/e04-sse-client.md
T
yhirose 9bfd2ddf1c Fix README and docs pages that no longer match the code
Bring the READMEs and the docs pages (en and ja) in line with the
current header:

- Fix the client sample that does not compile, the Transfer-Encoding
  statement for default headers, the verifier callback comment, the
  thread pool spawn rule, the sanitize_filename description, and the
  inverted Accept-Encoding sentence.
- Describe recent behaviour: TLS errors behind Read/Write failures,
  Host validation, Connection: close for a body without length,
  content_reader rules, refused CONNECT, WebSocketClient proxies,
  CPPHTTPLIB_INTERIM_RESPONSE_MAX_COUNT, the SSE event size limit, and
  the payload limit of the Stream API.
- Complete the API references of WebSocket, WebSocketClient and the
  stream:: helpers, and the SSE reconnect rules.
- Fix cookbook pages: keychain loading on every backend, received Close
  status, a multipart sample that trusted the client's file name, an
  echo sample that sent binary as text, SNI, pre-request handler scope,
  stop() with WebSocket handlers, and the thread pool notes.
- Restore the mutex in the LLM app's Chapter 5 code.
2026-10-18 10:13:57 -04:00

104 lines
3.5 KiB
Markdown

---
title: "E04. Receive SSE on the Client"
order: 51
status: "draft"
---
cpp-httplib ships a dedicated `sse::SSEClient` class. It handles auto-reconnect, per-event-name dispatch, and `Last-Event-ID` tracking for you — so receiving SSE is painless.
## Basic usage
```cpp
#include <httplib.h>
httplib::Client cli("http://localhost:8080");
httplib::sse::SSEClient sse(cli, "/events");
sse.on_message([](const httplib::sse::SSEMessage &msg) {
std::cout << "data: " << msg.data << std::endl;
});
sse.start(); // blocking
```
Build an `SSEClient` with a `Client` and a path, register a callback with `on_message()`, and call `start()`. The event loop kicks in and automatically reconnects if the connection drops.
## Dispatch by event name
When the server sends events with an `event:` field, register a handler per name via `on_event()`.
```cpp
sse.on_event("message", [](const auto &msg) {
std::cout << "chat: " << msg.data << std::endl;
});
sse.on_event("join", [](const auto &msg) {
std::cout << msg.data << " joined" << std::endl;
});
sse.on_event("leave", [](const auto &msg) {
std::cout << msg.data << " left" << std::endl;
});
```
`on_message()` is the generic fallback: it receives every event that has no handler registered through `on_event()`. With `on_event("message", ...)` registered as above, `message` events go there instead.
## Connection lifecycle and errors
```cpp
sse.on_open([] {
std::cout << "connected" << std::endl;
});
sse.on_error([](httplib::Error err) {
std::cerr << "error: " << httplib::to_string(err) << std::endl;
});
```
Hook into connection open and error events. Even when the error handler fires, `SSEClient` keeps trying to reconnect in the background. It stops after a 204, 403, or 404 response, or once the attempts set with `set_max_reconnect_attempts()` run out.
It also stops when an event, or a line still waiting for its end, grows past `CPPHTTPLIB_SSE_MAX_EVENT_LENGTH` (16MB by default, 0 for no limit). The error handler then gets `Error::ExceedMaxPayloadSize`. The client's `set_payload_max_length()` doesn't apply to the stream as a whole, since an event stream has no end.
## Run asynchronously
If you don't want to block the main thread, use `start_async()`.
```cpp
sse.start_async();
// main thread continues to do other things
do_other_work();
// when you're done, stop it
sse.stop();
```
`start_async()` spawns a background thread to run the event loop. Use `stop()` to shut it down cleanly.
## Configure reconnection
You can tune the reconnect interval and maximum retries.
```cpp
sse.set_reconnect_interval(5000); // 5 seconds
sse.set_max_reconnect_attempts(10); // up to 10 (0 = unlimited)
```
If the server sends a `retry:` field, that takes precedence.
## Automatic Last-Event-ID
`SSEClient` tracks the `id` of each received event internally and sends it back as `Last-Event-ID` on reconnect. As long as the server sends events with `id:`, this all works automatically.
An empty `id:` clears the stored id. An `id:` that can't be sent as a header value (one with NUL or other control characters, or with leading or trailing whitespace) is ignored, and the previous id stays.
```cpp
std::cout << "last id: " << sse.last_event_id() << std::endl;
```
Use `last_event_id()` to read the current value.
> **Note:** `SSEClient::start()` blocks, which is fine for a one-off command-line tool. For GUI apps or embedded in a server, the `start_async()` + `stop()` pair is the usual pattern.
> For the server side, see [E01. Implement an SSE server](../e01-sse-server).