![]() |
cxx_std_20
as a usage requirement, so a consumer can build with a newer standard.
The Conan recipe checks for C++20 at least.
<format>).
std::ranges::find
of a string literal in a vector of strings (the 405 Allow
computation and the CORS wildcard check).
test/cmake_subdir_test consumes taar with
add_subdirectory next to
its dependencies' sources, and test/cmake_install_test
with find_package(Boost CONFIG
COMPONENTS taar) after the superproject's cmake --install. Both build test/quick.cpp,
which serves and requests one REST route and is also a b2 test. The add_subdirectory case now configures
taar as a Boost library instead of looking for an installed Boost.
find_package(boost-taar) after cmake
--install
provides the target boost-taar::boost-taar,
also reachable as Boost::taar.
test/cmake_package_test checks it in CI. Tests
and examples are built by default only when taar is the top-level project
(BOOST_TAAR_BUILD_TESTS,
BOOST_TAAR_BUILD_EXAMPLES),
so FetchContent and CPM users get just the library. Adding taar that way
before Boost is found stops with a message saying to call find_package(Boost ...)
first.
basic_http::set_idle_timeout
(waiting for a request's first byte; silent close; default 60 s), set_header_timeout (first byte to end
of header; 408 Request
Timeout with Connection: close,
then close; 30 s), set_body_timeout
(inactivity while a body is read, buffered or through body_reader
/ multipart_reader; 408; 60 s) and set_send_timeout
(a blocked write; close; 60 s). Zero disables one. Handler time and the
gaps between a stream's chunks do not count. A slow or idle client no longer
holds a connection indefinitely, and a stalled upload delays a graceful
shutdown by at most the body timeout. Expired timeouts are not reported
to the hard error handler. http_status_for
maps beast::error::timeout to 408.
Date
header (RFC 9110 6.6.1) to every response that does not carry one, error,
304, HEAD, chunked and protocol-error
answers included; the value is cached per second per thread. set_date_header(false) disables
it.
408; raise or disable the timeouts for clients
that need longer.
BOOST_SUPERPROJECT_VERSION defined)
CMakeLists.txt defines the INTERFACE target boost_taar with alias Boost::taar,
lists its Boost dependencies one per line (so -DBOOST_INCLUDE_LIBRARIES=taar pulls in what it needs), installs
through boost_install,
and builds the tests when BUILD_TESTING
is on. Standalone builds also get the Boost::taar
alias. build.jam lists every Boost library the headers
include.
examples/ to example/, the Boost layout.
doc/namespaces.hpp,
read only by Doxygen), deduction guides are hidden from the reference,
and the response builder's setters and a few scanner members gained descriptions.
path_arg registration
checks no longer reject every route whose matcher has a leaf before its
target ==
(such as method ==
get &&
target ==
"/users/{id}") with "not
present as a placeholder in target template """. The capture
holding the template is now selected by type.
failure::as<E>()
returns std::optional<E>,
a copy of the kept exception, instead of E
const*.
Whether std::rethrow_exception copies the exception
object is unspecified; MSVC copies it, so the returned pointer referred
to a copy destroyed on return (an access violation in test_failure_as).
E must be copy constructible.
Migration: if (auto const* e = f.as<E>())
becomes if (auto const e = f.as<E>()); e->what() and if
(f.as<E>())
read as before; comparisons with nullptr
become .has_value().
emplace_back(a,
b)
into an aggregate fails), context_store
braces an aggregate context built from arguments, a static_assert(false)
in a generic lambda is now dependent (pre-P2593 compilers fire it at definition),
and openapi::generator::describe(summary)
is a template so a braced describe({.summary
= ...})
no longer trips a clang 16 / libc++ overload-resolution failure through
std::string's constructors.
handler::htdocs rework, keeping htdocs{root, default_doc}
source compatible and adding htdocs_options.
Fixed: the served path is the route's greedy capture, not the request target
(a /static/{*path} mount no longer serves root/static/...) and the query is ignored; paths are
percent-decoded and checked segment by segment (encoded /,
backslash, NUL, . and .. answer 400, a..b.txt is served) and must stay under the
canonical root, symbolic links included; error bodies are fixed text/plain texts that never echo the target;
directories without a trailing slash are redirected (301) instead of failing
with 500; I/O errors are 500 (403 for permission denied) instead of 400;
other methods answer 405 with Allow: GET, HEAD.
htdocs features: hidden
files refused, index_files,
SPA fallback_document,
a built-in MIME table with overrides and charset, nosniff,
Last-Modified and strong ETag,
RFC 9110 conditional requests (412, 304), Cache-Control
rules with cache_rules::immutable_hashed(), single and multipart ranges with If-Range,
precompressed br/gzip siblings with Vary,
metadata and small-file memory caches shared across threads, and an on_response hook. Responses are typed
http::response<segmented_body>,
so the response filter applies.
http_date.hpp
(format and parse HTTP-dates), mime.hpp,
byte_range.hpp (Range parsing and coalescing),
entity_tag.hpp (entity tags and If-Match / If-None-Match
lists), accept_encoding.hpp
(q-values) and segmented_body.hpp
(a body of text, shared buffers and file windows).
htdocs
users: operator()
returns http::response<segmented_body>
(it converts to message_generator);
.php
is no longer served as text/html
and unknown extensions are application/octet-stream
instead of application/text;
JavaScript is text/javascript; text types carry charset=utf-8; hidden files are 404 by default; a request
for a directory without the trailing slash is redirected.
jsonrpc::endpoint::cancel and MCP notifications/cancelled
into nested waits, streams, awaitable operators and explicit checks, and
the new cancellation_server
example shows the model end to end. A stream suspended in a &&
b that shutdown cancels no longer
reports a spurious hard error (asio::multiple_exceptions
of aborted waits).
basic_http::add_interceptor(fn)
and add_interceptor(matcher, fn) run
checks on every request header, before routing, before 405/404 and HEAD handling and before the body
is read. They return taar::interceptor_result
(taar::proceed(),
taar::reject(status, text)),
directly or as an awaitable; a rejection goes through the response filter
and closes the connection when a body was declared. Raw handlers pass through
them too.
handler::guard(check, inner):
the same check on one route, after the body is read; works over typed and
streaming handlers and composes with with_session.
matcher::context::attributes
holds values that interceptors and guards attach, and handler::attribute_arg<T>() delivers them to REST handlers, JSON-RPC
methods and MCP tools.
server::tcp and server::tls
overloads taking the executor that connection strands are made from as
their first argument, so a server spawned on a strand no longer serialises
its connections. The existing overloads use the executor they are spawned
on, as before.
boost::asio::ip::tcp::socket,
a raw handler receives boost::beast::tcp_stream&& (or session::tls_stream&&, now beast::ssl_stream<beast::tcp_stream>). session::http
is basic_http<no_context,
beast::tcp_stream>.
Inside the library every operation passes its completion token explicitly.
this_coro::executor
is inside a handler, what is serialised, and that registration happens
before io.run().
taar::failure
is the one error value: an error_code
from any category, a message for the occurrence, structured data, a nested
cause, the source location and, when made from a foreign exception, that
exception. text()
renders it for people and chain() with category tags for logs; as<E>()
returns the kept exception if it is an E.
taar::result<T>
(boost::system::result<T, failure>) is what argument providers and parsers
return; value()
on a failure throws taar::exception,
the one exception type the library throws. failure::from_exception
wraps anything else exactly once.
taar::failure const&:
the session's soft and hard error handlers, the JSON-RPC error mapper and
the MCP tool error handler. The defaults map by code: http_status_for
for REST, jsonrpc::default_error_mapper, mcp::default_tool_error_handler.
New taar code error::unknown_exception; new mcp::error category (resource_not_found,
request_cancelled, client_error, unknown_session).
DELETE,
end of stdio input, a replaced stdio session) closes its event stream and
fails its pending server-to-client requests at once with errc::operation_canceled; before, they waited
for request_timeout.
session::basic_http<Context>
creates one context object per connection; handler::rest
callables request it (and any other context in scope) through leading
T&,
T const& or shared_ptr<T> parameters.
core::context_store: thread-safe keyed store
with idle expiry, per-entry asynchronous locking and minted keys. handler::with_session binds a store to a cookie
or header and injects the keyed context into the wrapped handler.
jsonrpc::endpoint<Context...>
with named parameters (rpc_arg),
batches, notifications, standard error codes and a replaceable error mapper.
mcp::endpoint<Session, Context...>
over Streamable HTTP with tools, resources, URI templates, sessions, protocol
version negotiation, Origin validation, and a server-to-client event stream
(stream_handler, notify, broadcast).
openapi::generator renders an OpenAPI 3.1 document
from routes registered with describe(...); inexpressible matchers are rejected
at compile time. register_request_handler
accepts trailing registration observers.
rest
handlers expose params(). response_from
accepts boost::json::object and boost::json::array.
core::async_channel: executor-agnostic asynchronous
queue used for server-push streams.
boost::described
structs and enums, sequences and maps (json_schema_for),
shared by OpenAPI and MCP; described types and JSON containers as REST
return values.
header_arg,
cookie_arg, query_arg) accepted on JSON-RPC methods
and MCP tools.
mcp::session_info context; mcp::endpoint::serve_stdio
transport.
core::cors policy and basic_http::set_response_filter.
body_reader,
streaming_request and the
stream_body_arg provider;
the body limit does not apply to them.
multipart/form-data: parse_multipart
and multipart_form_arg
(buffered), multipart_reader
and multipart_stream_arg
(streamed).
params
array binds to rpc_arg
providers in order.
{+path},
{#f}, {.e},
{/s}, {;p},
{?q}, {&q}, lists,
*, :n); plain {var}
no longer spans a slash.
request() waits are bounded by server_info::request_timeout;
a pending async_channel
receive keeps the io_context
alive; Content-Type parameters such as charset no longer fail body providers;
the accept loop backs off on persistent errors; the session lingers briefly
on close so a response is not reset away; cors
appends to Vary; serve_stdio handles a final line without
a newline; server::tcp/tls
refuse a temporary session at compile time; request_scope::slot
documented as the endpoint's.
T const&
parameter no longer dangles); a raw handler may take the TLS stream (no
shutdown of the moved-from stream); the close sequence runs after a cancelled
loop so TLS sends close_notify
on shutdown; a stream interrupted by shutdown is a clean close, not a soft
error; sessions stay movable after handlers are installed; registration
is validated before observers record it; path_arg
placeholders are accepted from any ||
target alternative; event ids and queue order agree under concurrent producers;
idle event streams send keepalive comments (stream_keepalive);
{;a,b}
accepts pairs in any order; async_channel
never touches a consumer's slot from a producer; key generation reuses
a per-thread random_device.
io_context executor (not nested under
the acceptor's); a slot bound to the server coroutine by its spawner stops
it again; serve_stdio drains
running calls on end of input; the undelivered MCP event queue is bounded
by stream_history; cors never reflects a wildcard with credentials;
protocol-error responses (400/413/431) go through the response filter;
shutdown closes connections after message_generator
responses too; basic_http
can be moved after installing handlers; context_store::create
draws and inserts keys under one lock; async_channel
clears the consumer's cancellation slot on completion. Stateless MCP endpoints
no longer advertise logging.
logging/setLevel with log()/log_all(), completion/complete
with complete_prompt/complete_resource, and tool_spec for tool title,
annotations and outputSchema (derived from the return
type by default).
io_context:
every connection runs on its own strand and cancellation_signals::emit
dispatches through it, fixing a crash when shutting down a server driven
by several threads. taar::awaitable
now uses any_io_executor;
completion tokens spelled with io_context::executor_type
should use taar::use_awaitable. Binding signals.slot()
to the server coroutine is no longer needed.
query_list_arg: repeated
query keys bind to a container, element by element; OpenAPI marks query_arg CSV lists as explode: false.
with_session accepts streaming
inner handlers (chunked_response,
async_generator); session_key::*::key_header.
handler::constrained argument wrapper: declarative
validation with JSON Schema keywords on REST, JSON-RPC and MCP; error::constraint_violated.
default from with_default, security schemes (add_security_scheme, set_security,
operation::security, inferred from credential headers),
components.schemas holding the described types,
referenced from operations (set_inline_schemas
to opt out).
endpoint::request,
create_message (sampling),
elicit, list_roots;
client_capabilities and
client_info on session_info; client_error;
on_client_notification.
mcp::image,
audio, embedded_resource,
tool_result and blob resources; base64_encode/base64_decode. SSE event ids with Last-Event-ID resumption.
prompt(),
prompts/list, prompts/get),
resource subscriptions (resources/subscribe,
resource_updated), list-changed
broadcasts, and */list
pagination via server_info::page_size.
jsonrpc::request_scope: per-request id, cancellation
and notification sink injectable into methods and tools; endpoint::cancel(id);
MCP notifications/cancelled and notifications/progress.
405 Method
Not Allowed
with Allow for known paths,
and HEAD served from GET routes with the body stripped; set_method_not_allowed and set_head_from_get switch them off. matcher::route_shape exposes the route analysis
the OpenAPI generator and the session share.
basic_http takes its
stream type as a template parameter; session::https
and server::tls serve HTTPS (OpenSSL required).
ROADMAP.md lists the remaining gaps.
session::http: responses follow the request's
HTTP version and keep-alive; pipelined requests are served in order; a
peer closing an idle connection is no longer reported as a hard error;
shutdown lets in-flight handlers finish and closes afterwards; mid-stream
generator exceptions reach the hard error handler.
test/compile_fail/.
include/boost/taar/ to match the Boost modular layout.
build.jam, meta/libraries.json,
and a doc/
directory built on QuickBook + Doxygen for integration with the Boost documentation
build.
Breaking changes of the executor surface clean-up:
boost::beast::tcp_stream&& stream
instead of taar::rebind_executor<boost::beast::tcp_stream>&&
stream, and a WebSocket upgrade
becomes websocket::stream<beast::tcp_stream>
ws {std::move(stream)}.
These streams use Asio's default completion token: operations that relied
on the rebound tuple default now pass taar::use_awaitable_tuple
(for auto [ec, n] =
co_await ...)
or taar::use_awaitable. HTTPS raw handlers keep
declaring session::tls_stream&&,
which is now the plain beast::ssl_stream<beast::tcp_stream>.
server::tcp
is called with a boost::asio::ip::tcp::socket
instead of taar::rebind_executor<boost::asio::ip::tcp::socket>.
session::basic_http::operator()
takes the plain socket too; a rebound socket still converts to it implicitly.
taar::awaitable<T>
is boost::asio::awaitable<T, any_io_executor> (previously bound to io_context::executor_type), which changes mangled
names: rebuild everything that includes taar. Completion tokens spelled
use_awaitable_t<io_context::executor_type>
must become taar::use_awaitable (or use_awaitable_t<any_io_executor>).
taar::rebind_executor stays, as a convenience
for library-private objects and client sockets; it no longer appears in
any handler signature.
Breaking changes of the unified error mechanism:
set_soft_error_handler,
set_hard_error_handler,
jsonrpc::endpoint::set_error_mapper and mcp::endpoint::set_tool_error_handler
take callables invoked with taar::failure const&
instead of std::exception_ptr. Replace try/rethrow/catch
ladders with f.as<my_error>(),
f.is(code) or f.has(code); f.rethrow() still throws the original exception.
taar::exception
where it threw boost::system::system_error or std::system_error:
argument casts and the binder, constrained,
cookie parsing, parse_multipart,
body_reader::read_all and multipart_reader::read_all
(with body_limit), streaming
body transport errors, chunked_response
late metadata, path_arg
registration checks, and server::tcp
/ server::tls resolve, bind and listen failures.
Catch taar::exception and inspect e.error().code(); the codes are unchanged.
taar::result<V> instead of boost::system::result<V>; the not-found signal is taar::failure{taar::error::argument_not_found}.
matcher::parse_template and parse_cookies
also return taar::result. User providers returning boost::system::result<V>
still work.
mcp::client_error exception class is removed.
request, elicit, create_message
and list_roots throw taar::exception whose failure carries mcp::error::client_error (data {"code":
n,
"data":
...}), errc::timed_out,
errc::operation_canceled or mcp::error::unknown_session.
mcp::error is now an error-code enum in <boost/taar/mcp/error.hpp>; resource-not-found and cancelled failures
carry it and keep their wire codes (-32002, -32800).
-32602
errors from argument failures carry data
as the failure's object data plus "message": text() (for argument errors {"argument":
..., "position": ..., "message":
...}), or the text() string when there is no object data.
-32603
internal errors carry no data.
failure::text() (no category tags), e.g. argument page
(position
1) is invalid
(caused
by:
...); failures that do not map to
a 4xx
status escalate to the hard error handler.
cmake.test()
is used in the Conan recipe instead of a hand-built test executable path.
session::http: surface mid-stream chunked errors;
harden flatten_awaiter.
/bigobj
for heavy templates and a raw-handler test fix on Windows.
session::http: add register_raw_handler
for protocol upgrades such as WebSocket.