如何从 std::string 构造一个 boost::beast::http::message?
How to construct a boost::beast::http::message from std::string?
是否可以从 std::string
、std::string_view
或其他原始缓冲区构造一个 boost::beast::http::message
(特别是我必须构造一个 boost::beast::http::response<bb_http::string_body>
)?
也许有某种解析器?根据我在 Boost.Beast 样本中看到的情况,我们可以:
- 收到来自
boost::beast::read*
函数的响应。在这种情况下,第一个参数应该是 SyncReadStream
,它必须符合来自 boost/beast/core/type_traits.hpp
: 的 SyncReadStream
的合同
struct is_sync_read_stream<T, detail::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<boost::system::error_code&>()),
(void)0)>> : std::true_type {};
- 或者像
http::request<http::string_body> req{http::verb::get, target, version};
那样手工构造它
您可以手动调用解析器,例如使用这个简单的骨架功能:
http::response<http::string_body> do_parse(std::string_view input)
{
beast::error_code ec;
http::response_parser<http::string_body> p;
// read headers
auto buf = boost::asio::buffer(sample);
auto n = p.put(buf, ec);
assert(p.is_header_done());
// read body
if (!ec) {
buf += n;
n = p.put(buf, ec);
p.put_eof(ec);
}
if (ec)
throw boost::system::system_error(ec);
assert(p.is_done());
return p.release();
}
这假设输入是一个完整的请求。
现场演示
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <string_view>
#include <iostream>
#include <iomanip>
namespace beast = boost::beast;
namespace http = beast::http;
http::response<http::string_body> do_parse(std::string_view input)
{
beast::error_code ec;
http::response_parser<http::string_body> p;
// read headers
auto buf = boost::asio::buffer(input);
auto n = p.put(buf, ec);
assert(p.is_header_done());
// read body
if (!ec) {
buf += n;
n = p.put(buf, ec);
p.put_eof(ec);
}
if (ec)
throw boost::system::system_error(ec);
assert(p.is_done());
return p.release();
}
int main() {
auto res = do_parse(
"HTTP/1.1 200 OK\r\n"
"Date: Sun, 10 Oct 2010 23:26:07 GMT\r\n"
"Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g\r\n"
"Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT\r\n"
"ETag: 45b6-834-49130cc1182c0\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 12\r\n"
"Connection: close\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"Hello world!");
std::cout << res << '\n';
std::cout << "====== body:\n" << std::quoted(res.body()) << "\n";
}
版画
HTTP/1.1 200 OK
Date: Sun, 10 Oct 2010 23:26:07 GMT
Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g
Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT
ETag: 45b6-834-49130cc1182c0
Accept-Ranges: bytes
Content-Length: 12
Connection: close
Content-Type: text/html
Hello world!
====== body:
"Hello world!"
原来已经有一个示例片段可以从 std::istream 读取 boost::beast::message。
https://www.boost.org/doc/libs/1_66_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html
/** Read a message from a `std::istream`.
This function attempts to parse a complete HTTP/1 message from the stream.
@param is The `std::istream` to read from.
@param buffer The buffer to use.
@param msg The message to store the result.
@param ec Set to the error, if any occurred.
*/
template<
class Allocator,
bool isRequest,
class Body>
void
read_istream(
std::istream& is,
basic_flat_buffer<Allocator>& buffer,
message<isRequest, Body, fields>& msg,
error_code& ec)
{
// Create the message parser
//
// Arguments passed to the parser's constructor are
// forwarded to the message constructor. Here, we use
// a move construction in case the caller has constructed
// their message in a non-default way.
//
parser<isRequest, Body> p{std::move(msg)};
do
{
// Extract whatever characters are presently available in the istream
if(is.rdbuf()->in_avail() > 0)
{
// Get a mutable buffer sequence for writing
auto const b = buffer.prepare(
static_cast<std::size_t>(is.rdbuf()->in_avail()));
// Now get everything we can from the istream
buffer.commit(static_cast<std::size_t>(is.readsome(
reinterpret_cast<char*>(b.data()), b.size())));
}
else if(buffer.size() == 0)
{
// Our buffer is empty and we need more characters,
// see if we've reached the end of file on the istream
if(! is.eof())
{
// Get a mutable buffer sequence for writing
auto const b = buffer.prepare(1024);
// Try to get more from the istream. This might block.
is.read(reinterpret_cast<char*>(b.data()), b.size());
// If an error occurs on the istream then return it to the caller.
if(is.fail() && ! is.eof())
{
// We'll just re-use io_error since std::istream has no error_code interface.
ec = make_error_code(errc::io_error);
return;
}
// Commit the characters we got to the buffer.
buffer.commit(static_cast<std::size_t>(is.gcount()));
}
else
{
// Inform the parser that we've reached the end of the istream.
p.put_eof(ec);
if(ec)
return;
break;
}
}
// Write the data to the parser
auto const bytes_used = p.put(buffer.data(), ec);
// This error means that the parser needs additional octets.
if(ec == error::need_more)
ec = {};
if(ec)
return;
// Consume the buffer octets that were actually parsed.
buffer.consume(bytes_used);
}
while(! p.is_done());
// Transfer ownership of the message container in the parser to the caller.
msg = p.release();
}
是否可以从 std::string
、std::string_view
或其他原始缓冲区构造一个 boost::beast::http::message
(特别是我必须构造一个 boost::beast::http::response<bb_http::string_body>
)?
也许有某种解析器?根据我在 Boost.Beast 样本中看到的情况,我们可以:
- 收到来自
boost::beast::read*
函数的响应。在这种情况下,第一个参数应该是SyncReadStream
,它必须符合来自boost/beast/core/type_traits.hpp
: 的
SyncReadStream
的合同
struct is_sync_read_stream<T, detail::void_t<decltype(
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>()),
std::declval<std::size_t&>() = std::declval<T>().read_some(
std::declval<detail::MutableBufferSequence>(),
std::declval<boost::system::error_code&>()),
(void)0)>> : std::true_type {};
- 或者像
http::request<http::string_body> req{http::verb::get, target, version};
那样手工构造它
您可以手动调用解析器,例如使用这个简单的骨架功能:
http::response<http::string_body> do_parse(std::string_view input)
{
beast::error_code ec;
http::response_parser<http::string_body> p;
// read headers
auto buf = boost::asio::buffer(sample);
auto n = p.put(buf, ec);
assert(p.is_header_done());
// read body
if (!ec) {
buf += n;
n = p.put(buf, ec);
p.put_eof(ec);
}
if (ec)
throw boost::system::system_error(ec);
assert(p.is_done());
return p.release();
}
这假设输入是一个完整的请求。
现场演示
#include <boost/beast.hpp>
#include <boost/beast/http.hpp>
#include <string_view>
#include <iostream>
#include <iomanip>
namespace beast = boost::beast;
namespace http = beast::http;
http::response<http::string_body> do_parse(std::string_view input)
{
beast::error_code ec;
http::response_parser<http::string_body> p;
// read headers
auto buf = boost::asio::buffer(input);
auto n = p.put(buf, ec);
assert(p.is_header_done());
// read body
if (!ec) {
buf += n;
n = p.put(buf, ec);
p.put_eof(ec);
}
if (ec)
throw boost::system::system_error(ec);
assert(p.is_done());
return p.release();
}
int main() {
auto res = do_parse(
"HTTP/1.1 200 OK\r\n"
"Date: Sun, 10 Oct 2010 23:26:07 GMT\r\n"
"Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g\r\n"
"Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT\r\n"
"ETag: 45b6-834-49130cc1182c0\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 12\r\n"
"Connection: close\r\n"
"Content-Type: text/html\r\n"
"\r\n"
"Hello world!");
std::cout << res << '\n';
std::cout << "====== body:\n" << std::quoted(res.body()) << "\n";
}
版画
HTTP/1.1 200 OK
Date: Sun, 10 Oct 2010 23:26:07 GMT
Server: Apache/2.2.8 (Ubuntu) mod_ssl/2.2.8 OpenSSL/0.9.8g
Last-Modified: Sun, 26 Sep 2010 22:04:35 GMT
ETag: 45b6-834-49130cc1182c0
Accept-Ranges: bytes
Content-Length: 12
Connection: close
Content-Type: text/html
Hello world!
====== body:
"Hello world!"
原来已经有一个示例片段可以从 std::istream 读取 boost::beast::message。 https://www.boost.org/doc/libs/1_66_0/libs/beast/doc/html/beast/using_http/buffer_oriented_parsing.html
/** Read a message from a `std::istream`.
This function attempts to parse a complete HTTP/1 message from the stream.
@param is The `std::istream` to read from.
@param buffer The buffer to use.
@param msg The message to store the result.
@param ec Set to the error, if any occurred.
*/
template<
class Allocator,
bool isRequest,
class Body>
void
read_istream(
std::istream& is,
basic_flat_buffer<Allocator>& buffer,
message<isRequest, Body, fields>& msg,
error_code& ec)
{
// Create the message parser
//
// Arguments passed to the parser's constructor are
// forwarded to the message constructor. Here, we use
// a move construction in case the caller has constructed
// their message in a non-default way.
//
parser<isRequest, Body> p{std::move(msg)};
do
{
// Extract whatever characters are presently available in the istream
if(is.rdbuf()->in_avail() > 0)
{
// Get a mutable buffer sequence for writing
auto const b = buffer.prepare(
static_cast<std::size_t>(is.rdbuf()->in_avail()));
// Now get everything we can from the istream
buffer.commit(static_cast<std::size_t>(is.readsome(
reinterpret_cast<char*>(b.data()), b.size())));
}
else if(buffer.size() == 0)
{
// Our buffer is empty and we need more characters,
// see if we've reached the end of file on the istream
if(! is.eof())
{
// Get a mutable buffer sequence for writing
auto const b = buffer.prepare(1024);
// Try to get more from the istream. This might block.
is.read(reinterpret_cast<char*>(b.data()), b.size());
// If an error occurs on the istream then return it to the caller.
if(is.fail() && ! is.eof())
{
// We'll just re-use io_error since std::istream has no error_code interface.
ec = make_error_code(errc::io_error);
return;
}
// Commit the characters we got to the buffer.
buffer.commit(static_cast<std::size_t>(is.gcount()));
}
else
{
// Inform the parser that we've reached the end of the istream.
p.put_eof(ec);
if(ec)
return;
break;
}
}
// Write the data to the parser
auto const bytes_used = p.put(buffer.data(), ec);
// This error means that the parser needs additional octets.
if(ec == error::need_more)
ec = {};
if(ec)
return;
// Consume the buffer octets that were actually parsed.
buffer.consume(bytes_used);
}
while(! p.is_done());
// Transfer ownership of the message container in the parser to the caller.
msg = p.release();
}