mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-04-12 03:38:30 +00:00
Add Cookbook C01-C19 (draft)
This commit is contained in:
60
docs-src/pages/en/cookbook/c01-get-response-body.md
Normal file
60
docs-src/pages/en/cookbook/c01-get-response-body.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
title: "C01. Get the Response Body / Save to a File"
|
||||
order: 1
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
## Get it as a string
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
auto res = cli.Get("/hello");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
`res->body` is a `std::string`, ready to use as-is. The entire response is loaded into memory.
|
||||
|
||||
> **Warning:** If you fetch a large file with `res->body`, it all goes into memory. For large downloads, use a `ContentReceiver` as shown below.
|
||||
|
||||
## Save to a file
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
std::ofstream ofs("output.bin", std::ios::binary);
|
||||
if (!ofs) {
|
||||
std::cerr << "Failed to open file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto res = cli.Get("/large-file",
|
||||
[&](const char *data, size_t len) {
|
||||
ofs.write(data, len);
|
||||
return static_cast<bool>(ofs);
|
||||
});
|
||||
```
|
||||
|
||||
With a `ContentReceiver`, data arrives in chunks. You can write each chunk straight to disk without buffering the whole body in memory — perfect for large file downloads.
|
||||
|
||||
Return `false` from the callback to abort the download. In the example above, if writing to `ofs` fails, the download stops automatically.
|
||||
|
||||
> **Detail:** Want to check response headers like Content-Length before downloading? Combine a `ResponseHandler` with a `ContentReceiver`.
|
||||
>
|
||||
> ```cpp
|
||||
> auto res = cli.Get("/large-file",
|
||||
> [](const httplib::Response &res) {
|
||||
> auto len = res.get_header_value("Content-Length");
|
||||
> std::cout << "Size: " << len << std::endl;
|
||||
> return true; // return false to skip the download
|
||||
> },
|
||||
> [&](const char *data, size_t len) {
|
||||
> ofs.write(data, len);
|
||||
> return static_cast<bool>(ofs);
|
||||
> });
|
||||
> ```
|
||||
>
|
||||
> The `ResponseHandler` is called after headers arrive but before the body. Return `false` to skip the download entirely.
|
||||
|
||||
> To show download progress, see C11. Use the progress callback.
|
||||
36
docs-src/pages/en/cookbook/c02-json.md
Normal file
36
docs-src/pages/en/cookbook/c02-json.md
Normal file
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: "C02. Send and Receive JSON"
|
||||
order: 2
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
cpp-httplib doesn't include a JSON parser. Use a library like [nlohmann/json](https://github.com/nlohmann/json) to build and parse JSON. The examples here use `nlohmann/json`.
|
||||
|
||||
## Send JSON
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
nlohmann::json j = {{"name", "Alice"}, {"age", 30}};
|
||||
auto res = cli.Post("/api/users", j.dump(), "application/json");
|
||||
```
|
||||
|
||||
Pass the JSON string as the second argument to `Post()` and the Content-Type as the third. The same pattern works with `Put()` and `Patch()`.
|
||||
|
||||
> **Warning:** If you omit the Content-Type (the third argument), the server may not recognize the body as JSON. Always specify `"application/json"`.
|
||||
|
||||
## Receive a JSON response
|
||||
|
||||
```cpp
|
||||
auto res = cli.Get("/api/users/1");
|
||||
if (res && res->status == 200) {
|
||||
auto j = nlohmann::json::parse(res->body);
|
||||
std::cout << j["name"] << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
`res->body` is a `std::string`, so you can pass it straight to your JSON library.
|
||||
|
||||
> **Note:** Servers sometimes return HTML on errors. Check the status code before parsing to be safe. Some APIs also require an `Accept: application/json` header. If you're calling a JSON API repeatedly, C03. Set default headers can save you some boilerplate.
|
||||
|
||||
> For how to receive and return JSON on the server side, see S02. Receive JSON requests and return JSON responses.
|
||||
55
docs-src/pages/en/cookbook/c03-default-headers.md
Normal file
55
docs-src/pages/en/cookbook/c03-default-headers.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: "C03. Set Default Headers"
|
||||
order: 3
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
When you want the same headers on every request, use `set_default_headers()`. Once set, they're attached automatically to every request sent from that client.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
cli.set_default_headers({
|
||||
{"Accept", "application/json"},
|
||||
{"User-Agent", "my-app/1.0"},
|
||||
});
|
||||
|
||||
auto res = cli.Get("/users");
|
||||
```
|
||||
|
||||
Register the headers you need on every API call — like `Accept` or `User-Agent` — in one place. No need to repeat them on each request.
|
||||
|
||||
## Send a Bearer token on every request
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
cli.set_default_headers({
|
||||
{"Authorization", "Bearer " + token},
|
||||
{"Accept", "application/json"},
|
||||
});
|
||||
|
||||
auto res1 = cli.Get("/me");
|
||||
auto res2 = cli.Get("/projects");
|
||||
```
|
||||
|
||||
Set the auth token once, and every subsequent request carries it. Handy when you're writing an API client that hits multiple endpoints.
|
||||
|
||||
> **Note:** `set_default_headers()` **replaces** the existing default headers. Even if you only want to add one, pass the full set again.
|
||||
|
||||
## Combine with per-request headers
|
||||
|
||||
You can still pass extra headers on individual requests, even with defaults set.
|
||||
|
||||
```cpp
|
||||
httplib::Headers headers = {
|
||||
{"X-Request-ID", "abc-123"},
|
||||
};
|
||||
auto res = cli.Get("/users", headers);
|
||||
```
|
||||
|
||||
Per-request headers are **added** on top of the defaults. Both are sent to the server.
|
||||
|
||||
> For details on Bearer token auth, see C06. Call an API with a Bearer Token.
|
||||
38
docs-src/pages/en/cookbook/c04-follow-location.md
Normal file
38
docs-src/pages/en/cookbook/c04-follow-location.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
title: "C04. Follow Redirects"
|
||||
order: 4
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
By default, cpp-httplib does not follow HTTP redirects (3xx). If the server returns `302 Found`, you'll get it as a response with status code 302 — nothing more.
|
||||
|
||||
To follow redirects automatically, call `set_follow_location(true)`.
|
||||
|
||||
## Follow redirects
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://example.com");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/old-path");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
With `set_follow_location(true)`, the client reads the `Location` header and reissues the request to the new URL automatically. The final response ends up in `res`.
|
||||
|
||||
## Redirects from HTTP to HTTPS
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://example.com");
|
||||
cli.set_follow_location(true);
|
||||
|
||||
auto res = cli.Get("/");
|
||||
```
|
||||
|
||||
Many sites redirect HTTP traffic to HTTPS. With `set_follow_location(true)` on, this case is handled transparently — the client follows redirects even when the scheme or host changes.
|
||||
|
||||
> **Warning:** To follow redirects to HTTPS, you need to build cpp-httplib with OpenSSL (or another TLS backend). Without TLS support, redirects to HTTPS will fail.
|
||||
|
||||
> **Note:** Following redirects adds to the total request time. See C12. Set Timeouts for timeout configuration.
|
||||
46
docs-src/pages/en/cookbook/c05-basic-auth.md
Normal file
46
docs-src/pages/en/cookbook/c05-basic-auth.md
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "C05. Use Basic Authentication"
|
||||
order: 5
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
For endpoints that require Basic authentication, pass the username and password to `set_basic_auth()`. cpp-httplib builds the `Authorization: Basic ...` header for you.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_basic_auth("alice", "s3cret");
|
||||
|
||||
auto res = cli.Get("/private");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
Set it once, and every request from that client carries the credentials. No need to build the header each time.
|
||||
|
||||
## Per-request usage
|
||||
|
||||
If you want credentials on only one specific request, pass headers directly.
|
||||
|
||||
```cpp
|
||||
httplib::Headers headers = {
|
||||
httplib::make_basic_authentication_header("alice", "s3cret"),
|
||||
};
|
||||
auto res = cli.Get("/private", headers);
|
||||
```
|
||||
|
||||
`make_basic_authentication_header()` builds the Base64-encoded header for you.
|
||||
|
||||
> **Warning:** Basic authentication **encodes** credentials in Base64 — it does not encrypt them. Always use it over HTTPS. Over plain HTTP, your password travels the network in the clear.
|
||||
|
||||
## Digest authentication
|
||||
|
||||
For the more secure Digest authentication scheme, use `set_digest_auth()`. This is only available when cpp-httplib is built with OpenSSL (or another TLS backend).
|
||||
|
||||
```cpp
|
||||
cli.set_digest_auth("alice", "s3cret");
|
||||
```
|
||||
|
||||
> To call an API with a Bearer token, see C06. Call an API with a Bearer Token.
|
||||
50
docs-src/pages/en/cookbook/c06-bearer-token.md
Normal file
50
docs-src/pages/en/cookbook/c06-bearer-token.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "C06. Call an API with a Bearer Token"
|
||||
order: 6
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
For Bearer token authentication — common in OAuth 2.0 and modern Web APIs — use `set_bearer_token_auth()`. Pass the token and cpp-httplib builds the `Authorization: Bearer <token>` header for you.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_bearer_token_auth("eyJhbGciOiJIUzI1NiIs...");
|
||||
|
||||
auto res = cli.Get("/me");
|
||||
if (res && res->status == 200) {
|
||||
std::cout << res->body << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
Set it once and every subsequent request carries the token. This is the go-to pattern for token-based APIs like GitHub, Slack, or your own OAuth service.
|
||||
|
||||
## Per-request usage
|
||||
|
||||
When you want the token on only one request — or need a different token per request — pass it via headers.
|
||||
|
||||
```cpp
|
||||
httplib::Headers headers = {
|
||||
httplib::make_bearer_token_authentication_header(token),
|
||||
};
|
||||
auto res = cli.Get("/me", headers);
|
||||
```
|
||||
|
||||
`make_bearer_token_authentication_header()` builds the `Authorization` header for you.
|
||||
|
||||
## Refresh the token
|
||||
|
||||
When a token expires, just call `set_bearer_token_auth()` again with the new one.
|
||||
|
||||
```cpp
|
||||
if (res && res->status == 401) {
|
||||
auto new_token = refresh_token();
|
||||
cli.set_bearer_token_auth(new_token);
|
||||
res = cli.Get("/me");
|
||||
}
|
||||
```
|
||||
|
||||
> **Warning:** A Bearer token is itself a credential. Always send it over HTTPS, and never hard-code it into source or config files.
|
||||
|
||||
> To set multiple headers at once, see C03. Set Default Headers.
|
||||
52
docs-src/pages/en/cookbook/c07-multipart-upload.md
Normal file
52
docs-src/pages/en/cookbook/c07-multipart-upload.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "C07. Upload a File as Multipart Form Data"
|
||||
order: 7
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
When you want to send a file the same way an HTML `<input type="file">` does, use multipart form data (`multipart/form-data`). cpp-httplib offers two APIs — `UploadFormDataItems` and `FormDataProviderItems` — and you pick between them based on **file size**.
|
||||
|
||||
## Send a small file
|
||||
|
||||
Read the file into memory first, then send it. For small files, this is the simplest path.
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
std::ifstream ifs("avatar.png", std::ios::binary);
|
||||
std::string content((std::istreambuf_iterator<char>(ifs)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
httplib::UploadFormDataItems items = {
|
||||
{"name", "Alice", "", ""},
|
||||
{"avatar", content, "avatar.png", "image/png"},
|
||||
};
|
||||
|
||||
auto res = cli.Post("/upload", items);
|
||||
```
|
||||
|
||||
Each `UploadFormData` entry is `{name, content, filename, content_type}`. For plain text fields, leave `filename` and `content_type` empty.
|
||||
|
||||
## Stream a large file
|
||||
|
||||
To avoid loading the whole file into memory, use `make_file_provider()`. It reads the file in chunks as it sends — so even huge files won't blow up your memory footprint.
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
httplib::UploadFormDataItems items = {
|
||||
{"name", "Alice", "", ""},
|
||||
};
|
||||
|
||||
httplib::FormDataProviderItems provider_items = {
|
||||
httplib::make_file_provider("video", "large-video.mp4", "", "video/mp4"),
|
||||
};
|
||||
|
||||
auto res = cli.Post("/upload", httplib::Headers{}, items, provider_items);
|
||||
```
|
||||
|
||||
The arguments to `make_file_provider()` are `(form name, file path, file name, content type)`. Leave the file name empty to use the file path as-is.
|
||||
|
||||
> **Note:** You can mix `UploadFormDataItems` and `FormDataProviderItems` in the same request. A clean split is: text fields in `UploadFormDataItems`, files in `FormDataProviderItems`.
|
||||
|
||||
> To show upload progress, see C11. Use the progress callback.
|
||||
34
docs-src/pages/en/cookbook/c08-post-file-body.md
Normal file
34
docs-src/pages/en/cookbook/c08-post-file-body.md
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
title: "C08. POST a File as Raw Binary"
|
||||
order: 8
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
Sometimes you want to send a file's contents as the request body directly — no multipart wrapping. This is common for S3-compatible APIs or endpoints that take raw image data. For this, use `make_file_body()`.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://storage.example.com");
|
||||
|
||||
auto [size, provider] = httplib::make_file_body("backup.tar.gz");
|
||||
if (size == 0) {
|
||||
std::cerr << "Failed to open file" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto res = cli.Put("/bucket/backup.tar.gz", size,
|
||||
provider, "application/gzip");
|
||||
```
|
||||
|
||||
`make_file_body()` returns a pair of file size and a `ContentProvider`. Pass them to `Post()` or `Put()` and the file contents flow straight into the request body.
|
||||
|
||||
The `ContentProvider` reads the file in chunks, so even huge files never sit fully in memory.
|
||||
|
||||
## When the file can't be opened
|
||||
|
||||
If the file can't be opened, `make_file_body()` returns `size` as `0` and `provider` as an empty function object. Sending that would produce garbage — always check `size` first.
|
||||
|
||||
> **Warning:** `make_file_body()` needs to fix the Content-Length up front, so it reads the file size ahead of time. If the file size might change mid-upload, this API isn't the right fit.
|
||||
|
||||
> To send the file as multipart form data instead, see C07. Upload a File as Multipart Form Data.
|
||||
47
docs-src/pages/en/cookbook/c09-chunked-upload.md
Normal file
47
docs-src/pages/en/cookbook/c09-chunked-upload.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "C09. Send the Body with Chunked Transfer"
|
||||
order: 9
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
When you don't know the body size up front — for data generated on the fly or piped from another stream — use `ContentProviderWithoutLength`. The client sends the body with HTTP chunked transfer encoding.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
auto res = cli.Post("/stream",
|
||||
[&](size_t offset, httplib::DataSink &sink) {
|
||||
std::string chunk = produce_next_chunk();
|
||||
if (chunk.empty()) {
|
||||
sink.done(); // done sending
|
||||
return true;
|
||||
}
|
||||
return sink.write(chunk.data(), chunk.size());
|
||||
},
|
||||
"application/octet-stream");
|
||||
```
|
||||
|
||||
The lambda's job is just: produce the next chunk and send it with `sink.write()`. When there's no more data, call `sink.done()` and you're finished.
|
||||
|
||||
## When the size is known
|
||||
|
||||
If you **do** know the total size ahead of time, use the `ContentProvider` overload (taking `size_t offset, size_t length, DataSink &sink`) and pass the total size as well.
|
||||
|
||||
```cpp
|
||||
size_t total_size = get_total_size();
|
||||
|
||||
auto res = cli.Post("/upload", total_size,
|
||||
[&](size_t offset, size_t length, httplib::DataSink &sink) {
|
||||
auto data = read_range(offset, length);
|
||||
return sink.write(data.data(), data.size());
|
||||
},
|
||||
"application/octet-stream");
|
||||
```
|
||||
|
||||
With a known size, the request carries a Content-Length header — so the server can show progress. Prefer this form when you can.
|
||||
|
||||
> **Detail:** `sink.write()` returns a `bool` indicating whether the write succeeded. If it returns `false`, the connection is gone — return `false` from the lambda to stop.
|
||||
|
||||
> If you're just sending a file, `make_file_body()` is easier. See C08. POST a File as Raw Binary.
|
||||
52
docs-src/pages/en/cookbook/c10-stream-response.md
Normal file
52
docs-src/pages/en/cookbook/c10-stream-response.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "C10. Receive a Response as a Stream"
|
||||
order: 10
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
To receive a response body chunk by chunk, use a `ContentReceiver`. It's the obvious choice for large files, but it's equally handy for NDJSON (newline-delimited JSON) or log streams where you want to start processing data as it arrives.
|
||||
|
||||
## Process each chunk
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
auto res = cli.Get("/logs/stream",
|
||||
[](const char *data, size_t len) {
|
||||
std::cout.write(data, len);
|
||||
std::cout.flush();
|
||||
return true; // return false to stop receiving
|
||||
});
|
||||
```
|
||||
|
||||
Data arrives in the lambda in the order it's received from the server. Return `false` from the callback to stop the download partway through.
|
||||
|
||||
## Parse NDJSON line by line
|
||||
|
||||
Here's a buffered approach for processing newline-delimited JSON one line at a time.
|
||||
|
||||
```cpp
|
||||
std::string buffer;
|
||||
|
||||
auto res = cli.Get("/events",
|
||||
[&](const char *data, size_t len) {
|
||||
buffer.append(data, len);
|
||||
size_t pos;
|
||||
while ((pos = buffer.find('\n')) != std::string::npos) {
|
||||
auto line = buffer.substr(0, pos);
|
||||
buffer.erase(0, pos + 1);
|
||||
if (!line.empty()) {
|
||||
auto j = nlohmann::json::parse(line);
|
||||
handle_event(j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
```
|
||||
|
||||
Accumulate into a buffer, then pull out and parse one line each time you see a newline. This is the standard pattern for consuming a streaming API in real time.
|
||||
|
||||
> **Warning:** When you pass a `ContentReceiver`, `res->body` stays **empty**. Store or process the body inside the callback yourself.
|
||||
|
||||
> To track download progress, combine this with C11. Use the Progress Callback.
|
||||
> For Server-Sent Events (SSE), see E04. Receive SSE on the Client.
|
||||
59
docs-src/pages/en/cookbook/c11-progress-callback.md
Normal file
59
docs-src/pages/en/cookbook/c11-progress-callback.md
Normal file
@@ -0,0 +1,59 @@
|
||||
---
|
||||
title: "C11. Use the Progress Callback"
|
||||
order: 11
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
To display download or upload progress, pass a `DownloadProgress` or `UploadProgress` callback. Both take two arguments: `(current, total)`.
|
||||
|
||||
## Download progress
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
auto res = cli.Get("/large-file",
|
||||
[](size_t current, size_t total) {
|
||||
auto percent = (total > 0) ? (current * 100 / total) : 0;
|
||||
std::cout << "\rDownloading: " << percent << "% ("
|
||||
<< current << "/" << total << ")" << std::flush;
|
||||
return true; // return false to abort
|
||||
});
|
||||
std::cout << std::endl;
|
||||
```
|
||||
|
||||
The callback fires each time data arrives. `total` comes from the Content-Length header — if the server doesn't send one, it may be `0`. In that case, you can't compute a percentage, so just display bytes received.
|
||||
|
||||
## Upload progress
|
||||
|
||||
Uploads work the same way. Pass an `UploadProgress` as the last argument to `Post()` or `Put()`.
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
std::string body = load_large_body();
|
||||
|
||||
auto res = cli.Post("/upload", body, "application/octet-stream",
|
||||
[](size_t current, size_t total) {
|
||||
auto percent = current * 100 / total;
|
||||
std::cout << "\rUploading: " << percent << "%" << std::flush;
|
||||
return true;
|
||||
});
|
||||
std::cout << std::endl;
|
||||
```
|
||||
|
||||
## Cancel mid-transfer
|
||||
|
||||
Return `false` from the callback to abort the transfer. This is how you wire up a "Cancel" button in a UI — flip a flag, and the next progress tick stops the transfer.
|
||||
|
||||
```cpp
|
||||
std::atomic<bool> cancelled{false};
|
||||
|
||||
auto res = cli.Get("/large-file",
|
||||
[&](size_t current, size_t total) {
|
||||
return !cancelled.load();
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** `ContentReceiver` and the progress callback can be used together. When you want to stream to a file and show progress at the same time, pass both.
|
||||
|
||||
> For a concrete example of saving to a file, see C01. Get the Response Body / Save to a File.
|
||||
50
docs-src/pages/en/cookbook/c12-timeouts.md
Normal file
50
docs-src/pages/en/cookbook/c12-timeouts.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
title: "C12. Set Timeouts"
|
||||
order: 12
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
The client has three kinds of timeouts, each set independently.
|
||||
|
||||
| Kind | API | Default | Meaning |
|
||||
| --- | --- | --- | --- |
|
||||
| Connection | `set_connection_timeout` | 300s | Time to wait for the TCP connection to establish |
|
||||
| Read | `set_read_timeout` | 300s | Time to wait for a single `recv` when receiving the response |
|
||||
| Write | `set_write_timeout` | 5s | Time to wait for a single `send` when sending the request |
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
cli.set_connection_timeout(5, 0); // 5 seconds
|
||||
cli.set_read_timeout(10, 0); // 10 seconds
|
||||
cli.set_write_timeout(10, 0); // 10 seconds
|
||||
|
||||
auto res = cli.Get("/api/data");
|
||||
```
|
||||
|
||||
Pass seconds and microseconds as two arguments. If you don't need the sub-second part, you can omit the second argument.
|
||||
|
||||
## Use `std::chrono`
|
||||
|
||||
There's also an overload that takes a `std::chrono` duration directly. It's easier to read — recommended.
|
||||
|
||||
```cpp
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
cli.set_connection_timeout(5s);
|
||||
cli.set_read_timeout(10s);
|
||||
cli.set_write_timeout(500ms);
|
||||
```
|
||||
|
||||
## Watch out for the long 300s default
|
||||
|
||||
Connection and read timeouts default to **300 seconds (5 minutes)**. If the server hangs, you'll be waiting five minutes by default. Shorter values are usually a better idea.
|
||||
|
||||
```cpp
|
||||
cli.set_connection_timeout(3s);
|
||||
cli.set_read_timeout(10s);
|
||||
```
|
||||
|
||||
> **Warning:** The read timeout covers a single receive call — not the whole request. If data keeps trickling in during a large download, the request can take half an hour without ever hitting the timeout. To cap the total request time, use C13. Set an Overall Timeout.
|
||||
42
docs-src/pages/en/cookbook/c13-max-timeout.md
Normal file
42
docs-src/pages/en/cookbook/c13-max-timeout.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: "C13. Set an Overall Timeout"
|
||||
order: 13
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
The three timeouts from C12. Set Timeouts all apply to a single `send` or `recv` call. To cap the total time a request can take, use `set_max_timeout()`.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
|
||||
cli.set_max_timeout(5000); // 5 seconds (in milliseconds)
|
||||
|
||||
auto res = cli.Get("/slow-endpoint");
|
||||
```
|
||||
|
||||
The value is in milliseconds. Connection, send, and receive together — the whole request is aborted if it exceeds the limit.
|
||||
|
||||
## Use `std::chrono`
|
||||
|
||||
There's also an overload that takes a `std::chrono` duration.
|
||||
|
||||
```cpp
|
||||
using namespace std::chrono_literals;
|
||||
cli.set_max_timeout(5s);
|
||||
```
|
||||
|
||||
## When to use which
|
||||
|
||||
`set_read_timeout` fires when no data arrives for a while. If data keeps trickling in bit by bit, it will never fire. An endpoint that sends one byte per second can make `set_read_timeout` useless no matter how short you set it.
|
||||
|
||||
`set_max_timeout` caps elapsed time, so it handles those cases cleanly. It's great for calls to external APIs or anywhere you don't want users waiting forever.
|
||||
|
||||
```cpp
|
||||
cli.set_connection_timeout(3s);
|
||||
cli.set_read_timeout(10s);
|
||||
cli.set_max_timeout(30s); // abort if the whole request takes over 30s
|
||||
```
|
||||
|
||||
> **Note:** `set_max_timeout()` works alongside the regular timeouts. Short stalls get caught by `set_read_timeout`; long-running requests get capped by `set_max_timeout`. Use both for a safety net.
|
||||
53
docs-src/pages/en/cookbook/c14-keep-alive.md
Normal file
53
docs-src/pages/en/cookbook/c14-keep-alive.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "C14. Understand Connection Reuse and Keep-Alive"
|
||||
order: 14
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
When you send multiple requests through the same `httplib::Client` instance, the TCP connection is reused automatically. HTTP/1.1 Keep-Alive does the work for you — you don't pay the TCP and TLS handshake cost on every call.
|
||||
|
||||
## Connections are reused automatically
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
auto res1 = cli.Get("/users/1");
|
||||
auto res2 = cli.Get("/users/2"); // reuses the same connection
|
||||
auto res3 = cli.Get("/users/3"); // reuses the same connection
|
||||
```
|
||||
|
||||
No special config required. Just hold on to `cli` — internally, the socket stays open across calls. The effect is especially noticeable over HTTPS, where the TLS handshake is expensive.
|
||||
|
||||
## Disable Keep-Alive explicitly
|
||||
|
||||
To force a fresh connection every time, call `set_keep_alive(false)`. Mostly useful for testing.
|
||||
|
||||
```cpp
|
||||
cli.set_keep_alive(false);
|
||||
```
|
||||
|
||||
For normal use, leave it on (the default).
|
||||
|
||||
## Don't create a `Client` per request
|
||||
|
||||
If you create a `Client` inside a loop and let it fall out of scope each iteration, you lose the reuse benefit. Create the instance outside the loop.
|
||||
|
||||
```cpp
|
||||
// Bad: a new connection every iteration
|
||||
for (auto id : ids) {
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.Get("/users/" + id);
|
||||
}
|
||||
|
||||
// Good: the connection is reused
|
||||
httplib::Client cli("https://api.example.com");
|
||||
for (auto id : ids) {
|
||||
cli.Get("/users/" + id);
|
||||
}
|
||||
```
|
||||
|
||||
## Concurrent requests
|
||||
|
||||
If you want to send requests in parallel from multiple threads, give each thread its own `Client` instance. A single `Client` uses a single TCP connection, so firing concurrent requests at the same instance ends up serializing them anyway.
|
||||
|
||||
> **Note:** If the server closes the connection after its Keep-Alive timeout, cpp-httplib reconnects and retries transparently. You don't need to handle this in application code.
|
||||
47
docs-src/pages/en/cookbook/c15-compression.md
Normal file
47
docs-src/pages/en/cookbook/c15-compression.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "C15. Enable Compression"
|
||||
order: 15
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
cpp-httplib supports compression when sending and decompression when receiving. You just need to build it with zlib or Brotli enabled.
|
||||
|
||||
## Build-time setup
|
||||
|
||||
To use compression, define these macros before including `httplib.h`:
|
||||
|
||||
```cpp
|
||||
#define CPPHTTPLIB_ZLIB_SUPPORT // gzip / deflate
|
||||
#define CPPHTTPLIB_BROTLI_SUPPORT // brotli
|
||||
#include <httplib.h>
|
||||
```
|
||||
|
||||
You'll also need to link against `zlib` or `brotli`.
|
||||
|
||||
## Compress the request body
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_compress(true);
|
||||
|
||||
std::string big_payload = build_payload();
|
||||
auto res = cli.Post("/api/data", big_payload, "application/json");
|
||||
```
|
||||
|
||||
With `set_compress(true)`, the body of POST or PUT requests gets gzipped before sending. The server needs to handle compressed bodies too.
|
||||
|
||||
## Decompress the response
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_decompress(true); // on by default
|
||||
|
||||
auto res = cli.Get("/api/data");
|
||||
std::cout << res->body << std::endl;
|
||||
```
|
||||
|
||||
With `set_decompress(true)`, the client automatically decompresses responses that arrive with `Content-Encoding: gzip` or similar. `res->body` contains the decompressed data.
|
||||
|
||||
It's on by default, so normally you don't need to do anything. Set it to `false` only if you want the raw compressed bytes.
|
||||
|
||||
> **Warning:** If you build without `CPPHTTPLIB_ZLIB_SUPPORT`, calling `set_compress()` or `set_decompress()` does nothing. If compression isn't working, check the macro definition first.
|
||||
52
docs-src/pages/en/cookbook/c16-proxy.md
Normal file
52
docs-src/pages/en/cookbook/c16-proxy.md
Normal file
@@ -0,0 +1,52 @@
|
||||
---
|
||||
title: "C16. Send Requests Through a Proxy"
|
||||
order: 16
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
To route traffic through a corporate network or a specific path, send requests via an HTTP proxy. Just pass the proxy host and port to `set_proxy()`.
|
||||
|
||||
## Basic usage
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
cli.set_proxy("proxy.internal", 8080);
|
||||
|
||||
auto res = cli.Get("/users");
|
||||
```
|
||||
|
||||
The request goes through the proxy. For HTTPS, the client uses the CONNECT method to tunnel through — no extra setup required.
|
||||
|
||||
## Proxy authentication
|
||||
|
||||
If the proxy itself requires authentication, use `set_proxy_basic_auth()` or `set_proxy_bearer_token_auth()`.
|
||||
|
||||
```cpp
|
||||
cli.set_proxy("proxy.internal", 8080);
|
||||
cli.set_proxy_basic_auth("user", "password");
|
||||
```
|
||||
|
||||
```cpp
|
||||
cli.set_proxy_bearer_token_auth("token");
|
||||
```
|
||||
|
||||
If cpp-httplib is built with OpenSSL (or another TLS backend), you can also use Digest authentication for the proxy.
|
||||
|
||||
```cpp
|
||||
cli.set_proxy_digest_auth("user", "password");
|
||||
```
|
||||
|
||||
## Combine with end-server authentication
|
||||
|
||||
Proxy authentication is separate from authenticating to the end server (C05, C06). When both are needed, set both.
|
||||
|
||||
```cpp
|
||||
cli.set_proxy("proxy.internal", 8080);
|
||||
cli.set_proxy_basic_auth("proxy-user", "proxy-pass");
|
||||
|
||||
cli.set_bearer_token_auth("api-token"); // for the end server
|
||||
```
|
||||
|
||||
`Proxy-Authorization` is sent to the proxy, `Authorization` to the end server.
|
||||
|
||||
> **Note:** cpp-httplib does not read `HTTP_PROXY` or `HTTPS_PROXY` environment variables automatically. If you want to honor them, read them in your application and pass the values to `set_proxy()`.
|
||||
63
docs-src/pages/en/cookbook/c17-error-codes.md
Normal file
63
docs-src/pages/en/cookbook/c17-error-codes.md
Normal file
@@ -0,0 +1,63 @@
|
||||
---
|
||||
title: "C17. Handle Error Codes"
|
||||
order: 17
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
`cli.Get()`, `cli.Post()`, and friends return a `Result`. When the request fails — can't reach the server, times out, etc. — the result is "falsy". To get the specific reason, use `Result::error()`.
|
||||
|
||||
## Basic check
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("http://localhost:8080");
|
||||
auto res = cli.Get("/api/data");
|
||||
|
||||
if (res) {
|
||||
// the request was sent and a response came back
|
||||
std::cout << "status: " << res->status << std::endl;
|
||||
} else {
|
||||
// the network layer failed
|
||||
std::cerr << "error: " << httplib::to_string(res.error()) << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
Use `if (res)` to check success. On failure, `res.error()` returns a `httplib::Error` enum value. Pass it to `to_string()` to get a human-readable description.
|
||||
|
||||
## Common errors
|
||||
|
||||
| Value | Meaning |
|
||||
| --- | --- |
|
||||
| `Error::Connection` | Couldn't connect to the server |
|
||||
| `Error::ConnectionTimeout` | Connection timeout (`set_connection_timeout`) |
|
||||
| `Error::Read` / `Error::Write` | Error during send or receive |
|
||||
| `Error::Timeout` | Overall timeout set via `set_max_timeout` |
|
||||
| `Error::ExceedRedirectCount` | Too many redirects |
|
||||
| `Error::SSLConnection` | TLS handshake failed |
|
||||
| `Error::SSLServerVerification` | Server certificate verification failed |
|
||||
| `Error::Canceled` | A progress callback returned `false` |
|
||||
|
||||
## Network errors vs. HTTP errors
|
||||
|
||||
Even when `res` is truthy, the HTTP status code can still be 4xx or 5xx. These are two different things.
|
||||
|
||||
```cpp
|
||||
auto res = cli.Get("/api/data");
|
||||
if (!res) {
|
||||
// network error (no response received at all)
|
||||
std::cerr << "network error: " << httplib::to_string(res.error()) << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (res->status >= 400) {
|
||||
// HTTP error (response received, but the status is bad)
|
||||
std::cerr << "http error: " << res->status << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// success
|
||||
std::cout << res->body << std::endl;
|
||||
```
|
||||
|
||||
Keep them separated in your head: network-layer errors go through `res.error()`, HTTP-level errors through `res->status`.
|
||||
|
||||
> To dig deeper into SSL-related errors, see C18. Handle SSL Errors.
|
||||
53
docs-src/pages/en/cookbook/c18-ssl-errors.md
Normal file
53
docs-src/pages/en/cookbook/c18-ssl-errors.md
Normal file
@@ -0,0 +1,53 @@
|
||||
---
|
||||
title: "C18. Handle SSL Errors"
|
||||
order: 18
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
When an HTTPS request fails, `res.error()` returns values like `Error::SSLConnection` or `Error::SSLServerVerification`. Sometimes that's not enough to pinpoint the cause. That's where `Result::ssl_error()` and `Result::ssl_backend_error()` help.
|
||||
|
||||
## Get the SSL error details
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
auto res = cli.Get("/");
|
||||
|
||||
if (!res) {
|
||||
auto err = res.error();
|
||||
std::cerr << "error: " << httplib::to_string(err) << std::endl;
|
||||
|
||||
if (err == httplib::Error::SSLConnection ||
|
||||
err == httplib::Error::SSLServerVerification) {
|
||||
std::cerr << "ssl_error: " << res.ssl_error() << std::endl;
|
||||
std::cerr << "ssl_backend_error: " << res.ssl_backend_error() << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`ssl_error()` returns the error code from the SSL library (e.g., OpenSSL's `SSL_get_error()`). `ssl_backend_error()` gives you the backend's more detailed error value — for OpenSSL, that's `ERR_get_error()`.
|
||||
|
||||
## Format OpenSSL errors as strings
|
||||
|
||||
When you have a value from `ssl_backend_error()`, pass it to OpenSSL's `ERR_error_string()` to get a readable message.
|
||||
|
||||
```cpp
|
||||
#include <openssl/err.h>
|
||||
|
||||
if (res.ssl_backend_error() != 0) {
|
||||
char buf[256];
|
||||
ERR_error_string_n(res.ssl_backend_error(), buf, sizeof(buf));
|
||||
std::cerr << "openssl: " << buf << std::endl;
|
||||
}
|
||||
```
|
||||
|
||||
## Common causes
|
||||
|
||||
| Symptom | Usual suspect |
|
||||
| --- | --- |
|
||||
| `SSLServerVerification` | CA certificate path isn't configured, or the cert is self-signed |
|
||||
| `SSLServerHostnameVerification` | The cert's CN/SAN doesn't match the host |
|
||||
| `SSLConnection` | TLS version mismatch, no shared cipher suite |
|
||||
|
||||
> **Note:** `ssl_backend_error()` was previously called `ssl_openssl_error()`. The old name is deprecated — use `ssl_backend_error()` now.
|
||||
|
||||
> To change certificate verification settings, see T02. Control SSL Certificate Verification.
|
||||
57
docs-src/pages/en/cookbook/c19-client-logger.md
Normal file
57
docs-src/pages/en/cookbook/c19-client-logger.md
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "C19. Set a Logger on the Client"
|
||||
order: 19
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
To log requests sent and responses received by the client, use `set_logger()`. If you only care about errors, there's a separate `set_error_logger()`.
|
||||
|
||||
## Log requests and responses
|
||||
|
||||
```cpp
|
||||
httplib::Client cli("https://api.example.com");
|
||||
|
||||
cli.set_logger([](const httplib::Request &req, const httplib::Response &res) {
|
||||
std::cout << req.method << " " << req.path
|
||||
<< " -> " << res.status << std::endl;
|
||||
});
|
||||
|
||||
auto res = cli.Get("/users");
|
||||
```
|
||||
|
||||
The callback you pass to `set_logger()` fires once for each completed request. You get both the request and the response as arguments — so you can log the method, path, status, headers, body, or whatever else you need.
|
||||
|
||||
## Catch errors only
|
||||
|
||||
When a network-layer error happens (like `Error::Connection`), `set_logger()` is **not** called — there's no response to log. For those cases, use `set_error_logger()`.
|
||||
|
||||
```cpp
|
||||
cli.set_error_logger([](const httplib::Error &err, const httplib::Request *req) {
|
||||
std::cerr << "error: " << httplib::to_string(err);
|
||||
if (req) {
|
||||
std::cerr << " (" << req->method << " " << req->path << ")";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
The second argument `req` can be null — it happens when the failure occurred before the request was built. Always null-check before dereferencing.
|
||||
|
||||
## Use both together
|
||||
|
||||
A nice pattern is to log successes through one, failures through the other.
|
||||
|
||||
```cpp
|
||||
cli.set_logger([](const auto &req, const auto &res) {
|
||||
std::cout << "[ok] " << req.method << " " << req.path
|
||||
<< " " << res.status << std::endl;
|
||||
});
|
||||
|
||||
cli.set_error_logger([](const auto &err, const auto *req) {
|
||||
std::cerr << "[ng] " << httplib::to_string(err);
|
||||
if (req) std::cerr << " " << req->method << " " << req->path;
|
||||
std::cerr << std::endl;
|
||||
});
|
||||
```
|
||||
|
||||
> **Note:** The log callbacks run synchronously on the same thread as the request. Heavy work inside them slows the request down — push it to a background queue if you need to do anything expensive.
|
||||
@@ -4,93 +4,93 @@ order: 0
|
||||
status: "draft"
|
||||
---
|
||||
|
||||
A collection of recipes that answer "How do I...?" questions. Each recipe is self-contained — read only what you need.
|
||||
A collection of recipes that answer "How do I...?" questions. Each recipe is self-contained — read only what you need. For an introduction to the basics, see the [Tour](../tour/).
|
||||
|
||||
## Client
|
||||
|
||||
### Basics
|
||||
- Get the response body as a string / save to a file
|
||||
- Send and receive JSON
|
||||
- Set default headers (`set_default_headers`)
|
||||
- Follow redirects (`set_follow_location`)
|
||||
- [C01. Get the response body / save to a file](c01-get-response-body)
|
||||
- [C02. Send and receive JSON](c02-json)
|
||||
- [C03. Set default headers](c03-default-headers)
|
||||
- [C04. Follow redirects](c04-follow-location)
|
||||
|
||||
### Authentication
|
||||
- Use Basic authentication (`set_basic_auth`)
|
||||
- Call an API with a Bearer token
|
||||
- [C05. Use Basic authentication](c05-basic-auth)
|
||||
- [C06. Call an API with a Bearer token](c06-bearer-token)
|
||||
|
||||
### File Upload
|
||||
- Upload a file as multipart form data (`make_file_provider`)
|
||||
- POST a file as raw binary (`make_file_body`)
|
||||
- Send the body with chunked transfer (Content Provider)
|
||||
- [C07. Upload a file as multipart form data](c07-multipart-upload)
|
||||
- [C08. POST a file as raw binary](c08-post-file-body)
|
||||
- [C09. Send the body with chunked transfer](c09-chunked-upload)
|
||||
|
||||
### Streaming & Progress
|
||||
- Receive a response as a stream
|
||||
- Use the progress callback (`DownloadProgress` / `UploadProgress`)
|
||||
- [C10. Receive a response as a stream](c10-stream-response)
|
||||
- [C11. Use the progress callback](c11-progress-callback)
|
||||
|
||||
### Connection & Performance
|
||||
- Set timeouts (`set_connection_timeout` / `set_read_timeout`)
|
||||
- Set an overall timeout (`set_max_timeout`)
|
||||
- Understand connection reuse and Keep-Alive behavior
|
||||
- Enable compression (`set_compress` / `set_decompress`)
|
||||
- Send requests through a proxy (`set_proxy`)
|
||||
- [C12. Set timeouts](c12-timeouts)
|
||||
- [C13. Set an overall timeout](c13-max-timeout)
|
||||
- [C14. Understand connection reuse and Keep-Alive behavior](c14-keep-alive)
|
||||
- [C15. Enable compression](c15-compression)
|
||||
- [C16. Send requests through a proxy](c16-proxy)
|
||||
|
||||
### Error Handling & Debugging
|
||||
- Handle error codes (`Result::error()`)
|
||||
- Handle SSL errors (`ssl_error()` / `ssl_backend_error()`)
|
||||
- Set up client logging (`set_logger` / `set_error_logger`)
|
||||
- [C17. Handle error codes](c17-error-codes)
|
||||
- [C18. Handle SSL errors](c18-ssl-errors)
|
||||
- [C19. Set up client logging](c19-client-logger)
|
||||
|
||||
## Server
|
||||
|
||||
### Basics
|
||||
- Register GET / POST / PUT / DELETE handlers
|
||||
- Receive JSON requests and return JSON responses
|
||||
- Use path parameters (`/users/:id`)
|
||||
- Set up a static file server (`set_mount_point`)
|
||||
- S01. Register GET / POST / PUT / DELETE handlers
|
||||
- S02. Receive JSON requests and return JSON responses
|
||||
- S03. Use path parameters (`/users/:id`)
|
||||
- S04. Set up a static file server (`set_mount_point`)
|
||||
|
||||
### Streaming & Files
|
||||
- Stream a large file in the response (`ContentProvider`)
|
||||
- Return a file download response (`Content-Disposition`)
|
||||
- Receive multipart data as a stream (`ContentReader`)
|
||||
- Compress responses (gzip)
|
||||
- S05. Stream a large file in the response (`ContentProvider`)
|
||||
- S06. Return a file download response (`Content-Disposition`)
|
||||
- S07. Receive multipart data as a stream (`ContentReader`)
|
||||
- S08. Compress responses (gzip)
|
||||
|
||||
### Handler Chain
|
||||
- Add pre-processing to all routes (Pre-routing handler)
|
||||
- Add response headers with a Post-routing handler (CORS, etc.)
|
||||
- Authenticate per route with a Pre-request handler (`matched_route`)
|
||||
- Pass data between handlers with `res.user_data`
|
||||
- S09. Add pre-processing to all routes (Pre-routing handler)
|
||||
- S10. Add response headers with a Post-routing handler (CORS, etc.)
|
||||
- S11. Authenticate per route with a Pre-request handler (`matched_route`)
|
||||
- S12. Pass data between handlers with `res.user_data`
|
||||
|
||||
### Error Handling & Debugging
|
||||
- Return custom error pages (`set_error_handler`)
|
||||
- Catch exceptions (`set_exception_handler`)
|
||||
- Log requests (Logger)
|
||||
- Detect client disconnection (`req.is_connection_closed()`)
|
||||
- S13. Return custom error pages (`set_error_handler`)
|
||||
- S14. Catch exceptions (`set_exception_handler`)
|
||||
- S15. Log requests (Logger)
|
||||
- S16. Detect client disconnection (`req.is_connection_closed()`)
|
||||
|
||||
### Operations & Tuning
|
||||
- Assign a port dynamically (`bind_to_any_port`)
|
||||
- Control startup order with `listen_after_bind`
|
||||
- Shut down gracefully (`stop()` and signal handling)
|
||||
- Tune Keep-Alive (`set_keep_alive_max_count` / `set_keep_alive_timeout`)
|
||||
- Configure the thread pool (`new_task_queue`)
|
||||
- S17. Assign a port dynamically (`bind_to_any_port`)
|
||||
- S18. Control startup order with `listen_after_bind`
|
||||
- S19. Shut down gracefully (`stop()` and signal handling)
|
||||
- S20. Tune Keep-Alive (`set_keep_alive_max_count` / `set_keep_alive_timeout`)
|
||||
- S21. Configure the thread pool (`new_task_queue`)
|
||||
- S22. Communicate over Unix domain sockets (`set_address_family(AF_UNIX)`)
|
||||
|
||||
## TLS / Security
|
||||
|
||||
- Choosing between OpenSSL, mbedTLS, and wolfSSL (build-time `#define` differences)
|
||||
- Control SSL certificate verification (disable, custom CA, custom callback)
|
||||
- Use a custom certificate verification callback (`set_server_certificate_verifier`)
|
||||
- Set up an SSL/TLS server (certificate and private key)
|
||||
- Configure mTLS (mutual TLS with client certificates)
|
||||
- Access the peer certificate on the server (`req.peer_cert()` / SNI)
|
||||
- T01. Choosing between OpenSSL, mbedTLS, and wolfSSL (build-time `#define` differences)
|
||||
- T02. Control SSL certificate verification (disable, custom CA, custom callback)
|
||||
- T03. Set up an SSL/TLS server (certificate and private key)
|
||||
- T04. Configure mTLS (mutual TLS with client certificates)
|
||||
- T05. Access the peer certificate on the server (`req.peer_cert()` / SNI)
|
||||
|
||||
## SSE
|
||||
|
||||
- Implement an SSE server
|
||||
- Use event names to distinguish event types
|
||||
- Handle reconnection (`Last-Event-ID`)
|
||||
- Receive SSE events on the client
|
||||
- E01. Implement an SSE server
|
||||
- E02. Use event names to distinguish event types
|
||||
- E03. Handle reconnection (`Last-Event-ID`)
|
||||
- E04. Receive SSE events on the client
|
||||
|
||||
## WebSocket
|
||||
|
||||
- Implement a WebSocket echo server and client
|
||||
- Configure heartbeats (`set_websocket_ping_interval` / `CPPHTTPLIB_WEBSOCKET_PING_INTERVAL_SECOND`)
|
||||
- Handle connection close
|
||||
- Send and receive binary frames
|
||||
- W01. Implement a WebSocket echo server and client
|
||||
- W02. Configure heartbeats (`set_websocket_ping_interval`)
|
||||
- W03. Handle connection close
|
||||
- W04. Send and receive binary frames
|
||||
|
||||
Reference in New Issue
Block a user