From 37110f2d047729c1ecece47cd012da5d79d0a98b Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 8 Jul 2017 18:47:33 -0700 Subject: [PATCH] Add span_body --- doc/5_02_message.qbk | 9 + doc/quickref.xml | 1 + .../http_server_threaded.cpp | 4 +- include/beast/core/span.hpp | 2 +- include/beast/http.hpp | 1 + include/beast/http/span_body.hpp | 167 ++++++++++++++++++ test/core/span.cpp | 5 + test/http/CMakeLists.txt | 1 + test/http/Jamfile | 1 + test/http/span_body.cpp | 76 ++++++++ 10 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 include/beast/http/span_body.hpp create mode 100644 test/http/span_body.cpp diff --git a/doc/5_02_message.qbk b/doc/5_02_message.qbk index 89ab4789..3e6d1666 100644 --- a/doc/5_02_message.qbk +++ b/doc/5_02_message.qbk @@ -152,8 +152,17 @@ meet the requirements, or use the ones that come with the library: HTTP algorithms will use the open file for reading and writing, for streaming and incremental sends and receives. ]] +[[ + [link beast.ref.beast__http__span_body `span_body`] +][ + A body whose `value_type` is a + [link beast.ref.beast__span `span`], + a non-owning reference to a single linear buffer of bytes. + Messages with this body type may be serialized and parsed. +]] [[ [link beast.ref.beast__http__basic_string_body `basic_string_body`] + [link beast.ref.beast__http__string_body `string_body`] ][ A body whose `value_type` is `std::basic_string` or `std::string`. diff --git a/doc/quickref.xml b/doc/quickref.xml index d49491ba..60ed05f9 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -51,6 +51,7 @@ response_parser response_serializer serializer + span_body string_body string_view_body vector_body diff --git a/example/http-server-threaded/http_server_threaded.cpp b/example/http-server-threaded/http_server_threaded.cpp index 4389549c..e75b3796 100644 --- a/example/http-server-threaded/http_server_threaded.cpp +++ b/example/http-server-threaded/http_server_threaded.cpp @@ -56,10 +56,10 @@ public: private: // Send a client error response - http::response + http::response> client_error(http::status result, beast::string_view text) { - http::response res{result, 11}; + http::response> res{result, 11}; res.set(http::field::server, BEAST_VERSION_STRING); res.set(http::field::content_type, "text/plain"); res.set(http::field::connection, "close"); diff --git a/include/beast/core/span.hpp b/include/beast/core/span.hpp index d6998ea8..1d465ce4 100644 --- a/include/beast/core/span.hpp +++ b/include/beast/core/span.hpp @@ -190,7 +190,7 @@ public: { return data_; } - + /// Returns an iterator to one past the end of the span const_iterator end() const diff --git a/include/beast/http.hpp b/include/beast/http.hpp index a7e8519e..5c093d2d 100644 --- a/include/beast/http.hpp +++ b/include/beast/http.hpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/include/beast/http/span_body.hpp b/include/beast/http/span_body.hpp new file mode 100644 index 00000000..8e83cde2 --- /dev/null +++ b/include/beast/http/span_body.hpp @@ -0,0 +1,167 @@ +// +// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco 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_HTTP_SPAN_BODY_HPP +#define BEAST_HTTP_SPAN_BODY_HPP + +#include +#include +#include +#include +#include + +namespace beast { +namespace http { + +/** A @b Body using @ref span + + This body uses @ref span as a memory-based container for + holding message payloads. The container represents a + non-owning reference to a continguous area of memory. + Messages using this body type may be serialized and + parsed. + + Unlike @ref buffer_body, only one buffer may be provided + during a parse or serialize operation. +*/ +template +struct span_body +{ +private: + static_assert(std::is_pod::value, + "POD requirements not met"); + +public: + /** The type of container used for the body + + This determines the type of @ref message::body + when this body type is used with a message container. + */ + using value_type = span; + + /** Returns the payload size of the body + + When this body is used with @ref message::prepare_payload, + the Content-Length will be set to the payload size, and + any chunked Transfer-Encoding will be removed. + */ + static + std::uint64_t + size(value_type const& body) + { + return body.size(); + } + + /** The algorithm for serializing the body + + Meets the requirements of @b BodyReader. + */ +#if BEAST_DOXYGEN + using reader = implementation_defined; +#else + class reader + { + value_type const& body_; + + public: + using const_buffers_type = + boost::asio::const_buffers_1; + + template + explicit + reader(message const& msg) + : body_(msg.body) + { + } + + void + init(error_code& ec) + { + ec.assign(0, ec.category()); + } + + boost::optional> + get(error_code& ec) + { + ec.assign(0, ec.category()); + return {{ + { body_.data(), + body_.size() * sizeof(typename + value_type::value_type)}, + false}}; + } + }; +#endif + + /** The algorithm for parsing the body + + Meets the requirements of @b BodyReader. + */ +#if BEAST_DOXYGEN + using writer = implementation_defined; +#else + class writer + { + value_type& body_; + + public: + template + explicit + writer(message& m) + : body_(m.body) + { + } + + void + init(boost::optional< + std::uint64_t> const& length, error_code& ec) + { + if(length && *length > body_.size()) + { + ec = error::buffer_overflow; + return; + } + ec.assign(0, ec.category()); + } + + template + std::size_t + put(ConstBufferSequence const& buffers, + error_code& ec) + { + using boost::asio::buffer_size; + using boost::asio::buffer_copy; + auto const n = buffer_size(buffers); + auto const len = body_.size(); + if(n > len) + { + ec = error::buffer_overflow; + return 0; + } + ec.assign(0, ec.category()); + buffer_copy(boost::asio::buffer( + body_.data(), n), buffers); + body_ = value_type{ + body_.data() + n, body_.size() - n}; + return n; + } + + void + finish(error_code& ec) + { + ec.assign(0, ec.category()); + } + }; +#endif +}; + +} // http +} // beast + +#endif diff --git a/test/core/span.cpp b/test/core/span.cpp index 32892de3..ec8c7d3e 100644 --- a/test/core/span.cpp +++ b/test/core/span.cpp @@ -8,6 +8,7 @@ // Test that header file is self-contained. #include +#include #include namespace beast { @@ -15,6 +16,10 @@ namespace beast { class span_test : public beast::unit_test::suite { public: + BOOST_STATIC_ASSERT( + detail::is_contiguous_container< + string_view, char const>::value); + struct base {}; struct derived : base {}; diff --git a/test/http/CMakeLists.txt b/test/http/CMakeLists.txt index 9a5f2284..52e4767c 100644 --- a/test/http/CMakeLists.txt +++ b/test/http/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable (http-tests read.cpp rfc7230.cpp serializer.cpp + span_body.cpp status.cpp string_body.cpp string_view_body.cpp diff --git a/test/http/Jamfile b/test/http/Jamfile index 66a959f2..85b25fda 100644 --- a/test/http/Jamfile +++ b/test/http/Jamfile @@ -21,6 +21,7 @@ unit-test http-tests : read.cpp rfc7230.cpp serializer.cpp + span_body.cpp status.cpp string_body.cpp string_view_body.cpp diff --git a/test/http/span_body.cpp b/test/http/span_body.cpp new file mode 100644 index 00000000..3e9e3b17 --- /dev/null +++ b/test/http/span_body.cpp @@ -0,0 +1,76 @@ +// +// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco 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 + +#include +#include + +namespace beast { +namespace http { + +struct span_body_test + : public beast::unit_test::suite +{ + void + testSpanBody() + { + { + using B = span_body; + request req; + + BEAST_EXPECT(req.body.size() == 0); + BEAST_EXPECT(B::size(req.body) == 0); + + req.body = B::value_type("xyz", 3); + BEAST_EXPECT(req.body.size() == 3); + BEAST_EXPECT(B::size(req.body) == 3); + + B::reader r{req}; + error_code ec; + r.init(ec); + BEAST_EXPECTS(! ec, ec.message()); + auto const buf = r.get(ec); + BEAST_EXPECTS(! ec, ec.message()); + if(! BEAST_EXPECT(buf != boost::none)) + return; + BEAST_EXPECT(boost::asio::buffer_size(buf->first) == 3); + BEAST_EXPECT(! buf->second); + } + { + char buf[5]; + using B = span_body; + request req; + req.body = span{buf, sizeof(buf)}; + B::writer w{req}; + error_code ec; + w.init(boost::none, ec); + BEAST_EXPECTS(! ec, ec.message()); + w.put(boost::asio::const_buffers_1{ + "123", 3}, ec); + BEAST_EXPECTS(! ec, ec.message()); + BEAST_EXPECT(buf[0] == '1'); + BEAST_EXPECT(buf[1] == '2'); + BEAST_EXPECT(buf[2] == '3'); + w.put(boost::asio::const_buffers_1{ + "456", 3}, ec); + BEAST_EXPECTS(ec == error::buffer_overflow, ec.message()); + } + } + + void + run() override + { + testSpanBody(); + } +}; + +BEAST_DEFINE_TESTSUITE(span_body,http,beast); + +} // http +} // beast