diff --git a/CLAUDE.md b/CLAUDE.md index efb41cb..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. @@ -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/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/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/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/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/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 b0df813..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(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; } 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); });