Skip to content

Commit b35c468

Browse files
committed
Implement SSEClient
1 parent 7eb03e8 commit b35c468

File tree

6 files changed

+3036
-1797
lines changed

6 files changed

+3036
-1797
lines changed

‎README-sse.md‎

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
# SSEClient - Server-Sent Events Client
2+
3+
A simple, EventSource-like SSE client for C++11.
4+
5+
## Features
6+
7+
-**Auto-reconnect**: Automatically reconnects on connection loss
8+
-**Last-Event-ID**: Sends last received ID on reconnect for resumption
9+
-**retry field**: Respects server's reconnect interval
10+
-**Event types**: Supports custom event types via `on_event()`
11+
-**Async support**: Run in background thread with `start_async()`
12+
-**C++11 compatible**: No C++14/17/20 features required
13+
14+
## Quick Start
15+
16+
```cpp
17+
httplib::Client cli("http://localhost:8080");
18+
httplib::sse::SSEClient sse(cli, "/events");
19+
20+
sse.on_message([](const httplib::sse::SSEMessage &msg){
21+
std::cout << "Event: " << msg.event << std::endl;
22+
std::cout << "Data: " << msg.data << std::endl;
23+
});
24+
25+
sse.start(); // Blocking, with auto-reconnect
26+
```
27+
28+
## API Reference
29+
30+
### SSEMessage
31+
32+
```cpp
33+
struct SSEMessage{
34+
std::string event; // Event type (default: "message")
35+
std::string data; // Event payload
36+
std::string id; // Event ID
37+
};
38+
```
39+
40+
### SSEClient
41+
42+
#### Constructor
43+
44+
```cpp
45+
// Basic
46+
SSEClient(Client &client, const std::string &path);
47+
48+
// With custom headers
49+
SSEClient(Client &client, const std::string &path, const Headers &headers);
50+
```
51+
52+
#### Event Handlers
53+
54+
```cpp
55+
// Called for all events (or events without a specific handler)
56+
sse.on_message([](const SSEMessage &msg){});
57+
58+
// Called for specific event types
59+
sse.on_event("update", [](const SSEMessage &msg){});
60+
sse.on_event("delete", [](const SSEMessage &msg){});
61+
62+
// Called when connection is established
63+
sse.on_open([](){});
64+
65+
// Called on connection errors
66+
sse.on_error([](httplib::Error err){});
67+
```
68+
69+
#### Configuration
70+
71+
```cpp
72+
// Set reconnect interval (default: 3000ms)
73+
sse.set_reconnect_interval(5000);
74+
75+
// Set max reconnect attempts (default: 0 = unlimited)
76+
sse.set_max_reconnect_attempts(10);
77+
```
78+
79+
#### Control
80+
81+
```cpp
82+
// Blocking start with auto-reconnect
83+
sse.start();
84+
85+
// Non-blocking start (runs in background thread)
86+
sse.start_async();
87+
88+
// Stop the client (thread-safe)
89+
sse.stop();
90+
```
91+
92+
#### State
93+
94+
```cpp
95+
bool connected = sse.is_connected();
96+
const std::string &id = sse.last_event_id();
97+
```
98+
99+
## Examples
100+
101+
### Basic Usage
102+
103+
```cpp
104+
httplib::Client cli("http://localhost:8080");
105+
httplib::sse::SSEClient sse(cli, "/events");
106+
107+
sse.on_message([](const httplib::sse::SSEMessage &msg){
108+
std::cout << msg.data << std::endl;
109+
});
110+
111+
sse.start();
112+
```
113+
114+
### With Custom Event Types
115+
116+
```cpp
117+
httplib::sse::SSEClient sse(cli, "/events");
118+
119+
sse.on_event("notification", [](const httplib::sse::SSEMessage &msg){
120+
std::cout << "Notification: " << msg.data << std::endl;
121+
});
122+
123+
sse.on_event("update", [](const httplib::sse::SSEMessage &msg){
124+
std::cout << "Update: " << msg.data << std::endl;
125+
});
126+
127+
sse.start();
128+
```
129+
130+
### Async with Stop
131+
132+
```cpp
133+
httplib::sse::SSEClient sse(cli, "/events");
134+
135+
sse.on_message([](const httplib::sse::SSEMessage &msg){
136+
std::cout << msg.data << std::endl;
137+
});
138+
139+
sse.start_async(); // Returns immediately
140+
141+
// ... do other work ...
142+
143+
sse.stop(); // Stop when done
144+
```
145+
146+
### With Custom Headers (e.g., Authentication)
147+
148+
```cpp
149+
httplib::Headers headers ={
150+
{"Authorization", "Bearer token123"}
151+
};
152+
153+
httplib::sse::SSEClient sse(cli, "/events", headers);
154+
sse.start();
155+
```
156+
157+
### Error Handling
158+
159+
```cpp
160+
sse.on_error([](httplib::Error err){
161+
std::cerr << "Error: " << httplib::to_string(err) << std::endl;
162+
});
163+
164+
sse.set_reconnect_interval(1000);
165+
sse.set_max_reconnect_attempts(5);
166+
167+
sse.start();
168+
```
169+
170+
## SSE Protocol
171+
172+
The client parses SSE format according to the [W3C specification](https://html.spec.whatwg.org/multipage/server-sent-events.html):
173+
174+
```
175+
event: custom-type
176+
id: 123
177+
data:{"message": "hello"}
178+
179+
data: simple message
180+
181+
: this is a comment (ignored)
182+
```

‎README.md‎

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1206,8 +1206,8 @@ std::string decoded_component = httplib::decode_uri_component(encoded_component)
12061206

12071207
Use `encode_uri()` for full URLs and `encode_uri_component()` for individual query parameters or path segments.
12081208

1209-
Streaming API
1210-
-------------
1209+
Stream API
1210+
----------
12111211

12121212
Process large responses without loading everything into memory.
12131213

@@ -1234,6 +1234,28 @@ All HTTP methods are supported: `stream::Get`, `Post`, `Put`, `Patch`, `Delete`,
12341234
12351235
See [README-stream.md](README-stream.md) for more details.
12361236
1237+
SSE Client
1238+
----------
1239+
1240+
```cpp
1241+
#include <httplib.h>
1242+
1243+
int main(){
1244+
httplib::Client cli("http://localhost:8080");
1245+
httplib::sse::SSEClient sse(cli, "/events");
1246+
1247+
sse.on_message([](const httplib::sse::SSEMessage &msg){
1248+
std::cout << "Event: " << msg.event << std::endl;
1249+
std::cout << "Data: " << msg.data << std::endl;
1250+
});
1251+
1252+
sse.start(); // Blocking, with auto-reconnect
1253+
return 0;
1254+
}
1255+
```
1256+
1257+
See [README-sse.md](README-sse.md) for more details.
1258+
12371259
Split httplib.h into .h and .cc
12381260
-------------------------------
12391261

‎example/Makefile‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ ZLIB_SUPPORT = -DCPPHTTPLIB_ZLIB_SUPPORT -lz
1818
BROTLI_DIR = $(PREFIX)/opt/brotli
1919
BROTLI_SUPPORT = -DCPPHTTPLIB_BROTLI_SUPPORT -I$(BROTLI_DIR)/include -L$(BROTLI_DIR)/lib -lbrotlicommon -lbrotlienc -lbrotlidec
2020

21-
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli ssecli-stream benchmark one_time_request server_and_client accept_header
21+
all: server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client accept_header
2222

2323
server : server.cc ../httplib.h Makefile
2424
$(CXX) -o server $(CXXFLAGS) server.cc $(OPENSSL_SUPPORT)$(ZLIB_SUPPORT)$(BROTLI_SUPPORT)
@@ -47,9 +47,6 @@ ssesvr : ssesvr.cc ../httplib.h Makefile
4747
ssecli : ssecli.cc ../httplib.h Makefile
4848
$(CXX) -o ssecli $(CXXFLAGS) ssecli.cc $(OPENSSL_SUPPORT)$(ZLIB_SUPPORT)$(BROTLI_SUPPORT)
4949

50-
ssecli-stream : ssecli-stream.cc ../httplib.h ../httplib.h Makefile
51-
$(CXX) -o ssecli-stream $(CXXFLAGS) ssecli-stream.cc $(OPENSSL_SUPPORT)$(ZLIB_SUPPORT)$(BROTLI_SUPPORT)
52-
5350
benchmark : benchmark.cc ../httplib.h Makefile
5451
$(CXX) -o benchmark $(CXXFLAGS) benchmark.cc $(OPENSSL_SUPPORT)$(ZLIB_SUPPORT)$(BROTLI_SUPPORT)
5552

@@ -67,4 +64,4 @@ pem:
6764
openssl req -new -key key.pem | openssl x509 -days 3650 -req -signkey key.pem > cert.pem
6865

6966
clean:
70-
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli ssecli-stream benchmark one_time_request server_and_client accept_header *.pem
67+
rm server client hello simplecli simplesvr upload redirect ssesvr ssecli benchmark one_time_request server_and_client accept_header *.pem

‎example/ssecli.cc‎

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,52 @@
66
//
77

88
#include<httplib.h>
9+
10+
#include<csignal>
911
#include<iostream>
1012

1113
usingnamespacestd;
1214

15+
// Global SSEClient pointer for signal handling
16+
httplib::sse::SSEClient *g_sse = nullptr;
17+
18+
voidsignal_handler(int){
19+
if (g_sse){g_sse->stop()}
20+
}
21+
1322
intmain(void){
14-
httplib::Client("http://localhost:1234")
15-
.Get("/event1", [&](constchar *data, size_t data_length){
16-
std::cout << string(data, data_length);
17-
returntrue;
18-
});
23+
// Configuration
24+
const string host = "http://localhost:1234";
25+
const string path = "/event1";
26+
27+
cout << "SSE Client using httplib::sse::SSEClient\n";
28+
cout << "Connecting to: " << host << path << "\n";
29+
cout << "Press Ctrl+C to exit\n\n";
30+
31+
httplib::Client cli(host);
32+
httplib::sse::SSEClient sse(cli, path);
33+
34+
// Set up signal handler for graceful shutdown
35+
g_sse = &sse;
36+
signal(SIGINT, signal_handler);
37+
38+
// Event handlers
39+
sse.on_open([](){cout << "[Connected]\n\n"});
40+
41+
sse.on_message([](const httplib::sse::SSEMessage &msg){
42+
cout << "Event: " << msg.event << "\n";
43+
cout << "Data: " << msg.data << "\n";
44+
if (!msg.id.empty()){cout << "ID: " << msg.id << "\n"}
45+
cout << "\n";
46+
});
47+
48+
sse.on_error([](httplib::Error err){
49+
cerr << "[Error] " << httplib::to_string(err) << "\n";
50+
});
51+
52+
// Start with auto-reconnect (blocking)
53+
sse.start();
1954

55+
cout << "\n[Disconnected]\n";
2056
return0;
2157
}

0 commit comments

Comments
(0)