From 7c97f3542ebd8f681df8df9c2c27f91df94bc481 Mon Sep 17 00:00:00 2001 From: Peter Eisenlohr Date: Fri, 6 Nov 2026 20:58:29 +0000 Subject: [PATCH 1/2] refactor: name namespaces after their directories The protocol backends are anyhttp::h1, h2, h2v2, quic and h3, instead of beast_impl, nghttp2 (both HTTP/2 backends) and http3, and so is what their backend headers offer generic code: quic::QuicServer, quic::make_quic_server(), h3::make_http3_application(), h3::async_connect_http3(). The role files in quic/ and h3/ are in their protocol's namespace, no longer in anyhttp::server or anyhttp::client. h2v2/ defines the factories of h2/backend.hpp in anyhttp::h2, where they are declared, so that nothing above that header can tell the two HTTP/2 backends apart. The runtimes are the inline namespaces anyhttp::asio and anyhttp::corosio. Their vocabulary is still named as anyhttp's, but its symbols carry the runtime, so code of the two runtimes cannot link together. code() moves in from the global namespace, and normalize(IpAddress), which each runtime defines, is declared by the runtimes rather than common.hpp. That takes the names asio and corosio inside anyhttp: the aliases for boost::asio and boost::corosio are gone and Boost's namespaces are spelled in full, also in the tests and examples, where an alias of the same name is ambiguous with using namespace anyhttp. any_async_stream.hpp no longer leaks the global aliases asio and ip. Generic code must not reopen a runtime's namespace: clang makes "namespace anyhttp::io" a namespace of its own instead of extending the inline one, as GCC does, so net.hpp and net.cpp open the runtime's io by #if. Doxygen gets an input filter that presents the runtime's names as anyhttp's, as the reference has them. CLAUDE.md has both. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 19 +++ README.md | 4 +- docs/Doxyfile.in | 4 + docs/examples/hello_server.cpp | 2 +- docs/io-runtimes.rst | 46 +++--- docs/reference.rst | 26 ++-- docs/snippets.cpp | 24 +-- include/anyhttp/asio/any_async_stream.hpp | 43 +++--- .../anyhttp/asio/any_async_stream_impl.hpp | 8 +- include/anyhttp/asio/buffer_array.hpp | 4 +- include/anyhttp/asio/concepts.hpp | 4 +- include/anyhttp/asio/net.hpp | 33 ++-- include/anyhttp/asio/runtime.hpp | 146 +++++++++--------- include/anyhttp/client.hpp | 16 +- include/anyhttp/common.hpp | 5 +- include/anyhttp/corosio/net.hpp | 34 ++-- include/anyhttp/corosio/runtime.hpp | 38 ++--- include/anyhttp/detail/detect.hpp | 9 +- include/anyhttp/h1/backend.hpp | 4 +- include/anyhttp/h1/io.hpp | 9 +- include/anyhttp/h1/session.hpp | 12 +- include/anyhttp/h2/backend.hpp | 4 +- include/anyhttp/h2/common.hpp | 4 +- include/anyhttp/h2/session.hpp | 4 +- include/anyhttp/h2/session_details.hpp | 11 +- include/anyhttp/h2/stream.hpp | 23 +-- include/anyhttp/h2v2/common.hpp | 4 +- include/anyhttp/h2v2/session.hpp | 7 +- include/anyhttp/h2v2/session_details.hpp | 7 +- include/anyhttp/h2v2/stream.hpp | 23 +-- include/anyhttp/h3/application.hpp | 4 +- include/anyhttp/h3/backend.hpp | 20 +-- include/anyhttp/h3/common.hpp | 4 +- include/anyhttp/h3/stream.hpp | 4 +- include/anyhttp/net.hpp | 12 +- include/anyhttp/quic.hpp | 16 +- include/anyhttp/quic/application.hpp | 2 +- include/anyhttp/quic/backend.hpp | 10 +- include/anyhttp/quic/stream.hpp | 20 +-- include/anyhttp/reader.hpp | 38 ++--- include/anyhttp/reader_impl.hpp | 6 +- include/anyhttp/request_handlers.hpp | 4 +- include/anyhttp/server.hpp | 12 +- include/anyhttp/server_impl.hpp | 17 +- include/anyhttp/session.hpp | 40 ++--- include/anyhttp/writer.hpp | 41 ++--- include/anyhttp/writer_impl.hpp | 4 +- src/apps/client_main.cpp | 2 +- src/asio/any_async_stream_impl.cpp | 9 +- src/asio/net.cpp | 39 ++--- src/client_impl.cpp | 14 +- src/corosio/net.cpp | 42 ++--- src/file_handler.cpp | 2 +- src/h1/session.cpp | 26 ++-- src/h2/session.cpp | 4 +- src/h2/stream.cpp | 35 +++-- src/h2v2/session.cpp | 21 ++- src/h2v2/stream.cpp | 33 ++-- src/h3/application.cpp | 4 +- src/h3/client.cpp | 30 ++-- src/h3/common.cpp | 4 +- src/h3/server.cpp | 36 ++--- src/h3/stream.cpp | 4 +- src/net.cpp | 12 +- src/quic/client.cpp | 4 +- src/quic/server.cpp | 50 +++--- src/quic/stream.cpp | 31 ++-- src/reader.cpp | 4 +- src/request_handlers.cpp | 18 +-- src/server_impl.cpp | 14 +- src/session.cpp | 2 +- src/writer.cpp | 6 +- test/package/consumer.cpp | 4 +- test/test_alt_svc.cpp | 4 +- test/test_client_async.cpp | 97 ++++++------ test/test_client_async_cancellation.cpp | 46 +++--- test/test_client_connect.cpp | 2 +- test/test_client_tls.cpp | 29 ++-- test/test_connection_close.cpp | 26 ++-- test/test_connection_end.cpp | 29 ++-- test/test_corosio.cpp | 46 +++--- test/test_coroutine_api.cpp | 20 +-- test/test_external.cpp | 7 +- test/test_fixtures_asio.hpp | 7 +- test/test_fixtures_corosio.hpp | 2 +- test/test_formatter.cpp | 10 +- test/test_get.cpp | 18 +-- test/test_h2_push.cpp | 4 +- test/test_h2c_upgrade.cpp | 10 +- test/test_ipv6.cpp | 2 +- test/test_quic.cpp | 48 +++--- test/test_server.cpp | 2 +- 92 files changed, 882 insertions(+), 808 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index efb41cb..aeaa1cb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -142,6 +142,17 @@ runtime only is in `asio/` or `corosio/`, again in both; the library compiles th of its runtime and not the other. `src/apps/` has the `server` and `client` programs, `src/issues/` standalone reproducers of upstream bugs. Everything else at the top level is generic. +Each of these directories is a namespace of the same name: `anyhttp::h1`, `h2`, `h2v2`, `quic`, `h3`, +and what a backend header offers generic code is in it too (`h1::make_server_session()`, +`quic::make_quic_server()`, `h3::async_connect_http3()`). The one exception is `h2v2/`, which +defines the factories of `h2/backend.hpp` in `anyhttp::h2`, where they are declared, so that nothing +above that header can tell the two backends apart. The runtimes are *inline* namespaces, +`anyhttp::asio` and `anyhttp::corosio`: code names their vocabulary as anyhttp's (`Task`, +`io::read_some()`), but the symbols carry the runtime, so objects of the two cannot link together. +That takes the names `asio` and `corosio` inside anyhttp, so spell Boost's in full, +`boost::asio::` and `boost::corosio::`. An alias of either name is ambiguous anywhere +`using namespace anyhttp` reaches, and the tests and examples have it. + What gets installed is the public headers, the ones reachable from `client.hpp`, `server.hpp`, `quic.hpp`, `session.hpp` and the like; `cmake/install.cmake` leaves out the protocol directories, `net.hpp` and `*_impl.hpp`. A public header that starts including one of those needs the list changed @@ -252,6 +263,14 @@ constructed, for TCP and QUIC alike, and keeps serving the old ones. curl then f `verify result: 20`; the HTTP/3 client (which reads its CA file on every connect) with "unable to get local issuer certificate". +**Never reopen a runtime's namespace from generic code.** `namespace anyhttp::io {` (or `errors`, +`detail`) outside `asio/` and `corosio/` ought to extend the runtime's `anyhttp::asio::io`, and does +with GCC, but clang makes a namespace of its own: the declarations in it never meet their +definitions, which shows only at link time. `anyhttp/net.hpp` and `src/net.cpp` open +`anyhttp::inline asio::io` or `anyhttp::inline corosio::io` by `#if`. A function that a generic +header would declare and each runtime would define (like `normalize(IpAddress)`) is declared in the +runtime headers instead. + **One nghttp2 per process.** Version 2 installs as its own package (``, `libnghttp2v2`), but still exports nine of version 1's symbol names -- `nghttp2_strerror`, `nghttp2_http2_strerror`, `nghttp2_check_header_name`/`_value`, `nghttp2_check_authority` and the diff --git a/README.md b/README.md index 07e8903..54f513e 100644 --- a/README.md +++ b/README.md @@ -48,13 +48,13 @@ Task echo(server::Request request, server::Response response) std::array buffer; for (;;) { - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); if (ec == errors::eof) break; if (ec) co_return; - if (auto [write_ec] = co_await response.write(asio::buffer(buffer, n)); write_ec) + if (auto [write_ec] = co_await response.write(boost::asio::buffer(buffer, n)); write_ec) co_return; } diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index d95365b..db1ad82 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -42,6 +42,10 @@ PREDEFINED = ANYHTTP_ASIO=1 \ "BOOST_ASIO_COMPLETION_TOKEN_FOR(x)=typename" SEARCH_INCLUDES = NO +# The runtime's vocabulary is in the inline namespace anyhttp::asio, or anyhttp::corosio, so that +# the two runtimes do not link together. The API names it as anyhttp's, and so does the reference. +INPUT_FILTER = "sed -e 's/^namespace anyhttp::inline asio$/namespace anyhttp/'" + # what is not documented is not finished QUIET = YES WARN_IF_UNDOCUMENTED = YES diff --git a/docs/examples/hello_server.cpp b/docs/examples/hello_server.cpp index f2539d2..18ce421 100644 --- a/docs/examples/hello_server.cpp +++ b/docs/examples/hello_server.cpp @@ -24,7 +24,7 @@ static Task hello(server::Request request, server::Response response) if (auto [ec] = co_await response.submit(200, fields({{"Content-Length", body.size()}})); ec) co_return; - std::ignore = co_await response.write_eof(asio::buffer(body)); + std::ignore = co_await response.write_eof(boost::asio::buffer(body)); } int main(int argc, char* argv[]) diff --git a/docs/io-runtimes.rst b/docs/io-runtimes.rst index aebb3d9..5213f02 100644 --- a/docs/io-runtimes.rst +++ b/docs/io-runtimes.rst @@ -18,6 +18,12 @@ Both build the same library and protocol sources. Only a thin runtime layer diff ``anyhttp/runtime.hpp`` and ``anyhttp/net.hpp`` with what they include from ``asio/`` or ``corosio/``. :src:`docs/corosio-port-plan.md` has how that came about. +That layer is the inline namespace ``anyhttp::asio`` or ``anyhttp::corosio``. Its names are +anyhttp's, :cpp:type:`Task` or :cpp:var:`errors::eof`, and a library of one runtime does not link +with code compiled for the other. Because ``asio`` and ``corosio`` are names in ``anyhttp``, code +that says ``using namespace anyhttp`` writes Boost's namespaces in full, as in +``boost::asio::buffer()``: a namespace alias ``asio`` of its own would be ambiguous. + .. _io-runtimes-vocabulary: What the names stand for @@ -34,23 +40,23 @@ What the names stand for - ``-DANYHTTP_API=ASIO`` (default) - ``-DANYHTTP_API=COROSIO -DTLS_LIBRARY=OpenSSL`` * - :cpp:type:`Task\ ` - - ``asio::awaitable`` - - ``capy::task`` + - ``boost::asio::awaitable`` + - ``boost::capy::task`` * - :cpp:type:`Executor` - - ``asio::any_io_executor`` - - ``capy::any_executor`` + - ``boost::asio::any_io_executor`` + - ``boost::capy::any_executor`` * - :cpp:type:`error_code` - ``boost::system::error_code`` - ``std::error_code`` * - :cpp:type:`results\ ` - ``std::tuple`` - - ``capy::results`` + - ``boost::capy::results`` * - :cpp:type:`IoTask\ ` - - ``asio::awaitable>`` - - ``capy::task>`` + - ``boost::asio::awaitable>`` + - ``boost::capy::task>`` * - :cpp:type:`TcpEndpoint`, :cpp:type:`UdpEndpoint` - - ``asio::ip::tcp::endpoint``, ``asio::ip::udp::endpoint`` - - ``corosio::endpoint`` + - ``boost::asio::ip::tcp::endpoint``, ``boost::asio::ip::udp::endpoint`` + - ``boost::corosio::endpoint`` * - Operations - ``async_x(..., token)`` and ``x()`` - ``x()`` @@ -82,13 +88,13 @@ example, with the types of the library: // ASIO: the operation, which completes a handler of the signature void(error_code, size_t) ... template - auto async_read_some(asio::mutable_buffer buffer, CompletionToken&& token = CompletionToken()); + auto async_read_some(boost::asio::mutable_buffer buffer, CompletionToken&& token = CompletionToken()); // ... and the coroutine around it. - IoTask read_some(asio::mutable_buffer buffer); + IoTask read_some(boost::asio::mutable_buffer buffer); // COROSIO: the coroutine. - IoTask read_some(asio::mutable_buffer buffer); + IoTask read_some(boost::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 :cpp:type:`results`, a tuple with the error first, @@ -116,8 +122,8 @@ On ASIO, the implementation of each operation is hidden behind `any_completion_h `__, so that it can be compiled separately. On COROSIO, they are coroutines of `Capy `__, Corosio's base, cancelled through the awaiting chain's -``std::stop_token``. :cpp:class:`Reader` and :cpp:class:`Writer` model ``capy::ReadStream`` and -``capy::WriteStream``. +``std::stop_token``. :cpp:class:`Reader` and :cpp:class:`Writer` model ``boost::capy::ReadStream`` and +``boost::capy::WriteStream``. .. _io-runtimes-errors: @@ -134,19 +140,19 @@ Where the two runtimes spell an error differently, the namespace ``anyhttp::erro - ASIO - COROSIO * - :cpp:var:`errors::eof` - - ``asio::error::eof`` - - ``capy::error::eof`` + - ``boost::asio::error::eof`` + - ``boost::capy::error::eof`` * - :cpp:var:`errors::partial_message` - ``beast::http::error::partial_message`` - - ``capy::error::stream_truncated`` + - ``boost::capy::error::stream_truncated`` * - :cpp:var:`errors::canceled` - ``errc::operation_canceled`` - - ``capy::error::canceled`` + - ``boost::capy::error::canceled`` * - :cpp:var:`errors::would_block` - - ``asio::error::would_block`` + - ``boost::asio::error::would_block`` - ``errc::operation_would_block`` * - :cpp:var:`errors::connection_aborted` - - ``asio::error::connection_aborted`` + - ``boost::asio::error::connection_aborted`` - ``errc::connection_aborted`` * - :cpp:var:`errors::header_limit` - ``beast::http::error::header_limit`` diff --git a/docs/reference.rst b/docs/reference.rst index 684f156..7ebe438 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -151,16 +151,16 @@ Types A coroutine, as the runtime runs it. - :ASIO: ``asio::awaitable`` - :COROSIO: ``capy::task`` + :ASIO: ``boost::asio::awaitable`` + :COROSIO: ``boost::capy::task`` .. cpp:type:: template anyhttp::IoTask An operation, as the coroutine that yields its :cpp:type:`results`: ``IoTask`` is ``Task>``, ``IoTask<>`` one that reports only its error. - :ASIO: ``asio::awaitable>`` - :COROSIO: ``capy::task>`` + :ASIO: ``boost::asio::awaitable>`` + :COROSIO: ``boost::capy::task>`` .. cpp:type:: template anyhttp::results @@ -169,7 +169,7 @@ Types when there is an error is up to the operation. :ASIO: ``std::tuple`` - :COROSIO: ``capy::results``, which is ``std::tuple`` + :COROSIO: ``boost::capy::results``, which is ``std::tuple`` .. cpp:type:: anyhttp::error_code @@ -189,29 +189,29 @@ Types What sessions, streams and their readers and writers run on. - :ASIO: ``asio::any_io_executor`` - :COROSIO: ``capy::any_executor`` + :ASIO: ``boost::asio::any_io_executor`` + :COROSIO: ``boost::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`` + :ASIO: ``boost::asio::ip::address`` + :COROSIO: ``boost::corosio::ip_address`` .. cpp:type:: anyhttp::TcpEndpoint The runtime's own TCP endpoint. - :ASIO: ``asio::ip::tcp::endpoint`` - :COROSIO: ``corosio::endpoint`` + :ASIO: ``boost::asio::ip::tcp::endpoint`` + :COROSIO: ``boost::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` + :ASIO: ``boost::asio::ip::udp::endpoint`` + :COROSIO: ``boost::corosio::endpoint``, the same type as :cpp:type:`TcpEndpoint` .. doxygentypedef:: anyhttp::DefaultCompletionToken diff --git a/docs/snippets.cpp b/docs/snippets.cpp index 91dd1d7..8f157cb 100644 --- a/docs/snippets.cpp +++ b/docs/snippets.cpp @@ -58,13 +58,13 @@ Task serve_echo(server::Request request, server::Response response) std::array buffer; for (;;) { - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); if (ec == errors::eof) break; if (ec) co_return; - if (auto [write_ec] = co_await response.write(asio::buffer(buffer, n)); write_ec) + if (auto [write_ec] = co_await response.write(boost::asio::buffer(buffer, n)); write_ec) co_return; } @@ -80,7 +80,7 @@ Task respond_with(server::Response response, std::string body) ec) co_return; - std::ignore = co_await response.write_eof(asio::buffer(body)); + std::ignore = co_await response.write_eof(boost::asio::buffer(body)); } // [write-at-once-end] @@ -115,7 +115,7 @@ IoTask read_all(Reader& reader) std::array buffer; for (;;) { - auto [ec, n] = co_await reader.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await reader.read_some(boost::asio::buffer(buffer)); body.append(buffer.data(), n); if (ec == errors::eof) co_return std::tuple{error_code{}, std::move(body)}; @@ -133,7 +133,7 @@ IoTask post(Session& session, boost::urls::url url, std::string bod if (submit_ec) co_return std::tuple{submit_ec, std::string{}}; - if (auto [ec] = co_await request.write_eof(asio::buffer(body)); ec) + if (auto [ec] = co_await request.write_eof(boost::asio::buffer(body)); ec) co_return std::tuple{ec, std::string{}}; auto [response_ec, response] = co_await request.get_response(); @@ -174,7 +174,7 @@ Task read_at_most(Session session, boost::urls::url url, size_t limit) std::array buffer; while (total < limit) { - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); total += n; if (ec) break; @@ -189,7 +189,7 @@ Task read_at_most(Session session, boost::urls::url url, size_t limit) Task upload(Writer& writer, std::string_view body) { for (; !body.empty(); body.remove_prefix(std::min(body.size(), size_t{16_k}))) - if (auto [ec] = co_await writer.write(asio::buffer(body.substr(0, 16_k))); ec) + if (auto [ec] = co_await writer.write(boost::asio::buffer(body.substr(0, 16_k))); ec) throw_error(ec); if (auto [ec] = co_await writer.write_eof(); ec) @@ -352,13 +352,13 @@ Task quic_echo(quic::Stream stream) std::array buffer; for (;;) { - auto [ec, n] = co_await stream.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await stream.read_some(boost::asio::buffer(buffer)); if (ec == errors::eof) break; if (ec) co_return; - if (auto [write_ec] = co_await stream.write(asio::buffer(buffer, n)); write_ec) + if (auto [write_ec] = co_await stream.write(boost::asio::buffer(buffer, n)); write_ec) co_return; } std::ignore = co_await stream.write_eof(); @@ -393,7 +393,7 @@ IoTask quic_hello(Executor executor, std::string host, std::string if (open_ec) co_return std::tuple{open_ec, std::string{}}; - if (auto [ec] = co_await stream.write_eof(asio::buffer("hello", 5)); ec) + if (auto [ec] = co_await stream.write_eof(boost::asio::buffer("hello", 5)); ec) co_return std::tuple{ec, std::string{}}; co_return co_await read_all(stream); @@ -455,7 +455,7 @@ Task token_exception(Session& session, boost::urls::url url) // [token-tuple] Task token_tuple(Session& session, boost::urls::url url) { - auto [ec, message] = co_await session.async_get(url, {}, asio::as_tuple); + auto [ec, message] = co_await session.async_get(url, {}, boost::asio::as_tuple); if (ec) co_return ec.message(); co_return std::move(message.body()); @@ -476,7 +476,7 @@ void token_callback(Session& session, boost::urls::url url) Task response_within(client::Request& request, std::chrono::milliseconds limit) { auto [ec, response] = - co_await request.async_get_response(asio::cancel_after(limit, asio::as_tuple)); + co_await request.async_get_response(boost::asio::cancel_after(limit, boost::asio::as_tuple)); co_return ec != errc::operation_canceled; } // [cancel-after-end] diff --git a/include/anyhttp/asio/any_async_stream.hpp b/include/anyhttp/asio/any_async_stream.hpp index 2969e83..1478abd 100644 --- a/include/anyhttp/asio/any_async_stream.hpp +++ b/include/anyhttp/asio/any_async_stream.hpp @@ -22,32 +22,30 @@ #include #include -namespace asio = boost::asio; -namespace ip = asio::ip; - -namespace anyhttp +namespace anyhttp::inline asio { // ================================================================================================= using ReadWrite = void(boost::system::error_code, std::size_t); -using ReadWriteHandler = asio::any_completion_handler; +using ReadWriteHandler = boost::asio::any_completion_handler; using ConstBufferVector = const_buffer_array<16>; using MutableBufferVector = mutable_buffer_array<16>; using Shutdown = void(boost::system::error_code); -using ShutdownHandler = asio::any_completion_handler; +using ShutdownHandler = boost::asio::any_completion_handler; /** * The socket underneath a stream, as far as the sessions care about it: enough for shutdown() and * close(), which is all they do with it. A TLS stream hands out its \c lowest_layer(), which is - * this rather than the full ip::tcp::socket, so this is what the type-erased stream offers, too. + * this rather than the full boost::asio::ip::tcp::socket, so this is what the type-erased stream + * offers, too. */ -using TcpSocketBase = asio::basic_socket; +using TcpSocketBase = boost::asio::basic_socket; /// The TLS stream the server and client run on, spelled out often enough to deserve a name. -using SslStream = asio::ssl::stream; +using SslStream = boost::asio::ssl::stream; /** * Attempt to create a type-erased async stream with ASIO. @@ -56,8 +54,9 @@ using SslStream = asio::ssl::stream; * buffer_array, a fixed-capacity, non-allocating array of buffer descriptors that is itself a * buffer sequence. This seems to work reasonably well. * - * There is also \c asio::buffer_sequence_adapter and \c linearise(), which seem to be used in ASIO - * SSL code as well. It merges a set of buffers into a new, contiguous buffer. But that is slow. + * There is also \c boost::asio::buffer_sequence_adapter and \c linearise(), which seem to be used + * in ASIO SSL code as well. It merges a set of buffers into a new, contiguous buffer. But that is + * slow. */ class any_async_stream { @@ -85,8 +84,9 @@ class any_async_stream // The requirements for ConstBufferSequence are defined here: // https://live.boost.org/doc/libs/1_88_0/doc/html/boost_asio/reference/ConstBufferSequence.html // - // The iterators returned by asio::buffer_sequence_{begin,end} must be 'bidirectional', but are - // not required to be 'contiguous'. So those iterators cannot be simply converted to a span. + // The iterators returned by boost::asio::buffer_sequence_{begin,end} must be 'bidirectional', + // but are not required to be 'contiguous'. So those iterators cannot be simply converted to a + // span. // // * https://en.cppreference.com/w/cpp/iterator/bidirectional_iterator // * https://en.cppreference.com/w/cpp/iterator/contiguous_iterator.html @@ -98,8 +98,8 @@ class any_async_stream // just results in a shorter transfer. // template > + BOOST_ASIO_COMPLETION_TOKEN_FOR(ReadWrite) CompletionToken = + boost::asio::default_completion_token_t> auto async_write_some(const Buffers& buffers, CompletionToken&& token = CompletionToken()) { return boost::asio::async_initiate( @@ -113,8 +113,8 @@ class any_async_stream // async_read_some // template > + BOOST_ASIO_COMPLETION_TOKEN_FOR(ReadWrite) CompletionToken = + boost::asio::default_completion_token_t> auto async_read_some(const Buffers& buffers, CompletionToken&& token = CompletionToken()) { return boost::asio::async_initiate( @@ -131,8 +131,8 @@ class any_async_stream // in anyhttp/asio/net.hpp, which is how the sessions reach this. For a stream that has // nothing to end, this completes immediately and successfully. // - template > + template > auto async_shutdown(CompletionToken&& token = CompletionToken()) { return boost::asio::async_initiate(initiate_shutdown{this}, token); @@ -180,9 +180,10 @@ template requires(!std::is_reference_v) any_async_stream make_any_async_stream(Stream&& stream); -extern template any_async_stream make_any_async_stream(ip::tcp::socket&&); +extern template any_async_stream +make_any_async_stream(boost::asio::ip::tcp::socket&&); extern template any_async_stream make_any_async_stream(SslStream&&); // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/include/anyhttp/asio/any_async_stream_impl.hpp b/include/anyhttp/asio/any_async_stream_impl.hpp index 676e258..8a027a0 100644 --- a/include/anyhttp/asio/any_async_stream_impl.hpp +++ b/include/anyhttp/asio/any_async_stream_impl.hpp @@ -13,7 +13,7 @@ #include -namespace anyhttp +namespace anyhttp::inline asio { // ================================================================================================= @@ -38,7 +38,7 @@ class any_async_stream::Impl virtual void async_shutdown_impl(ShutdownHandler handler) { #if 1 - asio::async_immediate(get_executor(), [handler = std::move(handler)]() mutable { + boost::asio::async_immediate(get_executor(), [handler = std::move(handler)]() mutable { std::move(handler)(boost::system::error_code()); }); #else @@ -106,9 +106,9 @@ any_async_stream make_any_async_stream(Stream&& stream) return any_async_stream(std::make_unique>(std::move(stream))); } -extern template class any_async_stream_impl; +extern template class any_async_stream_impl; extern template class any_async_stream_impl; // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/include/anyhttp/asio/buffer_array.hpp b/include/anyhttp/asio/buffer_array.hpp index db96c1e..ac06921 100644 --- a/include/anyhttp/asio/buffer_array.hpp +++ b/include/anyhttp/asio/buffer_array.hpp @@ -19,7 +19,7 @@ #include #include -namespace anyhttp +namespace anyhttp::inline asio { using boost::asio::const_buffer; @@ -276,4 +276,4 @@ using const_buffer_array = buffer_array; template using mutable_buffer_array = buffer_array; -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/include/anyhttp/asio/concepts.hpp b/include/anyhttp/asio/concepts.hpp index 3bccf2e..3d260fc 100644 --- a/include/anyhttp/asio/concepts.hpp +++ b/include/anyhttp/asio/concepts.hpp @@ -6,7 +6,7 @@ #include #include -namespace anyhttp +namespace anyhttp::inline asio { template @@ -40,4 +40,4 @@ concept AsyncReadStream = stream.async_read_some(buffer, handler); }; -} // namespace anyhttp \ No newline at end of file +} // namespace anyhttp::inline asio \ No newline at end of file diff --git a/include/anyhttp/asio/net.hpp b/include/anyhttp/asio/net.hpp index 161dd42..8847235 100644 --- a/include/anyhttp/asio/net.hpp +++ b/include/anyhttp/asio/net.hpp @@ -35,16 +35,16 @@ #include #include -namespace anyhttp +namespace anyhttp::inline asio { // ================================================================================================= -using TcpSocket = asio::ip::tcp::socket; -using TcpAcceptor = asio::ip::tcp::acceptor; -using TlsContext = asio::ssl::context; -using TlsStream = asio::ssl::stream; -using UdpSocket = asio::ip::udp::socket; +using TcpSocket = boost::asio::ip::tcp::socket; +using TcpAcceptor = boost::asio::ip::tcp::acceptor; +using TlsContext = boost::asio::ssl::context; +using TlsStream = boost::asio::ssl::stream; +using UdpSocket = boost::asio::ip::udp::socket; /// What the server serves cleartext HTTP/1.1 and h2c over: for ASIO, the type-erased stream, /// which keeps it exercised. @@ -291,14 +291,14 @@ inline void close(TcpAcceptor& acceptor) noexcept /// Accepts the next connection into \p socket: (error_code). inline auto accept(TcpAcceptor& acceptor, TcpSocket& socket) { - return acceptor.async_accept(socket, asio::as_tuple); + return acceptor.async_accept(socket, boost::asio::as_tuple); } /// Turns off Nagle's algorithm, without which HTTP/2 is very slow (and the TLS handshake slower). inline void no_delay(TcpSocket& socket) { error_code ec; - socket.set_option(asio::ip::tcp::no_delay(true), ec); + socket.set_option(boost::asio::ip::tcp::no_delay(true), ec); } /// The kernel's send and receive buffer sizes of \p socket, for the log. @@ -327,9 +327,9 @@ void set_peer_name(TlsStream& stream, const std::string& host); /// The TLS handshake, in the role given: (error_code). inline auto handshake(TlsStream& stream, Role role) { - return stream.async_handshake(role == Role::server ? asio::ssl::stream_base::server - : asio::ssl::stream_base::client, - asio::as_tuple); + return stream.async_handshake(role == Role::server ? boost::asio::ssl::stream_base::server + : boost::asio::ssl::stream_base::client, + boost::asio::as_tuple); } /// The protocol ALPN has agreed on, empty for none. @@ -339,7 +339,10 @@ std::string_view alpn(TlsStream& stream); std::string tls_info(TlsStream& stream); /// Whether a read has failed because the TLS connection ended without a "close_notify". -inline bool is_truncated(const error_code& ec) { return ec == asio::ssl::error::stream_truncated; } +inline bool is_truncated(const error_code& ec) +{ + return ec == boost::asio::ssl::error::stream_truncated; +} // // UDP, for HTTP/3. The backend sends and receives on the native handle, with sendmsg() and @@ -391,12 +394,12 @@ class Signal void notify() { std::ignore = channel_.try_send(error_code{}); } /// auto [ec] = co_await signal.wait(); - auto wait() { return channel_.async_receive(asio::as_tuple); } + auto wait() { return channel_.async_receive(boost::asio::as_tuple); } private: - asio::experimental::concurrent_channel channel_; + boost::asio::experimental::concurrent_channel channel_; }; // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/include/anyhttp/asio/runtime.hpp b/include/anyhttp/asio/runtime.hpp index f6eee1b..e145f56 100644 --- a/include/anyhttp/asio/runtime.hpp +++ b/include/anyhttp/asio/runtime.hpp @@ -1,7 +1,7 @@ #pragma once // -// The runtime layer for Boost.Asio: completion tokens, asio::awaitable, any_io_executor. See +// The runtime layer for Boost.Asio: completion tokens, boost::asio::awaitable, any_io_executor. See // anyhttp/runtime.hpp for what this is, and corosio/runtime.hpp for the other runtime, which // defines the same names. // @@ -45,6 +45,9 @@ #include #include +namespace anyhttp::inline asio +{ + /// \cond internal /// The error code of what \p ptr holds, as thrown by the runtime. inline boost::system::error_code code(const std::exception_ptr& ptr) @@ -67,28 +70,27 @@ inline boost::system::error_code code(const std::exception_ptr& ptr) /// \endcond -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 -using Task = asio::awaitable; +using Task = boost::asio::awaitable; /// What sessions, streams and their readers and writers run on. -using Executor = asio::any_io_executor; +using Executor = boost::asio::any_io_executor; /// The runtime's own IP address, which the API speaks, too. -using IpAddress = asio::ip::address; +using IpAddress = boost::asio::ip::address; /// The runtime's own TCP endpoint. -using TcpEndpoint = asio::ip::tcp::endpoint; +using TcpEndpoint = boost::asio::ip::tcp::endpoint; /// The runtime's own UDP endpoint. -using UdpEndpoint = asio::ip::udp::endpoint; +using UdpEndpoint = boost::asio::ip::udp::endpoint; + +/// \p address, or the IPv4 address an IPv4-mapped IPv6 one maps. Defined in net.cpp. +IpAddress normalize(IpAddress address); /// What every operation reports its outcome with. using error_code = boost::system::error_code; @@ -126,9 +128,9 @@ namespace errc = boost::system::errc; namespace errors { /// The end of an incoming body. -/// \runtimes{asio::error::eof,capy::error::eof} +/// \runtimes{boost::asio::error::eof,capy::error::eof} /// \hideinitializer -inline const error_code eof = asio::error::eof; +inline const error_code eof = boost::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} @@ -141,24 +143,24 @@ inline const error_code partial_message = boost::beast::http::error::partial_mes 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} +/// \runtimes{boost::asio::error::would_block,errc::operation_would_block} /// \hideinitializer -inline const error_code would_block = asio::error::would_block; +inline const error_code would_block = boost::asio::error::would_block; /// A connection that can not carry any more requests or responses. -/// \runtimes{asio::error::connection_aborted,errc::connection_aborted} +/// \runtimes{boost::asio::error::connection_aborted,errc::connection_aborted} /// \hideinitializer -inline const error_code connection_aborted = asio::error::connection_aborted; +inline const error_code connection_aborted = boost::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} +/// \runtimes{boost::asio::error::bad_descriptor,errc::bad_file_descriptor} /// \hideinitializer -inline const error_code bad_descriptor = asio::error::bad_descriptor; +inline const error_code bad_descriptor = boost::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} +/// \runtimes{boost::asio::error::already_started,errc::connection_already_in_progress} /// \hideinitializer -inline const error_code already_started = asio::error::already_started; +inline const error_code already_started = boost::asio::error::already_started; /// A header section larger than Config::max_header_size. /// \runtimes{beast::http::error::header_limit,beast::http::error::header_limit} @@ -190,7 +192,7 @@ inline error_code last_error() noexcept { return {errno, boost::system::system_c * them from their engines' callbacks. */ template -using Completion = asio::any_completion_handler; +using Completion = boost::asio::any_completion_handler; // // Completing a parked operation. Invoking a Completion directly resumes its caller right there, @@ -214,12 +216,13 @@ inline void complete_immediately(Handler&& handler, const Executor& fallback, Ar return; #if 1 - asio::async_immediate(fallback, [handler = std::forward(handler), - ... args = std::forward(args)]() mutable { // + boost::asio::async_immediate(fallback, [handler = std::forward(handler), + ... args = std::forward(args)]() mutable { // std::move(handler)(std::move(args)...); }); #else - asio::any_completion_executor ex = asio::get_associated_immediate_executor(handler, fallback); + boost::asio::any_completion_executor ex = + boost::asio::get_associated_immediate_executor(handler, fallback); ex.execute([handler = std::forward(handler), ... args = std::forward(args)]() mutable { // std::move(handler)(std::move(args)...); @@ -235,8 +238,8 @@ inline void complete_immediately(Handler&& handler, const Executor& fallback, Ar template inline void complete_later(Handler&& handler, const Executor& executor, Args&&... args) { - asio::post(executor, [handler = std::forward(handler), - ... args = std::forward(args)]() mutable { // + boost::asio::post(executor, [handler = std::forward(handler), + ... args = std::forward(args)]() mutable { // std::move(handler)(std::move(args)...); }); } @@ -250,11 +253,12 @@ inline void complete_later(Handler&& handler, const Executor& executor, Args&&.. template inline void on_cancel(Completion& handler, F&& on_cancel) { - auto slot = asio::get_associated_cancellation_slot(handler); + auto slot = boost::asio::get_associated_cancellation_slot(handler); if (slot.is_connected() && !slot.has_handler()) - slot.assign([on_cancel = std::forward(on_cancel)](asio::cancellation_type_t) mutable { // - on_cancel(); - }); + slot.assign( + [on_cancel = std::forward(on_cancel)](boost::asio::cancellation_type_t) mutable { // + on_cancel(); + }); } // ------------------------------------------------------------------------------------------------- @@ -274,11 +278,12 @@ inline void on_cancel(Completion& handler, F&& on_cancel) template auto initiate(Init&& init) { - return asio::async_initiate&, Signature>( + return boost::asio::async_initiate&, + Signature>( [init = std::forward(init)](Completion handler) mutable { init(std::move(handler)); }, - asio::as_tuple(asio::deferred)); + boost::asio::as_tuple(boost::asio::deferred)); } /// \endcond @@ -288,7 +293,7 @@ auto initiate(Init&& init) /// \param task What to run. inline void launch(const Executor& executor, Task task) { - asio::co_spawn(executor, std::move(task), asio::detached); + boost::asio::co_spawn(executor, std::move(task), boost::asio::detached); } /// Starts \p task on \p executor and calls \p on_done with what it threw, if anything, when it @@ -299,7 +304,7 @@ inline void launch(const Executor& executor, Task task) template inline void launch(const Executor& executor, Task task, F&& on_done) { - asio::co_spawn(executor, std::move(task), std::forward(on_done)); + boost::asio::co_spawn(executor, std::move(task), std::forward(on_done)); } /// \cond internal @@ -315,47 +320,48 @@ template inline void launch(const Executor& executor, IoTask task, Completion&& handler) { - auto slot = asio::get_associated_cancellation_slot(handler); - auto handler_executor = asio::get_associated_executor(handler, executor); + auto slot = boost::asio::get_associated_cancellation_slot(handler); + auto handler_executor = boost::asio::get_associated_executor(handler, executor); // // co_spawn() gives the task a cancellation slot of its own, so binding the caller's to it is // what makes cancelling the operation reach whatever the task is waiting for. // - 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, - results result) mutable { - if (ep) - std::move(handler)(code(ep), T{}...); - else - std::apply(std::move(handler), std::move(result)); - }))); + boost::asio::co_spawn( + executor, std::move(task), + boost::asio::bind_cancellation_slot( + slot, boost::asio::bind_executor( + handler_executor, [handler = std::move(handler)](const std::exception_ptr& ep, + results result) mutable { + if (ep) + std::move(handler)(code(ep), T{}...); + else + std::apply(std::move(handler), std::move(result)); + }))); } // ------------------------------------------------------------------------------------------------- /// Lets whatever else is ready to run on the caller's executor run first. -inline auto yield_now() { return asio::post(asio::deferred); } +inline auto yield_now() { return boost::asio::post(boost::asio::deferred); } /// Calls \p function from \p executor's queue, after whatever is running now has returned. template inline void run_later(const Executor& executor, F&& function) { - asio::post(executor, std::forward(function)); + boost::asio::post(executor, std::forward(function)); } /// Calls \p function on \p executor: right away if the caller runs on it already, later if not. template inline void dispatch_to(const Executor& executor, F&& function) { - asio::dispatch(executor, std::forward(function)); + boost::asio::dispatch(executor, std::forward(function)); } /// A new strand on \p executor, for a connection that has to be serialized against itself /// while the io_context runs on several threads (\c server::Config::use_strand). -inline Executor new_strand(const Executor& executor) { return asio::make_strand(executor); } +inline Executor new_strand(const Executor& executor) { return boost::asio::make_strand(executor); } /// \endcond @@ -365,9 +371,9 @@ inline Executor new_strand(const Executor& executor) { return asio::make_strand( /// \return The error, if the wait has been cancelled. inline Task delay(std::chrono::steady_clock::duration duration) { - asio::steady_timer timer(co_await asio::this_coro::executor); + boost::asio::steady_timer timer(co_await boost::asio::this_coro::executor); timer.expires_after(duration); - auto [ec] = co_await timer.async_wait(asio::as_tuple); + auto [ec] = co_await timer.async_wait(boost::asio::as_tuple); co_return ec; } @@ -383,7 +389,7 @@ inline Task delay(std::chrono::steady_clock::duration duration) template auto when_both(Task a, Task b) { - using namespace asio::experimental::awaitable_operators; + using namespace boost::asio::experimental::awaitable_operators; return std::move(a) && std::move(b); } @@ -400,7 +406,7 @@ auto when_both(Task a, Task b) template auto when_either(Task a, Task b) { - using namespace asio::experimental::awaitable_operators; + using namespace boost::asio::experimental::awaitable_operators; return std::move(a) || std::move(b); } @@ -412,7 +418,7 @@ auto when_either(Task a, Task b) * * \return What to await. */ -inline auto reset_cancellation() { return asio::this_coro::reset_cancellation_state(); } +inline auto reset_cancellation() { return boost::asio::this_coro::reset_cancellation_state(); } /** * Awaits \p task although the caller has been cancelled already, once reset_cancellation() has @@ -473,9 +479,9 @@ class Event if (set_) co_return error_code{}; - executor_ = co_await asio::this_coro::executor; - co_return co_await asio::async_initiate&, - void(error_code)>( + executor_ = co_await boost::asio::this_coro::executor; + co_return co_await boost::asio::async_initiate< + const boost::asio::as_tuple_t&, void(error_code)>( [this](Completion waiter) { assert(!waiter_); waiter_ = std::move(waiter); @@ -484,7 +490,7 @@ class Event wake(errors::canceled); }); }, - asio::as_tuple(asio::deferred)); + boost::asio::as_tuple(boost::asio::deferred)); } private: @@ -521,7 +527,7 @@ class Timer void cancel() { timer_.cancel(); } private: - asio::steady_timer timer_; + boost::asio::steady_timer timer_; }; // ================================================================================================= @@ -539,9 +545,9 @@ namespace io /// Reads some bytes into \p buffer: (error_code, size_t). template -auto read_some(Stream& stream, asio::mutable_buffer buffer) +auto read_some(Stream& stream, boost::asio::mutable_buffer buffer) { - return stream.async_read_some(buffer, asio::as_tuple); + return stream.async_read_some(buffer, boost::asio::as_tuple); } /// Writes all of \p buffers, unless an error comes first: (error_code, size_t). @@ -553,27 +559,27 @@ auto write(Stream& stream, const ConstBufferSequence& buffers) // would split them into 64 KiB each. A large HTTP/1.1 header then takes several round trips // through the reactor, and a server that rejects it early catches the client in the middle. // - return asio::async_write( + return boost::asio::async_write( stream, buffers, [](const error_code& ec, size_t) -> size_t { return ec ? 0 : std::numeric_limits::max(); }, - asio::as_tuple); + boost::asio::as_tuple); } /// Peeks at what has arrived on \p socket, without taking it: (error_code, size_t). template -auto peek(Socket& socket, asio::mutable_buffer buffer) +auto peek(Socket& socket, boost::asio::mutable_buffer buffer) { - return socket.async_receive(buffer, Socket::message_peek, asio::as_tuple); + return socket.async_receive(buffer, Socket::message_peek, boost::asio::as_tuple); } /// Receives a datagram into \p buffer, on a connected datagram socket: (error_code, /// size_t). template -auto receive(Socket& socket, asio::mutable_buffer buffer) +auto receive(Socket& socket, boost::asio::mutable_buffer buffer) { - return socket.async_receive(buffer, asio::as_tuple); + return socket.async_receive(buffer, boost::asio::as_tuple); } /// Waits until \p socket has something to read, for a caller that reads it by hand @@ -581,7 +587,7 @@ auto receive(Socket& socket, asio::mutable_buffer buffer) template auto wait_readable(Socket& socket) { - return socket.async_wait(Socket::wait_read, asio::as_tuple); + return socket.async_wait(Socket::wait_read, boost::asio::as_tuple); } } // namespace io @@ -590,4 +596,4 @@ auto wait_readable(Socket& socket) /// \endcond -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/include/anyhttp/client.hpp b/include/anyhttp/client.hpp index ade69e7..5d2c394 100644 --- a/include/anyhttp/client.hpp +++ b/include/anyhttp/client.hpp @@ -203,12 +203,12 @@ class Request : public Writer template auto async_get_response(CompletionToken&& token = CompletionToken()) { - auto executor = asio::get_associated_executor(token, get_executor()); - return asio::async_initiate( - asio::bind_executor(executor, - [this](auto&& handler) { // - async_get_response_any(std::move(handler)); - }), + auto executor = boost::asio::get_associated_executor(token, get_executor()); + return boost::asio::async_initiate( + boost::asio::bind_executor(executor, + [this](auto&& handler) { // + async_get_response_any(std::move(handler)); + }), token); } #endif @@ -276,8 +276,8 @@ class Client template auto async_connect(CompletionToken&& token = CompletionToken()) { - auto executor = asio::get_associated_executor(token, get_executor()); - return asio::async_initiate( // + auto executor = boost::asio::get_associated_executor(token, get_executor()); + return boost::asio::async_initiate( // bind_executor(executor, [&](auto&& handler) { async_connect_any(std::move(handler)); }), token); } diff --git a/include/anyhttp/common.hpp b/include/anyhttp/common.hpp index fad40a5..fe4ce7d 100644 --- a/include/anyhttp/common.hpp +++ b/include/anyhttp/common.hpp @@ -149,7 +149,7 @@ using StatusHandler = Completion; #if ANYHTTP_ASIO /// What an operation of a runtime that has completion tokens takes when it is given none. -using DefaultCompletionToken = asio::default_completion_token_t; +using DefaultCompletionToken = boost::asio::default_completion_token_t; #endif // ================================================================================================= @@ -196,8 +196,7 @@ Defer defer(F&& f, T&&... t) return Defer(std::forward(f), std::forward(t)...); } -/// \p address, or the IPv4 address an IPv4-mapped IPv6 one maps. Defined by each runtime. -IpAddress normalize(IpAddress address); +/// \p endpoint, with its address normalized, see the runtime's normalize(IpAddress). TcpEndpoint normalize(const TcpEndpoint& endpoint); /// Which end of a connection a session is, for the colour of its log prefix. diff --git a/include/anyhttp/corosio/net.hpp b/include/anyhttp/corosio/net.hpp index 5bb994d..ac579f4 100644 --- a/include/anyhttp/corosio/net.hpp +++ b/include/anyhttp/corosio/net.hpp @@ -37,15 +37,15 @@ #include #include -namespace anyhttp +namespace anyhttp::inline corosio { // ================================================================================================= -using TcpSocket = corosio::tcp_socket; -using TcpAcceptor = corosio::tcp_acceptor; -using TlsContext = corosio::tls_context; -using UdpSocket = corosio::udp_socket; +using TcpSocket = boost::corosio::tcp_socket; +using TcpAcceptor = boost::corosio::tcp_acceptor; +using TlsContext = boost::corosio::tls_context; +using UdpSocket = boost::corosio::udp_socket; /** * TLS over a TCP socket. corosio's TLS stream erases the type of what it runs on, so it can not @@ -82,7 +82,7 @@ class TlsStream } TcpSocket& socket() noexcept { return *socket_; } - corosio::openssl_stream& tls() noexcept { return tls_; } + boost::corosio::openssl_stream& tls() noexcept { return tls_; } private: /// The most a TLS record holds: anything that does not fill one is worth a copy. @@ -110,7 +110,7 @@ class TlsStream } std::unique_ptr socket_; - corosio::openssl_stream tls_; + boost::corosio::openssl_stream tls_; std::unique_ptr linearized_; }; @@ -176,9 +176,9 @@ enum class Shutdown template error_code shutdown(Stream& stream, Shutdown what) noexcept { - return get_socket(stream).shutdown(what == Shutdown::receive ? corosio::shutdown_receive - : what == Shutdown::send ? corosio::shutdown_send - : corosio::shutdown_both); + return get_socket(stream).shutdown(what == Shutdown::receive ? boost::corosio::shutdown_receive + : what == Shutdown::send ? boost::corosio::shutdown_send + : boost::corosio::shutdown_both); } template @@ -217,7 +217,8 @@ Task async_teardown(Stream& stream) { if constexpr (std::same_as) { - auto [ec] = co_await corosio::timeout(stream.tls().shutdown(), std::chrono::seconds(2)); + auto [ec] = + co_await boost::corosio::timeout(stream.tls().shutdown(), std::chrono::seconds(2)); co_return ec; } else @@ -256,7 +257,7 @@ inline auto accept(TcpAcceptor& acceptor, TcpSocket& socket) { return acceptor.a /// As with ASIO, a failure here is not worth an error. inline void no_delay(TcpSocket& socket) { - std::ignore = socket.set_option(corosio::socket_option::no_delay(true)); + std::ignore = socket.set_option(boost::corosio::socket_option::no_delay(true)); } std::pair buffer_sizes(TcpSocket& socket); @@ -280,8 +281,8 @@ inline void set_peer_name(TlsStream& stream, const std::string& host) inline auto handshake(TlsStream& stream, Role role) { - return stream.tls().handshake(role == Role::server ? corosio::tls_role::server - : corosio::tls_role::client); + return stream.tls().handshake(role == Role::server ? boost::corosio::tls_role::server + : boost::corosio::tls_role::client); } inline std::string_view alpn(TlsStream& stream) { return stream.tls().alpn_protocol(); } @@ -296,7 +297,8 @@ inline UdpSocket make_udp_socket(const Executor& executor) { return UdpSocket(ex /// corosio opens every socket non-blocking. inline error_code open(UdpSocket& socket, const UdpEndpoint& endpoint) noexcept { - return socket.open(endpoint.address().is_v6() ? corosio::family::v6 : corosio::family::v4); + return socket.open(endpoint.address().is_v6() ? boost::corosio::family::v6 + : boost::corosio::family::v4); } inline void cancel(UdpSocket& socket) noexcept { socket.cancel(); } @@ -330,4 +332,4 @@ class Signal // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline corosio diff --git a/include/anyhttp/corosio/runtime.hpp b/include/anyhttp/corosio/runtime.hpp index d43e6ca..bbfe331 100644 --- a/include/anyhttp/corosio/runtime.hpp +++ b/include/anyhttp/corosio/runtime.hpp @@ -56,6 +56,9 @@ #include +namespace anyhttp::inline corosio +{ + /// The error code of what \p ptr holds, as thrown by the runtime. inline std::error_code code(const std::exception_ptr& ptr) { @@ -75,11 +78,7 @@ inline std::error_code code(const std::exception_ptr& ptr) } } -namespace anyhttp -{ -namespace asio = boost::asio; namespace capy = boost::capy; -namespace corosio = boost::corosio; // ================================================================================================= @@ -90,9 +89,12 @@ using Executor = capy::any_executor; /// corosio has one endpoint type for TCP and UDP alike: an overload set on TcpEndpoint and /// UdpEndpoint, which compiles with ASIO, is a redefinition here. -using IpAddress = corosio::ip_address; -using TcpEndpoint = corosio::endpoint; -using UdpEndpoint = corosio::endpoint; +using IpAddress = boost::corosio::ip_address; +using TcpEndpoint = boost::corosio::endpoint; +using UdpEndpoint = boost::corosio::endpoint; + +/// \p address, or the IPv4 address an IPv4-mapped IPv6 one maps. Defined in net.cpp. +IpAddress normalize(IpAddress address); using std::error_code; @@ -426,7 +428,7 @@ namespace detail class Strand { public: - explicit Strand(const corosio::io_context::executor_type& executor) : strand_(executor) {} + explicit Strand(const boost::corosio::io_context::executor_type& executor) : strand_(executor) {} auto& context() const noexcept { return strand_.context(); } void on_work_started() const noexcept { strand_.on_work_started(); } @@ -436,7 +438,7 @@ class Strand bool operator==(const Strand&) const noexcept = default; private: - capy::strand strand_; + capy::strand strand_; }; } // namespace detail @@ -447,14 +449,14 @@ class Strand */ inline Executor new_strand(const Executor& executor) { - auto& context = static_cast(executor.context()); + auto& context = static_cast(executor.context()); return Executor(detail::Strand(context.get_executor())); } /// Waits for \p duration to pass, or until cancelled (errors::canceled). inline Task delay(std::chrono::steady_clock::duration duration) { - auto [ec] = co_await corosio::delay(duration); + auto [ec] = co_await boost::corosio::delay(duration); co_return ec; } @@ -579,7 +581,7 @@ class Timer stop_.emplace(); auto wait = [](std::chrono::steady_clock::duration delay, std::function on_expiry) -> Task { - auto [ec] = co_await corosio::delay(delay); + auto [ec] = co_await boost::corosio::delay(delay); // Hoisted out of the condition: GCC cannot parse a co_await inside a member call. const std::stop_token token = co_await capy::this_coro::stop_token; if (!ec && !token.stop_requested()) @@ -610,7 +612,7 @@ namespace io { template -auto read_some(Stream& stream, asio::mutable_buffer buffer) +auto read_some(Stream& stream, boost::asio::mutable_buffer buffer) { return stream.read_some(capy::mutable_buffer(buffer.data(), buffer.size())); } @@ -624,11 +626,11 @@ auto write(Stream& stream, const ConstBufferSequence& buffers) /// Peeks at what has arrived on \p socket, without taking it. corosio has no flags for its /// receive operations, so this waits for readiness and peeks at the native socket. template -IoTask peek(Socket& socket, asio::mutable_buffer buffer) +IoTask peek(Socket& socket, boost::asio::mutable_buffer buffer) { for (;;) { - if (auto [ec] = co_await socket.wait(corosio::wait_type::read); ec) + if (auto [ec] = co_await socket.wait(boost::corosio::wait_type::read); ec) co_return std::tuple{ec, size_t{0}}; auto n = ::recv(socket.native_handle(), buffer.data(), buffer.size(), MSG_PEEK); @@ -645,7 +647,7 @@ IoTask peek(Socket& socket, asio::mutable_buffer buffer) /// read_some(): corosio's datagram operations do not copy their buffer sequence, as its stream /// operations do, but point to it, so it has to outlive the operation. template -IoTask receive(Socket& socket, asio::mutable_buffer buffer) +IoTask receive(Socket& socket, boost::asio::mutable_buffer buffer) { const capy::mutable_buffer buffers(buffer.data(), buffer.size()); auto [ec, n] = co_await socket.recv(buffers); @@ -656,11 +658,11 @@ IoTask receive(Socket& socket, asio::mutable_buffer buffer) template auto wait_readable(Socket& socket) { - return socket.wait(corosio::wait_type::read); + return socket.wait(boost::corosio::wait_type::read); } } // namespace io // ================================================================================================= -} // namespace anyhttp \ No newline at end of file +} // namespace anyhttp::inline corosio \ No newline at end of file diff --git a/include/anyhttp/detail/detect.hpp b/include/anyhttp/detail/detect.hpp index 308ca87..b9713e4 100644 --- a/include/anyhttp/detail/detect.hpp +++ b/include/anyhttp/detail/detect.hpp @@ -29,10 +29,11 @@ boost::tribool buffer_sequence_starts_with(const ConstBufferSequence& buffers, std::string_view prefix) { std::size_t matched = 0; - const auto end = asio::buffer_sequence_end(buffers); - for (auto it = asio::buffer_sequence_begin(buffers); it != end && matched < prefix.size(); ++it) + const auto end = boost::asio::buffer_sequence_end(buffers); + for (auto it = boost::asio::buffer_sequence_begin(buffers); it != end && matched < prefix.size(); + ++it) { - const asio::const_buffer buffer(*it); + const boost::asio::const_buffer buffer(*it); if (buffer.size() == 0) continue; // an empty buffer may well have a null pointer, which memcmp() must not get const auto to_compare = std::min(buffer.size(), prefix.size() - matched); @@ -64,7 +65,7 @@ template IoTask detect(Socket& socket, DynamicBuffer& buffer) { std::array first; - auto [ec, n] = co_await io::peek(socket, asio::buffer(first)); + auto [ec, n] = co_await io::peek(socket, boost::asio::buffer(first)); if (ec) co_return std::tuple{ec, Detected::h1}; if (n == 1 && first[0] == 0x16) diff --git a/include/anyhttp/h1/backend.hpp b/include/anyhttp/h1/backend.hpp index 07193e5..f1acc68 100644 --- a/include/anyhttp/h1/backend.hpp +++ b/include/anyhttp/h1/backend.hpp @@ -13,7 +13,7 @@ #include -namespace anyhttp::beast_impl +namespace anyhttp::h1 { // ================================================================================================= @@ -48,4 +48,4 @@ ANYHTTP_CLIENT_STREAMS(ANYHTTP_H1_CLIENT) // ================================================================================================= -} // namespace anyhttp::beast_impl +} // namespace anyhttp::h1 diff --git a/include/anyhttp/h1/io.hpp b/include/anyhttp/h1/io.hpp index 5c53fa5..74191bf 100644 --- a/include/anyhttp/h1/io.hpp +++ b/include/anyhttp/h1/io.hpp @@ -29,7 +29,7 @@ #include #include -namespace anyhttp::beast_impl::h1 +namespace anyhttp::h1 { namespace http = boost::beast::http; @@ -174,10 +174,10 @@ IoTask write_some(Stream& stream, http::serializer buffers; + boost::container::small_vector buffers; serializer.next(ec, [&](boost::system::error_code& next_ec, const auto& sequence) { next_ec = {}; - for (asio::const_buffer buffer : boost::beast::buffers_range_ref(sequence)) + for (boost::asio::const_buffer buffer : boost::beast::buffers_range_ref(sequence)) buffers.push_back(buffer); }); if (ec) @@ -199,7 +199,6 @@ IoTask write_until(Stream& stream, Serializer& serializer, Predicate don size_t written = 0; while (!done(serializer)) { - auto [ec, n] = co_await h1::write_some(stream, serializer); written += n; if (ec == to_error_code(http::error::need_buffer)) @@ -235,4 +234,4 @@ IoTask write_message(Stream& stream, http::message #include -namespace anyhttp::beast_impl +namespace anyhttp::h1 { // ================================================================================================= @@ -217,16 +217,16 @@ class RequestWriter; * * Operations that would have to wait for an earlier request or response don't. Waiting would * deadlock as soon as the earlier one is taken care of by the same code, after the operation that - * waits for it. Instead, they fail immediately with \c asio::error::would_block, so they can be - * retried later: + * waits for it. Instead, they fail immediately with \c boost::asio::error::would_block, so they can + * be retried later: * * - async_submit(), while the previous request is not complete yet, and * - async_get_response(), while the responses to earlier requests have not been read completely. * * When a request can not be completed -- a write fails, or the request goes away without being * complete -- nothing can be sent after it any more: later calls to async_submit() fail with - * \c asio::error::connection_aborted. Likewise, when a response can not be read completely -- a - * read fails, the response goes away before it has been read, or a request goes away without + * \c boost::asio::error::connection_aborted. Likewise, when a response can not be read completely + * -- a read fails, the response goes away before it has been read, or a request goes away without * asking for its response -- getting any later response fails. */ template @@ -279,4 +279,4 @@ class ClientSession : public ClientSessionBase, public BeastSession // ================================================================================================= -} // namespace anyhttp::beast_impl \ No newline at end of file +} // namespace anyhttp::h1 \ No newline at end of file diff --git a/include/anyhttp/h2/backend.hpp b/include/anyhttp/h2/backend.hpp index 3a6782f..bf3e35a 100644 --- a/include/anyhttp/h2/backend.hpp +++ b/include/anyhttp/h2/backend.hpp @@ -22,7 +22,7 @@ #include #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2 { // ================================================================================================= @@ -71,4 +71,4 @@ ANYHTTP_CLIENT_STREAMS(ANYHTTP_H2_CLIENT) // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/include/anyhttp/h2/common.hpp b/include/anyhttp/h2/common.hpp index c22d9ba..ec2a002 100644 --- a/include/anyhttp/h2/common.hpp +++ b/include/anyhttp/h2/common.hpp @@ -13,7 +13,7 @@ #include #include -namespace anyhttp +namespace anyhttp::h2 { // ================================================================================================= @@ -54,7 +54,7 @@ inline std::string_view value_of(const nghttp2_nv& nv) // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::h2 // ================================================================================================= diff --git a/include/anyhttp/h2/session.hpp b/include/anyhttp/h2/session.hpp index 3ebde16..9a05058 100644 --- a/include/anyhttp/h2/session.hpp +++ b/include/anyhttp/h2/session.hpp @@ -15,7 +15,7 @@ #include "nghttp2/nghttp2.h" -namespace anyhttp::nghttp2 +namespace anyhttp::h2 { // ================================================================================================= @@ -260,4 +260,4 @@ class ClientSession : public ClientReference, public NGHttp2SessionImpl // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/include/anyhttp/h2/session_details.hpp b/include/anyhttp/h2/session_details.hpp index 61b87ae..10a9a17 100644 --- a/include/anyhttp/h2/session_details.hpp +++ b/include/anyhttp/h2/session_details.hpp @@ -19,7 +19,7 @@ #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2 { // ================================================================================================= @@ -91,7 +91,8 @@ Task NGHttp2SessionImpl::send_loop() // if (nread && nread <= (buffer.capacity() - buffer.size())) { - auto copied = asio::buffer_copy(buffer.prepare(nread), asio::buffer(data, nread)); + auto copied = + boost::asio::buffer_copy(buffer.prepare(nread), boost::asio::buffer(data, nread)); assert(nread == copied); buffer.commit(nread); mylogd("send loop: buffered {} more bytes, total {}", nread, buffer.data().size()); @@ -104,8 +105,8 @@ Task NGHttp2SessionImpl::send_loop() // else if (const auto bytes_to_write = buffer.size() + nread; bytes_to_write > 0) { - const auto seq = - std::to_array({buffer.data(), asio::buffer(data, nread)}); + const auto seq = std::to_array( + {buffer.data(), boost::asio::buffer(data, nread)}); mylogd("send loop: writing {} bytes...", bytes_to_write); auto [ec, written] = co_await io::write(stream_, seq); if (ec) @@ -464,4 +465,4 @@ IoTask<> ClientSession::shutdown() // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/include/anyhttp/h2/stream.hpp b/include/anyhttp/h2/stream.hpp index c9adec9..97fb1b8 100644 --- a/include/anyhttp/h2/stream.hpp +++ b/include/anyhttp/h2/stream.hpp @@ -20,7 +20,7 @@ #include #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2 { // ================================================================================================= @@ -63,7 +63,7 @@ class NGHttp2Writer : public Base Executor get_executor() const noexcept override; void content_length(std::optional content_length) override; - void async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) override; + void async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) override; void detach() override; std::string log_prefix() const override; @@ -108,17 +108,20 @@ class NGHttp2Stream : public std::enable_shared_from_this * well. While in \c call_read_handler(), it may be viewing the newly received data that has not * been added to the pending read buffers, yet. */ - asio::const_buffer read_buffer_; + boost::asio::const_buffer read_buffer_; using Buffer = std::vector; std::deque pending_read_buffers_; - Buffer make_buffer(asio::const_buffer buffer) + Buffer make_buffer(boost::asio::const_buffer buffer) { return {static_cast(buffer.data()), - static_cast(buffer.data()) + asio::buffer_size(buffer)}; + static_cast(buffer.data()) + boost::asio::buffer_size(buffer)}; } - static bool is_empty(asio::const_buffer buffer) { return asio::buffer_size(buffer) == 0; } + static bool is_empty(boost::asio::const_buffer buffer) + { + return boost::asio::buffer_size(buffer) == 0; + } /** * Returns true if all data has been read by the user. @@ -137,7 +140,7 @@ class NGHttp2Stream : public std::enable_shared_from_this // // async_write() // - asio::const_buffer write_buffer; // undefined unless write_handler is set + boost::asio::const_buffer write_buffer; // undefined unless write_handler is set WriteHandler write_handler; bool is_deferred = false; @@ -205,11 +208,11 @@ class NGHttp2Stream : public std::enable_shared_from_this ReadSomeHandler read_handler_; boost::asio::mutable_buffer read_handler_buffer_; bool inside_call_read_handler_ = false; - void call_read_handler(asio::const_buffer buffer = {}); + void call_read_handler(boost::asio::const_buffer buffer = {}); // ---------------------------------------------------------------------------------------------- - void async_write(WriteHandler handler, asio::const_buffer buffer, bool eof); + void async_write(WriteHandler handler, boost::asio::const_buffer buffer, bool eof); void resume(); @@ -243,4 +246,4 @@ class NGHttp2Stream : public std::enable_shared_from_this // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/include/anyhttp/h2v2/common.hpp b/include/anyhttp/h2v2/common.hpp index 9544bc6..f3e289c 100644 --- a/include/anyhttp/h2v2/common.hpp +++ b/include/anyhttp/h2v2/common.hpp @@ -15,7 +15,7 @@ #include #include -namespace anyhttp +namespace anyhttp::h2v2 { // ================================================================================================= @@ -82,7 +82,7 @@ inline nghttp2_tstamp timestamp() // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::h2v2 // ================================================================================================= diff --git a/include/anyhttp/h2v2/session.hpp b/include/anyhttp/h2v2/session.hpp index d413044..1a74b2f 100644 --- a/include/anyhttp/h2v2/session.hpp +++ b/include/anyhttp/h2v2/session.hpp @@ -24,9 +24,12 @@ #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2v2 { +/// The backend interface is version-neutral, see anyhttp/h2/backend.hpp. +using Upgrade = h2::Upgrade; + // ================================================================================================= // @@ -281,4 +284,4 @@ class ClientSession : public ClientReference, public NGHttp2SessionImpl // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2v2 diff --git a/include/anyhttp/h2v2/session_details.hpp b/include/anyhttp/h2v2/session_details.hpp index 43a7a29..905ddd3 100644 --- a/include/anyhttp/h2v2/session_details.hpp +++ b/include/anyhttp/h2v2/session_details.hpp @@ -20,7 +20,7 @@ #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2v2 { // ================================================================================================= @@ -94,7 +94,8 @@ Task NGHttp2SessionImpl::send_loop() { const auto bytes_to_write = static_cast(nwrite); mylogd("send loop: writing {} bytes...", bytes_to_write); - auto [ec, written] = co_await io::write(stream_, asio::buffer(buffer.data(), nwrite)); + auto [ec, written] = + co_await io::write(stream_, boost::asio::buffer(buffer.data(), nwrite)); if (ec) { send_failed_ = true; @@ -396,4 +397,4 @@ IoTask<> ClientSession::shutdown() // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2v2 diff --git a/include/anyhttp/h2v2/stream.hpp b/include/anyhttp/h2v2/stream.hpp index 5a07d83..6e7281c 100644 --- a/include/anyhttp/h2v2/stream.hpp +++ b/include/anyhttp/h2v2/stream.hpp @@ -25,7 +25,7 @@ #include #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2v2 { // ================================================================================================= @@ -98,7 +98,7 @@ class NGHttp2Writer : public Base Executor get_executor() const noexcept override; void content_length(std::optional content_length) override; - void async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) override; + void async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) override; void detach() override; std::string log_prefix() const override; @@ -137,17 +137,20 @@ class NGHttp2Stream : public std::enable_shared_from_this * As long as \c pending_read_buffers_ is non-empty, \c read_buffer_ is viewing the part of * the first pending read buffer that has not been delivered, yet. */ - asio::const_buffer read_buffer_; + boost::asio::const_buffer read_buffer_; using Buffer = std::vector; std::deque pending_read_buffers_; - Buffer make_buffer(asio::const_buffer buffer) + Buffer make_buffer(boost::asio::const_buffer buffer) { return {static_cast(buffer.data()), - static_cast(buffer.data()) + asio::buffer_size(buffer)}; + static_cast(buffer.data()) + boost::asio::buffer_size(buffer)}; } - static bool is_empty(asio::const_buffer buffer) { return asio::buffer_size(buffer) == 0; } + static bool is_empty(boost::asio::const_buffer buffer) + { + return boost::asio::buffer_size(buffer) == 0; + } /** * Returns true if all data has been read by the user. @@ -180,7 +183,7 @@ class NGHttp2Stream : public std::enable_shared_from_this // static constexpr size_t write_chunk_size = 16 * 1024; // one DATA frame, one send loop pass const WriteMode write_mode; - asio::const_buffer write_buffer; // undefined unless write_handler is set + boost::asio::const_buffer write_buffer; // undefined unless write_handler is set WriteHandler write_handler; std::vector write_chunk_; // Staged: the piece of the body nghttp2 is pointing into size_t offered_ = 0; @@ -258,11 +261,11 @@ class NGHttp2Stream : public std::enable_shared_from_this ReadSomeHandler read_handler_; boost::asio::mutable_buffer read_handler_buffer_; bool inside_call_read_handler_ = false; - void call_read_handler(asio::const_buffer buffer = {}); + void call_read_handler(boost::asio::const_buffer buffer = {}); // ---------------------------------------------------------------------------------------------- - void async_write(WriteHandler handler, asio::const_buffer buffer, bool eof); + void async_write(WriteHandler handler, boost::asio::const_buffer buffer, bool eof); void resume(); @@ -310,4 +313,4 @@ nghttp2_data_reader make_data_reader(); // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2v2 diff --git a/include/anyhttp/h3/application.hpp b/include/anyhttp/h3/application.hpp index 4df1130..4b070f5 100644 --- a/include/anyhttp/h3/application.hpp +++ b/include/anyhttp/h3/application.hpp @@ -8,7 +8,7 @@ #include #include -namespace anyhttp::http3 +namespace anyhttp::h3 { class Http3Stream; @@ -83,4 +83,4 @@ class Http3Application : public quic::QuicApplication // ================================================================================================= -} // namespace anyhttp::http3 +} // namespace anyhttp::h3 diff --git a/include/anyhttp/h3/backend.hpp b/include/anyhttp/h3/backend.hpp index dd9e048..0a74275 100644 --- a/include/anyhttp/h3/backend.hpp +++ b/include/anyhttp/h3/backend.hpp @@ -16,28 +16,20 @@ #include #include -namespace anyhttp::server +namespace anyhttp::h3 { // ================================================================================================= /// HTTP/3 for QuicServer::add_application(): its requests go to the request handler of `server`. -quic::ApplicationFactory make_http3_application(Server::Impl& server); - -// ================================================================================================= - -} // namespace anyhttp::server - -namespace anyhttp::client -{ - -// ================================================================================================= +quic::ApplicationFactory make_http3_application(server::Server::Impl& server); /// Connects to the first of `endpoints` that it can over QUIC, and returns the HTTP/3 session /// running on it. -Task> -async_connect_http3(Executor executor, std::vector endpoints, const Config& config); +Task> async_connect_http3(Executor executor, + std::vector endpoints, + const client::Config& config); // ================================================================================================= -} // namespace anyhttp::client +} // namespace anyhttp::h3 diff --git a/include/anyhttp/h3/common.hpp b/include/anyhttp/h3/common.hpp index ab3fd03..013fe50 100644 --- a/include/anyhttp/h3/common.hpp +++ b/include/anyhttp/h3/common.hpp @@ -15,7 +15,7 @@ // (src/h3/client.cpp) differ only in the direction their messages travel, not in how an HTTP/3 // stream is driven. What is about QUIC rather than HTTP/3 is in anyhttp/quic/common.hpp. // -namespace anyhttp::http3 +namespace anyhttp::h3 { // ================================================================================================= @@ -32,4 +32,4 @@ void log_headers(std::string_view log_prefix, // ================================================================================================= -} // namespace anyhttp::http3 +} // namespace anyhttp::h3 diff --git a/include/anyhttp/h3/stream.hpp b/include/anyhttp/h3/stream.hpp index 816a48c..4c69be6 100644 --- a/include/anyhttp/h3/stream.hpp +++ b/include/anyhttp/h3/stream.hpp @@ -18,7 +18,7 @@ #include #include -namespace anyhttp::http3 +namespace anyhttp::h3 { class Http3Application; @@ -181,4 +181,4 @@ class Http3Writer : public quic::QuicWriter // ================================================================================================= -} // namespace anyhttp::http3 +} // namespace anyhttp::h3 diff --git a/include/anyhttp/net.hpp b/include/anyhttp/net.hpp index 22575b8..b6ed347 100644 --- a/include/anyhttp/net.hpp +++ b/include/anyhttp/net.hpp @@ -48,7 +48,13 @@ #include -namespace anyhttp::io +// The runtime's own namespace, which io:: is part of: a plain "namespace anyhttp::io" here would +// be a namespace of its own to clang, which does not look through the inline namespace for it. +#if ANYHTTP_COROSIO +namespace anyhttp::inline corosio::io +#else +namespace anyhttp::inline asio::io +#endif { /** @@ -144,6 +150,6 @@ error_code set_option(UdpSocket& socket, int level, int name, int value) noexcep /// Sends \p buffer on the connected \p socket, without waiting: when its send buffer is full, /// that is errc::operation_would_block. -error_code send(UdpSocket& socket, asio::const_buffer buffer) noexcept; +error_code send(UdpSocket& socket, boost::asio::const_buffer buffer) noexcept; -} // namespace anyhttp::io +} // namespace anyhttp::io (in the runtime's namespace) diff --git a/include/anyhttp/quic.hpp b/include/anyhttp/quic.hpp index cab70d7..1fa59f2 100644 --- a/include/anyhttp/quic.hpp +++ b/include/anyhttp/quic.hpp @@ -219,11 +219,11 @@ class Connection template auto async_accept_stream(CompletionToken&& token = CompletionToken()) { - return asio::async_initiate( - asio::bind_executor(asio::get_associated_executor(token, get_executor()), - [this](AcceptStreamHandler handler) { // - async_accept_stream_any(std::move(handler)); - }), + return boost::asio::async_initiate( + boost::asio::bind_executor(boost::asio::get_associated_executor(token, get_executor()), + [this](AcceptStreamHandler handler) { // + async_accept_stream_any(std::move(handler)); + }), token); } #endif @@ -283,9 +283,9 @@ template ( - asio::bind_executor( + auto handler_executor = boost::asio::get_associated_executor(token, executor); + return boost::asio::async_initiate( + boost::asio::bind_executor( handler_executor, [executor = std::move(executor), host = std::move(host), port = std::move(port), config = std::move(config)](ConnectHandler handler) mutable { diff --git a/include/anyhttp/quic/application.hpp b/include/anyhttp/quic/application.hpp index 9385286..270ecfd 100644 --- a/include/anyhttp/quic/application.hpp +++ b/include/anyhttp/quic/application.hpp @@ -21,7 +21,7 @@ class QuicStream; // ================================================================================================= // -// What runs on top of a QUIC connection: HTTP/3 (http3::Http3Application, on nghttp3) or plain +// What runs on top of a QUIC connection: HTTP/3 (h3::Http3Application, on nghttp3) or plain // streams. A session owns exactly one, picked by the ALPN the handshake negotiates -- on the server // in the TLS ALPN callback, on the client before it connects -- and forwards to it every ngtcp2 // callback that is about streams rather than about the connection. It is also the write diff --git a/include/anyhttp/quic/backend.hpp b/include/anyhttp/quic/backend.hpp index 5fd6ca3..918b1cd 100644 --- a/include/anyhttp/quic/backend.hpp +++ b/include/anyhttp/quic/backend.hpp @@ -26,11 +26,6 @@ using ApplicationFactory = std::function(QuicSe /// Plain streams for QuicServer::add_application(): each connection goes to `handler`. ApplicationFactory make_stream_application(Config config, ConnectionHandler handler); -} // namespace anyhttp::quic - -namespace anyhttp::server -{ - // ================================================================================================= // @@ -61,8 +56,9 @@ class QuicServer /// Binds the UDP socket to `endpoint`, usually the address and port the TCP acceptor is already /// listening on, so that all three HTTP versions share one endpoint. -std::shared_ptr make_quic_server(Server::Impl& server, const UdpEndpoint& endpoint); +std::shared_ptr make_quic_server(server::Server::Impl& server, + const UdpEndpoint& endpoint); // ================================================================================================= -} // namespace anyhttp::server +} // namespace anyhttp::quic diff --git a/include/anyhttp/quic/stream.hpp b/include/anyhttp/quic/stream.hpp index d8f5b02..0012b41 100644 --- a/include/anyhttp/quic/stream.hpp +++ b/include/anyhttp/quic/stream.hpp @@ -83,11 +83,11 @@ class QuicStream : public std::enable_shared_from_this // for it. // std::deque> pending_read; - asio::const_buffer read_head; // view of pending_read.front() not yet delivered - asio::const_buffer incoming; // chunk on_data_chunk() is delivering, not yet taken + boost::asio::const_buffer read_head; // view of pending_read.front() not yet delivered + boost::asio::const_buffer incoming; // chunk on_data_chunk() is delivering, not yet taken bool eof_received = false; ReadSomeHandler read_handler; - asio::mutable_buffer read_handler_buffer; + boost::asio::mutable_buffer read_handler_buffer; bool call_read_handler_active = false; // re-entrancy guard, see call_read_handler() // @@ -95,14 +95,14 @@ class QuicStream : public std::enable_shared_from_this // handler before issuing another (same contract as e.g. Beast) -- so this is flat per-stream // state rather than a queue of pending writes. // - // write_source is the caller's buffer, referenced, not copied, the way asio::async_write + // write_source is the caller's buffer, referenced, not copied, the way boost::asio::async_write // generally requires: it must stay valid until write_handler fires. What completes the write // depends on write_mode: all of it acknowledged in ZeroCopy, all of it copied out and handed // over in Staged. See WriteMode. // const WriteMode write_mode; bool write_active = false; - asio::const_buffer write_source; + boost::asio::const_buffer write_source; // // Whether the active write ends the body. The FIN then goes out with the write's last bytes // -- one QUIC STREAM frame carrying both the tail of the body and the FIN -- rather than @@ -162,7 +162,7 @@ class QuicStream : public std::enable_shared_from_this // // Data flow from user land to the stack (outgoing body). // - void start_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof); + void start_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof); // // Points `vec` at what there is to send from body offset `from` on, and returns how many @@ -256,14 +256,14 @@ class QuicReader : public Interface std::optional content_length() const noexcept override { return std::nullopt; } - void async_read_some(asio::mutable_buffer buffer, ReadSomeHandler&& handler) override + void async_read_some(boost::asio::mutable_buffer buffer, ReadSomeHandler&& handler) override { // // An empty buffer is not a request to read anything: complete right away, without looking // at whether the body has ended or the stream is even still there -- as ASIO does for a // zero-length read. // - if (asio::buffer_size(buffer) == 0) + if (boost::asio::buffer_size(buffer) == 0) { complete_immediately(std::move(handler), executor, error_code{}, size_t{0}); return; @@ -341,7 +341,7 @@ class QuicWriter : public Interface void content_length(std::optional) override {} - void async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) override + void async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) override { if (stream) { @@ -358,7 +358,7 @@ class QuicWriter : public Interface // body that was cleanly ended keeps answering as such -- bare re-end idempotent, data // broken_pipe -- and only a stream that vanished mid-body is a connection error. // - const bool empty = asio::buffer_size(buffer) == 0; + const bool empty = boost::asio::buffer_size(buffer) == 0; error_code ec; if (empty && !eof) ec = {}; diff --git a/include/anyhttp/reader.hpp b/include/anyhttp/reader.hpp index d4a95d7..c5e428a 100644 --- a/include/anyhttp/reader.hpp +++ b/include/anyhttp/reader.hpp @@ -86,17 +86,19 @@ class Reader * \return What \p token makes of the operation. */ template - auto async_read_some(asio::mutable_buffer buffer, CompletionToken&& token = CompletionToken()) + auto async_read_some(boost::asio::mutable_buffer buffer, + CompletionToken&& token = CompletionToken()) { // // Binding an executor to the initiating function lets tokens that need one -- the timer // behind cancel_after -- find it here, see Writer. // - return asio::async_initiate( - asio::bind_executor(asio::get_associated_executor(token, get_executor()), - [this](ReadSomeHandler handler, asio::mutable_buffer buffer) { // - async_read_some_any(buffer, std::move(handler)); - }), + return boost::asio::async_initiate( + boost::asio::bind_executor( + boost::asio::get_associated_executor(token, get_executor()), + [this](ReadSomeHandler handler, boost::asio::mutable_buffer buffer) { // + async_read_some_any(buffer, std::move(handler)); + }), token, buffer); } @@ -112,16 +114,16 @@ class Reader */ template - requires(asio::is_mutable_buffer_sequence::value && - !std::convertible_to) + requires(boost::asio::is_mutable_buffer_sequence::value && + !std::convertible_to) auto async_read_some(const Buffers& buffers, CompletionToken&& token = CompletionToken()) { for (auto& buffer : buffers) if (buffer.size() > 0) - return async_read_some(asio::mutable_buffer(buffer), + return async_read_some(boost::asio::mutable_buffer(buffer), std::forward(token)); - return async_read_some(asio::mutable_buffer{}, std::forward(token)); + return async_read_some(boost::asio::mutable_buffer{}, std::forward(token)); } #endif @@ -134,7 +136,7 @@ class Reader /// Reads a part of the incoming body: auto [ec, n] = co_await reader.read_some(buffer); /// \param buffer Where to put what has been read. /// \return The error, and how many bytes have been read. - IoTask read_some(asio::mutable_buffer buffer); + IoTask read_some(boost::asio::mutable_buffer buffer); /// \overload /// @@ -143,14 +145,14 @@ class Reader /// \param buffers Where to put what has been read. /// \return The error, and how many bytes have been read. template - requires(asio::is_mutable_buffer_sequence::value && - !std::convertible_to) + requires(boost::asio::is_mutable_buffer_sequence::value && + !std::convertible_to) IoTask read_some(const Buffers& buffers) { for (auto& buffer : buffers) if (buffer.size() > 0) - return read_some(asio::mutable_buffer(buffer)); - return read_some(asio::mutable_buffer{}); + return read_some(boost::asio::mutable_buffer(buffer)); + return read_some(boost::asio::mutable_buffer{}); } #if ANYHTTP_COROSIO @@ -163,8 +165,8 @@ class Reader for (auto it = capy::buffer_sequence_begin(buffers); it != capy::buffer_sequence_end(buffers); ++it) if (capy::mutable_buffer buffer = *it; buffer.size() > 0) - return read_some(asio::mutable_buffer(buffer.data(), buffer.size())); - return read_some(asio::mutable_buffer{}); + return read_some(boost::asio::mutable_buffer(buffer.data(), buffer.size())); + return read_some(boost::asio::mutable_buffer{}); } #endif @@ -176,7 +178,7 @@ class Reader private: #if ANYHTTP_ASIO - void async_read_some_any(asio::mutable_buffer buffer, ReadSomeHandler&& handler); + void async_read_some_any(boost::asio::mutable_buffer buffer, ReadSomeHandler&& handler); #endif std::shared_ptr impl_; diff --git a/include/anyhttp/reader_impl.hpp b/include/anyhttp/reader_impl.hpp index 55a456d..c2d8507 100644 --- a/include/anyhttp/reader_impl.hpp +++ b/include/anyhttp/reader_impl.hpp @@ -31,7 +31,7 @@ class Reader::Impl : public std::enable_shared_from_this // // Reads at most one buffer worth of the incoming body. The end of the body is reported the - // way ASIO reports it everywhere else: \c asio::error::eof with zero bytes, and again for + // way ASIO reports it everywhere else: \c boost::asio::error::eof with zero bytes, and again for // every further read -- including reads issued after the underlying stream object is long // gone. A body that ends before it was supposed to -- a reset stream, a connection that went // away mid-message -- is reported as \c http::error::partial_message instead, so the two @@ -40,7 +40,7 @@ class Reader::Impl : public std::enable_shared_from_this // An empty buffer is not a request to do anything; it completes immediately with success and // zero bytes, wherever the body stands. // - virtual void async_read_some(asio::mutable_buffer buffer, ReadSomeHandler&& handler) = 0; + virtual void async_read_some(boost::asio::mutable_buffer buffer, ReadSomeHandler&& handler) = 0; // // Every operation comes in two shapes: completing a handler (async_*()), which the completion @@ -49,7 +49,7 @@ class Reader::Impl : public std::enable_shared_from_this // handler (HTTP/2, HTTP/3) makes the coroutine cheap, through initiate(). Doing the I/O in a // coroutine (HTTP/1.1) makes the handler shape cost a spawn, through launch(). // - virtual IoTask read_some(asio::mutable_buffer buffer) + virtual IoTask read_some(boost::asio::mutable_buffer buffer) { co_return co_await initiate([this, buffer](ReadSomeHandler handler) { // async_read_some(buffer, std::move(handler)); diff --git a/include/anyhttp/request_handlers.hpp b/include/anyhttp/request_handlers.hpp index 567fbee..71d05ba 100644 --- a/include/anyhttp/request_handlers.hpp +++ b/include/anyhttp/request_handlers.hpp @@ -91,7 +91,7 @@ template Task send(Writer& request, Range range) { logd("send: (contiguous range)..."); - if (auto [ec] = co_await request.write(asio::buffer(range.data(), range.size())); ec) + if (auto [ec] = co_await request.write(boost::asio::buffer(range.data(), range.size())); ec) throw_error(ec); logd("send: (contiguous range)... done"); } @@ -113,7 +113,7 @@ Task send(Writer& request, Range range) for (; it != last && out != buffer.end(); ++it, ++out) *out = *it; const auto n = static_cast(out - buffer.begin()); - if (auto [ec] = co_await request.write(asio::buffer(buffer.data(), n)); ec) + if (auto [ec] = co_await request.write(boost::asio::buffer(buffer.data(), n)); ec) { if (ec == errc::operation_canceled) logd("[{}] send: (range) {} after {} bytes", request.log_prefix(), ec.message(), bytes); diff --git a/include/anyhttp/server.hpp b/include/anyhttp/server.hpp index 3077ef0..7a3f464 100644 --- a/include/anyhttp/server.hpp +++ b/include/anyhttp/server.hpp @@ -254,12 +254,12 @@ class Response : public Writer CompletionToken&& token = CompletionToken()) { // binding the executor lets tokens that need one -- cancel_after's timer -- find it here - return asio::async_initiate( - asio::bind_executor(asio::get_associated_executor(token, get_executor()), - [this](StatusHandler handler, unsigned int status_code, - const Fields& headers) { // - async_submit_any(std::move(handler), status_code, headers); - }), + return boost::asio::async_initiate( + boost::asio::bind_executor(boost::asio::get_associated_executor(token, get_executor()), + [this](StatusHandler handler, unsigned int status_code, + const Fields& headers) { // + async_submit_any(std::move(handler), status_code, headers); + }), token, status_code, headers); } #endif diff --git a/include/anyhttp/server_impl.hpp b/include/anyhttp/server_impl.hpp index 387d0f7..a65a32e 100644 --- a/include/anyhttp/server_impl.hpp +++ b/include/anyhttp/server_impl.hpp @@ -14,6 +14,15 @@ namespace anyhttp class Session; } +// +// The QUIC half of the server, behind anyhttp/quic/backend.hpp: it owns the UDP socket and +// everything QUIC, so that nothing of ngtcp2/nghttp3 reaches this header. +// +namespace anyhttp::quic +{ +class QuicServer; +} + namespace anyhttp::server { @@ -56,12 +65,6 @@ class Response::Impl : public Writer::Impl // ================================================================================================= -// -// The QUIC half of the server, behind anyhttp/quic/backend.hpp: it owns the UDP socket and -// everything QUIC, so that nothing of ngtcp2/nghttp3 reaches this header. -// -class QuicServer; - class Server::Impl : public std::enable_shared_from_this { public: @@ -123,7 +126,7 @@ class Server::Impl : public std::enable_shared_from_this std::mutex session_mutex_; std::set> sessions_; - std::shared_ptr quic_; + std::shared_ptr quic_; RequestHandler request_handler_; bool destroyed_ = false; diff --git a/include/anyhttp/session.hpp b/include/anyhttp/session.hpp index 3ba7456..7a82a8e 100644 --- a/include/anyhttp/session.hpp +++ b/include/anyhttp/session.hpp @@ -95,9 +95,9 @@ class Session auto async_submit(boost::urls::url target, const Fields& headers = {}, CompletionToken&& token = CompletionToken()) { - auto executor = asio::get_associated_executor(token, get_executor()); - return asio::async_initiate( - asio::bind_executor( + auto executor = boost::asio::get_associated_executor(token, get_executor()); + return boost::asio::async_initiate( + boost::asio::bind_executor( executor, [this](auto&& handler, boost::urls::url url, const Fields& headers) { // async_submit_any(std::move(handler), "POST", std::move(url), headers); @@ -129,14 +129,14 @@ class Session auto async_submit(std::string_view method, boost::urls::url target, const Fields& headers = {}, CompletionToken&& token = CompletionToken()) { - auto executor = asio::get_associated_executor(token, get_executor()); - return asio::async_initiate( - asio::bind_executor(executor, - [this](auto&& handler, const std::string& method, boost::urls::url url, - const Fields& headers) { // - async_submit_any(std::move(handler), method, std::move(url), - headers); - }), + auto executor = boost::asio::get_associated_executor(token, get_executor()); + return boost::asio::async_initiate( + boost::asio::bind_executor( + executor, + [this](auto&& handler, const std::string& method, boost::urls::url url, + const Fields& headers) { // + async_submit_any(std::move(handler), method, std::move(url), headers); + }), token, std::string(method), std::move(target), headers); } #endif @@ -179,9 +179,9 @@ class Session auto async_get(boost::urls::url url, const Fields& headers = {}, CompletionToken&& token = CompletionToken()) { - auto executor = asio::get_associated_executor(token, get_executor()); - return asio::async_initiate( - asio::bind_executor( + auto executor = boost::asio::get_associated_executor(token, get_executor()); + return boost::asio::async_initiate( + boost::asio::bind_executor( executor, [this](auto&& handler, boost::urls::url url, const Fields& headers) { // async_get_any(std::move(handler), std::move(url), headers); @@ -218,12 +218,12 @@ class Session CompletionToken = DefaultCompletionToken> auto async_shutdown(CompletionToken&& token = CompletionToken()) { - auto executor = asio::get_associated_executor(token, get_executor()); - return asio::async_initiate( - asio::bind_executor(executor, - [this](auto&& handler) { // - async_shutdown_any(std::move(handler)); - }), + auto executor = boost::asio::get_associated_executor(token, get_executor()); + return boost::asio::async_initiate( + boost::asio::bind_executor(executor, + [this](auto&& handler) { // + async_shutdown_any(std::move(handler)); + }), token); } #endif diff --git a/include/anyhttp/writer.hpp b/include/anyhttp/writer.hpp index 478a00c..ed3e657 100644 --- a/include/anyhttp/writer.hpp +++ b/include/anyhttp/writer.hpp @@ -78,13 +78,14 @@ class Writer * \return What \p token makes of the operation. */ template - auto async_write(asio::const_buffer buffer, CompletionToken&& token = CompletionToken()) + auto async_write(boost::asio::const_buffer buffer, CompletionToken&& token = CompletionToken()) { - return asio::async_initiate( - asio::bind_executor(write_executor(token), - [this](WriteHandler handler, asio::const_buffer buffer) { // - async_write_any(std::move(handler), buffer, false); - }), + return boost::asio::async_initiate( + boost::asio::bind_executor( + write_executor(token), + [this](WriteHandler handler, boost::asio::const_buffer buffer) { // + async_write_any(std::move(handler), buffer, false); + }), token, buffer); } @@ -102,13 +103,15 @@ class Writer * \return What \p token makes of the operation. */ template - auto async_write_eof(asio::const_buffer buffer, CompletionToken&& token = CompletionToken()) + auto async_write_eof(boost::asio::const_buffer buffer, + CompletionToken&& token = CompletionToken()) { - return asio::async_initiate( - asio::bind_executor(write_executor(token), - [this](WriteHandler handler, asio::const_buffer buffer) { // - async_write_any(std::move(handler), buffer, true); - }), + return boost::asio::async_initiate( + boost::asio::bind_executor( + write_executor(token), + [this](WriteHandler handler, boost::asio::const_buffer buffer) { // + async_write_any(std::move(handler), buffer, true); + }), token, buffer); } @@ -118,7 +121,7 @@ class Writer template auto async_write_eof(CompletionToken&& token = CompletionToken()) { - return async_write_eof(asio::const_buffer{}, std::forward(token)); + return async_write_eof(boost::asio::const_buffer{}, std::forward(token)); } #endif @@ -131,13 +134,13 @@ class Writer /// Writes \p buffer as part of the body: auto [ec] = co_await writer.write(buffer); /// \param buffer What to write. /// \return The error. - IoTask<> write(asio::const_buffer buffer); + IoTask<> write(boost::asio::const_buffer buffer); /// Writes \p buffer, if any, and ends the body: auto [ec] = co_await /// writer.write_eof(); /// \param buffer What to write. /// \return The error. - IoTask<> write_eof(asio::const_buffer buffer = {}); + IoTask<> write_eof(boost::asio::const_buffer buffer = {}); #if ANYHTTP_COROSIO /** @@ -151,7 +154,7 @@ class Writer ++it) if (capy::const_buffer buffer = *it; buffer.size() > 0) { - auto [ec] = co_await write(asio::const_buffer(buffer.data(), buffer.size())); + auto [ec] = co_await write(boost::asio::const_buffer(buffer.data(), buffer.size())); co_return std::tuple{ec, ec ? size_t{0} : buffer.size()}; } co_return std::tuple{error_code{}, size_t{0}}; @@ -175,13 +178,13 @@ class Writer // executor at all, so an empty one here is never used. // template - asio::associated_executor_t + boost::asio::associated_executor_t write_executor(const CompletionToken& token) const noexcept { - return asio::get_associated_executor(token, get_executor()); + return boost::asio::get_associated_executor(token, get_executor()); } - void async_write_any(WriteHandler&& handler, asio::const_buffer buffer, bool eof); + void async_write_any(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof); #endif std::shared_ptr impl_; diff --git a/include/anyhttp/writer_impl.hpp b/include/anyhttp/writer_impl.hpp index c75349f..a951af5 100644 --- a/include/anyhttp/writer_impl.hpp +++ b/include/anyhttp/writer_impl.hpp @@ -43,10 +43,10 @@ class Writer::Impl : public std::enable_shared_from_this // while re-ending it with no data attached is an idempotent no-op. Only then do stream-level // failures (closed, cancelled) get their say. // - virtual void async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) = 0; + virtual void async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) = 0; /// async_write() as a coroutine, see Reader::Impl::read_some(). - virtual IoTask<> write(asio::const_buffer buffer, bool eof) + virtual IoTask<> write(boost::asio::const_buffer buffer, bool eof) { co_return co_await initiate([this, buffer, eof](WriteHandler handler) { // async_write(std::move(handler), buffer, eof); diff --git a/src/apps/client_main.cpp b/src/apps/client_main.cpp index b0df813..c4a6302 100644 --- a/src/apps/client_main.cpp +++ b/src/apps/client_main.cpp @@ -24,7 +24,7 @@ using namespace boost::asio::experimental::awaitable_operators; static awaitable send(Request& request, std::string_view hello) { logd("send: sending string of {} bytes...", hello.size()); - co_await request.async_write_eof(asio::buffer(hello)); + co_await request.async_write_eof(boost::asio::buffer(hello)); logd("send: sending string of {} bytes... done", hello.size()); } diff --git a/src/asio/any_async_stream_impl.cpp b/src/asio/any_async_stream_impl.cpp index d8a2523..ba4c390 100644 --- a/src/asio/any_async_stream_impl.cpp +++ b/src/asio/any_async_stream_impl.cpp @@ -4,7 +4,7 @@ // #include "anyhttp/asio/any_async_stream_impl.hpp" -namespace anyhttp +namespace anyhttp::inline asio { // ================================================================================================= @@ -47,12 +47,13 @@ void any_async_stream::shutdown(ShutdownHandler handler) // this translation unit, so that including the type-erased stream stays cheap. // ================================================================================================= -template class any_async_stream_impl; +template class any_async_stream_impl; template class any_async_stream_impl; -template any_async_stream make_any_async_stream(ip::tcp::socket&&); +template any_async_stream +make_any_async_stream(boost::asio::ip::tcp::socket&&); template any_async_stream make_any_async_stream(SslStream&&); // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/src/asio/net.cpp b/src/asio/net.cpp index 21de371..e161555 100644 --- a/src/asio/net.cpp +++ b/src/asio/net.cpp @@ -21,7 +21,7 @@ #include -namespace anyhttp +namespace anyhttp::inline asio { // ================================================================================================= @@ -70,12 +70,12 @@ static int alpn_select_proto_cb(SSL* ssl, const unsigned char** out, unsigned ch TlsContext make_server_tls_context(const std::string& certificate_chain, const std::string& private_key) { - asio::ssl::context ctx{asio::ssl::context::tlsv13}; + boost::asio::ssl::context ctx{boost::asio::ssl::context::tlsv13}; SSL_CTX_set_next_protos_advertised_cb(ctx.native_handle(), next_proto_cb, nullptr); SSL_CTX_set_alpn_select_cb(ctx.native_handle(), alpn_select_proto_cb, nullptr); ctx.use_certificate_chain_file(certificate_chain); - ctx.use_private_key_file(private_key, asio::ssl::context::pem); + ctx.use_private_key_file(private_key, boost::asio::ssl::context::pem); return ctx; } @@ -84,11 +84,11 @@ TlsContext make_server_tls_context(const std::string& certificate_chain, TlsContext make_client_tls_context(const std::string& ca_file, Protocol protocol) { - asio::ssl::context ctx{asio::ssl::context::tls_client}; + boost::asio::ssl::context ctx{boost::asio::ssl::context::tls_client}; SSL_CTX_set_min_proto_version(ctx.native_handle(), TLS1_2_VERSION); tls_load_trusted_cas(ctx.native_handle(), ca_file); - ctx.set_verify_mode(asio::ssl::verify_peer); + ctx.set_verify_mode(boost::asio::ssl::verify_peer); // "http/1.1" is the tail of the list, what follows the three bytes of "h2". auto alpn = std::span{next_proto_list}; @@ -108,19 +108,20 @@ namespace io void listen(TcpAcceptor& acceptor, const TcpEndpoint& endpoint, bool reuse_port) { acceptor.open(endpoint.protocol()); - acceptor.set_option(asio::socket_base::reuse_address(true)); + acceptor.set_option(boost::asio::socket_base::reuse_address(true)); if (reuse_port) - acceptor.set_option(asio::detail::socket_option::boolean(true)); + acceptor.set_option( + boost::asio::detail::socket_option::boolean(true)); // // Accept IPv4 clients on an IPv6 listener, too. This has to go after open() -- there is no // socket to set it on before that -- and before bind(), which is when it takes effect. Not // fatal if it fails: most systems are dual-stack by default (net.ipv6.bindv6only=0) anyway. // - if (endpoint.protocol() == asio::ip::tcp::v6()) + if (endpoint.protocol() == boost::asio::ip::tcp::v6()) { error_code ec; - acceptor.set_option(asio::ip::v6_only(false), ec); + acceptor.set_option(boost::asio::ip::v6_only(false), ec); if (ec) logw("[{}] error enabling dual-stack on {}: {}", log_prefix(Role::server), endpoint, ec.what()); @@ -132,8 +133,8 @@ void listen(TcpAcceptor& acceptor, const TcpEndpoint& endpoint, bool reuse_port) std::pair buffer_sizes(TcpSocket& socket) { - asio::socket_base::send_buffer_size send; - asio::socket_base::receive_buffer_size receive; + boost::asio::socket_base::send_buffer_size send; + boost::asio::socket_base::receive_buffer_size receive; error_code ec; socket.get_option(send, ec); socket.get_option(receive, ec); @@ -144,16 +145,16 @@ void set_buffer_sizes(TcpSocket& socket, int send, int receive) { error_code ec; if (send > 0) - socket.set_option(asio::socket_base::send_buffer_size(send), ec); + socket.set_option(boost::asio::socket_base::send_buffer_size(send), ec); if (receive > 0) - socket.set_option(asio::socket_base::receive_buffer_size(receive), ec); + socket.set_option(boost::asio::socket_base::receive_buffer_size(receive), ec); } IoTask> resolve(Executor executor, std::string host, std::string port) { - asio::ip::tcp::resolver resolver(executor); + boost::asio::ip::tcp::resolver resolver(executor); auto [ec, results] = co_await resolver.async_resolve( - host, port, asio::ip::tcp::resolver::numeric_service, asio::as_tuple); + host, port, boost::asio::ip::tcp::resolver::numeric_service, boost::asio::as_tuple); std::vector endpoints; for (auto&& entry : results) @@ -163,7 +164,7 @@ IoTask> resolve(Executor executor, std::string host, st IoTask connect(TcpSocket& socket, std::vector endpoints) { - co_return co_await asio::async_connect(socket, endpoints, asio::as_tuple); + co_return co_await boost::asio::async_connect(socket, endpoints, boost::asio::as_tuple); } void set_peer_name(TlsStream& stream, const std::string& host) @@ -197,7 +198,7 @@ IpAddress normalize(IpAddress address) { const auto v6 = address.to_v6(); if (v6.is_v4_mapped()) - return asio::ip::make_address_v4(asio::ip::v4_mapped, v6); + return boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, v6); } return address; } @@ -207,7 +208,7 @@ namespace io IpAddress make_address(std::string_view text, error_code& ec) noexcept { - return asio::ip::make_address(text, ec); + return boost::asio::ip::make_address(text, ec); } SocketAddress to_sockaddr(const UdpEndpoint& endpoint) noexcept @@ -233,4 +234,4 @@ std::optional from_sockaddr(const sockaddr* address, socklen_t size // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline asio diff --git a/src/client_impl.cpp b/src/client_impl.cpp index e25eb5b..8f88fa1 100644 --- a/src/client_impl.cpp +++ b/src/client_impl.cpp @@ -167,7 +167,8 @@ Task Client::Impl::async_connect() auto alt_host = alt->host.empty() ? host : alt->host; mlogi("connecting to {}:{} over HTTP/3, as advertised by Alt-Svc", alt_host, alt->port); endpoints = co_await resolve(alt_host, alt->port); - co_return Session{co_await async_connect_http3(executor_, std::move(endpoints), config())}; + co_return Session{ + co_await h3::async_connect_http3(executor_, std::move(endpoints), config())}; } endpoints = co_await resolve(host, port); @@ -178,7 +179,8 @@ Task Client::Impl::async_connect() // handshake, ...) than the TCP-based h1/h2 paths below. // if (config().protocol == Protocol::h3) - co_return Session{co_await async_connect_http3(executor_, std::move(endpoints), config())}; + co_return Session{ + co_await h3::async_connect_http3(executor_, std::move(endpoints), config())}; // // Initiate connection. @@ -196,8 +198,8 @@ Task Client::Impl::async_connect() // // After connect(), not before: the socket a client starts with is not open yet, and - // asio::async_connect() closes and reopens it for every endpoint it tries, which would throw - // away anything set on it. Zero, the default, leaves the kernel's autotuning alone; see + // boost::asio::async_connect() closes and reopens it for every endpoint it tries, which would + // throw away anything set on it. Zero, the default, leaves the kernel's autotuning alone; see // Config::socket_send_buffer_size for why a test would want them pinned. // io::set_buffer_sizes(socket, config().socket_send_buffer_size, @@ -264,11 +266,11 @@ Session Client::Impl::start_session(Protocol protocol, Stream&& stream) switch (protocol) { case Protocol::h1: - impl = beast_impl::make_client_session(*this, executor_, std::forward(stream)); + impl = h1::make_client_session(*this, executor_, std::forward(stream)); break; case Protocol::h2: - impl = nghttp2::make_client_session(*this, executor_, std::forward(stream)); + impl = h2::make_client_session(*this, executor_, std::forward(stream)); break; case anyhttp::Protocol::h3: diff --git a/src/corosio/net.cpp b/src/corosio/net.cpp index 518daac..25712db 100644 --- a/src/corosio/net.cpp +++ b/src/corosio/net.cpp @@ -18,7 +18,7 @@ #include #include -namespace anyhttp +namespace anyhttp::inline corosio { // ================================================================================================= @@ -27,11 +27,11 @@ TlsContext make_server_tls_context(const std::string& certificate_chain, const std::string& private_key) { TlsContext ctx; - if (auto ec = ctx.set_min_protocol_version(corosio::tls_version::tls_1_3)) + if (auto ec = ctx.set_min_protocol_version(boost::corosio::tls_version::tls_1_3)) throw_error(ec); if (auto ec = ctx.use_certificate_chain_file(certificate_chain)) throw std::system_error(ec, "use_certificate_chain_file"); - if (auto ec = ctx.use_private_key_file(private_key, corosio::tls_file_format::pem)) + if (auto ec = ctx.use_private_key_file(private_key, boost::corosio::tls_file_format::pem)) throw std::system_error(ec, "use_private_key_file"); std::ignore = ctx.set_alpn({"h2", "http/1.1"}); return ctx; @@ -42,11 +42,11 @@ TlsContext make_server_tls_context(const std::string& certificate_chain, TlsContext make_client_tls_context(const std::string& ca_file, Protocol protocol) { TlsContext ctx; - if (auto ec = ctx.set_min_protocol_version(corosio::tls_version::tls_1_2)) + if (auto ec = ctx.set_min_protocol_version(boost::corosio::tls_version::tls_1_2)) throw_error(ec); if (auto ec = ca_file.empty() ? ctx.set_default_verify_paths() : ctx.load_verify_file(ca_file)) throw std::system_error(ec, "load_verify_file"); - if (auto ec = ctx.set_verify_mode(corosio::tls_verify_mode::peer)) + if (auto ec = ctx.set_verify_mode(boost::corosio::tls_verify_mode::peer)) throw_error(ec); if (protocol == Protocol::h2) std::ignore = ctx.set_alpn({"h2", "http/1.1"}); @@ -62,21 +62,22 @@ namespace io void listen(TcpAcceptor& acceptor, const TcpEndpoint& endpoint, bool reuse_port) { - const auto family = endpoint.address().is_v4() ? corosio::family::v4 : corosio::family::v6; + const auto family = + endpoint.address().is_v4() ? boost::corosio::family::v4 : boost::corosio::family::v6; if (auto ec = acceptor.open(family)) throw std::system_error(ec, "open"); - if (auto ec = acceptor.set_option(corosio::socket_option::reuse_address(true))) + if (auto ec = acceptor.set_option(boost::corosio::socket_option::reuse_address(true))) throw std::system_error(ec, "reuse_address"); if (reuse_port) { - if (auto ec = acceptor.set_option(corosio::socket_option::reuse_port(true))) + if (auto ec = acceptor.set_option(boost::corosio::socket_option::reuse_port(true))) throw std::system_error(ec, "reuse_port"); } // Accept IPv4 clients on an IPv6 listener, too, see asio/net.cpp. - if (family == corosio::family::v6) + if (family == boost::corosio::family::v6) { - if (auto ec = acceptor.set_option(corosio::socket_option::v6_only(false))) + if (auto ec = acceptor.set_option(boost::corosio::socket_option::v6_only(false))) logw("[{}] error enabling dual-stack on {}: {}", log_prefix(Role::server), endpoint, ec.message()); } @@ -107,9 +108,9 @@ void set_buffer_sizes(TcpSocket& socket, int send, int receive) IoTask> resolve(Executor executor, std::string host, std::string port) { - corosio::resolver resolver(executor.context()); + boost::corosio::resolver resolver(executor.context()); auto [ec, results] = - co_await resolver.resolve(host, port, corosio::resolve_flags::numeric_service); + co_await resolver.resolve(host, port, boost::corosio::resolve_flags::numeric_service); std::vector endpoints; for (auto&& entry : results) @@ -123,8 +124,8 @@ IoTask connect(TcpSocket& socket, std::vector endpoint for (const auto& endpoint : endpoints) { socket.close(); - if ((ec = - socket.open(endpoint.address().is_v4() ? corosio::family::v4 : corosio::family::v6))) + if ((ec = socket.open(endpoint.address().is_v4() ? boost::corosio::family::v4 + : boost::corosio::family::v6))) continue; std::tie(ec) = co_await socket.connect(endpoint); if (!ec) @@ -160,7 +161,7 @@ namespace io IpAddress make_address(std::string_view text, error_code& ec) noexcept { - auto [result, address] = corosio::make_ip_address(text); + auto [result, address] = boost::corosio::make_ip_address(text); ec = result; return address; } @@ -200,17 +201,18 @@ std::optional from_sockaddr(const sockaddr* address, socklen_t size { sockaddr_in in; std::memcpy(&in, address, sizeof(in)); - corosio::ipv4_address::bytes_type bytes; + boost::corosio::ipv4_address::bytes_type bytes; std::memcpy(bytes.data(), &in.sin_addr, bytes.size()); - return UdpEndpoint(corosio::ipv4_address(bytes), ntohs(in.sin_port)); + return UdpEndpoint(boost::corosio::ipv4_address(bytes), ntohs(in.sin_port)); } if (address->sa_family == AF_INET6 && size >= sizeof(sockaddr_in6)) { sockaddr_in6 in6; std::memcpy(&in6, address, sizeof(in6)); - corosio::ipv6_address::bytes_type bytes; + boost::corosio::ipv6_address::bytes_type bytes; std::memcpy(bytes.data(), &in6.sin6_addr, bytes.size()); - return UdpEndpoint(corosio::ipv6_address(bytes, in6.sin6_scope_id), ntohs(in6.sin6_port)); + return UdpEndpoint(boost::corosio::ipv6_address(bytes, in6.sin6_scope_id), + ntohs(in6.sin6_port)); } return std::nullopt; } @@ -219,4 +221,4 @@ std::optional from_sockaddr(const sockaddr* address, socklen_t size // ================================================================================================= -} // namespace anyhttp +} // namespace anyhttp::inline corosio diff --git a/src/file_handler.cpp b/src/file_handler.cpp index d7d61ba..8da81ad 100644 --- a/src/file_handler.cpp +++ b/src/file_handler.cpp @@ -362,7 +362,7 @@ Task serve_file(server::Request request, server::Response response, fs::pa // last of them instead of costing a write of its own. An empty file, which cannot be mmap()ed // at all, is then simply an empty buffer and ends the same way. // - co_await response.write_eof(asio::buffer(file.bytes())); + co_await response.write_eof(boost::asio::buffer(file.bytes())); } } // namespace anyhttp diff --git a/src/h1/session.cpp b/src/h1/session.cpp index ccea1fa..230d5b0 100644 --- a/src/h1/session.cpp +++ b/src/h1/session.cpp @@ -42,12 +42,12 @@ using namespace boost::asio; namespace beast = boost::beast; namespace http = beast::http; -namespace anyhttp::beast_impl +namespace anyhttp::h1 { // ================================================================================================= -using namespace asio; +using namespace boost::asio; using namespace boost::beast; /** @@ -142,13 +142,13 @@ class BeastReader : public Interface // The I/O is done in a coroutine, see Reader::Impl::read_some(). As a task of its own, it is // what the caller's cancellation reaches. // - void async_read_some(asio::mutable_buffer body_buffer, ReadSomeHandler&& handler) override + void async_read_some(boost::asio::mutable_buffer body_buffer, ReadSomeHandler&& handler) override { launch(get_executor(), read_some(body_buffer), std::move(handler)); } /// Reads until something of the body has arrived in \p body_buffer, or the body has ended. - IoTask read_some(asio::mutable_buffer body_buffer) override + IoTask read_some(boost::asio::mutable_buffer body_buffer) override { assert(!reading); @@ -309,12 +309,12 @@ class WriterBase : public Parent } /// The I/O is done in a coroutine, see Reader::Impl::read_some() and BeastReader. - void async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) override + void async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) override { launch(get_executor(), write(buffer, eof), std::move(handler)); } - IoTask<> write(asio::const_buffer buffer, bool eof) override + IoTask<> write(boost::asio::const_buffer buffer, bool eof) override { const bool empty = buffer.size() == 0; @@ -928,9 +928,9 @@ void ServerSession::destroy() noexcept * be read in HTTP/1.1 first, before switching protocols. Cleartext only, as h2 over TLS is * negotiated by ALPN instead. */ -static std::optional h2c_upgrade(std::string_view log_prefix, - const http::request& request, - const boost::urls::url& url, bool complete) +static std::optional h2c_upgrade(std::string_view log_prefix, + const http::request& request, + const boost::urls::url& url, bool complete) { // // nghttp2 version 2 dropped the h2c upgrade along with RFC 7540 -- there is no counterpart of @@ -977,7 +977,7 @@ static std::optional h2c_upgrade(std::string_view log_prefix, std::ranges::replace(encoded, '-', '+'); std::ranges::replace(encoded, '_', '/'); - nghttp2::Upgrade upgrade; + h2::Upgrade upgrade; upgrade.settings.resize(beast::detail::base64::decoded_size(encoded.size())); auto [written, read] = beast::detail::base64::decode(upgrade.settings.data(), encoded.data(), encoded.size()); @@ -1131,8 +1131,8 @@ Task ServerSession::do_session(Buffer&& buffer) mlogi("upgrading to h2c, {} bytes in buffer", buffer_.size()); // Stream is whatever this session runs on, but never a TLS one: h2c_upgrade() takes // cleartext requests only, as h2 over TLS is negotiated by ALPN instead. - upgraded_ = nghttp2::make_server_session(server(), super::get_executor(), - std::move(stream_), std::move(*upgrade)); + upgraded_ = h2::make_server_session(server(), super::get_executor(), std::move(stream_), + std::move(*upgrade)); co_await upgraded_->do_session(std::move(buffer_)); mlogi("h2c session done, served {} requests before upgrade", requestCounter - 1); co_return; @@ -1438,4 +1438,4 @@ ANYHTTP_CLIENT_STREAMS(ANYHTTP_H1_CLIENT) // ================================================================================================= -} // namespace anyhttp::beast_impl +} // namespace anyhttp::h1 diff --git a/src/h2/session.cpp b/src/h2/session.cpp index e8d2ff5..802b4b5 100644 --- a/src/h2/session.cpp +++ b/src/h2/session.cpp @@ -34,7 +34,7 @@ namespace http = boost::beast::http; // ================================================================================================= -namespace anyhttp::nghttp2 +namespace anyhttp::h2 { static std::string_view frame_type(uint8_t type) @@ -794,4 +794,4 @@ ANYHTTP_CLIENT_STREAMS(ANYHTTP_H2_CLIENT) // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/src/h2/stream.cpp b/src/h2/stream.cpp index 0bc9cce..8107346 100644 --- a/src/h2/stream.cpp +++ b/src/h2/stream.cpp @@ -24,7 +24,7 @@ #include #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2 { // ================================================================================================= @@ -100,7 +100,7 @@ void NGHttp2Reader::async_read_some(boost::asio::mutable_buffer buffer, // whether the body has ended or the stream is even still there -- as ASIO does for a // zero-length read. // - if (asio::buffer_size(buffer) == 0) + if (boost::asio::buffer_size(buffer) == 0) { complete_immediately(std::move(handler), get_executor(), error_code{}, size_t{0}); return; @@ -315,7 +315,8 @@ void NGHttp2Writer::async_submit(StatusHandler&& handler, unsigned int sta } template -void NGHttp2Writer::async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) +void NGHttp2Writer::async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, + bool eof) { if (stream) { @@ -329,7 +330,7 @@ void NGHttp2Writer::async_write(WriteHandler&& handler, asio::const_buffer // cleanly ended keeps answering as such -- bare re-end idempotent, data broken_pipe -- and // only a stream that vanished mid-body is a connection error. // - const bool empty = asio::buffer_size(buffer) == 0; + const bool empty = boost::asio::buffer_size(buffer) == 0; error_code ec; if (empty && !eof) ec = {}; @@ -366,7 +367,7 @@ std::string NGHttp2Stream::log_prefix() const { return parent.log_prefix(id); } size_t NGHttp2Stream::read_buffers_size() const { - return asio::buffer_size(read_buffer_) + // remaining part of first buffer + return boost::asio::buffer_size(read_buffer_) + // remaining part of first buffer std::ranges::fold_left( // sum of all other buffers pending_read_buffers_ | // std::views::drop(1) | @@ -374,7 +375,7 @@ size_t NGHttp2Stream::read_buffers_size() const size_t{0}, std::plus{}); } -void NGHttp2Stream::call_read_handler(asio::const_buffer view) +void NGHttp2Stream::call_read_handler(boost::asio::const_buffer view) { Defer finish_close([this] { finish_deferred_close(); }); @@ -390,7 +391,7 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) { pending_read_buffers_.emplace_back(make_buffer(view)); if (pending_read_buffers_.size() == 1) - read_buffer_ = asio::buffer(pending_read_buffers_.front()); + read_buffer_ = boost::asio::buffer(pending_read_buffers_.front()); } mlogd("read_callback: no pending read handler... ({} buffers and {} bytes pending)", pending_read_buffers_.size(), read_buffers_size()); @@ -432,17 +433,17 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) size_t count = 0, consumed = 0; while (read_handler_ && !is_empty(read_buffer_)) { - size_t copied = asio::buffer_copy(read_handler_buffer_, read_buffer_); + size_t copied = boost::asio::buffer_copy(read_handler_buffer_, read_buffer_); count++, consumed += copied; bytesRead += copied; read_buffer_ += copied; if (count == 1) mlogd("read_callback: calling read handler with {} bytes... (buf={})", copied, - asio::buffer_size(read_buffer_)); + boost::asio::buffer_size(read_buffer_)); else mlogd("read_callback: calling read handler with {} bytes... (#{} in a row, buf={})", - copied, count, asio::buffer_size(read_buffer_)); + copied, count, boost::asio::buffer_size(read_buffer_)); // // swap_and_invoke() moves the read handler into a local variable before invoking it, @@ -468,7 +469,7 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) pending_read_buffers_.pop_front(); if (!pending_read_buffers_.empty()) - read_buffer_ = asio::buffer(pending_read_buffers_.front()); + read_buffer_ = boost::asio::buffer(pending_read_buffers_.front()); else std::swap(read_buffer_, view); // clear view so that we don't write it twice } @@ -504,7 +505,7 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) if (pending_read_buffers_.empty()) { auto& buffer = pending_read_buffers_.emplace_back(make_buffer(read_buffer_)); - read_buffer_ = asio::buffer(buffer); + read_buffer_ = boost::asio::buffer(buffer); } else if (!is_empty(view)) { @@ -582,7 +583,7 @@ void NGHttp2Stream::on_data(const uint8_t* data, size_t len) // We try to invoke the read handler directly with the new buffer view. Only if there is no // pending read handler that can be invoked immediately, the data will be copied into a buffer. // - call_read_handler(asio::const_buffer(data, len)); + call_read_handler(boost::asio::const_buffer(data, len)); } void NGHttp2Stream::on_eof(nghttp2_session*, int32_t id_) @@ -630,7 +631,7 @@ NGHttp2Stream::~NGHttp2Stream() // ================================================================================================= -void NGHttp2Stream::async_write(WriteHandler handler, asio::const_buffer buffer, bool eof) +void NGHttp2Stream::async_write(WriteHandler handler, boost::asio::const_buffer buffer, bool eof) { const bool empty = buffer.size() == 0; @@ -778,7 +779,7 @@ ssize_t NGHttp2Stream::producer_callback(uint8_t* buf, size_t length, uint32_t* // will make nghttp2 call nghttp2_send_data_callback, which must emit a single, full // DATA frame. // - copied = asio::buffer_copy(boost::asio::buffer(buf, length), write_buffer); + copied = boost::asio::buffer_copy(boost::asio::buffer(buf, length), write_buffer); mlogd("write callback: copied {} bytes", copied); write_buffer += copied; @@ -931,7 +932,7 @@ void NGHttp2Stream::delete_reader() pending, pending_read_buffers_.size()); // Don't re-open the stream window here, or the peer will resume sending data to us. - read_buffer_ = asio::const_buffer{}; + read_buffer_ = boost::asio::const_buffer{}; pending_read_buffers_.clear(); } @@ -1014,4 +1015,4 @@ template class NGHttp2Reader; template class NGHttp2Writer; template class NGHttp2Writer; -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/src/h2v2/session.cpp b/src/h2v2/session.cpp index a64ac59..acc76a4 100644 --- a/src/h2v2/session.cpp +++ b/src/h2v2/session.cpp @@ -33,7 +33,7 @@ namespace http = boost::beast::http; // ================================================================================================= -namespace anyhttp::nghttp2 +namespace anyhttp::h2v2 { // @@ -718,16 +718,24 @@ void NGHttp2Session::handle_expiry() } // ================================================================================================= -// Factories, see anyhttp/h2/backend.hpp. Instantiating the session templates is kept to this -// translation unit, so that the generic server and client never see an nghttp2 type. + +} // namespace anyhttp::h2v2 + +// ================================================================================================= +// Factories, see anyhttp/h2/backend.hpp. They are declared in the version-neutral anyhttp::h2, so +// that the generic server and client cannot tell the two backends apart. Instantiating the session +// templates is kept to this translation unit, so that they never see an nghttp2 type either. // ================================================================================================= +namespace anyhttp::h2 +{ + template std::shared_ptr make_server_session(server::Server::Impl& server, Executor executor, Stream&& stream, std::optional upgrade) { auto session = - std::make_shared>(server, std::move(executor), std::move(stream)); + std::make_shared>(server, std::move(executor), std::move(stream)); session->upgrade_ = std::move(upgrade); return session; } @@ -736,7 +744,8 @@ template std::shared_ptr make_client_session(client::Client::Impl& client, Executor executor, Stream&& stream) { - return std::make_shared>(client, std::move(executor), std::move(stream)); + return std::make_shared>(client, std::move(executor), + std::move(stream)); } #define ANYHTTP_H2_SERVER(Stream) \ @@ -752,4 +761,4 @@ ANYHTTP_CLIENT_STREAMS(ANYHTTP_H2_CLIENT) // ================================================================================================= -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2 diff --git a/src/h2v2/stream.cpp b/src/h2v2/stream.cpp index 2a6511d..7a9ed23 100644 --- a/src/h2v2/stream.cpp +++ b/src/h2v2/stream.cpp @@ -22,7 +22,7 @@ #include #include -namespace anyhttp::nghttp2 +namespace anyhttp::h2v2 { // ================================================================================================= @@ -97,7 +97,7 @@ void NGHttp2Reader::async_read_some(boost::asio::mutable_buffer buffer, // whether the body has ended or the stream is even still there -- as ASIO does for a // zero-length read. // - if (asio::buffer_size(buffer) == 0) + if (boost::asio::buffer_size(buffer) == 0) { complete_immediately(std::move(handler), get_executor(), error_code{}, size_t{0}); return; @@ -333,7 +333,8 @@ void NGHttp2Writer::async_submit(StatusHandler&& handler, unsigned int sta } template -void NGHttp2Writer::async_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) +void NGHttp2Writer::async_write(WriteHandler&& handler, boost::asio::const_buffer buffer, + bool eof) { if (stream) { @@ -347,7 +348,7 @@ void NGHttp2Writer::async_write(WriteHandler&& handler, asio::const_buffer // cleanly ended keeps answering as such -- bare re-end idempotent, data broken_pipe -- and // only a stream that vanished mid-body is a connection error. // - const bool empty = asio::buffer_size(buffer) == 0; + const bool empty = boost::asio::buffer_size(buffer) == 0; error_code ec; if (empty && !eof) ec = {}; @@ -385,7 +386,7 @@ std::string NGHttp2Stream::log_prefix() const { return parent.log_prefix(id); } size_t NGHttp2Stream::read_buffers_size() const { - return asio::buffer_size(read_buffer_) + // remaining part of first buffer + return boost::asio::buffer_size(read_buffer_) + // remaining part of first buffer std::ranges::fold_left( // sum of all other buffers pending_read_buffers_ | // std::views::drop(1) | @@ -393,7 +394,7 @@ size_t NGHttp2Stream::read_buffers_size() const size_t{0}, std::plus{}); } -void NGHttp2Stream::call_read_handler(asio::const_buffer view) +void NGHttp2Stream::call_read_handler(boost::asio::const_buffer view) { Defer finish_close([this] { finish_deferred_close(); }); @@ -408,7 +409,7 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) { pending_read_buffers_.emplace_back(make_buffer(view)); if (pending_read_buffers_.size() == 1) - read_buffer_ = asio::buffer(pending_read_buffers_.front()); + read_buffer_ = boost::asio::buffer(pending_read_buffers_.front()); } mlogd("read_callback: no pending read handler... ({} buffers and {} bytes pending)", pending_read_buffers_.size(), read_buffers_size()); @@ -442,17 +443,17 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) size_t count = 0, consumed = 0; while (read_handler_ && !is_empty(read_buffer_)) { - size_t copied = asio::buffer_copy(read_handler_buffer_, read_buffer_); + size_t copied = boost::asio::buffer_copy(read_handler_buffer_, read_buffer_); count++, consumed += copied; bytesRead += copied; read_buffer_ += copied; if (count == 1) mlogd("read_callback: calling read handler with {} bytes... (buf={})", copied, - asio::buffer_size(read_buffer_)); + boost::asio::buffer_size(read_buffer_)); else mlogd("read_callback: calling read handler with {} bytes... (#{} in a row, buf={})", - copied, count, asio::buffer_size(read_buffer_)); + copied, count, boost::asio::buffer_size(read_buffer_)); // // swap_and_invoke() moves the read handler into a local variable before invoking it, @@ -478,7 +479,7 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) pending_read_buffers_.pop_front(); if (!pending_read_buffers_.empty()) - read_buffer_ = asio::buffer(pending_read_buffers_.front()); + read_buffer_ = boost::asio::buffer(pending_read_buffers_.front()); else std::swap(read_buffer_, view); // clear view so that we don't write it twice } @@ -510,7 +511,7 @@ void NGHttp2Stream::call_read_handler(asio::const_buffer view) if (pending_read_buffers_.empty()) { auto& buffer = pending_read_buffers_.emplace_back(make_buffer(read_buffer_)); - read_buffer_ = asio::buffer(buffer); + read_buffer_ = boost::asio::buffer(buffer); } else if (!is_empty(view)) { @@ -555,7 +556,7 @@ void NGHttp2Stream::on_data(const uint8_t* data, size_t len) // We try to invoke the read handler directly with the new buffer view. Only if there is no // pending read handler that can be invoked immediately, the data will be copied into a buffer. // - call_read_handler(asio::const_buffer(data, len)); + call_read_handler(boost::asio::const_buffer(data, len)); } void NGHttp2Stream::on_eof() @@ -602,7 +603,7 @@ NGHttp2Stream::~NGHttp2Stream() // ================================================================================================= -void NGHttp2Stream::async_write(WriteHandler handler, asio::const_buffer buffer, bool eof) +void NGHttp2Stream::async_write(WriteHandler handler, boost::asio::const_buffer buffer, bool eof) { const bool empty = buffer.size() == 0; @@ -982,7 +983,7 @@ void NGHttp2Stream::delete_reader() pending, pending_read_buffers_.size()); // Don't re-open the stream window here, or the peer will resume sending data to us. - read_buffer_ = asio::const_buffer{}; + read_buffer_ = boost::asio::const_buffer{}; pending_read_buffers_.clear(); } @@ -1060,4 +1061,4 @@ template class NGHttp2Reader; template class NGHttp2Writer; template class NGHttp2Writer; -} // namespace anyhttp::nghttp2 +} // namespace anyhttp::h2v2 diff --git a/src/h3/application.cpp b/src/h3/application.cpp index 8ad3035..3273458 100644 --- a/src/h3/application.cpp +++ b/src/h3/application.cpp @@ -11,7 +11,7 @@ #include // IWYU pragma: keep -namespace anyhttp::http3 +namespace anyhttp::h3 { // ================================================================================================= @@ -401,4 +401,4 @@ int Http3Application::h3_cb_reset_stream(nghttp3_conn*, int64_t stream_id, uint6 // ================================================================================================= -} // namespace anyhttp::http3 +} // namespace anyhttp::h3 diff --git a/src/h3/client.cpp b/src/h3/client.cpp index 25d48f1..566ddb5 100644 --- a/src/h3/client.cpp +++ b/src/h3/client.cpp @@ -2,8 +2,8 @@ // anyhttp HTTP/3 client. // // Almost all of it is shared with the server: `Http3ClientApplication` is an -// `http3::Http3Application` (see anyhttp/h3/application.hpp) on a QUIC connection the client end -// of src/quic/ makes, and `Http3ClientStream` is an `http3::Http3Stream` (anyhttp/h3/stream.hpp) +// `h3::Http3Application` (see anyhttp/h3/application.hpp) on a QUIC connection the client end +// of src/quic/ makes, and `Http3ClientStream` is an `h3::Http3Stream` (anyhttp/h3/stream.hpp) // that writes a request and reads a response, where the server's does the opposite. // // What is genuinely client-side here: async_submit(), which opens a stream and puts the request @@ -40,10 +40,7 @@ using namespace std::chrono_literals; -using anyhttp::http3::log_headers; -using anyhttp::http3::make_nv; - -namespace anyhttp::client +namespace anyhttp::h3 { // ================================================================================================= @@ -54,7 +51,7 @@ namespace anyhttp::client namespace { -class Http3ClientApplication : public http3::Http3Application +class Http3ClientApplication : public h3::Http3Application { public: using Http3Application::Http3Application; @@ -76,7 +73,7 @@ class Http3ClientApplication : public http3::Http3Application namespace { -class Http3ClientStream : public http3::Http3Stream +class Http3ClientStream : public h3::Http3Stream { public: Http3ClientStream(Http3ClientApplication& application, int64_t id); @@ -119,10 +116,10 @@ class Http3ClientStream : public http3::Http3Stream // The client's Request needs one thing the shared writer does not have: async_get_response(), // which client.hpp exposes and server::Response has no counterpart for. // -class Http3ClientWriter : public http3::Http3Writer +class Http3ClientWriter : public h3::Http3Writer { public: - using http3::Http3Writer::Http3Writer; + using h3::Http3Writer::Http3Writer; void async_get_response(client::Request::GetResponseHandler&& handler) override { @@ -145,10 +142,10 @@ namespace { // -// The reading half of a client response: what http3::Http3Reader has for both roles, plus the +// The reading half of a client response: what h3::Http3Reader has for both roles, plus the // status code, which only this role has. Its counterpart on the server is Http3RequestReader. // -class Http3ResponseReader final : public http3::Http3Reader +class Http3ResponseReader final : public h3::Http3Reader { public: using Http3Reader::Http3Reader; @@ -176,7 +173,7 @@ class Http3ResponseReader final : public http3::Http3Reader> -async_connect_http3(Executor executor, std::vector endpoints, const Config& config) +Task> async_connect_http3(Executor executor, + std::vector endpoints, + const client::Config& config) { // // The certificate has to be valid for the origin, the host of the URL -- also when `endpoints` @@ -392,4 +390,4 @@ async_connect_http3(Executor executor, std::vector endpoints, const // ================================================================================================= -} // namespace anyhttp::client +} // namespace anyhttp::h3 diff --git a/src/h3/common.cpp b/src/h3/common.cpp index c577a85..7289c28 100644 --- a/src/h3/common.cpp +++ b/src/h3/common.cpp @@ -7,7 +7,7 @@ #include -namespace anyhttp::http3 +namespace anyhttp::h3 { // ================================================================================================= @@ -40,4 +40,4 @@ void log_headers(std::string_view log_prefix, // ================================================================================================= -} // namespace anyhttp::http3 +} // namespace anyhttp::h3 diff --git a/src/h3/server.cpp b/src/h3/server.cpp index b27de1a..2cea4ed 100644 --- a/src/h3/server.cpp +++ b/src/h3/server.cpp @@ -3,11 +3,11 @@ // // Nearly everything that makes HTTP/3 work is shared with the client and lives in // anyhttp/h3/application.hpp and anyhttp/h3/stream.hpp, and everything about QUIC itself in -// src/quic/: `Http3ServerApplication` is the `http3::Http3Application` the QUIC server runs for -// connections that negotiate "h3", and `Http3ServerStream` is an `http3::Http3Stream` that reads a +// src/quic/: `Http3ServerApplication` is the `h3::Http3Application` the QUIC server runs for +// connections that negotiate "h3", and `Http3ServerStream` is an `h3::Http3Stream` that reads a // request and writes a response, where the client's does the opposite. Per-request state feeds an // `Http3Reader` (server::Request) and `Http3Writer` (server::Response) into the same -// `RequestHandler` used by the HTTP/1.1 and HTTP/2 backends. +// `server::RequestHandler` used by the HTTP/1.1 and HTTP/2 backends. // #include "anyhttp/formatter.hpp" // IWYU pragma: keep @@ -30,10 +30,7 @@ #include #include -using anyhttp::http3::log_headers; -using anyhttp::http3::make_nv; - -namespace anyhttp::server +namespace anyhttp::h3 { // ================================================================================================= @@ -44,10 +41,10 @@ namespace anyhttp::server namespace { -class Http3ServerApplication : public http3::Http3Application +class Http3ServerApplication : public h3::Http3Application { public: - Http3ServerApplication(quic::QuicSession& session, Server::Impl& server) + Http3ServerApplication(quic::QuicSession& session, server::Server::Impl& server) : Http3Application(session, server.config().max_header_size), server_(server) { } @@ -65,15 +62,18 @@ class Http3ServerApplication : public http3::Http3Application params.initial_max_streams_bidi = server_.config().max_concurrent_streams; } - const RequestHandler& request_handler() const noexcept { return server_.request_handler(); } + const server::RequestHandler& request_handler() const noexcept + { + return server_.request_handler(); + } private: - Server::Impl& server_; + server::Server::Impl& server_; }; // ------------------------------------------------------------------------------------------------- -class Http3ServerStream : public http3::Http3Stream +class Http3ServerStream : public h3::Http3Stream { public: Http3ServerStream(Http3ServerApplication& application, int64_t id); @@ -105,12 +105,12 @@ namespace { // -// The reading half of a server request: what http3::Http3Reader has for both roles, plus the +// The reading half of a server request: what h3::Http3Reader has for both roles, plus the // request line, which only this role has. Its counterpart on the client is Http3ResponseReader. // Like the fields, the request line is moved out of the stream on detach(): a handler may well // look at it only after the session is gone, e.g. when it first runs during shutdown. // -class Http3RequestReader final : public http3::Http3Reader +class Http3RequestReader final : public h3::Http3Reader { public: using Http3Reader::Http3Reader; @@ -140,7 +140,7 @@ class Http3RequestReader final : public http3::Http3Reader(*this)); - server::Response response(std::make_unique>(*this)); + server::Response response(std::make_unique>(*this)); auto& app = static_cast(application); if (header_limit_exceeded) @@ -228,7 +228,7 @@ void Http3ServerStream::submit_response(unsigned int status, const Fields& user_ // ================================================================================================= -quic::ApplicationFactory make_http3_application(Server::Impl& server) +quic::ApplicationFactory make_http3_application(server::Server::Impl& server) { return [&server](quic::QuicSession& session) -> std::unique_ptr { return std::make_unique(session, server); @@ -237,4 +237,4 @@ quic::ApplicationFactory make_http3_application(Server::Impl& server) // ================================================================================================= -} // namespace anyhttp::server +} // namespace anyhttp::h3 diff --git a/src/h3/stream.cpp b/src/h3/stream.cpp index 0c844f5..b926069 100644 --- a/src/h3/stream.cpp +++ b/src/h3/stream.cpp @@ -16,7 +16,7 @@ using namespace boost::asio; -namespace anyhttp::http3 +namespace anyhttp::h3 { // ================================================================================================= @@ -287,4 +287,4 @@ void Http3Stream::delete_writer() // ================================================================================================= -} // namespace anyhttp::http3 +} // namespace anyhttp::h3 diff --git a/src/net.cpp b/src/net.cpp index 547a793..3560f1c 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -11,7 +11,13 @@ // UDP, the same in both runtimes, see anyhttp/net.hpp. // ================================================================================================= -namespace anyhttp::io +// The runtime's own namespace, which io:: is part of: a plain "namespace anyhttp::io" here would +// be a namespace of its own to clang, which does not look through the inline namespace for it. +#if ANYHTTP_COROSIO +namespace anyhttp::inline corosio::io +#else +namespace anyhttp::inline asio::io +#endif { error_code bind(UdpSocket& socket, const UdpEndpoint& endpoint) noexcept @@ -49,11 +55,11 @@ error_code set_option(UdpSocket& socket, int level, int name, int value) noexcep return {}; } -error_code send(UdpSocket& socket, asio::const_buffer buffer) noexcept +error_code send(UdpSocket& socket, boost::asio::const_buffer buffer) noexcept { if (::send(socket.native_handle(), buffer.data(), buffer.size(), 0) < 0) return last_error(); return {}; } -} // namespace anyhttp::io +} // namespace anyhttp::io (in the runtime's namespace) diff --git a/src/quic/client.cpp b/src/quic/client.cpp index 9b91c12..b7dff40 100644 --- a/src/quic/client.cpp +++ b/src/quic/client.cpp @@ -267,7 +267,7 @@ Task QuicClientSession::do_session(Buffer&&) std::array buf; for (;;) { - auto [ec, n] = co_await io::receive(socket_, asio::buffer(buf)); + auto [ec, n] = co_await io::receive(socket_, boost::asio::buffer(buf)); if (ec) { if (ec != errc::operation_canceled) @@ -376,7 +376,7 @@ int QuicClientSession::send_datagrams(const ngtcp2_path& /*path*/, std::span data); @@ -320,14 +313,14 @@ struct QuicBatch // // The server's QUIC half (see anyhttp/quic/backend.hpp): the UDP socket every QUIC connection // shares, the receive loop de-multiplexing datagrams onto them by connection ID, and the table -// doing that lookup. The sessions themselves are owned by Server::Impl's session registry, like -// the TCP-based ones -- what is kept here is only what routing packets needs, and the +// doing that lookup. The sessions themselves are owned by server::Server::Impl's session registry, +// like the TCP-based ones -- what is kept here is only what routing packets needs, and the // applications to run on them. // class QuicServerImpl : public QuicServer, public std::enable_shared_from_this { public: - QuicServerImpl(Server::Impl& parent, const UdpEndpoint& endpoint); + QuicServerImpl(server::Server::Impl& parent, const UdpEndpoint& endpoint); /// For log lines that belong to no connection, see anyhttp::log_prefix(). std::string log_prefix() const { return anyhttp::log_prefix(Role::server); } @@ -343,8 +336,8 @@ class QuicServerImpl : public QuicServer, public std::enable_shared_from_this& session, QuicBatch&& batch); - /// Keeps the owning Server::Impl alive for as long as a pending operation of ours runs. - std::shared_ptr owner() { return parent_.shared_from_this(); } + /// Keeps the owning server::Server::Impl alive for as long as a pending operation of ours runs. + std::shared_ptr owner() { return parent_.shared_from_this(); } private: - Server::Impl& parent_; + server::Server::Impl& parent_; // // What add_application() registered, in the order of the server's preference. Read by the ALPN @@ -476,11 +469,11 @@ void QuicServerSession::do_destroy() noexcept // // Tear the streams down here, on the session's executor, rather than leaving it to the // destructor. The destructor runs wherever the last shared_ptr happens to drop -- at server - // shutdown that can be Server::Impl::~Impl() on a foreign thread -- and destroying streams - // detaches readers/writers and fires pending handlers, state that request-handler coroutines - // still running on this session's strand look at. Done here, that teardown is serialized - // with them; a handler that resumes afterwards finds its Reader/Writer detached and fails - // cleanly, exactly as in the single-threaded case. + // shutdown that can be server::Server::Impl::~Impl() on a foreign thread -- and destroying + // streams detaches readers/writers and fires pending handlers, state that request-handler + // coroutines still running on this session's strand look at. Done here, that teardown is + // serialized with them; a handler that resumes afterwards finds its Reader/Writer detached and + // fails cleanly, exactly as in the single-threaded case. // clear_streams(); @@ -492,7 +485,7 @@ void QuicServerSession::do_destroy() noexcept } // - // Explicit shutdown (e.g. the whole Server::Impl going away): let the peer know right + // Explicit shutdown (e.g. the whole server::Server::Impl going away): let the peer know right // away instead of leaving it to find out via idle timeout (up to 30s). Unlike // handle_error(), this doesn't linger for 3 PTO to handle retransmits of the CLOSE -- // this is a clean, voluntary shutdown, not an error condition worth that effort. @@ -786,7 +779,7 @@ void QuicServerSession::resend_conn_close() // QuicServerImpl: the UDP socket, the receive loop and the connection-ID demux. // ================================================================================================= -QuicServerImpl::QuicServerImpl(Server::Impl& parent, const UdpEndpoint& endpoint) +QuicServerImpl::QuicServerImpl(server::Server::Impl& parent, const UdpEndpoint& endpoint) : parent_(parent), tls_(parent.config(), &QuicServerImpl::alpn_select_cb, this), executor_(config().use_strand ? new_strand(parent.get_executor()) : parent.get_executor()), socket_(io::make_udp_socket(executor_)) @@ -900,7 +893,7 @@ void QuicServerImpl::destroy() // The socket lives on its own strand and udp_receive_loop() keeps re-arming async_wait() on // it there -- asio sockets are not thread-safe, so the close has to go through the same // strand instead of racing that from here. The QUIC sessions have already been destroyed by - // Server::Impl at this point, each sending its final CONNECTION_CLOSE through its own + // server::Server::Impl at this point, each sending its final CONNECTION_CLOSE through its own // dup()ed fd, so closing this socket doesn't race that. // dispatch_to(executor_, [self = shared_from_this(), owner = owner()] { @@ -1219,11 +1212,12 @@ Task QuicServerImpl::udp_receive_loop() // ================================================================================================= -std::shared_ptr make_quic_server(Server::Impl& server, const UdpEndpoint& endpoint) +std::shared_ptr make_quic_server(server::Server::Impl& server, + const UdpEndpoint& endpoint) { return std::make_shared(server, endpoint); } // ================================================================================================= -} // namespace anyhttp::server +} // namespace anyhttp::quic diff --git a/src/quic/stream.cpp b/src/quic/stream.cpp index 3edc73f..449d814 100644 --- a/src/quic/stream.cpp +++ b/src/quic/stream.cpp @@ -70,7 +70,7 @@ void QuicStream::on_data_chunk(const uint8_t* data, size_t len) // reader could not take is parked for later. // auto self = shared_from_this(); // a resumed reader may drop the last reference to this stream - incoming = asio::const_buffer{data, len}; + incoming = boost::asio::const_buffer{data, len}; call_read_handler(); if (incoming.size() > 0) @@ -78,7 +78,7 @@ void QuicStream::on_data_chunk(const uint8_t* data, size_t len) auto* rest = static_cast(incoming.data()); pending_read.emplace_back(rest, rest + incoming.size()); if (read_head.size() == 0) - read_head = asio::buffer(pending_read.front()); + read_head = boost::asio::buffer(pending_read.front()); incoming = {}; } } @@ -130,15 +130,15 @@ void QuicStream::call_read_handler() size_t copied = 0; while (dest.size() > 0 && read_head.size() > 0) { - auto n = asio::buffer_copy(dest, read_head); + auto n = boost::asio::buffer_copy(dest, read_head); dest += n; read_head += n; copied += n; if (read_head.size() == 0) { pending_read.pop_front(); - read_head = - pending_read.empty() ? asio::const_buffer{} : asio::buffer(pending_read.front()); + read_head = pending_read.empty() ? boost::asio::const_buffer{} + : boost::asio::buffer(pending_read.front()); } } @@ -149,7 +149,7 @@ void QuicStream::call_read_handler() // if (dest.size() > 0 && incoming.size() > 0) { - auto n = asio::buffer_copy(dest, incoming); + auto n = boost::asio::buffer_copy(dest, incoming); incoming += n; copied += n; } @@ -192,9 +192,9 @@ void QuicStream::call_read_handler() // Outgoing body // ================================================================================================= -void QuicStream::start_write(WriteHandler&& handler, asio::const_buffer buffer, bool eof) +void QuicStream::start_write(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) { - auto n = asio::buffer_size(buffer); + auto n = boost::asio::buffer_size(buffer); mlogd("start_write: n={} eof={}", n, eof); auto complete_immediately = [&](error_code ec) { // @@ -304,7 +304,7 @@ void QuicStream::bind_write_cancellation(WriteHandler& handler, uint64_t token) if (write_token != token || !write_handler) return; // already completed naturally before the cancellation was delivered - if (write_is_eof && asio::buffer_size(write_source) == 0) + if (write_is_eof && boost::asio::buffer_size(write_source) == 0) { // // A bare end-of-body carries no memory of the caller's, and a FIN cannot be un-sent -- @@ -381,7 +381,7 @@ size_t QuicStream::pending_data(uint64_t from, std::span vec, bool& if (vec.empty() || closed || write_aborted) return 0; - const size_t total = asio::buffer_size(write_source); + const size_t total = boost::asio::buffer_size(write_source); const auto* source = static_cast(write_source.data()); // @@ -451,7 +451,7 @@ void QuicStream::on_offered(uint64_t end, bool fin) // carries no memory of the caller's that we would have to keep alive until it is // acknowledged. // - if (write_active && asio::buffer_size(write_source) == 0) + if (write_active && boost::asio::buffer_size(write_source) == 0) finish_active_write(); } @@ -463,7 +463,7 @@ void QuicStream::on_sent(uint64_t end) if (write_mode != WriteMode::Staged || !write_active || tx_sent < tx_queued) return; - if (asio::buffer_size(write_source) == 0) + if (boost::asio::buffer_size(write_source) == 0) return; // a bare end-of-body completes when the FIN is handed over, see on_offered() // @@ -473,7 +473,7 @@ void QuicStream::on_sent(uint64_t end) // Without telling it otherwise, any single async_write() larger than kWriteChunkSize would // stall here forever, with the body truncated and no FIN. // - if (write_copied == asio::buffer_size(write_source)) + if (write_copied == boost::asio::buffer_size(write_source)) finish_active_write(); else on_writable(); @@ -500,8 +500,9 @@ void QuicStream::on_acked(uint64_t end) // The only notification that the peer is done with bytes we handed out by reference, and // hence that the caller's buffer may be released -- see WriteMode::ZeroCopy. // - if (write_mode == WriteMode::ZeroCopy && write_active && asio::buffer_size(write_source) > 0 && - tx_acked >= write_base + asio::buffer_size(write_source)) + if (write_mode == WriteMode::ZeroCopy && write_active && + boost::asio::buffer_size(write_source) > 0 && + tx_acked >= write_base + boost::asio::buffer_size(write_source)) finish_active_write(); } diff --git a/src/reader.cpp b/src/reader.cpp index b8199fe..0458ae9 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -39,13 +39,13 @@ std::optional Reader::content_length() const noexcept static IoTask no_reader() { co_return std::tuple{errors::bad_descriptor, size_t{0}}; } -IoTask Reader::read_some(asio::mutable_buffer buffer) +IoTask Reader::read_some(boost::asio::mutable_buffer buffer) { return impl_ ? impl_->read_some(buffer) : no_reader(); } #if ANYHTTP_ASIO -void Reader::async_read_some_any(asio::mutable_buffer buffer, ReadSomeHandler&& handler) +void Reader::async_read_some_any(boost::asio::mutable_buffer buffer, ReadSomeHandler&& handler) { if (impl_) impl_->async_read_some(buffer, std::move(handler)); diff --git a/src/request_handlers.cpp b/src/request_handlers.cpp index 593cf19..fad1276 100644 --- a/src/request_handlers.cpp +++ b/src/request_handlers.cpp @@ -83,7 +83,7 @@ Task dump(server::Request request, server::Response response) 200, fields({{"Content-Length", body.size()}, {"Content-Type", "text/plain"}})); ec) co_return; - co_await response.write_eof(asio::buffer(body)); + co_await response.write_eof(boost::asio::buffer(body)); } Task echo(server::Request request, server::Response response) @@ -97,13 +97,13 @@ Task echo(server::Request request, server::Response response) std::array buffer; for (;;) { - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); if (ec == errors::eof) break; if (ec) co_return; // a body cut short, see not ending ours below - if (auto [write_ec] = co_await response.write(asio::buffer(buffer, n)); write_ec) + if (auto [write_ec] = co_await response.write(boost::asio::buffer(buffer, n)); write_ec) co_return; } @@ -178,7 +178,7 @@ Task drain(Reader& reader) std::array buffer; for (;;) { - auto [ec, n] = co_await reader.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await reader.read_some(boost::asio::buffer(buffer)); bytes += n; // the regular end of the body is not something to report as an error @@ -202,7 +202,7 @@ Task read(Reader& reader) std::array buffer; for (;;) { - auto [ec, n] = co_await reader.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await reader.read_some(boost::asio::buffer(buffer)); body += std::string_view(buffer.data(), n); if (ec == errors::eof) { @@ -227,7 +227,7 @@ Task> try_receive(Reader& reader) std::array buffer; for (;;) { - auto [ec, n] = co_await reader.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await reader.read_some(boost::asio::buffer(buffer)); bytes += n; // the regular end of the body is not something to report as an error @@ -287,7 +287,7 @@ Task hello_world(server::Response response) { constexpr auto hello = "Hello, World!\n"sv; if (auto [ec] = co_await response.submit(200, fields({{"Content-Length", hello.size()}})); !ec) - co_await response.write_eof(asio::buffer(hello)); + co_await response.write_eof(boost::asio::buffer(hello)); } Task h2spec(server::Request request, server::Response response) @@ -302,12 +302,12 @@ Task h2spec(server::Request request, server::Response response) // std::ignore = co_await delay(10ms); std::array buffer; - std::ignore = co_await request.read_some(asio::buffer(buffer)); + std::ignore = co_await request.read_some(boost::asio::buffer(buffer)); constexpr auto hello = "Hello, World!\n"sv; if (auto [ec] = co_await response.submit(200, fields({{"Content-Length", hello.size()}})); ec) co_return; - co_await response.write_eof(asio::buffer(hello)); + co_await response.write_eof(boost::asio::buffer(hello)); co_await drain(request); } diff --git a/src/server_impl.cpp b/src/server_impl.cpp index dd7bd1a..2afa92e 100644 --- a/src/server_impl.cpp +++ b/src/server_impl.cpp @@ -65,8 +65,8 @@ Server::Impl::Impl(Executor executor, Config config) // listen_tcp(): with port=0 the actual port is only known once the acceptor is bound. // auto tcp_ep = io::local_endpoint(acceptor_); - quic_ = make_quic_server(*this, UdpEndpoint{tcp_ep.address(), tcp_ep.port()}); - quic_->add_application("h3", make_http3_application(*this)); + quic_ = quic::make_quic_server(*this, UdpEndpoint{tcp_ep.address(), tcp_ep.port()}); + quic_->add_application("h3", h3::make_http3_application(*this)); // // Advertise that endpoint to HTTP/1.1 and HTTP/2 clients, see Config::alt_svc_max_age. The @@ -246,16 +246,16 @@ Task Server::Impl::handle_connection(Executor executor, TcpSocket socket) // buy nothing: HTTP/1.1 is what a connection without a negotiated protocol speaks anyway. // if (alpn == "h2") - session = nghttp2::make_server_session(*this, executor, std::move(tls)); + session = h2::make_server_session(*this, executor, std::move(tls)); else - session = beast_impl::make_server_session(*this, executor, std::move(tls)); + session = h1::make_server_session(*this, executor, std::move(tls)); } else if (detected == detail::Detected::h2c) { logi("[{}] new connection, h2c (prior knowledge, {} bytes buffered)", prefix, buffer.size()); session = - nghttp2::make_server_session(*this, executor, make_plain_server_stream(std::move(socket))); + h2::make_server_session(*this, executor, make_plain_server_stream(std::move(socket))); } // @@ -264,8 +264,8 @@ Task Server::Impl::handle_connection(Executor executor, TcpSocket socket) else { logi("[{}] new connection, h1 (no HTTP/2 client preface)", prefix); - session = beast_impl::make_server_session(*this, executor, - make_plain_server_stream(std::move(socket))); + session = + h1::make_server_session(*this, executor, make_plain_server_stream(std::move(socket))); } // diff --git a/src/session.cpp b/src/session.cpp index 8aaaac4..6b8944a 100644 --- a/src/session.cpp +++ b/src/session.cpp @@ -130,7 +130,7 @@ IoTask get_message(std::shared_ptr session, boos std::array buffer; for (;;) { - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); body.append(buffer.data(), n); if (ec == errors::eof) diff --git a/src/writer.cpp b/src/writer.cpp index 1ded333..f8e5485 100644 --- a/src/writer.cpp +++ b/src/writer.cpp @@ -39,18 +39,18 @@ void Writer::content_length(std::optional content_length) static IoTask<> no_writer() { co_return std::tuple{errors::bad_descriptor}; } -IoTask<> Writer::write(asio::const_buffer buffer) +IoTask<> Writer::write(boost::asio::const_buffer buffer) { return impl_ ? impl_->write(buffer, false) : no_writer(); } -IoTask<> Writer::write_eof(asio::const_buffer buffer) +IoTask<> Writer::write_eof(boost::asio::const_buffer buffer) { return impl_ ? impl_->write(buffer, true) : no_writer(); } #if ANYHTTP_ASIO -void Writer::async_write_any(WriteHandler&& handler, asio::const_buffer buffer, bool eof) +void Writer::async_write_any(WriteHandler&& handler, boost::asio::const_buffer buffer, bool eof) { if (impl_) impl_->async_write(std::move(handler), buffer, eof); diff --git a/test/package/consumer.cpp b/test/package/consumer.cpp index 517eef2..7280cd9 100644 --- a/test/package/consumer.cpp +++ b/test/package/consumer.cpp @@ -29,14 +29,14 @@ static Task respond(server::Request request, server::Response response) std::array buffer; for (;;) { - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); if (ec) break; } if (auto [ec] = co_await response.submit(200, {}); ec) co_return; - co_await response.write_eof(asio::buffer(body)); + co_await response.write_eof(boost::asio::buffer(body)); } static Task get(client::Client& client, std::optional& server, diff --git a/test/test_alt_svc.cpp b/test/test_alt_svc.cpp index 20b2e4f..1a2caa6 100644 --- a/test/test_alt_svc.cpp +++ b/test/test_alt_svc.cpp @@ -349,11 +349,11 @@ Task serve_h2_with_altsvc(TcpSocket socket, std::string origin, std::strin auto n = nghttp2_session_mem_send2(raw, &data); if (n <= 0) break; - if (auto [ec, written] = co_await io::write(socket, asio::buffer(data, n)); ec) + if (auto [ec, written] = co_await io::write(socket, boost::asio::buffer(data, n)); ec) co_return; } - auto [ec, n] = co_await io::read_some(socket, asio::buffer(buffer)); + auto [ec, n] = co_await io::read_some(socket, boost::asio::buffer(buffer)); if (ec || nghttp2_session_mem_recv2(raw, buffer.data(), n) < 0) co_return; } diff --git a/test/test_client_async.cpp b/test/test_client_async.cpp index cbf4b16..b3a8e24 100644 --- a/test/test_client_async.cpp +++ b/test/test_client_async.cpp @@ -165,7 +165,7 @@ TEST_P(ClientAsync, WHEN_session_is_gone_THEN_request_reports_error) auto request = check(co_await session.submit(url.set_path("echo"), {})); session.reset(); - auto [ec] = co_await request.write(asio::buffer("Hello"sv)); + auto [ec] = co_await request.write(boost::asio::buffer("Hello"sv)); EXPECT_TRUE(is_connection_error(ec)) << what(ec); std::tie(ec) = co_await request.write_eof(); @@ -206,7 +206,7 @@ TEST_P(ClientAsync, WHEN_server_session_is_gone_THEN_response_reports_error) auto [ec] = co_await response.submit(200, {}); EXPECT_TRUE(is_connection_error(ec)) << what(ec); - std::tie(ec) = co_await response.write(asio::buffer("Hello"sv)); + std::tie(ec) = co_await response.write(boost::asio::buffer("Hello"sv)); EXPECT_TRUE(is_connection_error(ec)) << what(ec); responded->notify(); @@ -305,13 +305,13 @@ TEST_P(ClientAsync, clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write_eof(asio::buffer("Hello, Server #1!"sv))); + check(co_await request1.write_eof(boost::asio::buffer("Hello, Server #1!"sv))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); request1.reset(); session.reset(); - auto [ec] = co_await request2.write(asio::buffer("Hello"sv)); + auto [ec] = co_await request2.write(boost::asio::buffer("Hello"sv)); EXPECT_TRUE(is_connection_error(ec)) << what(ec); }; } @@ -323,7 +323,7 @@ TEST_P(ClientAsync, WHEN_session_is_gone_THEN_earlier_request_reports_error) clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write_eof(asio::buffer("Hello, Server #1!"sv))); + check(co_await request1.write_eof(boost::asio::buffer("Hello, Server #1!"sv))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); session.reset(); @@ -341,9 +341,9 @@ TEST_P(ClientAsync, clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write_eof(asio::buffer("Hello, Server #1!"sv))); + check(co_await request1.write_eof(boost::asio::buffer("Hello, Server #1!"sv))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write(asio::buffer("Hello, Server #2!"sv))); + check(co_await request2.write(boost::asio::buffer("Hello, Server #2!"sv))); auto response1 = check(co_await request1.get_response()); EXPECT_EQ(co_await drain(response1), 17); @@ -356,7 +356,7 @@ TEST_P(ClientAsync, // The body of the second response is still open, as its request has not been ended. // std::array buffer; - auto [ec, n] = co_await response2.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response2.read_some(boost::asio::buffer(buffer)); EXPECT_EQ(ec, errors::partial_message) << what(ec); }; } @@ -456,12 +456,12 @@ TEST_P(ClientAsync, YieldFuzz) fields.set("Content-Length", std::to_string(msg.size())); check(co_await response.submit(200, fields)); co_await yield(dist(server_gen)); - check(co_await response.write(asio::buffer(msg))); + check(co_await response.write(boost::asio::buffer(msg))); co_await yield(dist(server_gen)); check(co_await response.write_eof()); co_await yield(dist(server_gen)); std::array data; - co_await request.read_some(asio::buffer(data)); + co_await request.read_some(boost::asio::buffer(data)); }; clientSession = [this](Session session) -> Task { std::uniform_int_distribution<> dist(0, 10); @@ -494,7 +494,7 @@ TEST_P(ClientAsync, WHEN_body_ends_THEN_read_reports_eof) requestHandler = [this](server::Request request, server::Response response) -> Task { co_await drain(request); check(co_await response.submit(200, fields({{"Content-Length", hello.size()}}))); - check(co_await response.write_eof(asio::buffer(hello))); + check(co_await response.write_eof(boost::asio::buffer(hello))); }; clientSession = [this](Session session) -> Task { auto request = check(co_await session.submit(url)); @@ -505,7 +505,7 @@ TEST_P(ClientAsync, WHEN_body_ends_THEN_read_reports_eof) std::array buffer; // small on purpose: several reads before the end for (;;) { - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); if (ec) { EXPECT_EQ(ec, errors::eof); @@ -522,12 +522,12 @@ TEST_P(ClientAsync, WHEN_body_ends_THEN_read_reports_eof) // opportunity to do (both sides of the exchange are finished by now). // co_await yield(20); - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); EXPECT_EQ(ec, errors::eof); // ... but a zero-length read is not a read, and reports nothing std::array empty; - std::tie(ec, n) = co_await response.read_some(asio::buffer(empty)); + std::tie(ec, n) = co_await response.read_some(boost::asio::buffer(empty)); EXPECT_FALSE(ec); EXPECT_EQ(n, 0u); }; @@ -542,7 +542,7 @@ TEST_P(ClientAsync, WHEN_reading_into_buffer_sequence_THEN_empty_buffers_are_ski requestHandler = [this](server::Request request, server::Response response) -> Task { co_await drain(request); check(co_await response.submit(200, fields({{"Content-Length", hello.size()}}))); - check(co_await response.write_eof(asio::buffer(hello))); + check(co_await response.write_eof(boost::asio::buffer(hello))); }; clientSession = [this](Session session) -> Task { auto request = check(co_await session.submit(url)); @@ -551,8 +551,8 @@ TEST_P(ClientAsync, WHEN_reading_into_buffer_sequence_THEN_empty_buffers_are_ski std::array empty; std::array buffer; - auto n = - check(co_await response.read_some(std::array{asio::buffer(empty), asio::buffer(buffer)})); + auto n = check(co_await response.read_some( + std::array{boost::asio::buffer(empty), boost::asio::buffer(buffer)})); EXPECT_GT(n, 0u); EXPECT_THAT(hello, StartsWith(std::string_view(buffer.data(), n))); }; @@ -569,7 +569,7 @@ TEST_P(ClientAsync, WHEN_empty_buffer_is_written_THEN_body_stays_open) EXPECT_EQ(co_await drain(request), 0u); check(co_await response.submit(200, {})); check(co_await response.write({})); // writes nothing, leaves the body open - check(co_await response.write_eof(asio::buffer(tail))); + check(co_await response.write_eof(boost::asio::buffer(tail))); }; clientSession = [this](Session session) -> Task { auto request = check(co_await session.submit(url)); @@ -591,7 +591,7 @@ TEST_P(ClientAsync, WHEN_written_after_eof_THEN_reports_broken_pipe) requestHandler = [this](server::Request request, server::Response response) -> Task { co_await drain(request); check(co_await response.submit(200, fields({{"Content-Length", hello.size()}}))); - check(co_await response.write_eof(asio::buffer(hello))); + check(co_await response.write_eof(boost::asio::buffer(hello))); auto [ec] = co_await response.write_eof(); EXPECT_FALSE(ec); @@ -599,10 +599,10 @@ TEST_P(ClientAsync, WHEN_written_after_eof_THEN_reports_broken_pipe) std::tie(ec) = co_await response.write({}); EXPECT_FALSE(ec); - std::tie(ec) = co_await response.write(asio::buffer(hello)); + std::tie(ec) = co_await response.write(boost::asio::buffer(hello)); EXPECT_EQ(ec, errc::broken_pipe); - std::tie(ec) = co_await response.write_eof(asio::buffer(hello)); + std::tie(ec) = co_await response.write_eof(boost::asio::buffer(hello)); EXPECT_EQ(ec, errc::broken_pipe); }; clientSession = [this](Session session) -> Task { @@ -615,7 +615,7 @@ TEST_P(ClientAsync, HelloWorld) static const auto hello = "Hello, World!"sv; requestHandler = [this](server::Request request, server::Response response) -> Task { check(co_await response.submit(200, {})); - check(co_await response.write_eof(asio::buffer(hello))); + check(co_await response.write_eof(boost::asio::buffer(hello))); }; clientSession = [this](Session session) -> Task { auto message = check(co_await session.get(url)); @@ -645,7 +645,7 @@ TEST_P(ClientAsync, WHEN_server_writes_large_buffer_at_once_THEN_receives_all) co_await drain(request); check(co_await response.submit(200, fields({{"Content-Length", body.size()}}))); - check(co_await response.write_eof(asio::buffer(body))); + check(co_await response.write_eof(boost::asio::buffer(body))); }; clientSession = [this](Session session) -> Task { EXPECT_EQ((check(co_await session.get(url))).body().size(), body.size()); @@ -671,10 +671,10 @@ TEST_P(ClientAsync, WHEN_server_cancels_write_eof_THEN_client_sees_truncated_bod // this is over, so the write is guaranteed to still be in progress when it is cancelled. // #if ANYHTTP_COROSIO - auto [ec] = co_await stop_after(50ms, response.write_eof(asio::buffer(body))); + auto [ec] = co_await stop_after(50ms, response.write_eof(boost::asio::buffer(body))); #else auto [ec] = - co_await response.async_write_eof(asio::buffer(body), cancel_after(50ms, as_tuple)); + co_await response.async_write_eof(boost::asio::buffer(body), cancel_after(50ms, as_tuple)); #endif EXPECT_EQ(ec, errc::operation_canceled); }; @@ -711,7 +711,7 @@ TEST_P(ClientAsync, WHEN_client_cancels_write_eof_THEN_can_still_end) // far more than the send window, with nobody reading the echo yet: this cannot complete auto write_eof = [&]() -> Task { - check(co_await request.write_eof(asio::buffer(body))); + check(co_await request.write_eof(boost::asio::buffer(body))); }; auto ep = co_await cancel_task_after(100ms, write_eof()); EXPECT_EQ(code(ep), errc::operation_canceled); @@ -822,16 +822,16 @@ TEST_P(ClientAsync, Recursion) // verify that immediate completion (here, due to an empty buffer) does not cause recursion std::array empty; - check(co_await response.read_some(asio::buffer(empty))); + check(co_await response.read_some(boost::asio::buffer(empty))); auto s0 = stack_remaining_bytes().value(); - check(co_await response.read_some(asio::buffer(empty))); + check(co_await response.read_some(boost::asio::buffer(empty))); auto s1 = stack_remaining_bytes().value(); EXPECT_EQ(s0, s1); #if ANYHTTP_ASIO // however, ASIO allows us to control this behavior using "immediate executors" auto ex = co_await this_coro::executor; - co_await response.async_read_some(asio::buffer(empty), bind_immediate_executor(ex)); + co_await response.async_read_some(boost::asio::buffer(empty), bind_immediate_executor(ex)); auto s2 = stack_remaining_bytes().value(); EXPECT_GT(s1, s2); #endif @@ -847,13 +847,13 @@ TEST_P(ClientAsync, Custom) std::array buffer; for (;;) { - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); if (ec) { check(co_await response.write_eof()); co_return; } - check(co_await response.write(asio::buffer(buffer, n))); + check(co_await response.write(boost::asio::buffer(buffer, n))); } }; clientSession = [this](Session session) -> Task { @@ -900,7 +900,7 @@ TEST_P(ClientAsync, PostRange) { clientSession = [this](Session session) -> Task { auto request = check(co_await session.submit(url.set_path("echo"), {})); - // check(co_await request.write(asio::buffer("ping"sv))); // FIXME: + // check(co_await request.write(boost::asio::buffer("ping"sv))); // FIXME: auto response = check(co_await request.get_response()); // std::string s(10_m, 'a'); // auto sender = send(request, std::string_view("blah")); @@ -948,10 +948,10 @@ TEST_P(ClientAsync, WHEN_multiple_request_are_made_THEN_responses_are_received_i { clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write_eof(asio::buffer("Hello, Server #1!"sv))); + check(co_await request1.write_eof(boost::asio::buffer("Hello, Server #1!"sv))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write_eof(asio::buffer("Hello, Server #2! XYZ"sv))); + check(co_await request2.write_eof(boost::asio::buffer("Hello, Server #2! XYZ"sv))); auto response1 = check(co_await request1.get_response()); EXPECT_EQ(co_await drain(response1), 17); @@ -986,10 +986,10 @@ TEST_P(ClientAsync, WHEN_request_is_submitted_before_previous_is_complete_THEN_r else EXPECT_FALSE(ec) << what(ec); - check(co_await request1.write_eof(asio::buffer(body1))); + check(co_await request1.write_eof(boost::asio::buffer(body1))); if (limited) request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write_eof(asio::buffer(body2))); + check(co_await request2.write_eof(boost::asio::buffer(body2))); auto response1 = check(co_await request1.get_response()); EXPECT_EQ(co_await read(response1), body1); @@ -1037,7 +1037,7 @@ TEST_P(OneStream, WHEN_limit_is_one_THEN_second_request_reports_would_block) // against the limit until it is closed in *both* directions, so reading the response to its // end is part of letting the next request through; HTTP/1.1 is free again after the write. // - check(co_await request1.write_eof(asio::buffer(body1))); + check(co_await request1.write_eof(boost::asio::buffer(body1))); EXPECT_EQ(co_await read(response1), body1); // @@ -1062,7 +1062,7 @@ TEST_P(OneStream, WHEN_limit_is_one_THEN_second_request_reports_would_block) if (!request2) co_return; - check(co_await request2.write_eof(asio::buffer(body2))); + check(co_await request2.write_eof(boost::asio::buffer(body2))); auto response2 = check(co_await request2.get_response()); EXPECT_EQ(co_await read(response2), body2); }; @@ -1077,7 +1077,8 @@ TEST_P(ClientAsync, WHEN_many_requests_are_made_THEN_all_are_answered_in_order) for (size_t i = 0; i < 10; ++i) { requests.push_back(check(co_await session.submit(url.set_path("echo"), {}))); - check(co_await requests.back().write_eof(asio::buffer(std::format("request #{}", i)))); + check( + co_await requests.back().write_eof(boost::asio::buffer(std::format("request #{}", i)))); } for (size_t i = 0; i < requests.size(); ++i) @@ -1099,9 +1100,9 @@ TEST_P(ClientAsync, WHEN_getting_response_before_previous_is_read_THEN_reports_w clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write_eof(asio::buffer(body1))); + check(co_await request1.write_eof(boost::asio::buffer(body1))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write_eof(asio::buffer(body2))); + check(co_await request2.write_eof(boost::asio::buffer(body2))); auto [ec, response2] = co_await request2.get_response(); EXPECT_EQ(ec, errors::would_block) << "response #1 not requested yet"; @@ -1130,11 +1131,11 @@ TEST_P(ClientAsync, WHEN_request_has_no_body_THEN_it_is_complete_after_submit) auto request1 = check(co_await session.submit(url.set_path("echo"), fields({{"Content-Length", 0}}))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write_eof(asio::buffer(body2))); + check(co_await request2.write_eof(boost::asio::buffer(body2))); auto [ec] = co_await request1.write_eof(); EXPECT_FALSE(ec) << what(ec); - std::tie(ec) = co_await request1.write(asio::buffer(body1)); + std::tie(ec) = co_await request1.write(boost::asio::buffer(body1)); EXPECT_EQ(ec, errc::broken_pipe) << what(ec); auto response1 = check(co_await request1.get_response()); @@ -1156,14 +1157,14 @@ TEST_P(ClientAsync, WHEN_content_length_is_written_without_eof_THEN_request_is_n clientSession = [this](Session session) -> Task { auto request1 = check( co_await session.submit(url.set_path("echo"), fields({{"Content-Length", body1.size()}}))); - check(co_await request1.write(asio::buffer(body1))); + check(co_await request1.write(boost::asio::buffer(body1))); auto [ec, request2] = co_await session.submit(url.set_path("echo"), {}); EXPECT_EQ(ec, errors::would_block); check(co_await request1.write_eof()); request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write_eof(asio::buffer(body2))); + check(co_await request2.write_eof(boost::asio::buffer(body2))); auto response1 = check(co_await request1.get_response()); EXPECT_EQ(co_await read(response1), body1); @@ -1183,7 +1184,7 @@ TEST_P(ClientAsync, WHEN_incomplete_request_is_released_THEN_later_requests_repo clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write(asio::buffer(body1))); + check(co_await request1.write(boost::asio::buffer(body1))); request1.reset(); auto [ec, request2] = co_await session.submit(url.set_path("echo"), {}); @@ -1203,9 +1204,9 @@ TEST_P(ClientAsync, clientSession = [this](Session session) -> Task { auto request1 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request1.write_eof(asio::buffer(body1))); + check(co_await request1.write_eof(boost::asio::buffer(body1))); auto request2 = check(co_await session.submit(url.set_path("echo"), {})); - check(co_await request2.write_eof(asio::buffer(body2))); + check(co_await request2.write_eof(boost::asio::buffer(body2))); request1.reset(); auto [ec, response] = co_await request2.get_response(); diff --git a/test/test_client_async_cancellation.cpp b/test/test_client_async_cancellation.cpp index 9a59242..2ce9f48 100644 --- a/test/test_client_async_cancellation.cpp +++ b/test/test_client_async_cancellation.cpp @@ -31,8 +31,8 @@ TEST_P(ClientAsyncCancellation, Backpressure) auto response = check(co_await request.get_response()); auto sender = send(request, rv::iota(uint8_t(0))); co_await when_either(std::move(sender), sleep(2s)); - // FIXME: count bytes sent, just like asio::async_write() does - // FIXME: or even use asio::async_write() on top of a async_write_some() implementation + // FIXME: count bytes sent, just like boost::asio::async_write() does + // FIXME: or even use boost::asio::async_write() on top of a async_write_some() implementation // // Now that the flow control window is 0, we can't even send an EOF any more -- except over @@ -188,17 +188,18 @@ TEST_P(ClientAsyncCancellation, PerOperationCancellation) std::stop_source stop; Timer timer(context.get_executor()); timer.arm(110ms, [&stop] { stop.request_stop(); }); - auto [ec, n] = - co_await boost::capy::run(stop.get_token())(response.read_some(asio::buffer(buffer))); + auto [ec, n] = co_await boost::capy::run(stop.get_token())( + response.read_some(boost::asio::buffer(buffer))); #else - asio::cancellation_signal cancel; - asio::steady_timer timer(co_await asio::this_coro::executor, 110ms); + boost::asio::cancellation_signal cancel; + boost::asio::steady_timer timer(co_await boost::asio::this_coro::executor, 110ms); timer.async_wait([&cancel](const boost::system::error_code&) { // - cancel.emit(asio::cancellation_type::terminal); + cancel.emit(boost::asio::cancellation_type::terminal); }); - auto token = asio::bind_cancellation_slot(cancel.slot(), as_tuple); - auto [ec, n] = co_await response.async_read_some(asio::buffer(buffer), std::move(token)); + auto token = boost::asio::bind_cancellation_slot(cancel.slot(), as_tuple); + auto [ec, n] = + co_await response.async_read_some(boost::asio::buffer(buffer), std::move(token)); #endif EXPECT_EQ(ec, errc::operation_canceled); }; @@ -227,7 +228,7 @@ TEST_P(ClientAsyncCancellation, CancelAfter) EXPECT_FALSE(ec); constexpr auto msg = "Hello, Client!"sv; - check(co_await request.write_eof(asio::buffer(msg))); + check(co_await request.write_eof(boost::asio::buffer(msg))); EXPECT_EQ(co_await read(response), msg); }; } @@ -245,10 +246,10 @@ TEST_P(ClientAsyncCancellation, WHEN_client_read_some_with_cancel_after_THEN_can std::array buffer; #if ANYHTTP_COROSIO - auto [ec, n] = co_await stop_after(100ms, response.read_some(asio::buffer(buffer))); + auto [ec, n] = co_await stop_after(100ms, response.read_some(boost::asio::buffer(buffer))); #else - auto [ec, n] = - co_await response.async_read_some(asio::buffer(buffer), cancel_after(100ms, as_tuple)); + auto [ec, n] = co_await response.async_read_some(boost::asio::buffer(buffer), + cancel_after(100ms, as_tuple)); #endif EXPECT_EQ(ec, errc::operation_canceled); EXPECT_EQ(n, 0u); @@ -264,10 +265,10 @@ TEST_P(ClientAsyncCancellation, WHEN_server_read_some_with_cancel_after_THEN_can requestHandler = [](server::Request request, server::Response response) -> Task { std::array buffer; #if ANYHTTP_COROSIO - auto [ec, n] = co_await stop_after(100ms, request.read_some(asio::buffer(buffer))); + auto [ec, n] = co_await stop_after(100ms, request.read_some(boost::asio::buffer(buffer))); #else - auto [ec, n] = - co_await request.async_read_some(asio::buffer(buffer), cancel_after(100ms, as_tuple)); + auto [ec, n] = co_await request.async_read_some(boost::asio::buffer(buffer), + cancel_after(100ms, as_tuple)); #endif EXPECT_EQ(ec, errc::operation_canceled); EXPECT_EQ(n, 0u); @@ -292,14 +293,14 @@ TEST_P(ClientAsyncCancellation, WHEN_read_some_completes_before_cancel_after_THE auto response = check(co_await request.get_response()); constexpr auto msg = "Hello, Server!"sv; - check(co_await request.write_eof(asio::buffer(msg))); + check(co_await request.write_eof(boost::asio::buffer(msg))); std::array buffer; #if ANYHTTP_COROSIO - auto [ec, n] = co_await stop_after(10s, response.read_some(asio::buffer(buffer))); + auto [ec, n] = co_await stop_after(10s, response.read_some(boost::asio::buffer(buffer))); #else - auto [ec, n] = - co_await response.async_read_some(asio::buffer(buffer), cancel_after(10s, as_tuple)); + auto [ec, n] = co_await response.async_read_some(boost::asio::buffer(buffer), + cancel_after(10s, as_tuple)); #endif EXPECT_FALSE(ec); EXPECT_EQ(std::string_view(buffer.data(), n), msg.substr(0, n)); @@ -352,9 +353,10 @@ TEST_P(SmallSocketBuffers, WHEN_client_write_with_cancel_after_THEN_cancelled) const std::vector body(8_m, 'x'); #if ANYHTTP_COROSIO - auto [ec] = co_await stop_after(100ms, request.write(asio::buffer(body))); + auto [ec] = co_await stop_after(100ms, request.write(boost::asio::buffer(body))); #else - auto [ec] = co_await request.async_write(asio::buffer(body), cancel_after(100ms, as_tuple)); + auto [ec] = + co_await request.async_write(boost::asio::buffer(body), cancel_after(100ms, as_tuple)); #endif EXPECT_EQ(ec, errc::operation_canceled); }; diff --git a/test/test_client_connect.cpp b/test/test_client_connect.cpp index fd1f2cd..7b807e9 100644 --- a/test/test_client_connect.cpp +++ b/test/test_client_connect.cpp @@ -359,7 +359,7 @@ class ClientEndpoints : public ClientAsync co_await drain(request); const std::string authority(request.url().encoded_host_and_port()); check(co_await response.submit(200, fields({{"Content-Length", authority.size()}}))); - check(co_await response.write_eof(asio::buffer(authority))); + check(co_await response.write_eof(boost::asio::buffer(authority))); }; } diff --git a/test/test_client_tls.cpp b/test/test_client_tls.cpp index f187c38..ebf49e5 100644 --- a/test/test_client_tls.cpp +++ b/test/test_client_tls.cpp @@ -45,14 +45,13 @@ struct Peer #if ANYHTTP_COROSIO TlsContext make_peer_context(const Peer& peer) { - namespace corosio = boost::corosio; const server::Config config; TlsContext context; EXPECT_FALSE(context.use_certificate_chain_file(config.tls_certificate_chain)); EXPECT_FALSE( - context.use_private_key_file(config.tls_private_key, corosio::tls_file_format::pem)); + context.use_private_key_file(config.tls_private_key, boost::corosio::tls_file_format::pem)); if (peer.tls12) - EXPECT_FALSE(context.set_max_protocol_version(corosio::tls_version::tls_1_2)); + EXPECT_FALSE(context.set_max_protocol_version(boost::corosio::tls_version::tls_1_2)); if (!peer.alpn.empty()) EXPECT_FALSE(context.set_alpn({peer.alpn})); return context; @@ -78,9 +77,9 @@ int select_alpn(SSL*, const unsigned char** out, unsigned char* outlen, const un TlsContext make_peer_context(const Peer& peer) { const server::Config config; - asio::ssl::context context{asio::ssl::context::tls_server}; + boost::asio::ssl::context context{boost::asio::ssl::context::tls_server}; context.use_certificate_chain_file(config.tls_certificate_chain); - context.use_private_key_file(config.tls_private_key, asio::ssl::context::pem); + context.use_private_key_file(config.tls_private_key, boost::asio::ssl::context::pem); if (peer.tls12) SSL_CTX_set_max_proto_version(context.native_handle(), TLS1_2_VERSION); if (!peer.alpn.empty()) @@ -132,13 +131,13 @@ class ClientTlsPeer : public Test std::array buffer; while (!head.contains("\r\n\r\n")) { - auto [ec, n] = co_await io::read_some(stream, asio::buffer(buffer)); + auto [ec, n] = co_await io::read_some(stream, boost::asio::buffer(buffer)); head.append(buffer.data(), n); if (ec) throw_error(ec); } - check(co_await io::write(stream, asio::buffer(peer.response))); + check(co_await io::write(stream, boost::asio::buffer(peer.response))); if (peer.close_notify) co_await async_teardown(stream); @@ -283,7 +282,7 @@ class ClientTlsEnd : public TestWithParam std::array buffer; for (bool first = true;; first = false) { - auto [ec, n] = co_await io::read_some(stream, asio::buffer(buffer)); + auto [ec, n] = co_await io::read_some(stream, boost::asio::buffer(buffer)); received.append(buffer.data(), n); if (ec) { @@ -296,10 +295,10 @@ class ClientTlsEnd : public TestWithParam // make this fail. Neither does it matter if it does. // if (first && h2()) - co_await io::write(stream, - asio::buffer(then == Then::end_the_session ? settings_and_goaway - : then == Then::stall ? big_windows - : settings)); + co_await io::write(stream, boost::asio::buffer(then == Then::end_the_session + ? settings_and_goaway + : then == Then::stall ? big_windows + : settings)); if (then == Then::end_the_session) { @@ -432,7 +431,7 @@ TEST_P(ClientTlsEnd, WHEN_write_is_stuck_THEN_shutdown_interrupts_it_and_says_so auto request = check(co_await session.submit(target, {})); auto write = [&]() -> Task { - std::tie(write_result) = co_await request.write(asio::buffer(body)); + std::tie(write_result) = co_await request.write(boost::asio::buffer(body)); }; auto end_it = [&]() -> Task { co_await delay(50ms); @@ -489,7 +488,7 @@ TEST_P(ClientShutdown, WHEN_request_is_open_THEN_it_fails_right_away) auto read = [&]() -> Task { std::array buffer; - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); EXPECT_TRUE(ec); EXPECT_NE(ec, errors::eof); EXPECT_EQ(n, 0); @@ -501,7 +500,7 @@ TEST_P(ClientShutdown, WHEN_request_is_open_THEN_it_fails_right_away) }; co_await when_both(read(), end()); - auto [write_ec] = co_await request.write(asio::const_buffer("x", 1)); + auto [write_ec] = co_await request.write(boost::asio::const_buffer("x", 1)); EXPECT_TRUE(write_ec); }; } diff --git a/test/test_connection_close.cpp b/test/test_connection_close.cpp index 46a68e9..12182bd 100644 --- a/test/test_connection_close.cpp +++ b/test/test_connection_close.cpp @@ -20,7 +20,7 @@ using namespace testing; namespace http = boost::beast::http; -namespace h1 = anyhttp::beast_impl::h1; +namespace h1 = anyhttp::h1; // ================================================================================================= @@ -71,7 +71,7 @@ class ConnectionClose : public Server std::array buffer; Timer timer(context.get_executor()); timer.arm(5s, [&socket] { io::cancel(socket); }); - auto [ec, n] = co_await io::read_some(socket, asio::buffer(buffer)); + auto [ec, n] = co_await io::read_some(socket, boost::asio::buffer(buffer)); timer.cancel(); if (!ec) ADD_FAILURE() << std::format("{} bytes after the last response: '{}'", n, @@ -183,7 +183,8 @@ TEST_F(ConnectionClose, WHEN_requests_follow_one_that_asks_to_close_THEN_they_ar run([&]() -> Task { auto socket = co_await connect(); - check(co_await io::write(socket, asio::buffer(raw_get("Connection: close\r\n") + raw_get()))); + check(co_await io::write(socket, + boost::asio::buffer(raw_get("Connection: close\r\n") + raw_get()))); Response response; check(co_await h1::read_message(socket, buffer_, response)); @@ -210,7 +211,7 @@ TEST_F(ConnectionClose, WHEN_handler_says_close_THEN_stream_ends_and_the_rest_is run([&]() -> Task { auto socket = co_await connect(); - check(co_await io::write(socket, asio::buffer(raw_get() + raw_get()))); + check(co_await io::write(socket, boost::asio::buffer(raw_get() + raw_get()))); Response response; check(co_await h1::read_message(socket, buffer_, response)); @@ -255,7 +256,7 @@ TEST_F(ConnectionClose, WHEN_peer_resets_with_requests_pipelined_THEN_the_rest_a std::string requests; for (int i = 0; i < 3; ++i) requests += std::format("GET /custom HTTP/1.1\r\nHost: 127.0.0.2:{}\r\n\r\n", port()); - check(co_await io::write(socket, asio::buffer(requests))); + check(co_await io::write(socket, boost::asio::buffer(requests))); // // Reset only once the server has the requests: data still in its receive queue may be lost @@ -310,7 +311,7 @@ TEST_F(RejectedRequest, WHEN_request_is_rejected_THEN_the_response_arrives_anywa // Response response; auto send = [&]() -> Task { - auto [ec, n] = co_await io::write(socket, asio::buffer(request)); + auto [ec, n] = co_await io::write(socket, boost::asio::buffer(request)); co_return ec; }; auto receive = [&]() -> Task { @@ -343,7 +344,7 @@ class TlsConnectionClose : public ConnectionClose #if ANYHTTP_COROSIO stream.tls().set_hostname("127.0.0.2"); #else - stream.set_verify_callback(asio::ssl::host_name_verification("127.0.0.2")); + stream.set_verify_callback(boost::asio::ssl::host_name_verification("127.0.0.2")); #endif check(co_await io::handshake(stream, Role::client)); co_return stream; @@ -351,18 +352,17 @@ class TlsConnectionClose : public ConnectionClose #if ANYHTTP_COROSIO TlsContext tls_ = std::invoke([] { - namespace corosio = boost::corosio; - corosio::tls_context context; - throw_on_error(context.set_min_protocol_version(corosio::tls_version::tls_1_3)); + boost::corosio::tls_context context; + throw_on_error(context.set_min_protocol_version(boost::corosio::tls_version::tls_1_3)); throw_on_error(context.load_verify_file("pki/out/root.pem")); - throw_on_error(context.set_verify_mode(corosio::tls_verify_mode::peer)); + throw_on_error(context.set_verify_mode(boost::corosio::tls_verify_mode::peer)); return context; }); #else TlsContext tls_ = std::invoke([] { - asio::ssl::context context{asio::ssl::context::tlsv13}; + boost::asio::ssl::context context{boost::asio::ssl::context::tlsv13}; context.load_verify_file("pki/out/root.pem"); - context.set_verify_mode(asio::ssl::verify_peer); + context.set_verify_mode(boost::asio::ssl::verify_peer); return context; }); #endif diff --git a/test/test_connection_end.cpp b/test/test_connection_end.cpp index f858c76..4154c2c 100644 --- a/test/test_connection_end.cpp +++ b/test/test_connection_end.cpp @@ -26,7 +26,7 @@ using namespace testing; namespace http = boost::beast::http; -namespace h1 = anyhttp::beast_impl::h1; +namespace h1 = anyhttp::h1; // ================================================================================================= @@ -218,7 +218,7 @@ class ConnectionEndBase : public Server std::array data; while (!h2_.closed.contains(id)) { - auto [ec, n] = co_await read_some(stream, asio::buffer(data)); + auto [ec, n] = co_await read_some(stream, boost::asio::buffer(data)); if (ec) throw_error(ec); h2_.input({data.data(), n}); @@ -258,7 +258,7 @@ class ConnectionEndBase : public Server auto out = h2_.output(); if (out.empty()) co_return error_code{}; - auto [ec, n] = co_await io::write(stream, asio::buffer(out)); + auto [ec, n] = co_await io::write(stream, boost::asio::buffer(out)); co_return ec; } @@ -274,7 +274,7 @@ class ConnectionEndBase : public Server std::array data; for (;;) { - auto [ec, n] = co_await read_some(stream, asio::buffer(data)); + auto [ec, n] = co_await read_some(stream, boost::asio::buffer(data)); if (ec) co_return ec; if (h2()) @@ -318,7 +318,7 @@ class ConnectionEndBase : public Server #if ANYHTTP_COROSIO auto [ec] = co_await stream.tls().shutdown(); #else - auto [ec] = co_await stream.async_shutdown(asio::as_tuple); + auto [ec] = co_await stream.async_shutdown(boost::asio::as_tuple); #endif timer.cancel(); co_return ec; @@ -329,7 +329,7 @@ class ConnectionEndBase : public Server * an ASIO socket read once more after its end (see above), must fail a test, not hang it. */ template - IoTask read_some(S& stream, asio::mutable_buffer buffer) + IoTask read_some(S& stream, boost::asio::mutable_buffer buffer) { Timer timer(context.get_executor()); timer.arm(timeout, [&stream] { io::cancel(stream); }); @@ -350,7 +350,7 @@ class ConnectionEndBase : public Server std::array data; for (;;) { - auto [ec, n] = co_await read_some(socket_of(stream), asio::buffer(data)); + auto [ec, n] = co_await read_some(socket_of(stream), boost::asio::buffer(data)); if (ec) co_return ec; ADD_FAILURE() << std::format("{} bytes after the end of the stream", n); @@ -428,7 +428,7 @@ class ConnectionEndBase : public Server const std::string late(1000, 'x'); for (int i = 0; i < 2; ++i) { - auto [ec, n] = co_await io::write(socket, asio::buffer(late)); + auto [ec, n] = co_await io::write(socket, boost::asio::buffer(late)); EXPECT_FALSE(ec) << std::format("write #{}: {}", i + 1, ec.message()); co_await delay(100ms); } @@ -475,7 +475,7 @@ class TlsConnectionEnd : public ConnectionEndBase #if ANYHTTP_COROSIO stream.tls().set_hostname("127.0.0.2"); #else - stream.set_verify_callback(asio::ssl::host_name_verification("127.0.0.2")); + stream.set_verify_callback(boost::asio::ssl::host_name_verification("127.0.0.2")); #endif check(co_await io::handshake(stream, Role::client)); EXPECT_EQ(io::alpn(stream), h2() ? "h2" : "http/1.1"); @@ -485,11 +485,10 @@ class TlsConnectionEnd : public ConnectionEndBase #if ANYHTTP_COROSIO static TlsContext make_context(Protocol protocol) { - namespace corosio = boost::corosio; - corosio::tls_context context; - throw_on_error(context.set_min_protocol_version(corosio::tls_version::tls_1_3)); + boost::corosio::tls_context context; + throw_on_error(context.set_min_protocol_version(boost::corosio::tls_version::tls_1_3)); throw_on_error(context.load_verify_file("pki/out/root.pem")); - throw_on_error(context.set_verify_mode(corosio::tls_verify_mode::peer)); + throw_on_error(context.set_verify_mode(boost::corosio::tls_verify_mode::peer)); throw_on_error(context.set_alpn({protocol == Protocol::h2 ? "h2" : "http/1.1"})); return context; } @@ -502,9 +501,9 @@ class TlsConnectionEnd : public ConnectionEndBase #else static TlsContext make_context(Protocol protocol) { - asio::ssl::context context{asio::ssl::context::tlsv13}; + boost::asio::ssl::context context{boost::asio::ssl::context::tlsv13}; context.load_verify_file("pki/out/root.pem"); - context.set_verify_mode(asio::ssl::verify_peer); + context.set_verify_mode(boost::asio::ssl::verify_peer); const std::string_view alpn = protocol == Protocol::h2 ? "\x02h2" : "\x08http/1.1"; EXPECT_EQ(SSL_CTX_set_alpn_protos(context.native_handle(), reinterpret_cast(alpn.data()), diff --git a/test/test_corosio.cpp b/test/test_corosio.cpp index 29be488..ce0adb6 100644 --- a/test/test_corosio.cpp +++ b/test/test_corosio.cpp @@ -40,7 +40,6 @@ using namespace testing; namespace capy = boost::capy; -namespace corosio = boost::corosio; namespace http = boost::beast::http; // ================================================================================================= @@ -54,7 +53,7 @@ namespace // leaves has to be offered again. // template -capy::task read_message(corosio::tcp_socket& socket, Parser& parser) +capy::task read_message(boost::corosio::tcp_socket& socket, Parser& parser) { std::array buffer; while (!parser.is_done()) @@ -82,7 +81,8 @@ capy::task read_message(corosio::tcp_socket& socket, Parser& pa // consume(), so their descriptors are copied out of the visitor and consumed after the write. // template -capy::task write_message(corosio::tcp_socket& socket, Serializer& serializer) +capy::task write_message(boost::corosio::tcp_socket& socket, + Serializer& serializer) { while (!serializer.is_done()) { @@ -114,15 +114,15 @@ capy::task write_message(corosio::tcp_socket& socket, Serialize // TEST(Corosio, BeastParserAndSerializer) { - corosio::io_context context; - auto [ec, server, client] = corosio::test::make_socket_pair(context); + boost::corosio::io_context context; + auto [ec, server, client] = boost::corosio::test::make_socket_pair(context); ASSERT_FALSE(ec); std::error_code server_ec, client_ec; http::request_parser request_parser; http::response_parser response_parser; - auto serve = [](corosio::tcp_socket& socket, auto& parser, + auto serve = [](boost::corosio::tcp_socket& socket, auto& parser, std::error_code& result) -> capy::task<> { if ((result = co_await read_message(socket, parser))) co_return; @@ -133,7 +133,7 @@ TEST(Corosio, BeastParserAndSerializer) result = co_await write_message(socket, serializer); }; - auto request = [](corosio::tcp_socket& socket, auto& parser, + auto request = [](boost::corosio::tcp_socket& socket, auto& parser, std::error_code& result) -> capy::task<> { http::request request{http::verb::post, "/echo", 11, "hello, corosio"}; request.chunked(true); @@ -230,7 +230,7 @@ Task finish_later(Parked& parked, size_t n) TEST(CorosioRuntime, WHEN_parked_operation_is_completed_THEN_caller_resumes_with_result) { - corosio::io_context context; + boost::corosio::io_context context; Parked parked; std::optional> result; @@ -248,7 +248,7 @@ TEST(CorosioRuntime, WHEN_parked_operation_is_completed_THEN_caller_resumes_with TEST(CorosioRuntime, WHEN_caller_is_stopped_THEN_parked_operation_is_cancelled) { - corosio::io_context context; + boost::corosio::io_context context; Parked parked; std::stop_source stop; std::optional> result; @@ -267,7 +267,7 @@ TEST(CorosioRuntime, WHEN_caller_is_stopped_THEN_parked_operation_is_cancelled) TEST(CorosioRuntime, WHEN_completion_is_dropped_THEN_caller_resumes_cancelled) { - corosio::io_context context; + boost::corosio::io_context context; Parked parked; std::optional> result; @@ -284,7 +284,7 @@ TEST(CorosioRuntime, WHEN_completion_is_dropped_THEN_caller_resumes_cancelled) TEST(CorosioRuntime, WHEN_task_is_launched_for_a_completion_THEN_its_result_completes_it) { - corosio::io_context context; + boost::corosio::io_context context; std::optional> result; auto task = []() -> IoTask { @@ -325,7 +325,7 @@ TEST(CorosioRuntime, { int destroyed = 0, resumed = 0; { - corosio::io_context context; + boost::corosio::io_context context; auto inner = [&]() -> Task { FrameProbe probe{destroyed}; co_await yield_now(); @@ -347,7 +347,7 @@ TEST(CorosioRuntime, WHEN_context_goes_away_after_a_nested_completion_was_THEN_f { int destroyed = 0, resumed = 0; { - corosio::io_context context; + boost::corosio::io_context context; Completion parked; auto inner = [&]() -> Task { FrameProbe probe{destroyed}; @@ -373,7 +373,7 @@ TEST(CorosioRuntime, WHEN_context_goes_away_with_a_task_launched_for_a_completio { int destroyed = 0, resumed = 0; { - corosio::io_context context; + boost::corosio::io_context context; auto task = [&]() -> IoTask { FrameProbe probe{destroyed}; co_await yield_now(); @@ -402,7 +402,7 @@ TEST(CorosioRuntime, WHEN_context_goes_away_with_a_task_launched_for_a_completio TEST(CorosioRuntime, WHEN_event_is_set_before_and_while_waiting_THEN_waits_end) { - corosio::io_context context; + boost::corosio::io_context context; Event event; int woken = 0; @@ -424,7 +424,7 @@ TEST(CorosioRuntime, WHEN_event_is_set_before_and_while_waiting_THEN_waits_end) TEST(CorosioRuntime, WHEN_timer_is_rearmed_or_cancelled_THEN_only_the_last_callback_runs) { - corosio::io_context context; + boost::corosio::io_context context; Timer timer(context.get_executor()); std::vector fired; @@ -439,7 +439,7 @@ TEST(CorosioRuntime, WHEN_timer_is_rearmed_or_cancelled_THEN_only_the_last_callb TEST(CorosioRuntime, WHEN_both_tasks_run_THEN_when_both_waits_for_both) { - corosio::io_context context; + boost::corosio::io_context context; std::vector done; auto one = [](std::vector& done, int id) -> Task { @@ -458,20 +458,20 @@ TEST(CorosioRuntime, WHEN_both_tasks_run_THEN_when_both_waits_for_both) TEST(CorosioRuntime, WHEN_peeking_THEN_bytes_are_seen_but_not_taken) { - corosio::io_context context; - auto [ec, server, client] = corosio::test::make_socket_pair(context); + boost::corosio::io_context context; + auto [ec, server, client] = boost::corosio::test::make_socket_pair(context); ASSERT_FALSE(ec); std::string peeked, read; - auto run = [](corosio::tcp_socket& server, corosio::tcp_socket& client, std::string& peeked, - std::string& read) -> Task { + auto run = [](boost::corosio::tcp_socket& server, boost::corosio::tcp_socket& client, + std::string& peeked, std::string& read) -> Task { std::ignore = co_await capy::write(client, capy::const_buffer("\x16hello", 6)); std::array first; - auto [ec, n] = co_await io::peek(server, asio::buffer(first)); + auto [ec, n] = co_await io::peek(server, boost::asio::buffer(first)); EXPECT_FALSE(ec); peeked.assign(first.data(), n); std::array all; - auto [read_ec, m] = co_await io::read_some(server, asio::buffer(all)); + auto [read_ec, m] = co_await io::read_some(server, boost::asio::buffer(all)); EXPECT_FALSE(read_ec); read.assign(all.data(), m); }; diff --git a/test/test_coroutine_api.cpp b/test/test_coroutine_api.cpp index 3fedce8..86cc595 100644 --- a/test/test_coroutine_api.cpp +++ b/test/test_coroutine_api.cpp @@ -29,7 +29,7 @@ TEST_P(CoroutineApi, WHEN_exchanging_a_body_THEN_both_sides_see_all_of_it) std::array buffer; for (;;) { - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); body.append(buffer.data(), n); if (ec == errors::eof) break; @@ -40,23 +40,23 @@ TEST_P(CoroutineApi, WHEN_exchanging_a_body_THEN_both_sides_see_all_of_it) auto [submit_ec] = co_await response.submit(200, {}); EXPECT_FALSE(submit_ec) << submit_ec.message(); - auto [write_ec] = co_await response.write(asio::buffer(body)); + auto [write_ec] = co_await response.write(boost::asio::buffer(body)); EXPECT_FALSE(write_ec) << write_ec.message(); // // Not checked: over HTTP/3, the server's writes complete when the peer acknowledges them, // and a client that has read all of the response may close the connection before it // acknowledges the last of it. // - std::ignore = co_await response.write_eof(asio::buffer("!", 1)); + std::ignore = co_await response.write_eof(boost::asio::buffer("!", 1)); }; clientSession = [this](Session session) -> Task { auto [ec, request] = co_await session.submit(url); EXPECT_FALSE(ec) << ec.message(); - auto [write_ec] = co_await request.write(asio::buffer("Hello, ", 7)); + auto [write_ec] = co_await request.write(boost::asio::buffer("Hello, ", 7)); EXPECT_FALSE(write_ec) << write_ec.message(); - auto [eof_ec] = co_await request.write_eof(asio::buffer("World", 5)); + auto [eof_ec] = co_await request.write_eof(boost::asio::buffer("World", 5)); EXPECT_FALSE(eof_ec) << eof_ec.message(); auto [response_ec, response] = co_await request.get_response(); @@ -67,7 +67,7 @@ TEST_P(CoroutineApi, WHEN_exchanging_a_body_THEN_both_sides_see_all_of_it) std::array buffer; for (;;) { - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); body.append(buffer.data(), n); if (ec) { @@ -92,7 +92,7 @@ TEST_P(CoroutineApi, WHEN_writing_after_the_end_THEN_error_is_reported_not_throw EXPECT_FALSE(ec) << ec.message(); EXPECT_FALSE(std::get<0>(co_await request.write_eof())); - auto [write_ec] = co_await request.write(asio::buffer("late", 4)); + auto [write_ec] = co_await request.write(boost::asio::buffer("late", 4)); EXPECT_EQ(write_ec, errc::broken_pipe); auto [response_ec, response] = co_await request.get_response(); @@ -114,7 +114,7 @@ TEST_P(CoroutineApi, WHEN_reader_comes_back_after_the_body_has_ended_THEN_all_of co_await drain(request); std::string body(small_body, 'x'); std::ignore = co_await response.submit(200, fields({{"Content-Length", small_body}})); - std::ignore = co_await response.write_eof(asio::buffer(body)); + std::ignore = co_await response.write_eof(boost::asio::buffer(body)); }; clientSession = [this](Session session) -> Task { @@ -123,7 +123,7 @@ TEST_P(CoroutineApi, WHEN_reader_comes_back_after_the_body_has_ended_THEN_all_of auto response = check(co_await request.get_response()); std::array buffer; - auto n = check(co_await response.read_some(asio::buffer(buffer))); + auto n = check(co_await response.read_some(boost::asio::buffer(buffer))); co_await sleep(100ms); // meanwhile, the rest of the body arrives, and the stream closes EXPECT_EQ(n + co_await drain(response), small_body); @@ -135,7 +135,7 @@ TEST_P(CoroutineApi, WHEN_connecting_and_getting_THEN_message_arrives) requestHandler = [](server::Request request, server::Response response) -> Task { co_await drain(request); std::ignore = co_await response.submit(200, fields({{"Content-Length", 2}})); - std::ignore = co_await response.write_eof(asio::buffer("ok", 2)); + std::ignore = co_await response.write_eof(boost::asio::buffer("ok", 2)); }; clientSession = [this](Session) -> Task { diff --git a/test/test_external.cpp b/test/test_external.cpp index e1804e2..0f0e197 100644 --- a/test/test_external.cpp +++ b/test/test_external.cpp @@ -58,7 +58,7 @@ class External : public Server #if ANYHTTP_COROSIO // // Boost.Process needs Boost.Asio, so with COROSIO the child processes run on an io_context of - // their own, on a thread of their own, next to the server's corosio::io_context. + // their own, on a thread of their own, next to the server's boost::corosio::io_context. // void SetUp() override { @@ -138,7 +138,8 @@ class External : public Server awaitable read_all(readable_pipe pipe) { std::string result; - auto [ec, nread] = co_await asio::async_read(pipe, asio::dynamic_buffer(result), as_tuple); + auto [ec, nread] = + co_await boost::asio::async_read(pipe, boost::asio::dynamic_buffer(result), as_tuple); logi("STDOUT: {} bytes ({})", nread, what(ec)); if (ec && ec != error::eof) throw boost::system::system_error(ec); @@ -464,7 +465,7 @@ TEST_F(ExternalCustom, nghttp2_trailers) auto body = std::format("{} bytes, x-trailer: {}", received, std::string_view(request.fields()["x-trailer"])); check(co_await response.submit(200, fields({{"Content-Length", body.size()}}))); - check(co_await response.write_eof(asio::buffer(body))); + check(co_await response.write_eof(boost::asio::buffer(body))); }; auto url = std::format("http://127.0.0.2:{}/custom", port()); diff --git a/test/test_fixtures_asio.hpp b/test/test_fixtures_asio.hpp index c81ad7f..a97fb0a 100644 --- a/test/test_fixtures_asio.hpp +++ b/test/test_fixtures_asio.hpp @@ -40,10 +40,9 @@ #include #include -namespace asio = boost::asio; -using namespace asio; -using namespace asio::experimental::awaitable_operators; -using ip::tcp; +using namespace boost::asio; +using namespace boost::asio::experimental::awaitable_operators; +using boost::asio::ip::tcp; /// The context the tests run their servers and clients on. using IoContext = boost::asio::io_context; diff --git a/test/test_fixtures_corosio.hpp b/test/test_fixtures_corosio.hpp index 974b47d..6b51fc0 100644 --- a/test/test_fixtures_corosio.hpp +++ b/test/test_fixtures_corosio.hpp @@ -1,7 +1,7 @@ #pragma once // -// The fixtures of test_fixtures.hpp for the COROSIO runtime, on a corosio::io_context. +// The fixtures of test_fixtures.hpp for the COROSIO runtime, on a boost::corosio::io_context. // test_fixtures_asio.hpp has the same names and members, in the same order. MULTITHREADED (defined // here, or on the compiler's command line) runs the tests on several threads, with a strand per // connection. diff --git a/test/test_formatter.cpp b/test/test_formatter.cpp index 03ce066..e79ac57 100644 --- a/test/test_formatter.cpp +++ b/test/test_formatter.cpp @@ -8,8 +8,10 @@ // #if ANYHTTP_H2_V2 #include +namespace h2 = anyhttp::h2v2; #else #include +namespace h2 = anyhttp::h2; #endif #include @@ -266,12 +268,12 @@ TEST(FormatterTest, NgHttp2NvParseEmptySpec) TEST(FormatterTest, NgHttp2NvNameOfValueOf) { auto nv = make_nghttp2_nv("content-type", "text/html"); - EXPECT_EQ(anyhttp::name_of(nv), "content-type"); - EXPECT_EQ(anyhttp::value_of(nv), "text/html"); + EXPECT_EQ(h2::name_of(nv), "content-type"); + EXPECT_EQ(h2::value_of(nv), "text/html"); auto empty = make_nghttp2_nv("", ""); - EXPECT_EQ(anyhttp::name_of(empty), ""); - EXPECT_EQ(anyhttp::value_of(empty), ""); + EXPECT_EQ(h2::name_of(empty), ""); + EXPECT_EQ(h2::value_of(empty), ""); } // ================================================================================================= diff --git a/test/test_get.cpp b/test/test_get.cpp index bb278bd..182bbe4 100644 --- a/test/test_get.cpp +++ b/test/test_get.cpp @@ -42,7 +42,7 @@ class AsyncGet : public ClientAsync EXPECT_EQ(co_await drain(request), 0); // a GET has no body check(co_await response.submit( 200, fields({{"Content-Length", body.size()}, {"X-Answer", 42}}))); - check(co_await response.write_eof(asio::buffer(body))); + check(co_await response.write_eof(boost::asio::buffer(body))); }; } @@ -210,13 +210,13 @@ TEST(AsyncGetRaw, WHEN_get_THEN_request_line_says_GET) std::array buffer; while (!head.contains("\r\n\r\n")) { - auto [ec, n] = co_await io::read_some(socket, asio::buffer(buffer)); + auto [ec, n] = co_await io::read_some(socket, boost::asio::buffer(buffer)); head.append(buffer.data(), n); if (ec) throw_error(ec); } constexpr auto response = "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello"sv; - check(co_await io::write(socket, asio::buffer(response))); + check(co_await io::write(socket, boost::asio::buffer(response))); io::shutdown(socket, io::Shutdown::send); }; launch(context.get_executor(), peer()); @@ -256,7 +256,7 @@ class SubmitMethod : public ClientAsync std::array buffer; for (;;) { - auto [ec, n] = co_await response.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await response.read_some(boost::asio::buffer(buffer)); body.append(buffer.data(), n); if (ec == errors::eof) co_return body; @@ -282,7 +282,7 @@ TEST_P(SubmitMethod, WHEN_method_is_given_THEN_the_server_sees_it) }; clientSession = [this](Session session) -> Task { auto request = check(co_await session.submit("PUT", url)); - check(co_await request.write_eof(asio::buffer("body"sv))); + check(co_await request.write_eof(boost::asio::buffer("body"sv))); auto response = check(co_await request.get_response()); EXPECT_EQ(response.status_code(), 200); EXPECT_EQ(co_await read_all(response), "body"); @@ -303,7 +303,7 @@ TEST_P(SubmitMethod, WHEN_get_stops_reading_at_a_limit_THEN_status_and_fields_ar std::vector chunk(16_k); for (;;) { - auto [ec] = co_await response.write(asio::buffer(chunk)); + auto [ec] = co_await response.write(boost::asio::buffer(chunk)); if (ec) break; sent += chunk.size(); @@ -323,7 +323,7 @@ TEST_P(SubmitMethod, WHEN_get_stops_reading_at_a_limit_THEN_status_and_fields_ar std::array buffer; size_t total = 0; while (total < limit) - total += check(co_await response.read_some(asio::buffer(buffer))); + total += check(co_await response.read_some(boost::asio::buffer(buffer))); EXPECT_LE(total, limit + buffer.size()); // @@ -363,7 +363,7 @@ TEST_P(SubmitMethod, WHEN_response_is_released_early_THEN_only_http11_loses_the_ } check(co_await response.submit(200, {})); std::vector chunk(16_k); - while (!std::get<0>(co_await response.write(asio::buffer(chunk)))) + while (!std::get<0>(co_await response.write(boost::asio::buffer(chunk)))) ; stopped = true; }; @@ -374,7 +374,7 @@ TEST_P(SubmitMethod, WHEN_response_is_released_early_THEN_only_http11_loses_the_ check(co_await request.write_eof()); auto response = check(co_await request.get_response()); std::array buffer; - check(co_await response.read_some(asio::buffer(buffer))); + check(co_await response.read_some(boost::asio::buffer(buffer))); } auto next = url; diff --git a/test/test_h2_push.cpp b/test/test_h2_push.cpp index cdb54c2..3f2f31a 100644 --- a/test/test_h2_push.cpp +++ b/test/test_h2_push.cpp @@ -123,7 +123,7 @@ class Peer Task write(std::string frames) { - auto [ec, written] = co_await io::write(socket_, asio::buffer(frames)); + auto [ec, written] = co_await io::write(socket_, boost::asio::buffer(frames)); co_return !ec; } @@ -153,7 +153,7 @@ class Peer } } - auto [ec, n] = co_await io::read_some(socket_, asio::buffer(chunk)); + auto [ec, n] = co_await io::read_some(socket_, boost::asio::buffer(chunk)); if (ec) co_return std::nullopt; buffer_.append(chunk.data(), n); diff --git a/test/test_h2c_upgrade.cpp b/test/test_h2c_upgrade.cpp index e92f849..af3af7f 100644 --- a/test/test_h2c_upgrade.cpp +++ b/test/test_h2c_upgrade.cpp @@ -20,7 +20,7 @@ using namespace testing; -namespace h1 = anyhttp::beast_impl::h1; +namespace h1 = anyhttp::h1; // ================================================================================================= @@ -151,7 +151,7 @@ class H2CUpgrade : public Server EXPECT_GT(id, 0) << nghttp2_strerror(id); } - auto recv = [&](asio::const_buffer data) { + auto recv = [&](boost::asio::const_buffer data) { auto n = nghttp2_session_mem_recv2(session, static_cast(data.data()), data.size()); EXPECT_EQ(n, data.size()) << nghttp2_strerror(n); @@ -173,7 +173,7 @@ class H2CUpgrade : public Server out.append(reinterpret_cast(data), n); } if (!out.empty()) - check(co_await io::write(socket, asio::buffer(out))); + check(co_await io::write(socket, boost::asio::buffer(out))); out.clear(); }; @@ -181,11 +181,11 @@ class H2CUpgrade : public Server std::array data; for (co_await send(); !done(); co_await send()) { - auto [ec, n] = co_await io::read_some(socket, asio::buffer(data)); + auto [ec, n] = co_await io::read_some(socket, boost::asio::buffer(data)); EXPECT_FALSE(ec) << ec.message(); if (ec) break; - recv(asio::buffer(data, n)); + recv(boost::asio::buffer(data, n)); } nghttp2_session_terminate_session(session, NGHTTP2_NO_ERROR); diff --git a/test/test_ipv6.cpp b/test/test_ipv6.cpp index 2640047..1678659 100644 --- a/test/test_ipv6.cpp +++ b/test/test_ipv6.cpp @@ -27,7 +27,7 @@ class IPv6 : public ClientAsync EXPECT_EQ(request.url().encoded_host(), host); co_await drain(request); check(co_await response.submit(200, fields({{"Content-Length", body.size()}}))); - check(co_await response.write_eof(asio::buffer(body))); + check(co_await response.write_eof(boost::asio::buffer(body))); }; } }; diff --git a/test/test_quic.cpp b/test/test_quic.cpp index cce94cd..49c9a34 100644 --- a/test/test_quic.cpp +++ b/test/test_quic.cpp @@ -21,7 +21,7 @@ IoTask read_all(Reader& reader) std::array buffer; for (;;) { - auto [ec, n] = co_await reader.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await reader.read_some(boost::asio::buffer(buffer)); body.append(buffer.data(), n); if (ec) co_return std::tuple{ec == errors::eof ? error_code{} : ec, std::move(body)}; @@ -44,7 +44,7 @@ Task echo_streams(quic::Connection connection) auto echo = [](quic::Stream stream) -> Task { auto [ec, body] = co_await read_all(stream); EXPECT_FALSE(ec) << ec.message(); - auto [write_ec] = co_await stream.write_eof(asio::buffer(body)); + auto [write_ec] = co_await stream.write_eof(boost::asio::buffer(body)); std::ignore = write_ec; // the client may be gone before it acknowledges the end }; for (;;) @@ -139,9 +139,9 @@ TEST_P(QuicStreams, WHEN_stream_is_echoed_THEN_both_ends_see_the_whole_body) EXPECT_FALSE(ec) << ec.message(); EXPECT_EQ(stream.id(), 0); - auto [write_ec] = co_await stream.write(asio::buffer("Hello, ", 7)); + auto [write_ec] = co_await stream.write(boost::asio::buffer("Hello, ", 7)); EXPECT_FALSE(write_ec) << write_ec.message(); - auto [eof_ec] = co_await stream.write_eof(asio::buffer("World!", 6)); + auto [eof_ec] = co_await stream.write_eof(boost::asio::buffer("World!", 6)); EXPECT_FALSE(eof_ec) << eof_ec.message(); auto [read_ec, body] = co_await read_all(stream); @@ -162,7 +162,7 @@ TEST_P(QuicStreams, WHEN_bodies_are_larger_than_the_windows_THEN_they_arrive_int auto connection = co_await connect(); auto exchange = [](quic::Stream stream, std::string body) -> Task { - auto [write_ec] = co_await stream.write_eof(asio::buffer(body)); + auto [write_ec] = co_await stream.write_eof(boost::asio::buffer(body)); EXPECT_FALSE(write_ec) << write_ec.message(); auto [read_ec, echoed] = co_await read_all(stream); EXPECT_FALSE(read_ec) << read_ec.message(); @@ -191,7 +191,7 @@ TEST_P(QuicStreams, WHEN_one_side_ends_its_body_THEN_the_other_can_still_write) // the client's side is over, ours is not for (auto part : {"an", "s", "wer"}) { - auto [write_ec] = co_await stream.write(asio::buffer(std::string_view(part))); + auto [write_ec] = co_await stream.write(boost::asio::buffer(std::string_view(part))); EXPECT_FALSE(write_ec) << write_ec.message(); } std::ignore = co_await stream.write_eof(); @@ -201,7 +201,7 @@ TEST_P(QuicStreams, WHEN_one_side_ends_its_body_THEN_the_other_can_still_write) run([&]() -> Task { auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); - auto [eof_ec] = co_await stream.write_eof(asio::buffer("question", 8)); + auto [eof_ec] = co_await stream.write_eof(boost::asio::buffer("question", 8)); EXPECT_FALSE(eof_ec) << eof_ec.message(); auto [read_ec, response] = co_await read_all(stream); @@ -209,7 +209,7 @@ TEST_P(QuicStreams, WHEN_one_side_ends_its_body_THEN_the_other_can_still_write) EXPECT_EQ(response, "answer"); // the end is final: data after it is refused, a second end is a no-op - auto [late_ec] = co_await stream.write(asio::buffer("x", 1)); + auto [late_ec] = co_await stream.write(boost::asio::buffer("x", 1)); EXPECT_EQ(late_ec, errc::broken_pipe); auto [again_ec] = co_await stream.write_eof(); EXPECT_FALSE(again_ec) << again_ec.message(); @@ -228,19 +228,19 @@ TEST_P(QuicStreams, WHEN_peer_resets_its_side_THEN_reads_report_partial_message) // Our side of the stream is unaffected. (Not checked: with zero copy, the write completes // when the client acknowledges it, and the client may be gone by then.) // - std::ignore = co_await stream.write_eof(asio::buffer("still here", 10)); + std::ignore = co_await stream.write_eof(boost::asio::buffer("still here", 10)); std::ignore = co_await connection.accept_stream(); }; run([&]() -> Task { auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); - auto [write_ec] = co_await stream.write(asio::buffer("partial", 7)); + auto [write_ec] = co_await stream.write(boost::asio::buffer("partial", 7)); EXPECT_FALSE(write_ec) << write_ec.message(); co_await delay(50ms); // let it arrive, so that the server sees a stream to accept stream.reset_stream(42); - auto [late_ec] = co_await stream.write(asio::buffer("more", 4)); + auto [late_ec] = co_await stream.write(boost::asio::buffer("more", 4)); EXPECT_EQ(late_ec, errc::connection_reset); auto [read_ec, body] = co_await read_all(stream); @@ -272,13 +272,13 @@ TEST_P(QuicStreams, WHEN_peer_stops_reading_THEN_writes_fail_with_connection_res error_code write_ec; for (int i = 0; i < 100 && !write_ec; ++i) { - std::tie(write_ec) = co_await stream.write(asio::buffer("data", 4)); + std::tie(write_ec) = co_await stream.write(boost::asio::buffer("data", 4)); if (!write_ec) co_await delay(5ms); } EXPECT_EQ(write_ec, errc::connection_reset); - auto [late_ec] = co_await stream.write(asio::buffer("more", 4)); + auto [late_ec] = co_await stream.write(boost::asio::buffer("more", 4)); EXPECT_EQ(late_ec, errc::connection_reset); }()); } @@ -296,7 +296,7 @@ TEST_P(QuicStreams, WHEN_writer_is_dropped_before_the_end_THEN_peer_sees_partial run([&]() -> Task { auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); - auto [write_ec] = co_await stream.write(asio::buffer("unfinished", 10)); + auto [write_ec] = co_await stream.write(boost::asio::buffer("unfinished", 10)); EXPECT_FALSE(write_ec) << write_ec.message(); co_await delay(50ms); { @@ -340,7 +340,7 @@ TEST_P(QuicStreamsSmallWindow, WHEN_reader_is_slow_THEN_flow_control_holds_the_w auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); auto body = pattern(8 * window); - auto [write_ec] = co_await stream.write_eof(asio::buffer(body)); + auto [write_ec] = co_await stream.write_eof(boost::asio::buffer(body)); EXPECT_FALSE(write_ec) << write_ec.message(); EXPECT_TRUE(reading) << "the write completed before the server started reading"; std::ignore = co_await read_all(stream); @@ -363,7 +363,7 @@ TEST_P(QuicStreamsSmallWindow, WHEN_peer_allows_no_more_streams_THEN_open_stream // // Once a stream is over, the server lets us open another one. // - std::ignore = co_await first.write_eof(asio::buffer("1", 1)); + std::ignore = co_await first.write_eof(boost::asio::buffer("1", 1)); auto [read_ec, body] = co_await read_all(first); EXPECT_EQ(body, "1"); first.reset(); @@ -388,7 +388,7 @@ TEST_P(QuicStreams, WHEN_server_opens_a_stream_THEN_client_accepts_it) auto [ec, stream] = connection.open_stream(); EXPECT_FALSE(ec) << ec.message(); EXPECT_EQ(stream.id(), 1); // the first server-initiated bidirectional stream - std::ignore = co_await stream.write_eof(asio::buffer("push", 4)); + std::ignore = co_await stream.write_eof(boost::asio::buffer("push", 4)); auto [read_ec, body] = co_await read_all(stream); EXPECT_FALSE(read_ec) << read_ec.message(); EXPECT_EQ(body, "ack"); @@ -402,7 +402,7 @@ TEST_P(QuicStreams, WHEN_server_opens_a_stream_THEN_client_accepts_it) auto [read_ec, body] = co_await read_all(stream); EXPECT_FALSE(read_ec) << read_ec.message(); EXPECT_EQ(body, "push"); - std::ignore = co_await stream.write_eof(asio::buffer("ack", 3)); + std::ignore = co_await stream.write_eof(boost::asio::buffer("ack", 3)); co_await delay(50ms); }()); } @@ -420,7 +420,7 @@ TEST_P(QuicStreams, WHEN_connection_is_closed_THEN_what_waits_on_it_fails) run([&]() -> Task { auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); - std::ignore = co_await stream.write(asio::buffer("hi", 2)); + std::ignore = co_await stream.write(boost::asio::buffer("hi", 2)); auto [accept_ec, accepted] = co_await connection.accept_stream(); EXPECT_EQ(accept_ec, errors::connection_aborted); @@ -458,7 +458,7 @@ TEST_P(QuicStreams, WHEN_write_is_cancelled_THEN_the_stream_goes_on_with_a_short auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); auto copy = body; - auto result = co_await when_either(stream.write(asio::buffer(copy)), delay(100ms)); + auto result = co_await when_either(stream.write(boost::asio::buffer(copy)), delay(100ms)); EXPECT_EQ(result.index(), 1) << "the write completed, although nothing was read"; copy.assign(copy.size(), '!'); // a cancelled write releases the buffer @@ -468,7 +468,7 @@ TEST_P(QuicStreams, WHEN_write_is_cancelled_THEN_the_stream_goes_on_with_a_short // stream being blocked by flow control, it usually is. Otherwise the stream had to be // reset. // - auto [write_ec] = co_await stream.write_eof(asio::buffer("end", 3)); + auto [write_ec] = co_await stream.write_eof(boost::asio::buffer("end", 3)); if (GetParam() && write_ec) EXPECT_EQ(write_ec, errc::connection_reset); else @@ -499,7 +499,7 @@ TEST_P(QuicStreams, WHEN_http3_is_on_the_same_port_THEN_both_work) run([&]() -> Task { auto connection = co_await connect(); auto [ec, stream] = connection.open_stream(); - std::ignore = co_await stream.write_eof(asio::buffer("plain", 5)); + std::ignore = co_await stream.write_eof(boost::asio::buffer("plain", 5)); auto [read_ec, body] = co_await read_all(stream); EXPECT_EQ(body, "plain"); @@ -529,7 +529,7 @@ TEST_P(QuicStreams, WHEN_using_completion_tokens_THEN_the_same_works) auto [ec, stream] = co_await connection.async_accept_stream(as_tuple(use_awaitable)); EXPECT_FALSE(ec) << ec.message(); auto [read_ec, body] = co_await read_all(stream); - co_await stream.async_write_eof(asio::buffer(body), use_awaitable); + co_await stream.async_write_eof(boost::asio::buffer(body), use_awaitable); std::ignore = co_await connection.async_accept_stream(as_tuple(use_awaitable)); }; @@ -537,7 +537,7 @@ TEST_P(QuicStreams, WHEN_using_completion_tokens_THEN_the_same_works) auto connection = co_await quic::async_connect(context.get_executor(), "127.0.0.2", port(), client_config(), use_awaitable); auto [ec, stream] = connection.open_stream(); - co_await stream.async_write_eof(asio::buffer("token", 5), use_awaitable); + co_await stream.async_write_eof(boost::asio::buffer("token", 5), use_awaitable); auto [read_ec, body] = co_await read_all(stream); EXPECT_EQ(body, "token"); }()); diff --git a/test/test_server.cpp b/test/test_server.cpp index e565830..31349b1 100644 --- a/test/test_server.cpp +++ b/test/test_server.cpp @@ -77,7 +77,7 @@ class Http3IdleTimeout : public Test // the server gives up on the connection. // std::array buffer; - auto [ec, n] = co_await request.read_some(asio::buffer(buffer)); + auto [ec, n] = co_await request.read_some(boost::asio::buffer(buffer)); handler_result.set_value(ec); }); From fcc5be00c02d53810b2161d75e9e1ab750996352 Mon Sep 17 00:00:00 2001 From: Peter Eisenlohr Date: Fri, 6 Nov 2026 21:02:32 +0000 Subject: [PATCH 2/2] fix: client program on COROSIO too client_main.cpp is written on the runtime layer now, like the server program: Task, launch(), when_both() and the coroutine forms connect(), submit(), write_eof() and shutdown(), instead of Asio's awaitable, co_spawn and awaitable operators. Errors are printed and end the program with 1 rather than escaping run(), the session is shut down cleanly, --help prints the usage, and an optional third argument names the CA file, without which https fails verification. The client is built and installed on both runtimes. Co-Authored-By: Claude Opus 5.5 --- CLAUDE.md | 4 +- cmake/install.cmake | 5 +- docs/corosio-port-plan.md | 3 +- src/CMakeLists.txt | 15 +++-- src/apps/client_main.cpp | 124 ++++++++++++++++++++------------------ 5 files changed, 79 insertions(+), 72 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index aeaa1cb..e239f7a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,8 +16,8 @@ and `/usr/local`. Keep `build/` on clang: clangd reads its `compile_commands.jso `build-corosio/` is the COROSIO I/O runtime (`-DANYHTTP_API=COROSIO -DTLS_LIBRARY=OpenSSL`, see [docs/corosio-port-plan.md](docs/corosio-port-plan.md)); capy and corosio are FetchContent'd at pinned SHAs. Both runtimes build the same library sources and test files; what belongs to one -runtime only is guarded with `ANYHTTP_ASIO` or `ANYHTTP_COROSIO` (exactly one of them is 1). So is the -`server` program; the `client` program is ASIO-only so far. `build-corosio-asan/` is its ASAN +runtime only is guarded with `ANYHTTP_ASIO` or `ANYHTTP_COROSIO` (exactly one of them is 1). So are the +`server` and `client` programs. `build-corosio-asan/` is its ASAN tree, `build-corosio-release/` its Release tree (benchmark COROSIO with it) and `build-corosio-gcc/` its GCC one -- capy and corosio do build against libstdc++, but GCC is pickier about coroutine expressions than clang, so keep a `co_await` out of any larger expression. diff --git a/cmake/install.cmake b/cmake/install.cmake index afcb655..b7f90a9 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -14,10 +14,7 @@ include(CMakePackageConfigHelpers) # -------------------------------------------------------------------------------------------------- if (ANYHTTP_BUILD_APPS) - set(anyhttp_programs server) - if (ANYHTTP_ASIO) - list(APPEND anyhttp_programs client) - endif() + set(anyhttp_programs server client) set_target_properties(${anyhttp_programs} PROPERTIES INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") diff --git a/docs/corosio-port-plan.md b/docs/corosio-port-plan.md index 517d462..23840d0 100644 --- a/docs/corosio-port-plan.md +++ b/docs/corosio-port-plan.md @@ -216,7 +216,8 @@ the h3 write path also run under ASAN. From step 5 on, COROSIO's own tests must 7. **README**: the two I/O runtimes, the per-mode error table, the constraints (OpenSSL). The error table includes the resolver: corosio reports a host that does not resolve as `no_such_device_or_address` (`EAI_NONAME` mapped onto a generic code), ASIO as `netdb_errors::host_not_found`. -8. Later: threads in COROSIO; `client_main` for COROSIO; upstream issues for corosio +8. Later: threads in COROSIO; `client_main` for COROSIO (*done 2026-11-06*, on `Task`, + `launch()` and `when_both()` like the server); upstream issues for corosio (`openssl_stream` on AWS-LC, the read on an error event, the reactor's use-after-free with threads, and a TLS read that flushes what an earlier write left behind and fails with what that fails with: over TLS, a response that has already arrived can not be read once a write has diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4b25c6e..7360850 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -71,6 +71,13 @@ target_sources(server PRIVATE "apps/server_main.cpp") target_link_libraries(server PRIVATE anyhttp) target_link_libraries(server PRIVATE Boost::program_options) +# +# client +# +add_executable(client) +target_sources(client PRIVATE "apps/client_main.cpp") +target_link_libraries(client PRIVATE anyhttp) + if (ANYHTTP_COROSIO) # # corosio_issue_reactor_uaf: the reactor's use-after-free with several threads, standalone @@ -79,17 +86,9 @@ if (ANYHTTP_COROSIO) target_sources(corosio_issue_reactor_uaf PRIVATE "issues/corosio_issue_reactor_uaf.cpp") target_link_libraries(corosio_issue_reactor_uaf PRIVATE Boost::corosio Threads::Threads) - # the client program comes with the rest of the port return() endif() -# -# client -# -add_executable(client) -target_sources(client PRIVATE "apps/client_main.cpp") -target_link_libraries(client PRIVATE anyhttp) - # # beast_issue_3032 # diff --git a/src/apps/client_main.cpp b/src/apps/client_main.cpp index c4a6302..a7e2e67 100644 --- a/src/apps/client_main.cpp +++ b/src/apps/client_main.cpp @@ -1,92 +1,102 @@ +// +// Sends one request, over HTTP/2 unless the server says otherwise, and counts the bytes of the +// response body. Takes the URL, the method, POST by default, which sends a short body, and for a +// server with a certificate of a private CA, the file that has that CA in it. +// #include "anyhttp/client.hpp" -#include "anyhttp/request_handlers.hpp" // for drain() +#include "anyhttp/request_handlers.hpp" // for count_response() #include "anyhttp/session.hpp" -#include -#include -#include -#include -#include -#include +#if ANYHTTP_COROSIO +#include +#else #include -#include -#include +#endif #include +#include using namespace anyhttp; -using namespace anyhttp::client; -using namespace boost::asio; -using namespace std::chrono_literals; -using namespace boost::asio::experimental::awaitable_operators; +#if ANYHTTP_COROSIO +using IoContext = boost::corosio::io_context; +#else +using IoContext = boost::asio::io_context; +#endif -static awaitable send(Request& request, std::string_view hello) +static Task send(client::Request& request, std::string_view body) { - logd("send: sending string of {} bytes...", hello.size()); - co_await request.async_write_eof(boost::asio::buffer(hello)); - logd("send: sending string of {} bytes... done", hello.size()); + logd("send: sending string of {} bytes...", body.size()); + auto [ec] = co_await request.write_eof(boost::asio::buffer(body)); + if (ec) + throw_error(ec); + logd("send: sending string of {} bytes... done", body.size()); } -static awaitable do_request(Session& session, boost::urls::url url, std::string_view method) +static Task do_request(Session& session, boost::urls::url url, std::string_view method) { - const std::string hello = method == "POST" ? "Hello, World!\r\n" : ""; -#if 0 - auto request = co_await session.async_submit(url, {{"Content-Length", std::format("{}", hello.size())}}); -#else + const std::string body = method == "POST" ? "Hello, World!\r\n" : ""; + Fields fields; fields.set("X-Custom-Header", "value"); - auto request = co_await session.async_submit(method, url, fields); -#endif -#if 0 - size_t bytes = 64 * 1024 - 1; - auto result = co_await (send(request, bytes) && receive(request)); - assert(bytes == result); -#else - const size_t bytes = co_await (send(request, hello) && count_response(request)); + auto [ec, request] = co_await session.submit(method, url, fields); + if (ec) + throw_error(ec); + + const size_t bytes = co_await when_both(send(request, body), count_response(request)); logi("do_request: done, {} bytes of response body", bytes); -#endif } -static awaitable do_requests(any_io_executor executor, Session session, boost::urls::url url, - std::string_view method) +static Task do_session(client::Client& client, boost::urls::url url, std::string_view method, + int& result) { - for (size_t i = 0; i < 833; ++i) + auto [ec, session] = co_await client.connect(); + if (ec) + { + std::println(stderr, "connect: {}", ec.message()); + co_return; + } + + try + { co_await do_request(session, url, method); -} + } + catch (const boost::system::system_error& ex) + { + std::println(stderr, "request: {}", ex.code().message()); + co_return; + } -static awaitable do_session(Client& client, boost::urls::url url, std::string_view method) -{ - auto session = co_await client.async_connect(); + if (auto [shutdown_ec] = co_await session.shutdown(); shutdown_ec) + logw("shutdown: {}", shutdown_ec.message()); -#if 1 - for (size_t i = 0; i < 1; ++i) - co_await do_request(session, url, method); -#else - for (size_t i = 0; i < 3; ++i) - co_spawn(client.get_executor(), do_requests(client.get_executor(), session, url, method), - detached); -#endif - logi("do_requests: done"); + logi("do_session: done"); + result = 0; } int main(int argc, char* argv[]) { - if (argc < 2) + if (argc == 2 && (std::string_view(argv[1]) == "--help" || std::string_view(argv[1]) == "-h")) { - std::println("Usage: {} URL [METHOD]", argv[0]); - return 1; + std::println("Usage: {} URL [METHOD [CA-FILE]]", argv[0]); + return 0; + } + if (argc < 2 || argc > 4) + { + std::println(stderr, "Usage: {} URL [METHOD [CA-FILE]]", argv[0]); + return 2; } const std::string_view method = argc > 2 ? argv[2] : "POST"; - io_context context; - Config config{.url = boost::urls::url(argv[1]), .protocol = Protocol::h2}; - Client client(context.get_executor(), config); - - for (size_t i = 0; i < 1; ++i) - co_spawn(context, do_session(client, config.url, method), detached); + IoContext context; + client::Config config{.url = boost::urls::url(argv[1]), + .protocol = Protocol::h2, + .tls_ca_file = argc > 3 ? argv[3] : ""}; + client::Client client(context.get_executor(), config); + int result = 1; + launch(context.get_executor(), do_session(client, config.url, method, result)); context.run(); - return 0; + return result; }