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 ac57dac9..1766e74b 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. @@ -201,10 +240,58 @@ struct BOOST_HTTP_PROTO_SYMBOL_VISIBLE virtual void do_post(); std::unique_ptr task_; + std::function finish_; }; //----------------------------------------------- +template< + class ValueSink, + class Callback> +auto +route_params:: +read_body( + ValueSink&& sink, + Callback&& callback) -> + route_result +{ + using T = typename std::decay::type; + + struct on_finish + { + 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)); + }); +} + +//----------------------------------------------- + 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) ->