https://github.com/SSARCandy/cofetch
Chainable, high-performance async HTTP client for modern C++ event loops.
https://github.com/SSARCandy/cofetch
asio async coroutines cpp17 cpp20 curl http-client libcurl
Last synced: 1 day ago
JSON representation
Chainable, high-performance async HTTP client for modern C++ event loops.
- Host: GitHub
- URL: https://github.com/SSARCandy/cofetch
- Owner: SSARCandy
- License: mit
- Created: 2023-03-12T09:12:42.000Z (over 3 years ago)
- Default Branch: main
- Last Pushed: 2026-07-10T07:19:40.000Z (19 days ago)
- Last Synced: 2026-07-10T07:19:45.272Z (19 days ago)
- Topics: asio, async, coroutines, cpp17, cpp20, curl, http-client, libcurl
- Language: C++
- Homepage: http://ssarcandy.tw/cofetch/
- Size: 270 KB
- Stars: 2
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-cpp-with-stars - cofetch - 07-13 | (Networking)
- awesome-cpp - cofetch - Chainable async HTTP client built on libcurl's multi interface and ASIO. Callbacks, coroutines and futures from one implementation. [MIT] (Networking)
README
# cofetch: async HTTP client for C++.
[](https://github.com/SSARCandy/cofetch/actions/workflows/ci.yml)
[](https://codecov.io/gh/SSARCandy/cofetch)
[](https://ssarcandy.tw/cofetch)
[](LICENSE)
Requests **build as a chain**, setters flow off `request()` and the HTTP verb fires the transfer, without blocking the thread:
```cpp
http
.request("https://api.example.com/orders")
.headers({"content-type: application/json"})
.body(R"({"qty": 1})")
.timeout(std::chrono::seconds(2))
.post([](std::error_code ec, cofetch::Response res) {
// transport errors in ec; HTTP status in res.http_code_
});
```
On C++20 that includes coroutines: dependent requests in linear code, no nesting:
```cpp
const auto user = co_await http.async_get(api + "/user", asio::use_awaitable);
const auto posts = co_await http.async_post(api + "/posts", user.data_, asio::use_awaitable);
```
## Why cofetch
- **Header-only.** One file, bring libcurl (linked) and ASIO (include path), then `#include `.
- **Chainable syntax.** Build a request with setters, fire it with the HTTP verb. The same chain works with callbacks, futures, or coroutines.
- **ASIO-native.** Requests run on the `asio::io_context`. Drive it with `run()`, or `poll()` it from a busy loop that must never block.
- **C++17-friendly.** Works on C++17; C++20 adds the `co_await` interface.
- **libcurl underneath.** HTTP/1.1 and HTTP/2 multiplexing, TLS, compression, connection pooling, redirects. For anything cofetch does not wrap, `.curl([](CURL* h) { ... })` exposes the raw handle per request.
## Benchmarks
Same workload for every client: 20,000 GETs against a local nginx. [cpr](https://github.com/libcpr/cpr) and [cpp-httplib](https://github.com/yhirose/cpp-httplib) are synchronous (one request per thread); cofetch keeps 100 in flight requests on a single thread, and wins every scenario: **+35%** single-thread throughput over the fastest sync client, **+31%** with one event loop per core against equal-sized thread pools, **+16%** on sequential chains driven from a busy-poll loop.

Exact numbers, environment, and how to reproduce:
[bench/README.md](bench/README.md).
## Quick start
```cpp
#include
#include
#include
int main() {
asio::io_context io;
cofetch::Client http(io);
http
.request("https://postman-echo.com/post")
.body("hello=cofetch")
.post([](std::error_code ec, cofetch::Response res) {
if (ec) {
std::cerr << ec.message() << "\n"; // DNS, TLS, timeout...
return;
}
std::cout << res.http_code_ << " " << res.data_ << "\n";
});
io.run(); // or io.poll() from your own loop — examples/example_reactors.cpp
}
```
On C++20 the same flow reads linearly with `co_await` (`examples/example_coroutine.cpp`); the full set lives in [examples/](examples/README.md). Every public symbol at a glance: the [API reference](docs/api-reference.md) cheatsheet.
Prefer a plain value? `cofetch::Request` holds the same fields and fires later via `http.async_perform(std::move(req), token)`.
## Installation
Requirements:
- libcurl ≥ 7.55 (dev headers)
- C++17 compiler (the `co_await` interface needs C++20)
- [ASIO](https://github.com/chriskohlhoff/asio) or Boost.Asio, via `-DCOFETCH_USE_BOOST_ASIO=ON`
### vcpkg
cofetch is in the [vcpkg registry](https://github.com/microsoft/vcpkg/tree/master/ports/cofetch); `asio` and `curl` come along as dependencies, so this is the turnkey path:
```bash
vcpkg install cofetch
```
```cmake
find_package(cofetch CONFIG REQUIRED)
target_link_libraries(your_app PRIVATE cofetch::cofetch)
```
### CMake (FetchContent)
```cmake
include(FetchContent)
FetchContent_Declare(cofetch
GIT_REPOSITORY https://github.com/SSARCandy/cofetch.git
GIT_TAG v0.1.2
)
FetchContent_MakeAvailable(cofetch)
target_link_libraries(your_app PRIVATE cofetch::cofetch)
```
cofetch does not impose an ASIO on consumers: point it at yours (any include path providing ``), or enable the vendored submodule with `-DCOFETCH_USE_VENDORED_ASIO=ON`.
### Manual
Copy `include/cofetch.h`, add asio to your include path, link `libcurl`.
## What cofetch is not
- **Not a server.** Client only. For a server (or a tiny zero-dependency client), use [cpp-httplib](https://github.com/yhirose/cpp-httplib).
- **Not thread-safe.** One `Client` per `io_context` thread by design, no locks on the hot path. The client must outlive its in-flight requests.
## Development
```bash
git submodule update --init # asio + googletest (dev only)
./build.sh -t # Debug build + tests + coverage
./linter.sh # clang-format check (v19 pinned in CI)
doxygen Doxyfile # API reference -> docs/api/html/
```