Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
# clang-tidy config file
---
Checks: 'clang-diagnostic-*,clang-analyzer-*,modernize*,performance*,readability-*size*,readability*redundant*,-readability-redundant-member-init,-readability-redundant-access-specifiers,misc*,boost-use-to-string,cert*,google-explicit-constructor,-cert-err58-cpp,-modernize-concat-nested-namespaces,-modernize-use-nodiscard,-misc-non-private-member-variables-in-classes,-modernize-use-trailing-return-type,-misc-no-recursion,-misc-const-correctness,-misc-use-anonymous-namespace,-performance-avoid-endl'
# note DEV:
# disabled 'hicpp-signed-bitwise' because it's too noisy
# (IgnorePositiveIntegerLiterals doesn't seem to work, re-evaluate with Clang 11+)
Checks: 'clang-diagnostic-*,clang-analyzer-*,modernize*,performance*,readability-*size*,readability*redundant*,-readability-redundant-member-init,-readability-redundant-access-specifiers,misc*,boost-use-to-string,cert*,google-explicit-constructor,-cert-err58-cpp,-modernize-use-nodiscard,-misc-non-private-member-variables-in-classes,-modernize-use-trailing-return-type,-misc-no-recursion,-misc-const-correctness,-misc-use-anonymous-namespace,-readability-redundant-qualified-alias,-performance-enum-size'

WarningsAsErrors: ''
HeaderFilterRegex: ''
Expand All @@ -22,4 +19,6 @@ CheckOptions:
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'
- key: performance-unnecessary-value-param.AllowedTypes
value: 'std::(__[A-Za-z0-9_]+::)*exception_ptr$'
...
1 change: 1 addition & 0 deletions docs/Doxyfile.in
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ EXCLUDE_SYMBOLS = Impl \

# the first sentence of a comment is its summary, there is no need to say so with \brief
JAVADOC_AUTOBRIEF = YES
ALIASES = "runtimes{2}=\verbatim embed:rst^^:ASIO: ``\1``^^:COROSIO: ``\2``^^\endverbatim"

# Doxygen does not compile anything, so what a compiler would get from config.hpp and from Asio's
# headers is said here -- which is also why the reference takes neither Boost nor a configured
Expand Down
8 changes: 7 additions & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ apt-get install doxygen python3-sphinx python3-breathe python3-sphinx-rtd-theme

The target is not part of a regular build, and not there at all where CMake finds no `doxygen` or no
`sphinx-build`, or was told `ANYHTTP_BUILD_DOCS=OFF`. It can be made from a tree of either I/O runtime
and gives the same result: the comments on the types of the two runtimes are in `asio/runtime.hpp`.
and gives the same result: Doxygen reads `asio/runtime.hpp` for the runtime layer, whichever runtime
the tree is on.

## Writing it

Expand All @@ -44,6 +45,11 @@ Warnings are errors in both tools, so a public symbol without a comment, a param
* In a comment, Doxygen takes `http::...` and `https://` for a link, and `\c` of either, or of a word
that starts with `[`, comes out empty, which neither tool warns about. So write `errors::...`, as
that is the spelling that holds on both I/O runtimes anyway, and `<tt>[...]</tt>`.
* The runtime layer has one section for both I/O runtimes. Its functions come from `asio/runtime.hpp`
as everything else does, and so do the codes of `errors::`, each with `\hideinitializer` and a
`\runtimes{asio, corosio}` that says what it is on each (an alias in `Doxyfile.in`). Its types,
`Task`, `IoTask`, `results`, `error_code` and the others, are declared in `reference.rst` instead,
as Breathe would show what they stand for on ASIO only; keep those in step with the headers.
* Code in the guide comes from `snippets.cpp`, between a `// [name]` and a `// [name-end]` comment,
and is shown with `literalinclude`. Only pseudo code is written in place.
* In a page, names link to the reference with `` :cpp:func:`Session::submit` `` and the like, and
Expand Down
15 changes: 11 additions & 4 deletions docs/io-runtimes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ What the names stand for
* - :cpp:type:`error_code`
- ``boost::system::error_code``
- ``std::error_code``
* - :cpp:type:`results\<Ts...\> <results>`
- ``std::tuple<error_code, Ts...>``
- ``capy::results<Ts...>``
* - :cpp:type:`IoTask\<Ts...\> <IoTask>`
- ``asio::awaitable<results<Ts...>>``
- ``capy::task<results<Ts...>>``
* - :cpp:type:`TcpEndpoint`, :cpp:type:`UdpEndpoint`
- ``asio::ip::tcp::endpoint``, ``asio::ip::udp::endpoint``
- ``corosio::endpoint``
Expand Down Expand Up @@ -79,14 +85,15 @@ example, with the types of the library:
auto async_read_some(asio::mutable_buffer buffer, CompletionToken&& token = CompletionToken());

// ... and the coroutine around it.
Task<std::tuple<error_code, size_t>> read_some(asio::mutable_buffer buffer);
IoTask<size_t> read_some(asio::mutable_buffer buffer);

// COROSIO: the coroutine.
Task<std::tuple<error_code, size_t>> read_some(asio::mutable_buffer buffer);
IoTask<size_t> read_some(asio::mutable_buffer buffer);

The coroutine has the same declaration on both, so code that uses only coroutines builds with
either, which is what the guide does. It yields a tuple with the error first, and reports what goes
wrong instead of throwing it. Every operation has it: ``read_some``, ``write``, ``write_eof``,
either, which is what the guide does. It yields :cpp:type:`results`, a tuple with the error first,
and reports what goes wrong instead of throwing it; :cpp:type:`IoTask\<size_t\> <IoTask>` is short
for ``Task<results<size_t>>``. Every operation has it: ``read_some``, ``write``, ``write_eof``,
``submit``, ``get_response``, ``connect``, ``get``, and the others. What it does not have is a
completion token, so an ASIO build that wants one, for a callback, for ``cancel_after`` or to have
errors thrown, uses the ``async_...`` shape, see :ref:`completion-tokens` in the guide.
Expand Down
77 changes: 66 additions & 11 deletions docs/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -141,22 +141,77 @@ The runtime

#include <anyhttp/runtime.hpp>

What the interface is written in, and what each name is on ASIO. On COROSIO the same names are
something else, which :ref:`the table <io-runtimes-vocabulary>` has.
What the interface is written in. The names are the same on both I/O runtimes, and what each
stands for is the runtime's own, given here for both, as in :ref:`the table <io-runtimes-vocabulary>`.

.. doxygentypedef:: anyhttp::Task
Types
^^^^^

.. doxygentypedef:: anyhttp::Executor
.. cpp:type:: template <typename T = void> anyhttp::Task

.. doxygentypedef:: anyhttp::IpAddress
A coroutine, as the runtime runs it.

.. doxygentypedef:: anyhttp::TcpEndpoint
:ASIO: ``asio::awaitable<T>``
:COROSIO: ``capy::task<T>``

.. doxygentypedef:: anyhttp::UdpEndpoint
.. cpp:type:: template <typename... Ts> anyhttp::IoTask

.. doxygentypedef:: anyhttp::error_code
An operation, as the coroutine that yields its :cpp:type:`results`: ``IoTask<size_t>`` is
``Task<results<size_t>>``, ``IoTask<>`` one that reports only its error.

.. doxygentypedef:: anyhttp::system_error
:ASIO: ``asio::awaitable<std::tuple<error_code, Ts...>>``
:COROSIO: ``capy::task<capy::results<Ts...>>``

.. cpp:type:: template <typename... Ts> anyhttp::results

What an operation yields: its error first, then what it has to report besides, which
``auto [ec, n] = co_await reader.read_some(buffer);`` takes apart. Whether that is meaningful
when there is an error is up to the operation.

:ASIO: ``std::tuple<error_code, Ts...>``
:COROSIO: ``capy::results<Ts...>``, which is ``std::tuple<std::error_code, Ts...>``

.. cpp:type:: anyhttp::error_code

What every operation reports its outcome with.

:ASIO: ``boost::system::error_code``
:COROSIO: ``std::error_code``

.. cpp:type:: anyhttp::system_error

What the runtime throws an :cpp:type:`error_code` as.

:ASIO: ``boost::system::system_error``
:COROSIO: ``std::system_error``

.. cpp:type:: anyhttp::Executor

What sessions, streams and their readers and writers run on.

:ASIO: ``asio::any_io_executor``
:COROSIO: ``capy::any_executor``

.. cpp:type:: anyhttp::IpAddress

The runtime's own IP address, which the API speaks, too.

:ASIO: ``asio::ip::address``
:COROSIO: ``corosio::ip_address``

.. cpp:type:: anyhttp::TcpEndpoint

The runtime's own TCP endpoint.

:ASIO: ``asio::ip::tcp::endpoint``
:COROSIO: ``corosio::endpoint``

.. cpp:type:: anyhttp::UdpEndpoint

The runtime's own UDP endpoint.

:ASIO: ``asio::ip::udp::endpoint``
:COROSIO: ``corosio::endpoint``, the same type as :cpp:type:`TcpEndpoint`

.. doxygentypedef:: anyhttp::DefaultCompletionToken

Expand All @@ -165,8 +220,8 @@ something else, which :ref:`the table <io-runtimes-vocabulary>` has.
Errors
^^^^^^

Where the two runtimes spell an error differently, see :ref:`the table <io-runtimes-errors>`. Anything
else compares against ``errc::``.
The codes anyhttp reports where the two runtimes spell them differently. Anything else compares
against ``errc::``, which is ``boost::system::errc`` on ASIO and ``std::errc`` on COROSIO.

.. doxygenvariable:: anyhttp::errors::eof

Expand Down
12 changes: 5 additions & 7 deletions docs/snippets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ Task<void> route(server::Request request, server::Response response)
//

// [read-all]
Task<std::tuple<error_code, std::string>> read_all(Reader& reader)
IoTask<std::string> read_all(Reader& reader)
{
std::string body;
std::array<char, 16_k> buffer;
Expand All @@ -126,8 +126,7 @@ Task<std::tuple<error_code, std::string>> read_all(Reader& reader)
// [read-all-end]

// [post]
Task<std::tuple<error_code, std::string>> post(Session& session, boost::urls::url url,
std::string body)
IoTask<std::string> post(Session& session, boost::urls::url url, std::string body)
{
auto [submit_ec, request] =
co_await session.submit(url, fields({{"Content-Length", body.size()}}));
Expand Down Expand Up @@ -220,8 +219,7 @@ Task<std::string> round_trip(Session& session, boost::urls::url url, std::string
// [both-ways-end]

// [would-block]
Task<std::tuple<error_code, client::Request>> submit_patiently(Session& session,
boost::urls::url url)
IoTask<client::Request> submit_patiently(Session& session, boost::urls::url url)
{
for (;;)
{
Expand Down Expand Up @@ -383,8 +381,8 @@ inline void quic_serve(server::Server& server)
}

// [quic-client]
Task<std::tuple<error_code, std::string>> quic_hello(Executor executor, std::string host,
std::string port, std::string ca_file)
IoTask<std::string> quic_hello(Executor executor, std::string host, std::string port,
std::string ca_file)
{
auto [connect_ec, connection] = co_await quic::connect(
executor, std::move(host), std::move(port), {.alpn = "echo", .tls_ca_file = ca_file});
Expand Down
6 changes: 2 additions & 4 deletions include/anyhttp/asio/net.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,10 @@ std::pair<int, int> buffer_sizes(TcpSocket& socket);
void set_buffer_sizes(TcpSocket& socket, int send, int receive);

/// Resolves \p host and \p port (a number): <tt>(error_code, endpoints)</tt>.
Task<std::tuple<error_code, std::vector<TcpEndpoint>>> resolve(Executor executor, std::string host,
std::string port);
IoTask<std::vector<TcpEndpoint>> resolve(Executor executor, std::string host, std::string port);

/// Connects \p socket to the first of \p endpoints that takes it: <tt>(error_code, endpoint)</tt>.
Task<std::tuple<error_code, TcpEndpoint>> connect(TcpSocket& socket,
std::vector<TcpEndpoint> endpoints);
IoTask<TcpEndpoint> connect(TcpSocket& socket, std::vector<TcpEndpoint> endpoints);

inline TlsStream make_tls_stream(TcpSocket&& socket, TlsContext& context)
{
Expand Down
61 changes: 46 additions & 15 deletions include/anyhttp/asio/runtime.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ namespace anyhttp
{
namespace asio = boost::asio;

// The reference declares the types from here to errc in docs/reference.rst, with what each is on
// both runtimes: from here, Breathe would show the ASIO one only.

/// A coroutine, as the runtime runs it.
template <typename T = void>
using Task = asio::awaitable<T>;
Expand All @@ -90,6 +93,22 @@ using UdpEndpoint = asio::ip::udp::endpoint;
/// What every operation reports its outcome with.
using error_code = boost::system::error_code;

/**
* What an operation yields: its error first, then what it has to report besides, which
* `auto [ec, n] = co_await reader.read_some(buffer);` takes apart. Whether that is meaningful
* when there is an error is up to the operation. Capy's `capy::results` with the runtime's
* #error_code, so that on COROSIO the two are the same type.
*/
template <typename... Ts>
using results = std::tuple<error_code, Ts...>;

/**
* An operation, as the coroutine that yields its #results: `IoTask<size_t>` is
* `Task<results<size_t>>`, `IoTask<>` one that reports only its error.
*/
template <typename... Ts>
using IoTask = Task<results<Ts...>>;

// =================================================================================================

/**
Expand All @@ -107,27 +126,43 @@ namespace errc = boost::system::errc;
namespace errors
{
/// The end of an incoming body.
/// \runtimes{asio::error::eof,capy::error::eof}
/// \hideinitializer
inline const error_code eof = asio::error::eof;

/// An incoming body that ended before it was complete: a reset stream, a lost connection.
/// \runtimes{beast::http::error::partial_message,capy::error::stream_truncated}
/// \hideinitializer
inline const error_code partial_message = boost::beast::http::error::partial_message;

/// An operation that was cancelled before it completed.
/// \runtimes{errc::operation_canceled,capy::error::canceled}
/// \hideinitializer
inline const error_code canceled = make_error_code(errc::operation_canceled);

/// HTTP/1.1: a request or response that has to wait for an earlier one, see Session.
/// \runtimes{asio::error::would_block,errc::operation_would_block}
/// \hideinitializer
inline const error_code would_block = asio::error::would_block;

/// A connection that can not carry any more requests or responses.
/// \runtimes{asio::error::connection_aborted,errc::connection_aborted}
/// \hideinitializer
inline const error_code connection_aborted = asio::error::connection_aborted;

/// An operation on a Reader or Writer after its implementation has been released.
/// \runtimes{asio::error::bad_descriptor,errc::bad_file_descriptor}
/// \hideinitializer
inline const error_code bad_descriptor = asio::error::bad_descriptor;

/// An operation that may be in progress only once at a time.
/// \runtimes{asio::error::already_started,errc::connection_already_in_progress}
/// \hideinitializer
inline const error_code already_started = asio::error::already_started;

/// A header section larger than Config::max_header_size.
/// \runtimes{beast::http::error::header_limit,beast::http::error::header_limit}
/// \hideinitializer
inline const error_code header_limit = boost::beast::http::error::header_limit;
} // namespace errors

Expand All @@ -141,10 +176,7 @@ using system_error = boost::system::system_error;

/// Throws \p ec, for code that reports errors as exceptions, as the runtime's own does.
/// \param ec The error.
[[noreturn]] inline void throw_error(const error_code& ec)
{
throw boost::system::system_error(ec);
}
[[noreturn]] inline void throw_error(const error_code& ec) { throw system_error(ec); }

/// \cond internal
/// What `errno` holds, for code that calls the operating system itself.
Expand Down Expand Up @@ -280,7 +312,7 @@ inline void launch(const Executor& executor, Task<void> task, F&& on_done)
* other runtime has no tokens, and returns such a task's result directly.
*/
template <typename... T>
inline void launch(const Executor& executor, Task<std::tuple<error_code, T...>> task,
inline void launch(const Executor& executor, IoTask<T...> task,
Completion<void(error_code, T...)>&& handler)
{
auto slot = asio::get_associated_cancellation_slot(handler);
Expand All @@ -292,15 +324,14 @@ inline void launch(const Executor& executor, Task<std::tuple<error_code, T...>>
//
asio::co_spawn(executor, std::move(task),
asio::bind_cancellation_slot(
slot, asio::bind_executor(
handler_executor, [handler = std::move(handler)](
const std::exception_ptr& ep,
std::tuple<error_code, T...> result) mutable {
if (ep)
std::move(handler)(code(ep), T{}...);
else
std::apply(std::move(handler), std::move(result));
})));
slot, asio::bind_executor(handler_executor, [handler = std::move(handler)](
const std::exception_ptr& ep,
results<T...> result) mutable {
if (ep)
std::move(handler)(code(ep), T{}...);
else
std::apply(std::move(handler), std::move(result));
})));
}

// -------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -437,7 +468,7 @@ class Event
* Waits for the event to be set: <tt>auto [ec] = co_await event.wait();</tt>. The wait can be
* cancelled, and then completes with \c errors::canceled.
*/
Task<std::tuple<error_code>> wait()
IoTask<> wait()
{
if (set_)
co_return error_code{};
Expand Down
4 changes: 2 additions & 2 deletions include/anyhttp/client.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ class Request : public Writer
/// \c async_get_response() as a coroutine:
/// <tt>auto [ec, response] = co_await request.get_response();</tt>
/// \return The error and the response.
Task<std::tuple<error_code, Response>> get_response();
IoTask<Response> get_response();

private:
#if ANYHTTP_ASIO
Expand Down Expand Up @@ -285,7 +285,7 @@ class Client

/// \c async_connect() as a coroutine: <tt>auto [ec, session] = co_await client.connect();</tt>
/// \return The error and the session.
Task<std::tuple<error_code, Session>> connect();
IoTask<Session> connect();

private:
#if ANYHTTP_ASIO
Expand Down
2 changes: 1 addition & 1 deletion include/anyhttp/client_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class Request::Impl : public Writer::Impl
virtual void async_get_response(GetResponseHandler&& handler) = 0;

/// async_get_response() as a coroutine, see Reader::Impl::read_some().
virtual Task<std::tuple<error_code, Response>> get_response()
virtual IoTask<Response> get_response()
{
co_return co_await initiate<GetResponse>(
[this](GetResponseHandler handler) { async_get_response(std::move(handler)); });
Expand Down
Loading
Loading