| Docs | GitHub Actions | Drone | Codecov | |
|---|---|---|---|---|
master |
||||
develop |
Coroutines-Only Networking for Modern C++
Boost.Asio transformed C++ networking. It also burdened every call site with templates, platform headers, and implementation machinery, yet its quarter century of field experience produced capable, working abstractions.
The C++26 std::execution API offers a different model, designed to support heterogenous computing. Our research indicates it optimizes for the wrong constraints: TCP servers don't run on GPUs. Networking demands zero-allocation steady-state, type erasure without indirection, and ABI stability across (e.g.) SSL implementations. C++26 delivers things that networking doesn't need, and none of the things that networking does need.
Peter Dimov insists we begin with what developers actually write:
auto [ec, n] = co_await sock.read_some( buf );One line. No platform headers. No completion handlers. Just coroutines. This simplicity drives every architectural decision:
- Clean call sites — no platform or implementation headers leak through
- Extensible concepts — user-defined buffer sequences and executors; stream wrappers without templates
- Zero-alloc steady state — no coroutine frame allocations in hot paths
- Minimal template exposure — implementation details stay in the translation unit
- Allocation-free type erasure — user types preserved without small buffers or malloc
- Fine-grained execution control — strands, processor affinity, priority scheduling
Boost.Asio gave us the field experience. These design choices reflect what we learned.
Beast2 is an umbrella term for a family of libraries that work together:
Boost.Capy — The execution foundation, optimized for async I/O. Core types: task<T>, thread_pool, strand, coro_lock. Tasks propagate executor context through await_suspend automatically, guaranteeing correct executor affinity. Provides allocation-free type erasure, frame allocators for coroutine memory, automatic stop token propagation, and buffer algorithms. This should go into the C++ Standard Library.
Boost.Corosio — Coroutine-only portable networking and I/O that wrap per-platform implementations. Every operation returns an awaitable; no callbacks. Core types: socket, acceptor, resolver, strand, io_context, SSL stream wrappers (WolfSSL and OpenSSL), non-templated streams, scatter/gather buffers, native std::stop_token cancellation.
Boost.Http — Sans-I/O HTTP/1.1 with strict RFC compliance. Protocol logic isolated from I/O for testability and framework independence. Core types: request, response, parser, serializer, router. Non-templated containers, memory-bounded parsers, body streaming, Express.js-style routing, automatic gzip/deflate/brotli application, and an extensive collection of HTTP "middleware" algorithms such as multipart/form processing, cookie management, bcrypt, and more.
Boost.Websocket — WebSocket algorithms and structures. Same philosophy.
Boost.Beast2 — High-level HTTP and WebSocket servers. Express.js-style routing, multithreaded, idiomatic C++.
Boost.Burl — High-level HTTP client. The features of curl, the ergonomics of coroutines, and the design sensibility of Python Requests.
Each of these libraries is built for direct use. Boost.Capy can be standardized without risky socket or SSL decisions. Combine as needed.
Beast2 continues the Boost tradition of innovation through working code.
capy::task coroutine frames reuse memory (delete before dispatch). Type erasure is achieved without small-buffer optimizations or malloc. User types are preserved through the abstraction boundary. Key insight:
The coroutine frame allocation we cannot avoid, pays for all the type-erasure we need.
Boost.Asio:
template< class AsyncStream, class CompletionToken >
auto do_io( AsyncStream&, CompletionToken&& );Boost.Corosio:
auto do_io( capy::any_stream& ); // returns awaitableNo loss of generality. Buffer sequences and executors remain concept-driven. Stream wrappers require no templates. The drawbacks of std::execution can be seen in a single line:
connect( sndr, rcvr ); // C++26, unavoidable templatesHere, the compiler must see all the types (or perform expensive type-erasure, which is inconvenient at this call site and uncompetitive). No encapsulation. Long compile times. ABI fragility. Unfriendly to network programs.
Boost.Asio:
template< class AsyncStream >
class ssl_stream
{
AsyncStream stream_;
...Boost.Corosio:
class wolfssl_stream : public tls_stream
{
io_stream& wrapped_stream_;
...Stream algorithms see tls_stream& not wolfssl_stream& and can be written as ordinary functions (non-templates). Link your HTTP library against Corosio and WolfSSL. Relink against OpenSSL. No recompilation. SSL implementation becomes a runtime decision with zero ABI consequence. Have both in the same executable if you want (maybe useful for researchers).
There is only one configuration of the library:
Boost.Asio:
#define BOOST_ASIO_NO_SCHEDULER_MUTEX 1
{
asio::io_context ioc;
}Boost.Corosio:
{
corosio::unsafe_io_context uioc; // no macro needed
corosio::io_context ioc; // use one or both, linker removes whats not used
}One library. One object file. Runtime configuration.
No platform headers at call sites. No implementation structures in user code. Translation-unit isolation by default. ABI is stable by design, and thus if adopted in the standard can be evolved rather than freezing.
Unlike its predecessor, Beast2 is full-featured and high-level. Everything is in scope:
app.use( "/", serve_static( "C:\\Users\\Vinnie\\my-website" ) );Express.js patterns. Multithreaded execution. C++ performance.
The Beast2 family of libraries responds to the pain points related by users and delivers everything they want. It demonstrates through working code what modern C++ networking requires: coroutine-native design, fast compilation without loss of generality, ABI stability across implementations, and good performance, while preserving every capability that made Boost.Asio great.
This is the future of C++ networking.
Boost.Beast2 is under active development. API subject to change.