From 4cfa1d5cd34533caf4930455c6d3817b3bd00e89 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 26 Apr 2016 06:22:58 -0400 Subject: [PATCH] Add streambuf write(): This function performs serialization of a variable list of arguments to a streambuf. It accepts a wide variety of argument types, using boost::asio::buffer and boost::lexical_cast where possible. --- doc/quickref.xml | 1 + include/beast/detail/write_streambuf.hpp | 146 ++++++++++++++++++ include/beast/http/basic_headers.hpp | 1 - .../beast/http/detail/write_preparation.hpp | 3 +- include/beast/http/detail/writes.hpp | 66 -------- include/beast/http/impl/basic_headers.ipp | 1 - include/beast/http/impl/message.ipp | 55 +++---- include/beast/http/impl/write.ipp | 1 - include/beast/write_streambuf.hpp | 59 +++++++ test/Jamfile | 1 - test/write_streambuf.cpp | 35 +++++ 11 files changed, 271 insertions(+), 98 deletions(-) create mode 100644 include/beast/detail/write_streambuf.hpp delete mode 100644 include/beast/http/detail/writes.hpp create mode 100644 include/beast/write_streambuf.hpp create mode 100644 test/write_streambuf.cpp diff --git a/doc/quickref.xml b/doc/quickref.xml index cdb33e20b2..460e6d3c45 100644 --- a/doc/quickref.xml +++ b/doc/quickref.xml @@ -131,6 +131,7 @@ buffer_cat prepare_buffer prepare_buffers + write diff --git a/include/beast/detail/write_streambuf.hpp b/include/beast/detail/write_streambuf.hpp new file mode 100644 index 0000000000..ccac4b4cae --- /dev/null +++ b/include/beast/detail/write_streambuf.hpp @@ -0,0 +1,146 @@ +// +// Copyright (c) 2013-2016 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_DETAIL_WRITE_STREAMBUF_HPP +#define BEAST_DETAIL_WRITE_STREAMBUF_HPP + +#include +#include +#include + +namespace beast { +namespace detail { + +// detects string literals. +template +struct is_string_literal : std::integral_constant::type>::value && + std::is_same::type>::value> +{ +}; + +// `true` if a call to boost::asio::buffer(T const&) is possible +// note: we exclude string literals because boost::asio::buffer() +// will include the null terminator, which we don't want. +template +class is_BufferConvertible +{ + template()), + std::true_type{})> + static R check(int); + template + static std::false_type check(...); + using type = decltype(check(0)); +public: + static bool const value = type::value && + ! is_string_literal::value; +}; + +template +inline +void +write_streambuf(Streambuf&) +{ +} + +template +void +write_streambuf(Streambuf& streambuf, + boost::asio::const_buffer const& buffer) +{ + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + streambuf.commit(buffer_copy( + streambuf.prepare(buffer_size(buffer)), + buffer)); +} + +template +void +write_streambuf(Streambuf& streambuf, + boost::asio::mutable_buffer const& buffer) +{ + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + streambuf.commit(buffer_copy( + streambuf.prepare(buffer_size(buffer)), + buffer)); +} + +template +typename std::enable_if< + is_BufferConvertible::value && + ! std::is_convertible::value && + ! std::is_convertible::value +>::type +write_streambuf(Streambuf& streambuf, T const& t) +{ + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + auto const buffers = boost::asio::buffer(t); + streambuf.commit(buffer_copy( + streambuf.prepare(buffer_size(buffers)), + buffers)); +} + +template +typename std::enable_if< + is_ConstBufferSequence::value && + ! is_BufferConvertible::value && + ! std::is_convertible::value && + ! std::is_convertible::value +>::type +write_streambuf(Streambuf& streambuf, Buffers const& buffers) +{ + using boost::asio::buffer_copy; + using boost::asio::buffer_size; + streambuf.commit(buffer_copy( + streambuf.prepare(buffer_size(buffers)), + buffers)); +} + +template +void +write_streambuf(Streambuf& streambuf, const char (&s)[N]) +{ + using boost::asio::buffer_copy; + streambuf.commit(buffer_copy( + streambuf.prepare(N - 1), + boost::asio::buffer(s, N - 1))); +} + +template +typename std::enable_if< + ! is_string_literal::value && + ! is_ConstBufferSequence::value && + ! is_BufferConvertible::value && + ! std::is_convertible::value && + ! std::is_convertible::value +>::type +write_streambuf(Streambuf& streambuf, T const& t) +{ + using boost::asio::buffer; + using boost::asio::buffer_copy; + auto const s = boost::lexical_cast(t); + streambuf.commit(buffer_copy( + streambuf.prepare(s.size()), buffer(s))); +} + +template +void +write_streambuf(Streambuf& streambuf, T0&& t0, T1&& t1, TN... tn) +{ + write_streambuf(streambuf, std::forward(t0)); + write_streambuf(streambuf, std::forward(t1)); + write_streambuf(streambuf, std::forward(tn)...); +} + +} // detail +} // beast + +#endif diff --git a/include/beast/http/basic_headers.hpp b/include/beast/http/basic_headers.hpp index 6bbff654d6..500d0b611b 100644 --- a/include/beast/http/basic_headers.hpp +++ b/include/beast/http/basic_headers.hpp @@ -8,7 +8,6 @@ #ifndef BEAST_HTTP_BASIC_HEADERS_HPP #define BEAST_HTTP_BASIC_HEADERS_HPP -#include #include #include #include diff --git a/include/beast/http/detail/write_preparation.hpp b/include/beast/http/detail/write_preparation.hpp index 937ecafd0a..13dd6f13ad 100644 --- a/include/beast/http/detail/write_preparation.hpp +++ b/include/beast/http/detail/write_preparation.hpp @@ -10,6 +10,7 @@ #include #include +#include namespace beast { namespace http { @@ -84,7 +85,7 @@ struct write_preparation msg.write_firstline(sb); write_fields(sb, h); - detail::write(sb, "\r\n"); + beast::write(sb, "\r\n"); } private: diff --git a/include/beast/http/detail/writes.hpp b/include/beast/http/detail/writes.hpp deleted file mode 100644 index bd68bb2c9b..0000000000 --- a/include/beast/http/detail/writes.hpp +++ /dev/null @@ -1,66 +0,0 @@ -// -// Copyright (c) 2013-2016 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_DETAIL_WRITES_HPP -#define BEAST_HTTP_DETAIL_WRITES_HPP - -#include -#include -#include -#include -#include -#include - -namespace beast { -namespace http { -namespace detail { - -template::value>::type> -void -write(Streambuf& streambuf, T&& t) -{ - using boost::asio::buffer; - using boost::asio::buffer_copy; - auto const& s = - boost::lexical_cast( - std::forward(t)); - streambuf.commit(buffer_copy( - streambuf.prepare(s.size()), buffer(s))); -} - -template0) && - is_Streambuf::value>::type> -void -write(Streambuf& streambuf, char const(&s)[N]) -{ - using boost::asio::buffer; - using boost::asio::buffer_copy; - streambuf.commit(buffer_copy( - streambuf.prepare(N), buffer(s, N-1))); -} - -template::value>::type> -void -write(Streambuf& streambuf, boost::string_ref const& s) -{ - using boost::asio::buffer; - using boost::asio::buffer_copy; - streambuf.commit(buffer_copy( - streambuf.prepare(s.size()), - buffer(s.data(), s.size()))); -} - -} // detail -} // http -} // beast - -#endif diff --git a/include/beast/http/impl/basic_headers.ipp b/include/beast/http/impl/basic_headers.ipp index 45a42322d7..8880bd890c 100644 --- a/include/beast/http/impl/basic_headers.ipp +++ b/include/beast/http/impl/basic_headers.ipp @@ -8,7 +8,6 @@ #ifndef BEAST_HTTP_IMPL_BASIC_HEADERS_IPP #define BEAST_HTTP_IMPL_BASIC_HEADERS_IPP -#include #include namespace beast { diff --git a/include/beast/http/impl/message.ipp b/include/beast/http/impl/message.ipp index 23d748d93a..0f89cc4adb 100644 --- a/include/beast/http/impl/message.ipp +++ b/include/beast/http/impl/message.ipp @@ -9,10 +9,11 @@ #define BEAST_HTTP_IMPL_MESSAGE_IPP #include -#include -#include #include #include +#include +#include +#include #include #include #include @@ -54,23 +55,23 @@ message:: write_firstline(Streambuf& streambuf, std::true_type) const { - detail::write(streambuf, to_string(this->method)); - detail::write(streambuf, " "); - detail::write(streambuf, this->url); + write(streambuf, to_string(this->method)); + write(streambuf, " "); + write(streambuf, this->url); switch(version) { case 10: - detail::write(streambuf, " HTTP/1.0\r\n"); + write(streambuf, " HTTP/1.0\r\n"); break; case 11: - detail::write(streambuf, " HTTP/1.1\r\n"); + write(streambuf, " HTTP/1.1\r\n"); break; default: - detail::write(streambuf, " HTTP/"); - detail::write(streambuf, version / 10); - detail::write(streambuf, "."); - detail::write(streambuf, version % 10); - detail::write(streambuf, "\r\n"); + write(streambuf, " HTTP/"); + write(streambuf, version / 10); + write(streambuf, "."); + write(streambuf, version % 10); + write(streambuf, "\r\n"); break; } } @@ -85,23 +86,23 @@ write_firstline(Streambuf& streambuf, switch(version) { case 10: - detail::write(streambuf, "HTTP/1.0 "); + write(streambuf, "HTTP/1.0 "); break; case 11: - detail::write(streambuf, "HTTP/1.1 "); + write(streambuf, "HTTP/1.1 "); break; default: - detail::write(streambuf, " HTTP/"); - detail::write(streambuf, version / 10); - detail::write(streambuf, "."); - detail::write(streambuf, version % 10); - detail::write(streambuf, " "); + write(streambuf, " HTTP/"); + write(streambuf, version / 10); + write(streambuf, "."); + write(streambuf, version % 10); + write(streambuf, " "); break; } - detail::write(streambuf, this->status); - detail::write(streambuf, " "); - detail::write(streambuf, this->reason); - detail::write(streambuf, "\r\n"); + write(streambuf, this->status); + write(streambuf, " "); + write(streambuf, this->reason); + write(streambuf, "\r\n"); } namespace detail { @@ -273,10 +274,10 @@ write_fields(Streambuf& streambuf, FieldSequence const& fields) // "FieldSequence requirements not met"); for(auto const& field : fields) { - detail::write(streambuf, field.name()); - detail::write(streambuf, ": "); - detail::write(streambuf, field.value()); - detail::write(streambuf, "\r\n"); + write(streambuf, field.name()); + write(streambuf, ": "); + write(streambuf, field.value()); + write(streambuf, "\r\n"); } } diff --git a/include/beast/http/impl/write.ipp b/include/beast/http/impl/write.ipp index 8e538c999d..f1640c0287 100644 --- a/include/beast/http/impl/write.ipp +++ b/include/beast/http/impl/write.ipp @@ -10,7 +10,6 @@ #include #include -#include #include #include #include diff --git a/include/beast/write_streambuf.hpp b/include/beast/write_streambuf.hpp new file mode 100644 index 0000000000..750a024fa8 --- /dev/null +++ b/include/beast/write_streambuf.hpp @@ -0,0 +1,59 @@ +// +// Copyright (c) 2013-2016 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_WRITE_STREAMBUF_HPP +#define BEAST_WRITE_STREAMBUF_HPP + +#include +#include +#include +#include + +namespace beast { + +/** Write to a Streambuf. + + This function appends the serialized representation of each provided + argument into the stream buffer. It is capable of converting the + following types of arguments: + + * `boost::asio::const_buffer` + * `boost::asio::mutable_buffer` + * A type for which the call to `boost::asio::buffer()` is defined + * A type meeting the requirements of `ConstBufferSequence` + * A type meeting the requirements of `MutableBufferSequence` + + For all types not listed above, the function will invoke + `boost::lexical_cast` on the argument in an attempt to convert to + a string, which is then appended to the stream buffer. + + When this function serializes numbers, it converts them to + their text representation as if by a call to `std::to_string`. + + @param streambuf The stream buffer to write to. + + @param args A list of one or more arguments to write. + + @throws Any exceptions thrown by `boost::lexical_cast`. + + @note This function participates in overload resolution only if + the `streambuf` parameter meets the requirements of Streambuf. +*/ +template +#if GENERATING_DOCS +void +#else +typename std::enable_if::value>::type +#endif +write(Streambuf& streambuf, Args&&... args) +{ + detail::write_streambuf(streambuf, std::forward(args)...); +} + +} // beast + +#endif diff --git a/test/Jamfile b/test/Jamfile index e384b4416d..356cda9af2 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -50,7 +50,6 @@ unit-test http_tests : unit-test websocket_tests : main.cpp - ../src/beast_http_nodejs_parser.cpp websocket/error.cpp websocket/option.cpp websocket/rfc6455.cpp diff --git a/test/write_streambuf.cpp b/test/write_streambuf.cpp new file mode 100644 index 0000000000..68290fbce7 --- /dev/null +++ b/test/write_streambuf.cpp @@ -0,0 +1,35 @@ +// +// Copyright (c) 2013-2016 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 { + +class write_streambuf_test : public beast::detail::unit_test::suite +{ +public: + void run() override + { + streambuf sb; + std::string s; + write(sb, boost::asio::const_buffer{"", 0}); + write(sb, boost::asio::mutable_buffer{nullptr, 0}); + write(sb, boost::asio::null_buffers{}); + write(sb, boost::asio::const_buffers_1{"", 0}); + write(sb, boost::asio::mutable_buffers_1{nullptr, 0}); + write(sb, s); + write(sb, 23); + pass(); + } +}; + +BEAST_DEFINE_TESTSUITE(write_streambuf,core,beast); + +} // beast