From 6c0c79d14daf6cbd93d40ba3e5ba7d7faa8b25f2 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 24 Dec 2025 15:22:44 -0800 Subject: [PATCH 1/2] chore: route type tests --- .../boost/http_proto/server/route_handler.hpp | 69 +++++++++++++++++++ src/server/router_types.cpp | 14 ++-- test/unit/server/router_types.cpp | 30 ++++++++ 3 files changed, 106 insertions(+), 7 deletions(-) diff --git a/include/boost/http_proto/server/route_handler.hpp b/include/boost/http_proto/server/route_handler.hpp index ac57dac9..2ba31b7a 100644 --- a/include/boost/http_proto/server/route_handler.hpp +++ b/include/boost/http_proto/server/route_handler.hpp @@ -110,6 +110,45 @@ struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE route_params& set_body(std::string s); + /** Read the request body and receive a value. + + This function reads the entire request body into the specified sink. + When the read operation completes, the given callback is invoked with + the result. + + The @p callback parameter must be a function object with this + equivalent signature, where `T` is the type produced by the value sink: + @code + void ( T&& t ); + @endcode + + @par Example + @code + rp.read_body( + capy::string_body_sink(), + []( std::string s ) + { + // body read successfully + }); + @endcode + + If an error or an exception occurs during the read, it is propagated + through the router to the next error or exception handler. + + @param sink The body sink to read into. + @param callback The function to call when the read completes. + @return The route result, which must be returned immediately + from the route handler. + */ + template< + class ValueSink, + class Callback> + auto + read_body( + ValueSink&& sink, + Callback&& callback) -> + route_result; + #ifdef BOOST_HTTP_PROTO_HAS_CORO /** Spawn a coroutine for this route. @@ -205,6 +244,36 @@ struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE //----------------------------------------------- +template< + class ValueSink, + class Callback> +auto +route_params:: +read_body( + ValueSink&& sink, + Callback&& callback) -> + route_result +{ + (void)callback; + /* + return suspend( + [&](resumer resume) + { + parser.read_body( + std::forward(sink), + [resume, cb = std::forward(callback)] + (auto&&... args) + { + cb(std::forward(args)...); + resume(route_result::next); + }); + }); + */ + return route::next; +} + +//----------------------------------------------- + template auto route_params:: diff --git a/src/server/router_types.cpp b/src/server/router_types.cpp index fec7ceb9..21cfe625 100644 --- a/src/server/router_types.cpp +++ b/src/server/router_types.cpp @@ -21,7 +21,7 @@ const char* route_cat_type:: name() const noexcept { - return "boost.http"; + return "boost.http.route"; } std::string @@ -40,12 +40,12 @@ message( { switch(static_cast(code)) { - case route::close: return "route::close"; - case route::complete: return "route::complete"; - case route::suspend: return "route::suspend"; - case route::next: return "route::next"; - case route::next_route: return "route::next_route"; - case route::send: return "route::send"; + case route::close: return "close"; + case route::complete: return "complete"; + case route::suspend: return "suspend"; + case route::next: return "next"; + case route::next_route: return "next_route"; + case route::send: return "send"; default: return "?"; } diff --git a/test/unit/server/router_types.cpp b/test/unit/server/router_types.cpp index 297df6ed..78676bc9 100644 --- a/test/unit/server/router_types.cpp +++ b/test/unit/server/router_types.cpp @@ -18,9 +18,39 @@ namespace http_proto { struct router_types_test { + template + void + check( + char const* name, + Error ev) + { + auto const ec = make_error_code(ev); + BOOST_TEST(std::string(ec.category().name()) == name); + BOOST_TEST(! ec.message().empty()); + BOOST_TEST( + std::addressof(ec.category()) == + std::addressof(make_error_code(ev).category())); + BOOST_TEST(ec.category().equivalent( + static_cast::type>(ev), + ec.category().default_error_condition( + static_cast::type>(ev)))); + BOOST_TEST(ec.category().equivalent(ec, + static_cast::type>(ev))); + } + void run() { + { + char const* const n = "boost.http.route"; + check(n, route::close); + check(n, route::complete); + check(n, route::suspend); + check(n, route::next); + check(n, route::next_route); + check(n, route::send); + } + basic_router r; r.add(http_proto::method::post, "/", [](route_params_base& rp) -> From 622d39bf2b7246680b939edd487d3392214af84f Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Wed, 24 Dec 2025 17:23:03 -0800 Subject: [PATCH 2/2] feat: read_body --- include/boost/http_proto/parser.hpp | 10 ++-- .../boost/http_proto/server/route_handler.hpp | 48 +++++++++++++------ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/include/boost/http_proto/parser.hpp b/include/boost/http_proto/parser.hpp index 6af8c8dd..5441d236 100644 --- a/include/boost/http_proto/parser.hpp +++ b/include/boost/http_proto/parser.hpp @@ -287,6 +287,12 @@ class parser parse( system::error_code& ec); + /** Return true if a body has been attached. + */ + BOOST_HTTP_PROTO_DECL + bool + is_body_set() const noexcept; + /** Attach an elastic buffer body. This function attaches the specified elastic @@ -634,10 +640,6 @@ class parser detail::workspace& ws() noexcept; - BOOST_HTTP_PROTO_DECL - bool - is_body_set() const noexcept; - BOOST_HTTP_PROTO_DECL void set_body_impl(buffers::any_dynamic_buffer&) noexcept; diff --git a/include/boost/http_proto/server/route_handler.hpp b/include/boost/http_proto/server/route_handler.hpp index 2ba31b7a..1766e74b 100644 --- a/include/boost/http_proto/server/route_handler.hpp +++ b/include/boost/http_proto/server/route_handler.hpp @@ -240,6 +240,7 @@ struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE virtual void do_post(); std::unique_ptr task_; + std::function finish_; }; //----------------------------------------------- @@ -254,22 +255,39 @@ read_body( Callback&& callback) -> route_result { - (void)callback; - /* - return suspend( - [&](resumer resume) + using T = typename std::decay::type; + + struct on_finish { - parser.read_body( - std::forward(sink), - [resume, cb = std::forward(callback)] - (auto&&... args) - { - cb(std::forward(args)...); - resume(route_result::next); - }); - }); - */ - return route::next; + T& sink; + resumer resume; + typename std::decay::type cb; + + on_finish( + T& sink_, + resumer resume_, + Callback&& cb_) + : sink(sink_) + , resume(resume_) + , cb(std::forward(cb_)) + { + } + + void operator()() + { + resume(std::move(cb)(sink.release())); + } + }; + + return suspend( + [&](resumer resume) + { + finish_ = on_finish( + this->parser.set_body( + std::forward(sink)), + resume, + std::forward(callback)); + }); } //-----------------------------------------------