mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 05:17:26 +02:00
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:
@ -3,6 +3,7 @@
|
|||||||
HTTP
|
HTTP
|
||||||
|
|
||||||
* Make chunk_encode public
|
* Make chunk_encode public
|
||||||
|
* Add write, async_write, operator<< for message_headers
|
||||||
|
|
||||||
WebSocket
|
WebSocket
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@
|
|||||||
<member><link linkend="beast.ref.http__swap">swap</link></member>
|
<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__with_body">with_body</link></member>
|
||||||
<member><link linkend="beast.ref.http__write">write</link></member>
|
<member><link linkend="beast.ref.http__write">write</link></member>
|
||||||
|
<member><link linkend="beast.ref.http__operator_ls_">operator<<</link></member>
|
||||||
</simplelist>
|
</simplelist>
|
||||||
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
<bridgehead renderas="sect3">Type Traits</bridgehead>
|
||||||
<simplelist type="vert" columns="1">
|
<simplelist type="vert" columns="1">
|
||||||
|
@ -195,6 +195,18 @@
|
|||||||
select="concat(substring-before($name, '::'), '__', substring-after($name, '::'))"/>
|
select="concat(substring-before($name, '::'), '__', substring-after($name, '::'))"/>
|
||||||
</xsl:call-template>
|
</xsl:call-template>
|
||||||
</xsl:when>
|
</xsl:when>
|
||||||
|
<xsl:when test="substring($name, string-length($name) - 1) = '<<'">
|
||||||
|
<xsl:call-template name="make-id">
|
||||||
|
<xsl:with-param name="name"
|
||||||
|
select="concat(substring-before($name, '<<'), '_ls_')"/>
|
||||||
|
</xsl:call-template>
|
||||||
|
</xsl:when>
|
||||||
|
<xsl:when test="substring($name, string-length($name) - 1) = '>>'">
|
||||||
|
<xsl:call-template name="make-id">
|
||||||
|
<xsl:with-param name="name"
|
||||||
|
select="concat(substring-before($name, '>>'), '_rs_')"/>
|
||||||
|
</xsl:call-template>
|
||||||
|
</xsl:when>
|
||||||
<xsl:when test="contains($name, '=')">
|
<xsl:when test="contains($name, '=')">
|
||||||
<xsl:call-template name="make-id">
|
<xsl:call-template name="make-id">
|
||||||
<xsl:with-param name="name"
|
<xsl:with-param name="name"
|
||||||
@ -1698,6 +1710,14 @@
|
|||||||
</xsl:choose>
|
</xsl:choose>
|
||||||
<xsl:text>```
</xsl:text>
|
<xsl:text>```
</xsl:text>
|
||||||
<xsl:for-each select="../memberdef[name = $unqualified-name]">
|
<xsl:for-each select="../memberdef[name = $unqualified-name]">
|
||||||
|
<xsl:if test="position() > 1">
|
||||||
|
<xsl:text>
</xsl:text>
|
||||||
|
<xsl:if test=" not(briefdescription = preceding-sibling::*/briefdescription)">
|
||||||
|
<xsl:text>```
</xsl:text>
|
||||||
|
<xsl:apply-templates select="briefdescription" mode="markup"/>
|
||||||
|
<xsl:text>```
</xsl:text>
|
||||||
|
</xsl:if>
|
||||||
|
</xsl:if>
|
||||||
<xsl:variable name="stripped-type">
|
<xsl:variable name="stripped-type">
|
||||||
<xsl:call-template name="cleanup-type">
|
<xsl:call-template name="cleanup-type">
|
||||||
<xsl:with-param name="name" select="type"/>
|
<xsl:with-param name="name" select="type"/>
|
||||||
|
@ -32,11 +32,12 @@ namespace http {
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<class DynamicBuffer, class Body, class Headers>
|
template<class DynamicBuffer, class Headers>
|
||||||
void
|
void
|
||||||
write_firstline(DynamicBuffer& dynabuf,
|
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, msg.method);
|
||||||
write(dynabuf, " ");
|
write(dynabuf, " ");
|
||||||
write(dynabuf, msg.url);
|
write(dynabuf, msg.url);
|
||||||
@ -48,23 +49,15 @@ write_firstline(DynamicBuffer& dynabuf,
|
|||||||
case 11:
|
case 11:
|
||||||
write(dynabuf, " HTTP/1.1\r\n");
|
write(dynabuf, " HTTP/1.1\r\n");
|
||||||
break;
|
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
|
void
|
||||||
write_firstline(DynamicBuffer& dynabuf,
|
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)
|
switch(msg.version)
|
||||||
{
|
{
|
||||||
case 10:
|
case 10:
|
||||||
@ -73,15 +66,6 @@ write_firstline(DynamicBuffer& dynabuf,
|
|||||||
case 11:
|
case 11:
|
||||||
write(dynabuf, "HTTP/1.1 ");
|
write(dynabuf, "HTTP/1.1 ");
|
||||||
break;
|
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, msg.status);
|
||||||
write(dynabuf, " ");
|
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>
|
template<bool isRequest, class Body, class Headers>
|
||||||
struct write_preparation
|
struct write_preparation
|
||||||
{
|
{
|
||||||
@ -135,6 +290,7 @@ struct write_preparation
|
|||||||
w.init(ec);
|
w.init(ec);
|
||||||
if(ec)
|
if(ec)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
write_firstline(sb, msg);
|
write_firstline(sb, msg);
|
||||||
write_fields(sb, msg.headers);
|
write_fields(sb, msg.headers);
|
||||||
beast::write(sb, "\r\n");
|
beast::write(sb, "\r\n");
|
||||||
@ -462,8 +618,6 @@ public:
|
|||||||
|
|
||||||
} // detail
|
} // detail
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class SyncWriteStream,
|
template<class SyncWriteStream,
|
||||||
bool isRequest, class Body, class Headers>
|
bool isRequest, class Body, class Headers>
|
||||||
void
|
void
|
||||||
@ -599,6 +753,21 @@ async_write(AsyncWriteStream& stream,
|
|||||||
return completion.result.get();
|
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>
|
template<bool isRequest, class Body, class Headers>
|
||||||
std::ostream&
|
std::ostream&
|
||||||
operator<<(std::ostream& os,
|
operator<<(std::ostream& os,
|
||||||
|
@ -230,6 +230,43 @@ public:
|
|||||||
return ss.str;
|
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
|
void
|
||||||
testAsyncWrite(yield_context do_yield)
|
testAsyncWrite(yield_context do_yield)
|
||||||
{
|
{
|
||||||
@ -242,7 +279,7 @@ public:
|
|||||||
m.headers.insert("Content-Length", "5");
|
m.headers.insert("Content-Length", "5");
|
||||||
m.body = "*****";
|
m.body = "*****";
|
||||||
error_code ec;
|
error_code ec;
|
||||||
string_write_stream ss(ios_);
|
string_write_stream ss{ios_};
|
||||||
async_write(ss, m, do_yield[ec]);
|
async_write(ss, m, do_yield[ec]);
|
||||||
if(BEAST_EXPECTS(! ec, ec.message()))
|
if(BEAST_EXPECTS(! ec, ec.message()))
|
||||||
BEAST_EXPECT(ss.str ==
|
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;
|
message<true, string_body, headers> m;
|
||||||
m.method = "GET";
|
m.method = "GET";
|
||||||
m.url = "/";
|
m.url = "/";
|
||||||
m.version = 11;
|
m.version = 11;
|
||||||
m.headers.insert("User-Agent", "test");
|
m.headers.insert("User-Agent", "test");
|
||||||
m.body = "*";
|
m.body = "*";
|
||||||
prepare(m);
|
|
||||||
BEAST_EXPECT(boost::lexical_cast<std::string>(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()
|
void testOstream()
|
||||||
@ -637,12 +702,14 @@ public:
|
|||||||
|
|
||||||
void run() override
|
void run() override
|
||||||
{
|
{
|
||||||
|
yield_to(std::bind(&write_test::testAsyncWriteHeaders,
|
||||||
|
this, std::placeholders::_1));
|
||||||
yield_to(std::bind(&write_test::testAsyncWrite,
|
yield_to(std::bind(&write_test::testAsyncWrite,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
yield_to(std::bind(&write_test::testFailures,
|
yield_to(std::bind(&write_test::testFailures,
|
||||||
this, std::placeholders::_1));
|
this, std::placeholders::_1));
|
||||||
testOutput();
|
testOutput();
|
||||||
testConvert();
|
test_std_ostream();
|
||||||
testOstream();
|
testOstream();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user