mirror of
https://github.com/boostorg/beast.git
synced 2025-07-31 13:27:33 +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)]
|
[HTTP crawl (asynchronous)]
|
||||||
[[path_link example/http/client/crawl/http_crawl.cpp http_crawl.cpp]]
|
[[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
|
These WebSocket clients connect to a
|
||||||
|
@@ -11,6 +11,7 @@ add_subdirectory (async)
|
|||||||
add_subdirectory (coro)
|
add_subdirectory (coro)
|
||||||
add_subdirectory (crawl)
|
add_subdirectory (crawl)
|
||||||
add_subdirectory (sync)
|
add_subdirectory (sync)
|
||||||
|
add_subdirectory (json)
|
||||||
|
|
||||||
if (OPENSSL_FOUND)
|
if (OPENSSL_FOUND)
|
||||||
add_subdirectory (async-ssl)
|
add_subdirectory (async-ssl)
|
||||||
|
@@ -16,3 +16,5 @@ build-project sync ;
|
|||||||
build-project async-ssl ;
|
build-project async-ssl ;
|
||||||
build-project coro-ssl ;
|
build-project coro-ssl ;
|
||||||
build-project sync-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/tuple \
|
||||||
libs/type_index \
|
libs/type_index \
|
||||||
libs/typeof \
|
libs/typeof \
|
||||||
libs/unordered
|
libs/unordered \
|
||||||
|
libs/json
|
||||||
|
|
||||||
echo Submodule update complete
|
echo Submodule update complete
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user