Skip to content

Commit 5a8fba4

Browse files
committed
Update README and examples to reflect changes from GetStream() to stream::Get() and StreamingResult to stream::Result
1 parent 5921d1f commit 5a8fba4

File tree

4 files changed

+81
-58
lines changed

4 files changed

+81
-58
lines changed

README-stream.md

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,34 @@ The C++20 streaming API allows you to process HTTP response bodies chunk by chun
1111
- **Large file downloads** with progress tracking
1212
- **Reverse proxy implementations**
1313

14+
## API Layers
15+
16+
cpp-httplib provides multiple API layers for different use cases:
17+
18+
```text
19+
┌─────────────────────────────────────────────┐
20+
│ SSEClient (planned) │ ← SSE-specific, parsed events
21+
│ - on_message(), on_event() │
22+
│ - Auto-reconnect, Last-Event-ID │
23+
├─────────────────────────────────────────────┤
24+
│ stream::Get() / stream::Result │ ← C++20, Generator-based
25+
│ - for (auto chunk : result.body()) │
26+
├─────────────────────────────────────────────┤
27+
│ open_stream() / StreamHandle │ ← General-purpose streaming
28+
│ - handle.read(buf, len) │
29+
├─────────────────────────────────────────────┤
30+
│ Client::Get() │ ← Traditional, full buffering
31+
└─────────────────────────────────────────────┘
32+
```
33+
34+
| Use Case | Recommended API |
35+
|----------|----------------|
36+
| SSE with auto-reconnect | SSEClient (planned) or `ssecli-stream.cc` example |
37+
| LLM streaming (JSON Lines) | `stream::Get()` |
38+
| Large file download | `stream::Get()` or `open_stream()` |
39+
| Reverse proxy | `open_stream()` |
40+
| Small responses with Keep-Alive | `Client::Get()` |
41+
1442
## Requirements
1543

1644
- C++20 compiler with coroutine support
@@ -25,7 +53,7 @@ int main() {
2553
httplib::Client cli("http://localhost:8080");
2654

2755
// Get streaming response
28-
auto result = httplib::GetStream(cli, "/stream");
56+
auto result = httplib::stream::Get(cli, "/stream");
2957

3058
if (result) {
3159
// Process response body in chunks
@@ -80,7 +108,7 @@ if (handle.is_valid()) {
80108
| `read(buf, len)` | `ssize_t` | Read up to `len` bytes directly from socket |
81109
| `read_all()` | `std::string` | Read all remaining content |
82110
83-
### High-Level API: `GetStream()` and `StreamingResult`
111+
### High-Level API: `stream::Get()` and `stream::Result`
84112
85113
The `httplib-stream.h` header provides a more ergonomic API using C++20 coroutines.
86114
@@ -90,14 +118,14 @@ The `httplib-stream.h` header provides a more ergonomic API using C++20 coroutin
90118
httplib::Client cli("http://localhost:8080");
91119
92120
// Simple GET
93-
auto result = httplib::GetStream(cli, "/path");
121+
auto result = httplib::stream::Get(cli, "/path");
94122
95123
// GET with custom headers
96124
httplib::Headers headers = {{"Authorization", "Bearer token"}};
97-
auto result = httplib::GetStream(cli, "/path", headers);
125+
auto result = httplib::stream::Get(cli, "/path", headers);
98126
```
99127

100-
#### StreamingResult Members
128+
#### stream::Result Members
101129

102130
| Member | Type | Description |
103131
|--------|------|-------------|
@@ -130,7 +158,7 @@ for (auto chunk : result.body(1024)) {
130158
int main() {
131159
httplib::Client cli("http://localhost:1234");
132160

133-
auto result = httplib::GetStream(cli, "/events");
161+
auto result = httplib::stream::Get(cli, "/events");
134162
if (!result) { return 1; }
135163

136164
for (auto chunk : result.body()) {
@@ -152,7 +180,7 @@ For a complete SSE client with auto-reconnection and event parsing, see `example
152180
int main() {
153181
httplib::Client cli("http://localhost:11434"); // Ollama
154182

155-
auto result = httplib::GetStream(cli, "/api/generate");
183+
auto result = httplib::stream::Get(cli, "/api/generate");
156184

157185
if (result && result.status() == 200) {
158186
for (auto chunk : result.body()) {
@@ -178,7 +206,7 @@ int main() {
178206

179207
int main() {
180208
httplib::Client cli("http://example.com");
181-
auto result = httplib::GetStream(cli, "/large-file.zip");
209+
auto result = httplib::stream::Get(cli, "/large-file.zip");
182210

183211
if (!result || result.status() != 200) {
184212
std::cerr << "Download failed\n";
@@ -236,8 +264,8 @@ svr.listen("0.0.0.0", 3000);
236264

237265
## Comparison with Existing APIs
238266

239-
| Feature | `Client::Get()` | `open_stream()` | `GetStream()` |
240-
|---------|----------------|-----------------|---------------|
267+
| Feature | `Client::Get()` | `open_stream()` | `stream::Get()` |
268+
|---------|----------------|-----------------|----------------|
241269
| Headers available | After complete | Immediately | Immediately |
242270
| Body reading | All at once | Direct from socket | Generator-based |
243271
| Memory usage | Full body in RAM | Minimal (controlled) | Minimal (controlled) |
@@ -259,15 +287,15 @@ svr.listen("0.0.0.0", 3000);
259287

260288
### Keep-Alive Behavior
261289

262-
The streaming API (`GetStream()` / `open_stream()`) takes ownership of the socket connection for the duration of the stream. This means:
290+
The streaming API (`stream::Get()` / `open_stream()`) takes ownership of the socket connection for the duration of the stream. This means:
263291

264292
- **Keep-Alive is not supported** for streaming connections
265293
- The socket is closed when `StreamHandle` is destroyed
266294
- For Keep-Alive scenarios, use the standard `client.Get()` API instead
267295

268296
```cpp
269297
// Use for streaming (no Keep-Alive)
270-
auto stream = httplib::GetStream(cli, "/large-stream");
298+
auto stream = httplib::stream::Get(cli, "/large-stream");
271299

272300
// Use for Keep-Alive connections
273301
auto result = cli.Get("/api/data"); // Connection can be reused

example/ssecli-stream.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ int main(void) {
125125
}
126126

127127
// Open streaming connection
128-
auto result = httplib::GetStream(cli, path, headers);
128+
auto result = httplib::stream::Get(cli, path, headers);
129129

130130
//--------------------------------------------------------------------------
131131
// Connection error handling

httplib-stream.h

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// When C++23 is adopted, this header can be simplified:
1313
// - Replace custom Generator<T> with std::generator<T> from <generator>
1414
// - The Generator interface is designed to be compatible with std::generator
15-
// - StreamingResult and GetStream() can remain unchanged
15+
// - stream::Result and stream::Get() can remain unchanged
1616
//
1717

1818
#ifndef CPPHTTPLIB_HTTPLIB20_H
@@ -219,33 +219,36 @@ inline Generator<std::string_view> stream_body(ClientImpl::StreamHandle handle,
219219
} // namespace detail
220220

221221
//------------------------------------------------------------------------------
222-
// StreamingResult - High-level wrapper for streaming HTTP responses
222+
// stream namespace - C++20 streaming API
223223
//------------------------------------------------------------------------------
224224
//
225225
// Provides a convenient interface for streaming HTTP response bodies.
226-
// Supports both memory-buffered and socket-direct streaming modes.
226+
// Data is read directly from the socket without buffering.
227227
//
228228
// Usage:
229-
// auto result = httplib::GetStream(client, "/large-file");
229+
// auto result = httplib::stream::Get(client, "/large-file");
230230
// if (result) {
231231
// for (auto chunk : result.body()) {
232232
// process(chunk);
233233
// }
234234
// }
235235
//
236236

237-
class StreamingResult {
237+
namespace stream {
238+
239+
// Result - wrapper for streaming HTTP responses
240+
class Result {
238241
public:
239-
StreamingResult() = default;
242+
Result() = default;
240243

241-
explicit StreamingResult(ClientImpl::StreamHandle &&handle)
244+
explicit Result(ClientImpl::StreamHandle &&handle)
242245
: handle_(std::move(handle)) {}
243246

244247
// Move-only semantics
245-
StreamingResult(StreamingResult &&) = default;
246-
StreamingResult &operator=(StreamingResult &&) = default;
247-
StreamingResult(const StreamingResult &) = delete;
248-
StreamingResult &operator=(const StreamingResult &) = delete;
248+
Result(Result &&) = default;
249+
Result &operator=(Result &&) = default;
250+
Result(const Result &) = delete;
251+
Result &operator=(const Result &) = delete;
249252

250253
// Validity check
251254
bool is_valid() const { return handle_.is_valid(); }
@@ -290,10 +293,10 @@ class StreamingResult {
290293
};
291294

292295
//------------------------------------------------------------------------------
293-
// Free functions for streaming HTTP requests
296+
// Streaming HTTP request functions
294297
//------------------------------------------------------------------------------
295298
//
296-
// GetStream reads response body directly from the socket without buffering
299+
// stream::Get reads response body directly from the socket without buffering
297300
// the entire response in memory. This is ideal for:
298301
// - Large file downloads
299302
// - Server-Sent Events (SSE)
@@ -304,30 +307,22 @@ class StreamingResult {
304307
// where connection reuse matters, use client.Get() instead.
305308
//
306309
// Usage:
307-
// auto result = httplib::GetStream(client, "/huge-file");
310+
// auto result = httplib::stream::Get(client, "/huge-file");
308311
// for (auto chunk : result.body()) {
309312
// write_to_file(chunk);
310313
// }
311314
//
312315

313-
inline StreamingResult GetStream(Client &cli, const std::string &path) {
314-
return StreamingResult{cli.open_stream(path)};
315-
}
316-
317-
inline StreamingResult GetStream(Client &cli, const std::string &path,
318-
const Headers &headers) {
319-
return StreamingResult{cli.open_stream(path, headers)};
316+
inline Result Get(Client &cli, const std::string &path) {
317+
return Result{cli.open_stream(path)};
320318
}
321319

322-
// Overloads for ClientImpl (direct use without Client wrapper)
323-
inline StreamingResult GetStream(ClientImpl &cli, const std::string &path) {
324-
return StreamingResult{cli.open_stream(path)};
320+
inline Result Get(Client &cli, const std::string &path,
321+
const Headers &headers) {
322+
return Result{cli.open_stream(path, headers)};
325323
}
326324

327-
inline StreamingResult GetStream(ClientImpl &cli, const std::string &path,
328-
const Headers &headers) {
329-
return StreamingResult{cli.open_stream(path, headers)};
330-
}
325+
} // namespace stream
331326

332327
} // namespace httplib
333328

test/test-stream.cc

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -202,29 +202,29 @@ TEST_F(StreamingServerTest, ReadSmallBuffer) {
202202

203203
#include "../httplib-stream.h"
204204

205-
TEST_F(StreamingServerTest, GetStreamReturnsStreamingResult) {
205+
TEST_F(StreamingServerTest, stream_Get_ReturnsResult) {
206206
httplib::Client cli("localhost", 8787);
207207

208-
auto result = httplib::GetStream(cli, "/hello");
208+
auto result = httplib::stream::Get(cli, "/hello");
209209

210210
EXPECT_TRUE(result.is_valid());
211211
EXPECT_EQ(200, result.status());
212212
}
213213

214-
TEST_F(StreamingServerTest, GetStreamWithHeaders) {
214+
TEST_F(StreamingServerTest, stream_Get_WithHeaders) {
215215
httplib::Client cli("localhost", 8787);
216216

217-
auto result = httplib::GetStream(cli, "/hello");
217+
auto result = httplib::stream::Get(cli, "/hello");
218218

219219
ASSERT_TRUE(result.is_valid());
220220
EXPECT_TRUE(result.has_header("Content-Type"));
221221
EXPECT_EQ("text/plain", result.get_header_value("Content-Type"));
222222
}
223223

224-
TEST_F(StreamingServerTest, GetStreamBodyGenerator) {
224+
TEST_F(StreamingServerTest, stream_Get_BodyGenerator) {
225225
httplib::Client cli("localhost", 8787);
226226

227-
auto result = httplib::GetStream(cli, "/hello");
227+
auto result = httplib::stream::Get(cli, "/hello");
228228
ASSERT_TRUE(result.is_valid());
229229

230230
std::string body;
@@ -235,10 +235,10 @@ TEST_F(StreamingServerTest, GetStreamBodyGenerator) {
235235
EXPECT_EQ("Hello World!", body);
236236
}
237237

238-
TEST_F(StreamingServerTest, GetStreamBodyGeneratorSmallChunks) {
238+
TEST_F(StreamingServerTest, stream_Get_BodyGeneratorSmallChunks) {
239239
httplib::Client cli("localhost", 8787);
240240

241-
auto result = httplib::GetStream(cli, "/hello");
241+
auto result = httplib::stream::Get(cli, "/hello");
242242
ASSERT_TRUE(result.is_valid());
243243

244244
std::string body;
@@ -252,29 +252,29 @@ TEST_F(StreamingServerTest, GetStreamBodyGeneratorSmallChunks) {
252252
EXPECT_GT(chunk_count, 1u); // Should have multiple chunks
253253
}
254254

255-
TEST_F(StreamingServerTest, GetStreamReadAll) {
255+
TEST_F(StreamingServerTest, stream_Get_ReadAll) {
256256
httplib::Client cli("localhost", 8787);
257257

258-
auto result = httplib::GetStream(cli, "/hello");
258+
auto result = httplib::stream::Get(cli, "/hello");
259259
ASSERT_TRUE(result.is_valid());
260260

261261
std::string body = result.read_all();
262262
EXPECT_EQ("Hello World!", body);
263263
}
264264

265-
TEST_F(StreamingServerTest, GetStreamConnectionError) {
265+
TEST_F(StreamingServerTest, stream_Get_ConnectionError) {
266266
httplib::Client cli("localhost", 9999); // No server
267267

268-
auto result = httplib::GetStream(cli, "/hello");
268+
auto result = httplib::stream::Get(cli, "/hello");
269269

270270
EXPECT_FALSE(result.is_valid());
271271
EXPECT_NE(httplib::Error::Success, result.error());
272272
}
273273

274-
TEST_F(StreamingServerTest, GetStream404) {
274+
TEST_F(StreamingServerTest, stream_Get_404) {
275275
httplib::Client cli("localhost", 8787);
276276

277-
auto result = httplib::GetStream(cli, "/nonexistent");
277+
auto result = httplib::stream::Get(cli, "/nonexistent");
278278

279279
EXPECT_TRUE(result.is_valid());
280280
EXPECT_EQ(404, result.status());
@@ -459,7 +459,7 @@ TEST_F(ChunkedStreamingTest, SSELikeStreaming) {
459459

460460
TEST_F(ChunkedStreamingTest, GeneratorWithChunkedResponse) {
461461
httplib::Client cli("http://127.0.0.1:8787");
462-
auto result = httplib::GetStream(cli, "/chunked");
462+
auto result = httplib::stream::Get(cli, "/chunked");
463463

464464
ASSERT_TRUE(result);
465465
EXPECT_EQ(200, result.status());
@@ -479,7 +479,7 @@ TEST_F(ChunkedStreamingTest, GeneratorWithChunkedResponse) {
479479

480480
TEST_F(ChunkedStreamingTest, GeneratorWithLargeResponse) {
481481
httplib::Client cli("http://127.0.0.1:8787");
482-
auto result = httplib::GetStream(cli, "/large");
482+
auto result = httplib::stream::Get(cli, "/large");
483483

484484
ASSERT_TRUE(result);
485485

@@ -496,7 +496,7 @@ TEST_F(ChunkedStreamingTest, GeneratorWithLargeResponse) {
496496

497497
TEST_F(ChunkedStreamingTest, SSELikeWithGenerator) {
498498
httplib::Client cli("http://127.0.0.1:8787");
499-
auto result = httplib::GetStream(cli, "/sse-like");
499+
auto result = httplib::stream::Get(cli, "/sse-like");
500500

501501
ASSERT_TRUE(result);
502502

0 commit comments

Comments
 (0)