View in context in the libbitcoin Slack #general mirror.
boost::beast::http bodies):
namespace libbitcoin {
namespace network {
namespace http {
using json_body = json::body<json::parser, json::serializer>;
using empty_body = boost::beast::http::empty_body;
using data_body = boost::beast::http::vector_body<uint8_t>;
using string_body = boost::beast::http::string_body;
using file_body b= oost::beast::http::file_body;
using json_value = json_body::value_type;
using data_value = data_body::value_type;
using file_value = file_body::value_type;
using string_value = string_body::value_type;
using empty_value = empty_body::value_type;
using json_reader = json_body::reader;
using data_reader = data_body::reader;
using file_reader = file_body::reader;
using string_reader = string_body::reader;
using empty_reader = empty_body::reader;
using json_writer = json_body::writer;
using data_writer = data_body::writer;
using file_writer = file_body::writer;
using string_writer = string_body::writer;
using empty_writer = empty_body::writer;
using value_variant = std::variant
<
empty_value,
json_value,
data_value,
file_value,
string_value
>;
using reader_variant = std::variant
<
empty_reader,
json_reader,
data_reader,
file_reader,
string_reader
>;
using writer_variant = std::variant
<
empty_writer,
json_writer,
data_writer,
file_writer,
string_writer
>;
struct variant_payload
{
mutable std::optional<value_variant> inner{};
};
/// boost::beast::http body template for all message types.
/// This encapsulates a variant of supported body types, selects a type upon
/// reader or writer construction, and then passes all calls through to it.
struct body
{
using value_type = variant_payload;
class reader
{
public:
using buffer_type = asio::const_buffer;
template <bool IsRequest, class Fields>
inline explicit reader(header<IsRequest, Fields>& header,
value_type& payload) NOEXCEPT;
inline void init(const length_type& length, error_code& ec) NOEXCEPT;
inline size_t put(const buffer_type& buffer, error_code& ec) NOEXCEPT;
inline void finish(error_code& ec) NOEXCEPT;
private:
reader_variant reader_;
};
class writer
{
public:
using const_buffers_type = asio::const_buffer;
using out_buffer = get_buffer<const_buffers_type>;
template <bool IsRequest, class Fields>
inline explicit writer(header<IsRequest, Fields>& header,
const value_type& payload) NOEXCEPT;
inline void init(error_code& ec) NOEXCEPT;
inline out_buffer get(error_code& ec) NOEXCEPT;
private:
writer_variant writer_;
};
};
} // namespace http
} // namespace network
} // namespace libbitcoinnamespace libbitcoin {
namespace network {
namespace http {
inline bool is_attachment(auto& head) NOEXCEPT
{
const auto& disposition = head[field::content_disposition];
const auto content = system::ascii_to_lower(disposition);
return content.find("filename=") != std::string::npos;
}
// reader
// ----------------------------------------------------------------------------
template <class Body>
inline reader_variant reader_from_body(auto& head,
variant_payload& pay) NOEXCEPT
{
BC_ASSERT(pay.inner.has_value());
using reader = typename Body::reader;
using value = typename Body::value_type;
return reader_variant{ std::in_place_type<reader>, head,
std::get<value>(pay.inner.value()) };
}
/// Select reader based on content-type header.
inline reader_variant to_reader(auto& head, variant_payload& pay) NOEXCEPT
{
switch (content_mime_type(head[field::content_type]))
{
case mime_type::application_json:
{
pay.inner = json_value{};
return reader_from_body<json_body>(head, pay);
}
case mime_type::text_plain:
{
pay.inner = string_value{};
return reader_from_body<string_body>(head, pay);
}
case mime_type::application_octet:
{
if (is_attachment(head))
{
pay.inner = file_value{};
return reader_from_body<file_body>(head, pay);
}
else
{
pay.inner = data_value{};
return reader_from_body<data_body>(head, pay);
}
}
default:
{
pay.inner = empty_value{};
return reader_from_body<empty_body>(head, pay);
}
}
}
template <bool IsRequest, class Fields>
inline body::reader::reader(header<IsRequest, Fields>& header,
value_type& payload) NOEXCEPT
: reader_{ to_reader(header, payload) }
{
}
inline void body::reader::init(const length_type& length,
error_code& ec) NOEXCEPT
{
std::visit(overload
{
[&](auto& read) NOEXCEPT
{
try
{
read.init(length, ec);
}
catch (...)
{
ec = boost::system::errc::io_error;
}
}
}, reader_);
}
inline size_t body::reader::put(const buffer_type& buffer,
error_code& ec) NOEXCEPT
{
return std::visit(overload
{
[&](auto& read) NOEXCEPT
{
try
{
return read.put(buffer, ec);
}
catch (...)
{
ec = boost::system::errc::io_error;
return size_t{};
}
}
}, reader_);
}
inline void body::reader::finish(error_code& ec) NOEXCEPT
{
return std::visit(overload
{
[&](auto& read) NOEXCEPT
{
try
{
return read.finish(ec);
}
catch (...)
{
ec = boost::system::errc::io_error;
}
}
}, reader_);
}
// writer
// ----------------------------------------------------------------------------
template <class Body>
inline writer_variant writer_from_body(auto& head,
const variant_payload& pay) NOEXCEPT
{
BC_ASSERT(pay.inner.has_value());
using writer = typename Body::writer;
using value = typename Body::value_type;
return writer_variant{ std::in_place_type<writer>, head,
std::get<value>(pay.inner.value()) };
}
/// Create writer matching the caller-defined body.inner (variant) type.
inline writer_variant to_writer(auto& head,
const variant_payload& pay) NOEXCEPT
{
// Caller should have set inner, otherwise set it to empty (it's mutable).
if (!pay.inner.has_value())
pay.inner = empty_value{};
return std::visit(overload
{
[&](const json_value&) NOEXCEPT
{
return writer_from_body<json_body>(head, pay);
},
[&](const string_value&) NOEXCEPT
{
return writer_from_body<string_body>(head, pay);
},
[&](const data_value&) NOEXCEPT
{
return writer_from_body<data_body>(head, pay);
},
[&](const file_value&) NOEXCEPT
{
return writer_from_body<file_body>(head, pay);
},
[&](const empty_value&) NOEXCEPT
{
return writer_from_body<empty_body>(head, pay);
}
}, pay.inner.value());
}
template <bool IsRequest, class Fields>
inline body::writer::writer(header<IsRequest, Fields>& header,
const value_type& payload) NOEXCEPT
: writer_{ to_writer(header, payload) }
{
}
inline void body::writer::init(error_code& ec) NOEXCEPT
{
return std::visit(overload
{
[&] (auto& write) NOEXCEPT
{
try
{
write.init(ec);
}
catch (...)
{
ec = boost::system::errc::io_error;
}
}
}, writer_);
}
inline body::writer::out_buffer body::writer::get(error_code& ec) NOEXCEPT
{
return std::visit(overload
{
[&](auto& write) NOEXCEPT
{
try
{
return write.get(ec);
}
catch (...)
{
ec = boost::system::errc::io_error;
return out_buffer{};
}
}
}, writer_);
}
} // namespace http
} // namespace network
} // namespace libbitcoin