Add write, async_write, operator<< for message_headers:

fix #155

This adds overloads of write, async_write, and operator<<
for message_headers.
This commit is contained in:
Vinnie Falco
2016-11-07 13:51:10 -05:00
parent f98ec17121
commit 8035dac88c
5 changed files with 287 additions and 29 deletions

View File

@ -3,6 +3,7 @@
HTTP
* Make chunk_encode public
* Add write, async_write, operator<< for message_headers
WebSocket

View File

@ -69,6 +69,7 @@
<member><link linkend="beast.ref.http__swap">swap</link></member>
<member><link linkend="beast.ref.http__with_body">with_body</link></member>
<member><link linkend="beast.ref.http__write">write</link></member>
<member><link linkend="beast.ref.http__operator_ls_">operator&lt;&lt;</link></member>
</simplelist>
<bridgehead renderas="sect3">Type Traits</bridgehead>
<simplelist type="vert" columns="1">

View File

@ -195,6 +195,18 @@
select="concat(substring-before($name, '::'), '__', substring-after($name, '::'))"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="substring($name, string-length($name) - 1) = '&lt;&lt;'">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '&lt;&lt;'), '_ls_')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="substring($name, string-length($name) - 1) = '&gt;&gt;'">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
select="concat(substring-before($name, '&gt;&gt;'), '_rs_')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($name, '=')">
<xsl:call-template name="make-id">
<xsl:with-param name="name"
@ -1698,6 +1710,14 @@
</xsl:choose>
<xsl:text>```&#xd;</xsl:text>
<xsl:for-each select="../memberdef[name = $unqualified-name]">
<xsl:if test="position() &gt; 1">
<xsl:text>&#xd;</xsl:text>
<xsl:if test=" not(briefdescription = preceding-sibling::*/briefdescription)">
<xsl:text>```&#xd;</xsl:text>
<xsl:apply-templates select="briefdescription" mode="markup"/>
<xsl:text>```&#xd;</xsl:text>
</xsl:if>
</xsl:if>
<xsl:variable name="stripped-type">
<xsl:call-template name="cleanup-type">
<xsl:with-param name="name" select="type"/>

View File

@ -32,11 +32,12 @@ namespace http {
namespace detail {
template<class DynamicBuffer, class Body, class Headers>
template<class DynamicBuffer, class Headers>
void
write_firstline(DynamicBuffer& dynabuf,
message<true, Body, Headers> const& msg)
message_headers<true, Headers> const& msg)
{
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
write(dynabuf, msg.method);
write(dynabuf, " ");
write(dynabuf, msg.url);
@ -48,23 +49,15 @@ write_firstline(DynamicBuffer& dynabuf,
case 11:
write(dynabuf, " HTTP/1.1\r\n");
break;
default:
{
std::string s;
s = " HTTP/" +
std::to_string(msg.version/10) + '.' +
std::to_string(msg.version%10) + "\r\n";
write(dynabuf, s);
break;
}
}
}
template<class DynamicBuffer, class Body, class Headers>
template<class DynamicBuffer, class Headers>
void
write_firstline(DynamicBuffer& dynabuf,
message<false, Body, Headers> const& msg)
message_headers<false, Headers> const& msg)
{
BOOST_ASSERT(msg.version == 10 || msg.version == 11);
switch(msg.version)
{
case 10:
@ -73,15 +66,6 @@ write_firstline(DynamicBuffer& dynabuf,
case 11:
write(dynabuf, "HTTP/1.1 ");
break;
default:
{
std::string s;
s = " HTTP/" +
std::to_string(msg.version/10) + '.' +
std::to_string(msg.version%10) + ' ';
write(dynabuf, s);
break;
}
}
write(dynabuf, msg.status);
write(dynabuf, " ");
@ -106,6 +90,177 @@ write_fields(DynamicBuffer& dynabuf, FieldSequence const& fields)
}
}
} // detail
//------------------------------------------------------------------------------
namespace detail {
template<class Stream, class Handler>
class write_streambuf_op
{
using alloc_type =
handler_alloc<char, Handler>;
struct data
{
Stream& s;
streambuf sb;
Handler h;
bool cont;
int state = 0;
template<class DeducedHandler>
data(DeducedHandler&& h_, Stream& s_,
streambuf&& sb_)
: s(s_)
, sb(std::move(sb_))
, h(std::forward<DeducedHandler>(h_))
, cont(boost_asio_handler_cont_helpers::
is_continuation(h))
{
}
};
std::shared_ptr<data> d_;
public:
write_streambuf_op(write_streambuf_op&&) = default;
write_streambuf_op(write_streambuf_op const&) = default;
template<class DeducedHandler, class... Args>
write_streambuf_op(DeducedHandler&& h, Stream& s,
Args&&... args)
: d_(std::allocate_shared<data>(alloc_type{h},
std::forward<DeducedHandler>(h), s,
std::forward<Args>(args)...))
{
(*this)(error_code{}, 0, false);
}
explicit
write_streambuf_op(std::shared_ptr<data> d)
: d_(std::move(d))
{
}
void
operator()(error_code ec,
std::size_t bytes_transferred, bool again = true);
friend
void* asio_handler_allocate(
std::size_t size, write_streambuf_op* op)
{
return boost_asio_handler_alloc_helpers::
allocate(size, op->d_->h);
}
friend
void asio_handler_deallocate(
void* p, std::size_t size, write_streambuf_op* op)
{
return boost_asio_handler_alloc_helpers::
deallocate(p, size, op->d_->h);
}
friend
bool asio_handler_is_continuation(write_streambuf_op* op)
{
return op->d_->cont;
}
template<class Function>
friend
void asio_handler_invoke(Function&& f, write_streambuf_op* op)
{
return boost_asio_handler_invoke_helpers::
invoke(f, op->d_->h);
}
};
template<class Stream, class Handler>
void
write_streambuf_op<Stream, Handler>::
operator()(error_code ec, std::size_t, bool again)
{
auto& d = *d_;
d.cont = d.cont || again;
while(! ec && d.state != 99)
{
switch(d.state)
{
case 0:
{
d.state = 99;
boost::asio::async_write(d.s,
d.sb.data(), std::move(*this));
return;
}
}
}
d.h(ec);
}
} // detail
template<class SyncWriteStream,
bool isRequest, class Headers>
void
write(SyncWriteStream& stream,
message_headers<isRequest, Headers> const& msg)
{
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
error_code ec;
write(stream, msg, ec);
if(ec)
throw system_error{ec};
}
template<class SyncWriteStream,
bool isRequest, class Headers>
void
write(SyncWriteStream& stream,
message_headers<isRequest, Headers> const& msg,
error_code& ec)
{
static_assert(is_SyncWriteStream<SyncWriteStream>::value,
"SyncWriteStream requirements not met");
streambuf sb;
detail::write_firstline(sb, msg);
detail::write_fields(sb, msg.headers);
beast::write(sb, "\r\n");
boost::asio::write(stream, sb.data(), ec);
}
template<class AsyncWriteStream,
bool isRequest, class Headers,
class WriteHandler>
typename async_completion<
WriteHandler, void(error_code)>::result_type
async_write(AsyncWriteStream& stream,
message_headers<isRequest, Headers> const& msg,
WriteHandler&& handler)
{
static_assert(is_AsyncWriteStream<AsyncWriteStream>::value,
"AsyncWriteStream requirements not met");
beast::async_completion<WriteHandler,
void(error_code)> completion(handler);
streambuf sb;
detail::write_firstline(sb, msg);
detail::write_fields(sb, msg.headers);
beast::write(sb, "\r\n");
detail::write_streambuf_op<AsyncWriteStream,
decltype(completion.handler)>{
completion.handler, stream, std::move(sb)};
return completion.result.get();
}
//------------------------------------------------------------------------------
namespace detail {
template<bool isRequest, class Body, class Headers>
struct write_preparation
{
@ -135,6 +290,7 @@ struct write_preparation
w.init(ec);
if(ec)
return;
write_firstline(sb, msg);
write_fields(sb, msg.headers);
beast::write(sb, "\r\n");
@ -462,8 +618,6 @@ public:
} // detail
//------------------------------------------------------------------------------
template<class SyncWriteStream,
bool isRequest, class Body, class Headers>
void
@ -599,6 +753,21 @@ async_write(AsyncWriteStream& stream,
return completion.result.get();
}
//------------------------------------------------------------------------------
template<bool isRequest, class Headers>
std::ostream&
operator<<(std::ostream& os,
message_headers<isRequest, Headers> const& msg)
{
beast::detail::sync_ostream oss{os};
error_code ec;
write(oss, msg, ec);
if(ec)
throw system_error{ec};
return os;
}
template<bool isRequest, class Body, class Headers>
std::ostream&
operator<<(std::ostream& os,

View File

@ -230,6 +230,43 @@ public:
return ss.str;
}
void
testAsyncWriteHeaders(yield_context do_yield)
{
{
message_headers<true, headers> m;
m.version = 11;
m.method = "GET";
m.url = "/";
m.headers.insert("User-Agent", "test");
error_code ec;
string_write_stream ss{ios_};
async_write(ss, m, do_yield[ec]);
if(BEAST_EXPECTS(! ec, ec.message()))
BEAST_EXPECT(ss.str ==
"GET / HTTP/1.1\r\n"
"User-Agent: test\r\n"
"\r\n");
}
{
message_headers<false, headers> m;
m.version = 10;
m.status = 200;
m.reason = "OK";
m.headers.insert("Server", "test");
m.headers.insert("Content-Length", "5");
error_code ec;
string_write_stream ss{ios_};
async_write(ss, m, do_yield[ec]);
if(BEAST_EXPECTS(! ec, ec.message()))
BEAST_EXPECT(ss.str ==
"HTTP/1.0 200 OK\r\n"
"Server: test\r\n"
"Content-Length: 5\r\n"
"\r\n");
}
}
void
testAsyncWrite(yield_context do_yield)
{
@ -242,7 +279,7 @@ public:
m.headers.insert("Content-Length", "5");
m.body = "*****";
error_code ec;
string_write_stream ss(ios_);
string_write_stream ss{ios_};
async_write(ss, m, do_yield[ec]);
if(BEAST_EXPECTS(! ec, ec.message()))
BEAST_EXPECT(ss.str ==
@ -599,17 +636,45 @@ public:
}
}
void testConvert()
void test_std_ostream()
{
// Conversion to std::string via operator<<
message<true, string_body, headers> m;
m.method = "GET";
m.url = "/";
m.version = 11;
m.headers.insert("User-Agent", "test");
m.body = "*";
prepare(m);
BEAST_EXPECT(boost::lexical_cast<std::string>(m) ==
"GET / HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 1\r\n\r\n*");
"GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n*");
BEAST_EXPECT(boost::lexical_cast<std::string>(m.base()) ==
"GET / HTTP/1.1\r\nUser-Agent: test\r\n\r\n");
// Cause exceptions in operator<<
{
std::stringstream ss;
ss.setstate(ss.rdstate() |
std::stringstream::failbit);
try
{
// message_headers
ss << m.base();
fail("", __FILE__, __LINE__);
}
catch(std::exception const&)
{
pass();
}
try
{
// message
ss << m;
fail("", __FILE__, __LINE__);
}
catch(std::exception const&)
{
pass();
}
}
}
void testOstream()
@ -637,12 +702,14 @@ public:
void run() override
{
yield_to(std::bind(&write_test::testAsyncWriteHeaders,
this, std::placeholders::_1));
yield_to(std::bind(&write_test::testAsyncWrite,
this, std::placeholders::_1));
yield_to(std::bind(&write_test::testFailures,
this, std::placeholders::_1));
testOutput();
testConvert();
test_std_ostream();
testOstream();
}
};