Conversation
async_get() submits the request, ends its empty body, waits for the response and reads all of it, handing back a plain Beast message -- status, header fields and the body as a std::string, aliased as client::Message. What a simple GET spread over four steps is one asynchronous operation now, completion tokens and cancellation included. Sending an actual GET means the method can no longer be hardcoded: all three backends built their request with ":method: POST", so Session::Impl::async_submit() takes the method as a parameter. The public async_submit() still passes "POST", so nothing else changes on the wire. The request goes out with "Content-Length: 0" unless the caller frames a body itself. That keeps HTTP/1.1 from making a bodiless request chunked, and with it from counting the request as incomplete -- and the session as busy -- until the body is ended. The message that comes with an error is empty and says status::unknown, rather than the 200 a default-constructed Beast response would claim. test_get.cpp covers status, fields, body, request headers, an empty body, a 1 MiB one, two requests in a row and cancellation, for all three protocols, plus the request line as a raw HTTP/1.1 peer sees it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The header, file handler and client testcases that send no request body and read the response to its end say so in one line now. client::Message is a Beast fields, so expect_contains() and values_of() take it unchanged. FileHandler::get() was async_get() spelled out, and hands back the message itself instead of a (status, body) tuple. HeaderLimits::request() loses its two error checks along with the operations they belonged to. These requests go out as GET with "Content-Length: 0" now, instead of as a chunked POST -- which is what the testcases describe anyway. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The constructor taking a buffer sequence ended its loop at buffer_sequence_begin() rather than buffer_sequence_end(), so first and last were the same iterator and it copied nothing at all: n_ and size_ stayed 0, leaving an empty buffer array. Nothing ever instantiated it, which is why this never showed. AnyAsyncStream spelled out the iterator pair constructor instead, and that one was correct. It passes the sequence itself now, which is what the constructor is there for -- the two loops are otherwise the same, both capping at N and skipping empty buffers. The bug could not have survived being used: with buffer_sequence_begin() put back, the HTTP/1.1 testcases hang, reading and writing zero bytes forever, as an empty buffer sequence makes them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nothing functional, except that get_unused_port() now probes on the IPv6 loopback: its only caller connects to "localhost" and expects to be refused, and that resolves to ::1 first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The TCP side built a fresh asio::ssl::context inside handle_connection(), reading the PEM files from disk for every single connection, while HTTP/3 kept its SSL_CTX in a function-local static for the lifetime of the process. Apart from the per-connection cost, the two disagreed about when a rotated certificate is picked up: regenerating the test PKI under a running server broke HTTP/3 while HTTP/2 silently carried on with the new chain. Both now hold their context for as long as the server lives -- Server::Impl for TCP, Http3ServerImpl for QUIC -- so a regenerated PKI needs a restart either way, and nothing is read from disk per connection. Also adds anyhttp.org to the test server certificate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A session is a template over its stream, and there are four of those: a plain tcp::socket, a TLS stream, beast's tcp_stream and the type-erased AnyAsyncStream. Beyond the async read and write operations they already have in common, a session needs the underlying socket -- to shut it down or close it -- and an executor to run its loops on, and neither is spelled the same way by all four. Both h1_session.cpp and h2_session_details.hpp had their own private set of get_socket() overloads for this, and the executor was passed in from outside. stream_traits<Stream> now provides get_socket() and get_executor() for all of them, with a SocketStream concept and a free get_socket() on top. There is no free get_executor(), because the classes calling these have a get_executor() of their own that would hide it. With the executor reachable from the stream, each backend's per-stream-type factory overloads collapse into a single constrained template taking the stream by rvalue reference and no executor argument -- ten declarations down to four. The h2 h2c variant folds into the same template via an optional<Upgrade>, which retires the three make_h2c_session() dispatch helpers in h1_session.cpp, including the one that only existed to throw for TLS. The definitions stay in the .cpp files, explicitly instantiated there, so beast and nghttp2 are still confined to one translation unit each. Taking the executor from the stream only holds if the stream is on the right one to begin with. It was not: with Config::use_strand, tcp_accept_loop() made the strand at co_spawn() time, leaving the socket on the plain executor. The socket is now accepted onto the connection's executor, as the HTTP/3 side already does, and handle_connection() is spawned on that. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The type-erased stream, the TLS detection and the HTTP/2 preface detection are internals of the server, not part of its interface, so they move to include/anyhttp/detail/ -- h2_detect.hpp as detect_h2.hpp, to match the name of the operation it declares. AnyAsyncStream becomes any_async_stream, like the other stream types it stands in for. Its implementation is now only forward declared. The buffer-erasing async operations stay inline, but the buffer sequence is copied into the buffer_array before async_initiate(), so the initiation can forward to an out-of-line member instead of dereferencing the incomplete implementation. The implementation itself lives in detail/any_async_stream_impl.hpp, which only src/any_async_stream_impl.cpp includes. What used to be TestStream in server_impl.cpp is a template over the stream now, taking its socket and executor from stream_traits instead of assuming a bare one, and is explicitly instantiated for a TCP socket and a TLS stream -- the same pattern the session backends use. Callers get make_any_async_stream(), because converting a unique_ptr to the base is no longer possible where the base is incomplete. get_socket() returns TcpSocketBase rather than an ip::tcp::socket, which is what a TLS stream can actually hand out and all a session needs for shutdown() and close(). The alias moves next to the stream returning it, together with SslStream, which the two backends had been spelling out separately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.