diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c9bdcdd..956be4d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Version 68: * Adjust buffer size in fast server * Use string_ref in older Boost versions * Optimize field lookups +* Add const_body, mutable_body to examples API Changes: diff --git a/doc/2_examples.qbk b/doc/2_examples.qbk index 0fdf034b..703e63d0 100644 --- a/doc/2_examples.qbk +++ b/doc/2_examples.qbk @@ -130,9 +130,11 @@ the example described in the Core Foundations document section. This code is reused between some of the examples. The header files stand alone can be directly included in your projects. +* [repo_file example/common/const_body.hpp] * [repo_file example/common/detect_ssl.hpp] * [repo_file example/common/file_body.hpp] * [repo_file example/common/mime_types.hpp] +* [repo_file example/common/mutable_body.hpp] * [repo_file example/common/rfc7231.hpp] * [repo_file example/common/ssl_stream.hpp] * [repo_file example/common/write_msg.hpp] diff --git a/example/common/const_body.hpp b/example/common/const_body.hpp new file mode 100644 index 00000000..c4a0f743 --- /dev/null +++ b/example/common/const_body.hpp @@ -0,0 +1,89 @@ +// +// Copyright (c) 2017 Mike Gresens (mike dot gresens at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_EXAMPLE_COMMON_CONST_BODY_HPP +#define BEAST_EXAMPLE_COMMON_CONST_BODY_HPP + +#include +#include +#include + +namespace detail { + +template +using is_const_character = std::integral_constant::value && + sizeof(T) == 1 +>; + +template +struct is_const_container : std::false_type { }; + +template +struct is_const_container() = std::declval().size() ), + decltype( std::declval() = std::declval().data() ) +> > : std::true_type { }; + +} + +/** An HTTP message body represented by a constant character container. + + Meets the requirements of @b Body. +*/ +template +struct const_body +{ + static_assert( + detail::is_const_character::value && + detail::is_const_container::value, + "ConstContainer requirements not met"); + + /// The type of the body member when used in a message. + using value_type = ConstContainer; + + /// Returns the content length of the body in a message. + static + std::uint64_t + size(value_type const& v) + { + return v.size(); + } + +#if BEAST_DOXYGEN + /// The algorithm to obtain buffers representing the body + using reader = implementation_defined; +#else + class reader + { + value_type const& body_; + + public: + using const_buffers_type = + boost::asio::const_buffers_1; + + template + explicit + reader(beast::http::message const& msg, beast::error_code& ec) + : body_(msg.body) + { + ec.assign(0, ec.category()); + } + + boost::optional> + get(beast::error_code& ec) + { + ec.assign(0, ec.category()); + return {{const_buffers_type{ + body_.data(), body_.size()}, false}}; + } + }; +#endif +}; + +#endif diff --git a/example/common/mutable_body.hpp b/example/common/mutable_body.hpp new file mode 100644 index 00000000..1125a4e6 --- /dev/null +++ b/example/common/mutable_body.hpp @@ -0,0 +1,155 @@ +// +// Copyright (c) 2017 Mike Gresens (mike dot gresens at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BEAST_EXAMPLE_COMMON_MUTABLE_BODY_HPP +#define BEAST_EXAMPLE_COMMON_MUTABLE_BODY_HPP + +#include +#include +#include +#include + +namespace detail { + +template +using is_mutable_character = std::integral_constant::value && + sizeof(T) == 1 +>; + +template +struct is_mutable_container : std::false_type { }; + +template< class T > +struct is_mutable_container() = std::declval().size() ), + decltype( std::declval() = std::declval().data() ), + decltype( std::declval().reserve(0) ), + decltype( std::declval().resize(0) ) +> > : std::true_type { }; + +} + +/** An HTTP message body represented by a mutable character container. + + Meets the requirements of @b Body. +*/ +template +struct mutable_body +{ + static_assert( + detail::is_mutable_character::value && + detail::is_mutable_container::value, + "MutableContainer requirements not met"); + + /// The type of the body member when used in a message. + using value_type = MutableContainer; + + /// Returns the content length of the body in a message. + static + std::uint64_t + size(value_type const& v) + { + return v.size(); + } + +#if BEAST_DOXYGEN + /// The algorithm to obtain buffers representing the body + using reader = implementation_defined; +#else + class reader + { + value_type const& body_; + + public: + using const_buffers_type = + boost::asio::const_buffers_1; + + template + explicit + reader(beast::http::message const& msg, beast::error_code& ec) + : body_(msg.body) + { + ec.assign(0, ec.category()); + } + + boost::optional> + get(beast::error_code& ec) + { + ec.assign(0, ec.category()); + return {{const_buffers_type{ + body_.data(), body_.size()}, false}}; + } + }; +#endif + +#if BEAST_DOXYGEN + /// The algorithm used store buffers in this body + using writer = implementation_defined; +#else + class writer + { + value_type& body_; + + public: + template + explicit + writer(beast::http::message& m, + boost::optional const& content_length, + beast::error_code& ec) + : body_(m.body) + { + if(content_length) + { + if(*content_length > (std::numeric_limits< + std::size_t>::max)()) + { + ec = boost::system::errc::make_error_code( + boost::system::errc::not_enough_memory); + return; + } + ec.assign(0, ec.category()); + body_.reserve(static_cast< + std::size_t>(*content_length)); + } + } + + template + void + put(ConstBufferSequence const& buffers, + beast::error_code& ec) + { + using boost::asio::buffer_size; + using boost::asio::buffer_copy; + auto const n = buffer_size(buffers); + auto const len = body_.size(); + try + { + body_.resize(len + n); + } + catch(std::length_error const&) + { + ec = beast::http::error::buffer_overflow; + return; + } + ec.assign(0, ec.category()); + buffer_copy(boost::asio::buffer( + &body_[0] + len, n), buffers); + } + + void + finish(beast::error_code& ec) + { + ec.assign(0, ec.category()); + } + }; +#endif + +}; + +#endif diff --git a/test/common/CMakeLists.txt b/test/common/CMakeLists.txt index 96b3fa3a..0f7910bb 100644 --- a/test/common/CMakeLists.txt +++ b/test/common/CMakeLists.txt @@ -10,6 +10,8 @@ add_executable (common-test ${COMMON_INCLUDES} detect_ssl.cpp file_body.cpp + const_body.cpp + mutable_body.cpp mime_types.cpp rfc7231.cpp ssl_stream.cpp diff --git a/test/common/const_body.cpp b/test/common/const_body.cpp new file mode 100644 index 00000000..10cdd823 --- /dev/null +++ b/test/common/const_body.cpp @@ -0,0 +1,10 @@ +// +// Copyright (c) 2017 Mike Gresens (mike dot gresens at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include "../../example/common/const_body.hpp" + diff --git a/test/common/mutable_body.cpp b/test/common/mutable_body.cpp new file mode 100644 index 00000000..db195b09 --- /dev/null +++ b/test/common/mutable_body.cpp @@ -0,0 +1,10 @@ +// +// Copyright (c) 2017 Mike Gresens (mike dot gresens at gmail dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +// Test that header file is self-contained. +#include "../../example/common/mutable_body.hpp" + diff --git a/test/http/doc_examples.cpp b/test/http/doc_examples.cpp index 3b073715..1e190e09 100644 --- a/test/http/doc_examples.cpp +++ b/test/http/doc_examples.cpp @@ -7,6 +7,8 @@ #include "example/doc/http_examples.hpp" #include "example/common/file_body.hpp" +#include "example/common/const_body.hpp" +#include "example/common/mutable_body.hpp" #include #include @@ -16,10 +18,37 @@ #include #include #include +#include +#include +#include namespace beast { namespace http { +struct Thing { + char value; +}; + +BOOST_STATIC_ASSERT(::detail::is_const_character::value == true); +BOOST_STATIC_ASSERT(::detail::is_const_character::value == true); +BOOST_STATIC_ASSERT(::detail::is_const_character::value == false); +BOOST_STATIC_ASSERT(::detail::is_const_character::value == false); + +BOOST_STATIC_ASSERT(::detail::is_const_container::value == true); +BOOST_STATIC_ASSERT(::detail::is_const_container::value == true); +BOOST_STATIC_ASSERT(::detail::is_const_container>::value == true); +BOOST_STATIC_ASSERT(::detail::is_const_container>::value == false); + +BOOST_STATIC_ASSERT(::detail::is_mutable_character::value == true); +BOOST_STATIC_ASSERT(::detail::is_mutable_character::value == true); +BOOST_STATIC_ASSERT(::detail::is_mutable_character::value == false); +BOOST_STATIC_ASSERT(::detail::is_mutable_character::value == false); + +BOOST_STATIC_ASSERT(::detail::is_mutable_container::value == true); +BOOST_STATIC_ASSERT(::detail::is_mutable_container::value == false); +BOOST_STATIC_ASSERT(::detail::is_mutable_container>::value == true); +BOOST_STATIC_ASSERT(::detail::is_mutable_container>::value == false); + class doc_http_samples_test : public beast::unit_test::suite , public beast::test::enable_yield_to @@ -307,6 +336,54 @@ public: BEAST_EXPECTS(! ec, ec.message()); } + void + doConstAndMutableBody() + { + test::pipe c{ios_}; + + // people using std::array need to be careful + // because the entire array will be written out + // no matter how big the string is! + std::array const body {{"Hello, world!\n"}}; + { + request>> req; + req.version = 11; + req.method(verb::put); + req.target("/"); + req.body = body; + req.prepare_payload(); + write(c.client, req); + } + { + flat_buffer b; + request_parser p0; + read_header(c.server, b, p0); + BEAST_EXPECTS(p0.get().method() == verb::put, + p0.get().method_string()); + { + request_parser>> p{std::move(p0)}; + p.get().body = std::vector(body.begin(), body.end()); + read(c.server, b, p); + } + } + { + response>> res; + res.version = 11; + res.result(status::ok); + res.insert(field::server, "test"); + res.body = body; + res.prepare_payload(); + write(c.server, res); + } + { + flat_buffer b; + response>> res; + read(c.client, b, res); + BEAST_EXPECTS(res.body == std::vector(body.begin(), body.end()), + std::string(body.begin(), body.end())); + } + } + //-------------------------------------------------------------------------- void @@ -321,6 +398,7 @@ public: doHEAD(); doDeferredBody(); doFileBody(); + doConstAndMutableBody(); } };