mirror of
https://github.com/boostorg/beast.git
synced 2025-07-30 04:47:29 +02:00
streaming JSON body is an example
This commit is contained in:
committed by
GitHub
parent
bd69638e9d
commit
12c2d14ebc
@ -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
|
||||
|
@ -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)
|
||||
|
@ -16,3 +16,5 @@ build-project sync ;
|
||||
build-project async-ssl ;
|
||||
build-project coro-ssl ;
|
||||
build-project sync-ssl ;
|
||||
|
||||
build-project body ;
|
||||
|
23
example/http/client/body/CMakeLists.txt
Normal file
23
example/http/client/body/CMakeLists.txt
Normal 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")
|
14
example/http/client/body/Jamfile
Normal file
14
example/http/client/body/Jamfile
Normal 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
|
||||
;
|
115
example/http/client/body/json_body.hpp
Normal file
115
example/http/client/body/json_body.hpp
Normal 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
|
61
example/http/client/body/json_client.cpp
Normal file
61
example/http/client/body/json_client.cpp
Normal 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;
|
||||
}
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user