mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 13:27:33 +02:00
New ostream() returns dynamic buffer output stream (API Change):
This eliminates beast::write output for dynamic buffers and replaces it with the function ostream() that wraps a caller provided dynamic buffer and returns the result as a std::ostream derived object. Callers may now produce formatted output into any object meeting the requirements of DynamicBuffer using operator<< and the standard stream modifiers such as std::endl. This new technique is more efficient, as implementations of operator<< can now write directly into the output using std::ostream::write and std::ostream::put. Example of use: beast::streambuf sb; beast::ostream(sb) << "Hello, world!" << std::endl;
This commit is contained in:
@@ -8,6 +8,7 @@ WebSocket:
|
||||
API Changes:
|
||||
|
||||
* Refactor http::header contents
|
||||
* New ostream() returns dynamic buffer output stream
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@@ -176,11 +176,11 @@
|
||||
<simplelist type="vert" columns="1">
|
||||
<member><link linkend="beast.ref.bind_handler">bind_handler</link></member>
|
||||
<member><link linkend="beast.ref.buffer_cat">buffer_cat</link></member>
|
||||
<member><link linkend="beast.ref.ostream">ostream</link></member>
|
||||
<member><link linkend="beast.ref.prepare_buffer">prepare_buffer</link></member>
|
||||
<member><link linkend="beast.ref.prepare_buffers">prepare_buffers</link></member>
|
||||
<member><link linkend="beast.ref.system_category">system_category</link></member>
|
||||
<member><link linkend="beast.ref.to_string">to_string</link></member>
|
||||
<member><link linkend="beast.ref.write">write</link></member>
|
||||
</simplelist>
|
||||
</entry>
|
||||
<entry valign="top">
|
||||
|
@@ -16,6 +16,8 @@
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/buffers_adapter.hpp>
|
||||
#include <beast/core/consuming_buffers.hpp>
|
||||
#include <beast/core/dynabuf_readstream.hpp>
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/core/error.hpp>
|
||||
#include <beast/core/flat_streambuf.hpp>
|
||||
#include <beast/core/handler_alloc.hpp>
|
||||
@@ -28,8 +30,6 @@
|
||||
#include <beast/core/static_string.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/dynabuf_readstream.hpp>
|
||||
#include <beast/core/to_string.hpp>
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
|
||||
#endif
|
||||
|
210
include/beast/core/detail/ostream.hpp
Normal file
210
include/beast/core/detail/ostream.hpp
Normal file
@@ -0,0 +1,210 @@
|
||||
//
|
||||
// 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_DETAIL_OSTREAM_HPP
|
||||
#define BEAST_DETAIL_OSTREAM_HPP
|
||||
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/utility/base_from_member.hpp>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <streambuf>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
template<
|
||||
class DynamicBuffer,
|
||||
class CharT,
|
||||
class Traits
|
||||
>
|
||||
class ostream_buffer
|
||||
: public std::basic_streambuf<CharT, Traits>
|
||||
{
|
||||
using int_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::int_type;
|
||||
|
||||
using traits_type = typename
|
||||
std::basic_streambuf<CharT, Traits>::traits_type;
|
||||
|
||||
static std::size_t constexpr max_size = 512;
|
||||
|
||||
DynamicBuffer& buf_;
|
||||
|
||||
public:
|
||||
ostream_buffer(ostream_buffer&&) = default;
|
||||
ostream_buffer(ostream_buffer const&) = delete;
|
||||
|
||||
~ostream_buffer() noexcept;
|
||||
|
||||
explicit
|
||||
ostream_buffer(DynamicBuffer& buf);
|
||||
|
||||
int_type
|
||||
overflow(int_type ch) override;
|
||||
|
||||
int
|
||||
sync() override;
|
||||
|
||||
private:
|
||||
void
|
||||
prepare();
|
||||
|
||||
void
|
||||
flush(int extra = 0);
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>::
|
||||
~ostream_buffer() noexcept
|
||||
{
|
||||
sync();
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>::
|
||||
ostream_buffer(DynamicBuffer& buf)
|
||||
: buf_(buf)
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
auto
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>::
|
||||
overflow(int_type ch) ->
|
||||
int_type
|
||||
{
|
||||
if(ch != traits_type::eof())
|
||||
{
|
||||
Traits::assign(*this->pptr(), ch);
|
||||
flush(1);
|
||||
prepare();
|
||||
return ch;
|
||||
}
|
||||
flush();
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
int
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>::
|
||||
sync()
|
||||
{
|
||||
flush();
|
||||
prepare();
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
void
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>::
|
||||
prepare()
|
||||
{
|
||||
using boost::asio::buffer_cast;
|
||||
using boost::asio::buffer_size;
|
||||
auto mbs = buf_.prepare(
|
||||
read_size_helper(buf_, max_size));
|
||||
auto const mb = *mbs.begin();
|
||||
auto const p = buffer_cast<CharT*>(mb);
|
||||
this->setp(p,
|
||||
p + buffer_size(mb) / sizeof(CharT) - 1);
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
void
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>::
|
||||
flush(int extra)
|
||||
{
|
||||
buf_.commit(
|
||||
(this->pptr() - this->pbase() + extra) *
|
||||
sizeof(CharT));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
template<class DynamicBuffer,
|
||||
class CharT, class Traits, bool isMovable>
|
||||
class ostream_helper;
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
class ostream_helper<
|
||||
DynamicBuffer, CharT, Traits, true>
|
||||
: public std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits> osb_;
|
||||
|
||||
public:
|
||||
explicit
|
||||
ostream_helper(DynamicBuffer& buf);
|
||||
|
||||
ostream_helper(ostream_helper&& other);
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, true>::
|
||||
ostream_helper(DynamicBuffer& buf)
|
||||
: std::basic_ostream<CharT, Traits>(
|
||||
&this->osb_)
|
||||
, osb_(buf)
|
||||
{
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, true>::
|
||||
ostream_helper(
|
||||
ostream_helper&& other)
|
||||
: std::basic_ostream<CharT, Traits>(&osb_)
|
||||
, osb_(std::move(other.osb_))
|
||||
{
|
||||
}
|
||||
|
||||
// This work-around is for libstdc++ versions that
|
||||
// don't have a movable std::basic_streambuf
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
struct ostream_helper<
|
||||
DynamicBuffer, CharT, Traits, false>
|
||||
: private boost::base_from_member<
|
||||
std::unique_ptr<ostream_buffer<
|
||||
DynamicBuffer, CharT, Traits>>>
|
||||
, std::basic_ostream<CharT, Traits>
|
||||
{
|
||||
explicit
|
||||
ostream_helper(DynamicBuffer& buf);
|
||||
|
||||
ostream_helper(ostream_helper&& other);
|
||||
};
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, false>::
|
||||
ostream_helper(DynamicBuffer& buf)
|
||||
: boost::base_from_member<std::unique_ptr<
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>>>(
|
||||
new ostream_buffer<DynamicBuffer, CharT, Traits>(buf))
|
||||
, std::basic_ostream<CharT, Traits>(
|
||||
this->member.get())
|
||||
{
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class CharT, class Traits>
|
||||
ostream_helper<DynamicBuffer, CharT, Traits, false>::
|
||||
ostream_helper(ostream_helper&& other)
|
||||
: boost::base_from_member<std::unique_ptr<
|
||||
ostream_buffer<DynamicBuffer, CharT, Traits>>>(
|
||||
std::move(other.member))
|
||||
, std::basic_ostream<CharT, Traits>(
|
||||
this->member.get())
|
||||
{
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
@@ -1,140 +0,0 @@
|
||||
//
|
||||
// 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_DETAIL_WRITE_DYNABUF_HPP
|
||||
#define BEAST_DETAIL_WRITE_DYNABUF_HPP
|
||||
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <boost/asio/buffer.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
namespace detail {
|
||||
|
||||
// detects string literals.
|
||||
template<class T>
|
||||
struct is_string_literal : std::integral_constant<bool,
|
||||
! std::is_same<T, typename std::remove_extent<T>::type>::value &&
|
||||
std::is_same<char, typename std::remove_extent<T>::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 T>
|
||||
class is_BufferConvertible
|
||||
{
|
||||
template<class U, class R = decltype(
|
||||
boost::asio::buffer(std::declval<U const&>()),
|
||||
std::true_type{})>
|
||||
static R check(int);
|
||||
template<class>
|
||||
static std::false_type check(...);
|
||||
using type = decltype(check<T>(0));
|
||||
public:
|
||||
static bool const value = type::value &&
|
||||
! is_string_literal<T>::value;
|
||||
};
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
boost::asio::const_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
boost::asio::mutable_buffer const& buffer)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffer)),
|
||||
buffer));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T>
|
||||
typename std::enable_if<
|
||||
is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
auto const buffers = boost::asio::buffer(t);
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class Buffers>
|
||||
typename std::enable_if<
|
||||
is_ConstBufferSequence<Buffers>::value &&
|
||||
! is_BufferConvertible<Buffers>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<Buffers, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, Buffers const& buffers)
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
using boost::asio::buffer_size;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(buffer_size(buffers)),
|
||||
buffers));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, std::size_t N>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf, const char (&s)[N])
|
||||
{
|
||||
using boost::asio::buffer_copy;
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(N - 1),
|
||||
boost::asio::buffer(s, N - 1)));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T>
|
||||
typename std::enable_if<
|
||||
! is_string_literal<T>::value &&
|
||||
! is_ConstBufferSequence<T>::value &&
|
||||
! is_BufferConvertible<T>::value &&
|
||||
! std::is_convertible<T, boost::asio::const_buffer>::value &&
|
||||
! std::is_convertible<T, boost::asio::mutable_buffer>::value
|
||||
>::type
|
||||
write_dynabuf(DynamicBuffer& dynabuf, T const& t)
|
||||
{
|
||||
using boost::asio::buffer;
|
||||
using boost::asio::buffer_copy;
|
||||
auto const s = boost::lexical_cast<std::string>(t);
|
||||
dynabuf.commit(buffer_copy(
|
||||
dynabuf.prepare(s.size()), buffer(s)));
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class T0, class T1, class... TN>
|
||||
void
|
||||
write_dynabuf(DynamicBuffer& dynabuf,
|
||||
T0 const& t0, T1 const& t1, TN const&... tn)
|
||||
{
|
||||
write_dynabuf(dynabuf, t0);
|
||||
write_dynabuf(dynabuf, t1, tn...);
|
||||
}
|
||||
|
||||
} // detail
|
||||
} // beast
|
||||
|
||||
#endif
|
@@ -9,7 +9,6 @@
|
||||
#define BEAST_IMPL_STREAMBUF_IPP
|
||||
|
||||
#include <beast/core/detail/type_traits.hpp>
|
||||
#include <beast/core/detail/write_dynabuf.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
@@ -871,14 +870,6 @@ read_size_helper(basic_streambuf<
|
||||
return (std::min)(max_size, low);
|
||||
}
|
||||
|
||||
template<class Alloc, class T>
|
||||
basic_streambuf<Alloc>&
|
||||
operator<<(basic_streambuf<Alloc>& streambuf, T const& t)
|
||||
{
|
||||
detail::write_dynabuf(streambuf, t);
|
||||
return streambuf;
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
||||
|
66
include/beast/core/ostream.hpp
Normal file
66
include/beast/core/ostream.hpp
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// 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_WRITE_OSTREAM_HPP
|
||||
#define BEAST_WRITE_OSTREAM_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/ostream.hpp>
|
||||
#include <type_traits>
|
||||
#include <streambuf>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Return an output stream that formats values into a @b DynamicBuffer.
|
||||
|
||||
This function wraps the caller provided @b DynamicBuffer into
|
||||
a `std::ostream` derived class, to allow `operator<<` stream style
|
||||
formatting operations.
|
||||
|
||||
@par Example
|
||||
@code
|
||||
ostream(buffer) << "Hello, world!" << std::endl;
|
||||
@endcode
|
||||
|
||||
@note Calling members of the underlying buffer before the output
|
||||
stream is destroyed results in undefined behavior.
|
||||
|
||||
@param buffer An object meeting the requirements of @b DynamicBuffer
|
||||
into which the formatted output will be placed.
|
||||
|
||||
@return An object derived from `std::ostream` which directs output
|
||||
into the specified dynamic buffer. Ownership of the dynamic buffer
|
||||
is not transferred. The caller is responsible for ensuring the
|
||||
dynamic buffer is not destroyed for the lifetime of the output
|
||||
stream.
|
||||
*/
|
||||
template<class DynamicBuffer>
|
||||
#if BEAST_DOXYGEN
|
||||
implementation_defined
|
||||
#else
|
||||
detail::ostream_helper<
|
||||
DynamicBuffer, char, std::char_traits<char>,
|
||||
std::is_move_constructible<
|
||||
detail::ostream_buffer<DynamicBuffer,
|
||||
char, std::char_traits<char>>>::value>
|
||||
#endif
|
||||
ostream(DynamicBuffer& buffer)
|
||||
{
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
return detail::ostream_helper<
|
||||
DynamicBuffer, char, std::char_traits<char>,
|
||||
std::is_move_constructible<
|
||||
detail::ostream_buffer<DynamicBuffer,
|
||||
char, std::char_traits<char>>>::value>{buffer};
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
@@ -326,18 +326,6 @@ private:
|
||||
*/
|
||||
using streambuf = basic_streambuf<std::allocator<char>>;
|
||||
|
||||
/** Format output to a @ref basic_streambuf.
|
||||
|
||||
@param streambuf The @ref basic_streambuf to write to.
|
||||
|
||||
@param t The object to write.
|
||||
|
||||
@return A reference to the @ref basic_streambuf.
|
||||
*/
|
||||
template<class Allocator, class T>
|
||||
basic_streambuf<Allocator>&
|
||||
operator<<(basic_streambuf<Allocator>& streambuf, T const& t);
|
||||
|
||||
} // beast
|
||||
|
||||
#include <beast/core/impl/streambuf.ipp>
|
||||
|
@@ -1,64 +0,0 @@
|
||||
//
|
||||
// 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_WRITE_DYNABUF_HPP
|
||||
#define BEAST_WRITE_DYNABUF_HPP
|
||||
|
||||
#include <beast/config.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/detail/write_dynabuf.hpp>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace beast {
|
||||
|
||||
/** Write to a @b `DynamicBuffer`.
|
||||
|
||||
This function appends the serialized representation of each provided
|
||||
argument into the dynamic buffer. It is capable of converting the
|
||||
following types of arguments:
|
||||
|
||||
@li `boost::asio::const_buffer`
|
||||
|
||||
@li `boost::asio::mutable_buffer`
|
||||
|
||||
@li A type meeting the requirements of @b `ConvertibleToConstBuffer`
|
||||
|
||||
@li A type meeting the requirements of @b `ConstBufferSequence`
|
||||
|
||||
@li A type meeting the requirements of @b `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 dynamic buffer.
|
||||
|
||||
When this function serializes numbers, it converts them to
|
||||
their text representation as if by a call to `std::to_string`.
|
||||
|
||||
@param dynabuf The dynamic buffer to write to.
|
||||
|
||||
@param args A list of one or more arguments to write.
|
||||
|
||||
@throws unspecified Any exceptions thrown by `boost::lexical_cast`.
|
||||
|
||||
@note This function participates in overload resolution only if
|
||||
the `dynabuf` parameter meets the requirements of @b `DynamicBuffer`.
|
||||
*/
|
||||
template<class DynamicBuffer, class... Args>
|
||||
#if BEAST_DOXYGEN
|
||||
void
|
||||
#else
|
||||
typename std::enable_if<is_DynamicBuffer<DynamicBuffer>::value>::type
|
||||
#endif
|
||||
write(DynamicBuffer& dynabuf, Args const&... args)
|
||||
{
|
||||
detail::write_dynabuf(dynabuf, args...);
|
||||
}
|
||||
|
||||
} // beast
|
||||
|
||||
#endif
|
@@ -13,11 +13,11 @@
|
||||
#include <beast/core/buffer_cat.hpp>
|
||||
#include <beast/core/bind_handler.hpp>
|
||||
#include <beast/core/buffer_concepts.hpp>
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/core/handler_helpers.hpp>
|
||||
#include <beast/core/handler_ptr.hpp>
|
||||
#include <beast/core/stream_concepts.hpp>
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
#include <beast/core/detail/sync_ostream.hpp>
|
||||
#include <boost/asio/write.hpp>
|
||||
#include <condition_variable>
|
||||
@@ -31,53 +31,39 @@ namespace http {
|
||||
|
||||
namespace detail {
|
||||
|
||||
template<class DynamicBuffer, class Fields>
|
||||
template<class Fields>
|
||||
void
|
||||
write_start_line(DynamicBuffer& dynabuf,
|
||||
write_start_line(std::ostream& os,
|
||||
header<true, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
write(dynabuf, msg.method());
|
||||
write(dynabuf, " ");
|
||||
write(dynabuf, msg.target());
|
||||
os << msg.method() << " " << msg.target();
|
||||
switch(msg.version)
|
||||
{
|
||||
case 10:
|
||||
write(dynabuf, " HTTP/1.0\r\n");
|
||||
break;
|
||||
case 11:
|
||||
write(dynabuf, " HTTP/1.1\r\n");
|
||||
break;
|
||||
case 10: os << " HTTP/1.0\r\n"; break;
|
||||
case 11: os << " HTTP/1.1\r\n"; break;
|
||||
}
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class Fields>
|
||||
template<class Fields>
|
||||
void
|
||||
write_start_line(DynamicBuffer& dynabuf,
|
||||
write_start_line(std::ostream& os,
|
||||
header<false, Fields> const& msg)
|
||||
{
|
||||
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
|
||||
switch(msg.version)
|
||||
{
|
||||
case 10:
|
||||
write(dynabuf, "HTTP/1.0 ");
|
||||
break;
|
||||
case 11:
|
||||
write(dynabuf, "HTTP/1.1 ");
|
||||
break;
|
||||
case 10: os << "HTTP/1.0 "; break;
|
||||
case 11: os << "HTTP/1.1 "; break;
|
||||
}
|
||||
write(dynabuf, msg.status);
|
||||
write(dynabuf, " ");
|
||||
write(dynabuf, msg.reason());
|
||||
write(dynabuf, "\r\n");
|
||||
os << msg.status << " " << msg.reason() << "\r\n";
|
||||
}
|
||||
|
||||
template<class DynamicBuffer, class FieldSequence>
|
||||
template<class FieldSequence>
|
||||
void
|
||||
write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
|
||||
write_fields(std::ostream& os,
|
||||
FieldSequence const& fields)
|
||||
{
|
||||
static_assert(is_DynamicBuffer<DynamicBuffer>::value,
|
||||
"DynamicBuffer requirements not met");
|
||||
//static_assert(is_FieldSequence<FieldSequence>::value,
|
||||
// "FieldSequence requirements not met");
|
||||
for(auto const& field : fields)
|
||||
@@ -86,10 +72,7 @@ write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
|
||||
BOOST_ASSERT(! name.empty());
|
||||
if(name[0] == ':')
|
||||
continue;
|
||||
write(dynabuf, field.name());
|
||||
write(dynabuf, ": ");
|
||||
write(dynabuf, field.value());
|
||||
write(dynabuf, "\r\n");
|
||||
os << field.name() << ": " << field.value() << "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,9 +201,12 @@ write(SyncWriteStream& stream,
|
||||
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
|
||||
"SyncWriteStream requirements not met");
|
||||
streambuf sb;
|
||||
detail::write_start_line(sb, msg);
|
||||
detail::write_fields(sb, msg.fields);
|
||||
beast::write(sb, "\r\n");
|
||||
{
|
||||
auto os = ostream(sb);
|
||||
detail::write_start_line(os, msg);
|
||||
detail::write_fields(os, msg.fields);
|
||||
os << "\r\n";
|
||||
}
|
||||
boost::asio::write(stream, sb.data(), ec);
|
||||
}
|
||||
|
||||
@@ -238,9 +224,12 @@ async_write(AsyncWriteStream& stream,
|
||||
beast::async_completion<WriteHandler,
|
||||
void(error_code)> completion{handler};
|
||||
streambuf sb;
|
||||
detail::write_start_line(sb, msg);
|
||||
detail::write_fields(sb, msg.fields);
|
||||
beast::write(sb, "\r\n");
|
||||
{
|
||||
auto os = ostream(sb);
|
||||
detail::write_start_line(os, msg);
|
||||
detail::write_fields(os, msg.fields);
|
||||
os << "\r\n";
|
||||
}
|
||||
detail::write_streambuf_op<AsyncWriteStream,
|
||||
decltype(completion.handler)>{
|
||||
completion.handler, stream, std::move(sb)};
|
||||
@@ -281,9 +270,10 @@ struct write_preparation
|
||||
if(ec)
|
||||
return;
|
||||
|
||||
write_start_line(sb, msg);
|
||||
write_fields(sb, msg.fields);
|
||||
beast::write(sb, "\r\n");
|
||||
auto os = ostream(sb);
|
||||
write_start_line(os, msg);
|
||||
write_fields(os, msg.fields);
|
||||
os << "\r\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -29,6 +29,7 @@ unit-test core-tests :
|
||||
core/handler_alloc.cpp
|
||||
core/handler_concepts.cpp
|
||||
core/handler_ptr.cpp
|
||||
core/ostream.cpp
|
||||
core/placeholders.cpp
|
||||
core/prepare_buffer.cpp
|
||||
core/prepare_buffers.cpp
|
||||
@@ -37,7 +38,6 @@ unit-test core-tests :
|
||||
core/stream_concepts.cpp
|
||||
core/streambuf.cpp
|
||||
core/to_string.cpp
|
||||
core/write_dynabuf.cpp
|
||||
core/base64.cpp
|
||||
core/empty_base_optimization.cpp
|
||||
core/get_lowest_layer.cpp
|
||||
|
@@ -22,6 +22,7 @@ add_executable (core-tests
|
||||
handler_alloc.cpp
|
||||
handler_concepts.cpp
|
||||
handler_ptr.cpp
|
||||
ostream.cpp
|
||||
placeholders.cpp
|
||||
prepare_buffer.cpp
|
||||
prepare_buffers.cpp
|
||||
@@ -30,7 +31,6 @@ add_executable (core-tests
|
||||
stream_concepts.cpp
|
||||
streambuf.cpp
|
||||
to_string.cpp
|
||||
write_dynabuf.cpp
|
||||
base64.cpp
|
||||
empty_base_optimization.cpp
|
||||
get_lowest_layer.cpp
|
||||
|
32
test/core/ostream.cpp
Normal file
32
test/core/ostream.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// 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 <beast/core/ostream.hpp>
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
#include <ostream>
|
||||
|
||||
#include <beast/core/to_string.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class ostream_test : public beast::unit_test::suite
|
||||
{
|
||||
public:
|
||||
void run() override
|
||||
{
|
||||
streambuf sb;
|
||||
ostream(sb) << "Hello, world!\n";
|
||||
BEAST_EXPECT(to_string(sb.data()) == "Hello, world!\n");
|
||||
}
|
||||
};
|
||||
|
||||
BEAST_DEFINE_TESTSUITE(ostream,core,beast);
|
||||
|
||||
} // beast
|
@@ -273,13 +273,6 @@ public:
|
||||
BEAST_EXPECT(test::buffer_count(sb.data()) == 4);
|
||||
}
|
||||
|
||||
void testOutputStream()
|
||||
{
|
||||
streambuf sb;
|
||||
sb << "x";
|
||||
BEAST_EXPECT(to_string(sb.data()) == "x");
|
||||
}
|
||||
|
||||
void testCapacity()
|
||||
{
|
||||
using boost::asio::buffer_size;
|
||||
@@ -353,7 +346,6 @@ public:
|
||||
testConsume();
|
||||
testMatrix();
|
||||
testIterators();
|
||||
testOutputStream();
|
||||
testCapacity();
|
||||
}
|
||||
};
|
||||
|
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// 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 <beast/core/write_dynabuf.hpp>
|
||||
|
||||
#include <beast/core/streambuf.hpp>
|
||||
#include <beast/unit_test/suite.hpp>
|
||||
|
||||
namespace beast {
|
||||
|
||||
class write_dynabuf_test : public beast::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_dynabuf,core,beast);
|
||||
|
||||
} // beast
|
@@ -8,7 +8,7 @@
|
||||
#ifndef BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
|
||||
#define BEAST_HTTP_TEST_MESSAGE_FUZZ_HPP
|
||||
|
||||
#include <beast/core/write_dynabuf.hpp>
|
||||
#include <beast/core/ostream.hpp>
|
||||
#include <beast/http/detail/rfc7230.hpp>
|
||||
#include <cstdint>
|
||||
#include <random>
|
||||
@@ -319,7 +319,7 @@ public:
|
||||
}
|
||||
|
||||
std::string
|
||||
uri()
|
||||
target()
|
||||
{
|
||||
//switch(rand(4))
|
||||
switch(1)
|
||||
@@ -348,7 +348,7 @@ public:
|
||||
|
||||
#if 0
|
||||
std::string
|
||||
uri()
|
||||
target()
|
||||
{
|
||||
static char constexpr alpha[63] =
|
||||
"0123456789" "ABCDEFGHIJ" "KLMNOPQRST"
|
||||
@@ -476,13 +476,13 @@ public:
|
||||
void
|
||||
fields(DynamicBuffer& db)
|
||||
{
|
||||
auto os = ostream(db);
|
||||
while(rand(6))
|
||||
{
|
||||
write(db, field());
|
||||
write(db, rand(4) ? ": " : ":");
|
||||
write(db, value());
|
||||
write(db, "\r\n");
|
||||
}
|
||||
os <<
|
||||
field() <<
|
||||
(rand(4) ? ": " : ":") <<
|
||||
value() <<
|
||||
"\r\n";
|
||||
}
|
||||
|
||||
template<class DynamicBuffer>
|
||||
@@ -491,13 +491,15 @@ public:
|
||||
{
|
||||
if(! rand(4))
|
||||
{
|
||||
write(db, "Content-Length: 0\r\n\r\n");
|
||||
ostream(db) <<
|
||||
"Content-Length: 0\r\n\r\n";
|
||||
return;
|
||||
}
|
||||
if(rand(2))
|
||||
{
|
||||
auto const len = rand(500);
|
||||
write(db, "Content-Length: ", len, "\r\n\r\n");
|
||||
ostream(db) <<
|
||||
"Content-Length: " << len << "\r\n\r\n";
|
||||
for(auto const& b : db.prepare(len))
|
||||
{
|
||||
auto p = boost::asio::buffer_cast<char*>(b);
|
||||
@@ -510,12 +512,14 @@ public:
|
||||
else
|
||||
{
|
||||
auto len = rand(500);
|
||||
write(db, "Transfer-Encoding: chunked\r\n\r\n");
|
||||
ostream(db) <<
|
||||
"Transfer-Encoding: chunked\r\n\r\n";
|
||||
while(len > 0)
|
||||
{
|
||||
auto n = (std::min)(1 + rand(300), len);
|
||||
len -= n;
|
||||
write(db, to_hex(n), "\r\n");
|
||||
ostream(db) <<
|
||||
to_hex(n) << "\r\n";
|
||||
for(auto const& b : db.prepare(n))
|
||||
{
|
||||
auto p = boost::asio::buffer_cast<char*>(b);
|
||||
@@ -524,9 +528,9 @@ public:
|
||||
*p++ = static_cast<char>(32 + rand(26+26+10+6));
|
||||
}
|
||||
db.commit(n);
|
||||
write(db, "\r\n");
|
||||
ostream(db) << "\r\n";
|
||||
}
|
||||
write(db, "0\r\n\r\n");
|
||||
ostream(db) << "0\r\n\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +538,8 @@ public:
|
||||
void
|
||||
request(DynamicBuffer& db)
|
||||
{
|
||||
write(db, method(), " ", uri(), " HTTP/1.1\r\n");
|
||||
ostream(db) <<
|
||||
method() << " " << target() << " HTTP/1.1\r\n";
|
||||
fields(db);
|
||||
body(db);
|
||||
}
|
||||
@@ -543,14 +548,15 @@ public:
|
||||
void
|
||||
response(DynamicBuffer& db)
|
||||
{
|
||||
write(db, "HTTP/1.");
|
||||
write(db, rand(2) ? "0" : "1");
|
||||
write(db, " ", 100 + rand(401), " ");
|
||||
write(db, token());
|
||||
write(db, "\r\n");
|
||||
ostream(db) <<
|
||||
"HTTP/1." <<
|
||||
(rand(2) ? "0" : "1") << " " <<
|
||||
(100 + rand(401)) << " " <<
|
||||
token() <<
|
||||
"\r\n";
|
||||
fields(db);
|
||||
body(db);
|
||||
write(db, "\r\n");
|
||||
ostream(db) << "\r\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user