Boost C++ Libraries

Next

Chapter 1. Boost.Taar 1.1.0

Reza Jahanbakhshi

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

Table of Contents

Introduction
Quick Start
Concepts
Examples
Design Notes
Limitations and Roadmap
Quick Reference
Reference
Release Notes

Overview

Boost.Taar is a portable, header-only C++20 library that provides ergonomic building blocks for writing HTTP servers on top of Boost.Asio, Boost.Beast, Boost.URL, and Boost.JSON. It is licensed under the Boost Software License, Version 1.0.

The library is organised around four ideas:

On top of the HTTP layer sit three optional protocol layers that reuse the same callables and state: a JSON-RPC 2.0 endpoint, a Model Context Protocol (MCP) server endpoint, and an OpenAPI 3.1 document generator. Handlers may also receive per-connection or per-session state objects by simply declaring a parameter of the right type.

All of it is asynchronous by construction. The session and the TCP acceptor are C++20 coroutines, and a request handler can itself be a coroutine: declare its result as taar::awaitable<T> and co_await a database query, an outbound HTTP call, or a timer before responding, without ever blocking the connection. Long or open-ended responses can stream incrementally through an async generator. See the asynchronous model for the full picture.

At a glance

Each block below is a complete registration; the last one runs them. They share these declarations:

namespace taar = boost::taar;
namespace http = boost::beast::http;
using taar::matcher::method;
using taar::matcher::target;

taar::session::http http_session;

REST. Path placeholders are parsed into the callable's parameter types and the result becomes the response; here a JSON object (Tour):

http_session.register_request_handler(
    method == http::verb::get && target == "/api/sum/{a}/{b}",
    taar::handler::rest(
        [](int a, int b) { return boost::json::value {{"result", a + b}}; },
        taar::handler::path_arg("a"),
        taar::handler::path_arg("b")));

JSON-RPC 2.0. An endpoint owns its methods; batches, notifications and error codes are handled for you:

taar::jsonrpc::endpoint<> rpc;
rpc.method("sum", [](int a, int b) { return a + b; },
    taar::jsonrpc::rpc_arg("a"), taar::jsonrpc::rpc_arg("b"));

http_session.register_request_handler(
    method == http::verb::post && target == "/rpc", rpc.handler());

MCP. Tools get an input schema derived from their parameters, and a stateful endpoint hands each client session its own state:

struct notes { std::vector<std::string> items; };
taar::mcp::endpoint<notes> mcp {{.name = "notes", .version = "1.0.0"}};
mcp.tool("notes/add", "Append a note",
    [](notes& n, std::string text) { n.items.push_back(text); return n.items.size(); },
    taar::mcp::mcp_arg("text", "The note"));

http_session.register_request_handler(
    method == http::verb::get && target == "/mcp", mcp.stream_handler());
http_session.register_request_handler(target == "/mcp", mcp.handler());

Static files. A directory served with validators, ranges, precompressed variants and a single-page-app fallback:

taar::handler::htdocs_options site;
site.root = "./public";
site.fallback_document = "index.html";
http_session.register_request_handler(
    method == http::verb::get && target == "/{*path}",
    taar::handler::htdocs {std::move(site)});

Running it. One coroutine accepts connections for every route above; signals.emit() shuts it down gracefully:

boost::asio::io_context io;
taar::cancellation_signals signals;
boost::asio::co_spawn(
    io,
    taar::server::tcp("0.0.0.0", "8080", http_session, signals),
    boost::asio::detached);
io.run();

The same callables can also be documented with the OpenAPI generator, served over TLS, or streamed as chunked responses. See the Quick Start for a walk-through, the Examples for complete programs, and the Quick Reference for every public name.


Next