streaming JSON body is an example

This commit is contained in:
Klemens Morgenstern
2022-10-03 09:53:05 +08:00
committed by GitHub
parent bd69638e9d
commit 12c2d14ebc
8 changed files with 222 additions and 1 deletions

View File

@ -44,6 +44,10 @@ used to evaluate robustness. All asynchronous clients support timeouts.
[HTTP crawl (asynchronous)]
[[path_link example/http/client/crawl/http_crawl.cpp http_crawl.cpp]]
[]
][
[HTTP json_body (synchronous)]
[[path_link example/http/client/body/json_body.hpp example/http/client/body/json_client.cpp]]
]]
These WebSocket clients connect to a

View File

@ -11,6 +11,7 @@ add_subdirectory (async)
add_subdirectory (coro)
add_subdirectory (crawl)
add_subdirectory (sync)
add_subdirectory (json)
if (OPENSSL_FOUND)
add_subdirectory (async-ssl)

View File

@ -16,3 +16,5 @@ build-project sync ;
build-project async-ssl ;
build-project coro-ssl ;
build-project sync-ssl ;
build-project body ;

View File

@ -0,0 +1,23 @@
#
# Copyright (c) 2016-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)
#
# Official repository: https://github.com/boostorg/beast
#
GroupSources(include/boost/beast beast)
GroupSources(example/http/client/coro "/")
add_executable (http-client-json
${BOOST_BEAST_FILES}
Jamfile
json_client.cpp
)
target_link_libraries(http-client-json
lib-asio
lib-beast)
set_property(TARGET http-client-json PROPERTY FOLDER "example-json-client")

View File

@ -0,0 +1,14 @@
#
# Copyright (c) 2016-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)
#
# Official repository: https://github.com/boostorg/beast
#
exe json_client : json_client.cpp
:
<variant>coverage:<build>no
<variant>ubasan:<build>no
;

View File

@ -0,0 +1,115 @@
//
// Copyright (c) 2022 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)
//
// Official repository: https://github.com/boostorg/beast
//
//------------------------------------------------------------------------------
//
// Example: JSON body
//
//------------------------------------------------------------------------------
#ifndef BOOST_BEAST_EXAMPLE_JSON_BODY
#define BOOST_BEAST_EXAMPLE_JSON_BODY
#include <boost/json.hpp>
#include <boost/json/stream_parser.hpp>
#include <boost/json/monotonic_resource.hpp>
#include <boost/beast/http.hpp>
#include <boost/asio/buffer.hpp>
namespace json = boost::json;
struct json_body
{
using value_type = json::value;
struct writer
{
using const_buffers_type = boost::asio::const_buffer;
template<bool isRequest, class Fields>
writer(boost::beast::http::header<isRequest, Fields> const& h,
value_type const& body)
{
// The serializer holds a pointer to the value, so all we need to do is to reset it.
serializer.reset(&body);
}
void
init(boost::system::error_code& ec)
{
// The serializer always works, so no error can occur here.
ec = {};
}
boost::optional<std::pair<const_buffers_type, bool>>
get(boost::system::error_code& ec)
{
ec = {};
// We serialize as much as we can with the buffer. Often that'll suffice
const auto len = serializer.read(buffer, sizeof(buffer));
return std::make_pair(
boost::asio::const_buffer(len.data(), len.size()),
!serializer.done());
}
private:
json::serializer serializer;
// half of the probable networking buffer, let's leave some space for headers
char buffer[32768];
};
struct reader
{
template<bool isRequest, class Fields>
reader(boost::beast::http::header<isRequest, Fields>& h, value_type& body)
: body(body)
{
}
void
init(
boost::optional<std::uint64_t> const& content_length,
boost::system::error_code& ec)
{
// If we know the content-length, we can allocate a monotonic resource to increase the parsing speed.
// We're using it rather then a static_resource, so a consumer can modify the resulting value.
// It is also only assumption that the parsed json will be smaller than the serialize one,
// it might not always be the case.
if (content_length)
parser.reset(json::make_shared_resource<json::monotonic_resource>(*content_length));
ec = {};
}
template<class ConstBufferSequence>
std::size_t
put(ConstBufferSequence const& buffers, boost::system::error_code& ec)
{
ec = {};
// The parser just uses the `ec` to indicate errors, so we don't need to do anything.
return parser.write_some(
static_cast<const char*>(buffers.data()), buffers.size(), ec);
}
void
finish(boost::system::error_code& ec)
{
ec = {};
// We check manually if the json is complete.
if (parser.done())
body = parser.release();
else
ec = boost::json::error::incomplete;
}
private:
json::stream_parser parser;
value_type& body;
};
};
#endif

View File

@ -0,0 +1,61 @@
#include "json_body.hpp"
#include <boost/json/src.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <iostream>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
int main(int argc, char ** argv)
{
// Our test endpoint for testing the json
const auto host = "postman-echo.com";
const auto target = "/post";
// The io_context is required for all I/O
net::io_context ioc;
// These objects perform our I/O
tcp::resolver resolver(ioc);
beast::tcp_stream stream(ioc);
// Look up the domain name
auto const results = resolver.resolve(host, "80");
// Make the connection on the IP address we get from a lookup
stream.connect(results);
// Set up an HTTP GET request message
http::request<json_body> req{http::verb::post, target, 11};
req.set(http::field::host, host);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
req.set(http::field::content_type, "application/json");
req.body() = {{"type", "test"}, {"content", "pure awesomeness"}};
req.prepare_payload();
// Send the HTTP request to the remote host
http::write(stream, req);
// This buffer is used for reading and must be persisted
beast::flat_buffer buffer;
// Get the response
http::response<json_body> res;
// Receive the HTTP response
http::read(stream, buffer, res);
// Write the message to standard out
std::cout << res << std::endl;
// Gracefully close the socket
beast::error_code ec;
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
return 0;
}

View File

@ -82,7 +82,8 @@ git submodule update --init --depth 20 --jobs 4 \
libs/tuple \
libs/type_index \
libs/typeof \
libs/unordered
libs/unordered \
libs/json
echo Submodule update complete