cpr is a modern HTTP library for C++, built for people.
This project is maintained by Fabian Sauter and Tim Stack
CPR supports Server-Sent Events (SSE), which allows servers to push real-time updates to clients over HTTP. SSE is a standard for unidirectional server-to-client communication over HTTP, commonly used for notifications, live updates, and streaming data.
#include <cpr/cpr.h>
#include <iostream>
int main() {
cpr::Session session;
session.SetUrl(cpr::Url{"https://example.com/events"});
// Set up SSE callback
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t userdata) {
std::cout << "Event Type: " << event.event << std::endl;
std::cout << "Data: " << event.data << std::endl;
if (event.id.has_value()) {
std::cout << "ID: " << event.id.value() << std::endl;
}
if (event.retry.has_value()) {
std::cout << "Retry: " << event.retry.value() << " ms" << std::endl;
}
// Return true to continue receiving events, false to stop
return true;
}
}
);
cpr::Response response = session.Get();
std::cout << "Status: " << response.status_code << std::endl;
return 0;
}
Each SSE event contains the following fields:
struct ServerSentEvent {
std::optional<std::string> id; // Event ID for tracking/resumption
std::string event; // Event type (default: "message")
std::string data; // Event data
std::optional<size_t> retry; // Reconnection time in milliseconds
};
You can pass custom user data to the SSE callback:
#include <cpr/cpr.h>
#include <iostream>
#include <vector>
int main() {
std::vector<std::string> received_events;
cpr::Session session;
session.SetUrl(cpr::Url{"https://example.com/events"});
// Pass a pointer to user data
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t userdata) {
auto* events = reinterpret_cast<std::vector<std::string>*>(userdata);
events->push_back(event.data);
return true;
},
reinterpret_cast<intptr_t>(&received_events)
}
);
session.Get();
std::cout << "Received " << received_events.size() << " events" << std::endl;
return 0;
}
You can control when to stop receiving events by returning false from the callback:
#include <cpr/cpr.h>
#include <iostream>
int main() {
int event_count = 0;
cpr::Session session;
session.SetUrl(cpr::Url{"https://example.com/events"});
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t userdata) {
int* count = reinterpret_cast<int*>(userdata);
(*count)++;
std::cout << "Event #" << *count << ": " << event.data << std::endl;
// Stop after receiving 10 events
return *count < 10;
},
reinterpret_cast<intptr_t>(&event_count)
}
);
session.Get();
std::cout << "Processed " << event_count << " events" << std::endl;
return 0;
}
SSE callbacks can be set using the SetOption method:
#include <cpr/cpr.h>
#include <iostream>
int main() {
cpr::Session session;
session.SetOption(cpr::Url{"https://example.com/events"});
session.SetOption(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t /*userdata*/) {
std::cout << event.data << std::endl;
return true;
}
}
);
session.Get();
return 0;
}
SSE events can have different types. By default, events have type "message", but servers can specify custom event types:
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t /*userdata*/) {
if (event.event == "update") {
std::cout << "Update: " << event.data << std::endl;
} else if (event.event == "notification") {
std::cout << "Notification: " << event.data << std::endl;
} else {
std::cout << "Message: " << event.data << std::endl;
}
return true;
}
}
);
SSE events can include additional metadata:
std::optional<std::string> last_event_id;
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t userdata) {
auto* last_id = reinterpret_cast<std::optional<std::string>*>(userdata);
if (event.id.has_value()) {
*last_id = event.id.value();
}
std::cout << "Data: " << event.data << std::endl;
return true;
},
reinterpret_cast<intptr_t>(&last_event_id)
}
);
session.Get();
// Use last_event_id to resume from last received event if needed
if (last_event_id.has_value()) {
session.SetHeader(cpr::Header{{"Last-Event-ID", last_event_id.value()}});
session.Get();
}
Server-Sent Events follow this text-based format according to the HTML5 specification:
event: custom
id: 123
retry: 5000
data: First line of data
data: Second line of data
\n\n)data: fields are concatenated with newlines: are comments and are ignoredWriteCallback. If you set an SSE callback, don’t set a write callback and vice versa.false from the callback to stop receiving events and close the connection.:) are automatically ignored per the SSE specification.cpr::Session session;
session.SetUrl(cpr::Url{"https://api.example.com/notifications"});
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t /*userdata*/) {
if (event.event == "notification") {
std::cout << "New notification: " << event.data << std::endl;
}
return true; // Keep listening
}
}
);
session.Get();
cpr::Session session;
session.SetUrl(cpr::Url{"https://api.example.com/stock-prices"});
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t /*userdata*/) {
if (event.event == "price-update") {
// Parse JSON data and update UI
std::cout << "Price update: " << event.data << std::endl;
}
return true;
}
}
);
session.Get();
cpr::Session session;
session.SetUrl(cpr::Url{"https://api.example.com/long-running-task/123"});
session.SetServerSentEventCallback(
cpr::ServerSentEventCallback{
[](cpr::ServerSentEvent&& event, intptr_t /*userdata*/) {
if (event.event == "progress") {
std::cout << "Progress: " << event.data << std::endl;
} else if (event.event == "complete") {
std::cout << "Task completed!" << std::endl;
return false; // Stop listening
}
return true;
}
}
);
session.Get();