'Boost.beast : how to return json response only

code : https://gist.github.com/Naseefabu/173c928603e564879683ccdf10d9d0f8

When i run this and print the response to the console:

Sending GET /api/v3/time HTTP/1.1
Host: api.binance.com
content-type: application/json
User-Agent: Boost.Beast/330


HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Content-Length: 28
Connection: keep-alive
Date: Fri, 13 May 2022 17:18:47 GMT
Server: nginx
x-mbx-uuid: 7e7465db-011a-4308-aa1e-6603d72c8c9a
x-mbx-used-weight: 1
x-mbx-used-weight-1m: 1
Strict-Transport-Security: max-age=31536000; includeSubdomains
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Security-Policy: default-src 'self'
X-Content-Security-Policy: default-src 'self'
X-WebKit-CSP: default-src 'self'
Cache-Control: no-cache, no-store, must-revalidate
Pragma: no-cache
Expires: 0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD, OPTIONS
X-Cache: Miss from cloudfront
Via: 1.1 dfccb338f8c0489ab09835ea7dbad1a8.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: MAA51-P1
X-Amz-Cf-Id: waTqtDHSDpk74QB7zkF5Ya0CdRVWuJuC-M4TZqSuMd2bfXawkq6o6g==

{"serverTime":1652462327804}
shutdown: stream truncated

what if i just want to get the json response : {"serverTime":1652462327804} ? and store in the json variable so i could use it for my needs, other informations is not that important for me, advance thanks!



Solution 1:[1]

 binapi::AsyncRest::httpClient* client;

That's extremely suspect, since the class is using enable_shared_from_this(). Pretty sure that should be

auto client =
    std::make_shared<binapi::AsyncRest::httpClient>(ioc.get_executor(), ctx);

Next up, I assume get_server_time is a static function. I don't see why it is a member of httpClient.

So adding all the missing code back in (using lots of experience):

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/url.hpp>
#include <boost/url/src.hpp> // for header-only
#include <fstream>
#include <iomanip>
#include <iostream>
namespace net   = boost::asio;
namespace beast = boost::beast;
namespace http  = boost::beast::http;
namespace ssl   = boost::asio::ssl;
using net::ip::tcp;

namespace binapi { namespace AsyncRest {

    // Report a failure
    void fail_http(beast::error_code ec, char const* what) {
        std::cerr << what << ": " << ec.message() << "\n";
    }

    struct httpClient : std::enable_shared_from_this<httpClient> {
        using executor = net::any_io_executor;
        using Stream   = beast::ssl_stream<beast::tcp_stream>;

        tcp::resolver                     resolver_;
        Stream                            stream_;
        beast::flat_buffer                buffer_;
        http::request<http::empty_body>   req_;
        http::response<http::string_body> res_;

        httpClient(executor ex, ssl::context& ctx);

        // Start the asynchronous operation
        void run(boost::url, http::verb);
        void on_resolve(beast::error_code, tcp::resolver::results_type);
        void on_connect(beast::error_code, tcp::resolver::results_type::endpoint_type);
        void on_handshake(beast::error_code);
        void on_write(beast::error_code, size_t bytes_transferred);
        void on_read(beast::error_code, size_t bytes_transferred);
        void on_shutdown(beast::error_code);
    };

    httpClient::httpClient(executor ex, ssl::context& ctx)
        : resolver_(ex)
        , stream_(ex, ctx) {}

    // Start the asynchronous operation
    void httpClient::run(boost::url url, http::verb action) {

        std::string const host(url.host());
        std::string const service = url.has_port() //
            ? url.port()
            : (url.scheme_id() == boost::urls::scheme::https) //
                ? "https"
                : "http";
        url.remove_origin(); // becomes req_.target()

        // Set SNI Hostname (many hosts need this to handshake
        // successfully)
        if (!SSL_set_tlsext_host_name(stream_.native_handle(), host.c_str())) {
            beast::error_code ec{static_cast<int>(::ERR_get_error()),
                                 net::error::get_ssl_category()};
            std::cerr << ec.message() << "\n";
            return;
        }

        // Set up an HTTP GET/POST/DELETE/PUT request message
        // req_.version(version);
        req_.method(action);
        req_.target(url.c_str());
        req_.set(http::field::host, host);
        req_.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
        req_.prepare_payload(); // make HTTP 1.1 compliant

        // Look up the domain name
        resolver_.async_resolve(
            host, service,
            beast::bind_front_handler(&httpClient::on_resolve, shared_from_this()));
    }

    void httpClient::on_resolve(beast::error_code           ec,
                                tcp::resolver::results_type results) {
        if (ec)
            return fail_http(ec, "resolve");

        // Set a timeout on the operation
        beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

        // Make the connection on the IP address we get from a lookup
        beast::get_lowest_layer(stream_).async_connect(
            results,
            beast::bind_front_handler(&httpClient::on_connect, shared_from_this()));
    }

    void httpClient::on_connect(beast::error_code ec,
                                tcp::resolver::results_type::endpoint_type) {
        if (ec)
            return fail_http(ec, "connect");

        // Perform the SSL handshake
        stream_.async_handshake(
            ssl::stream_base::client,
            beast::bind_front_handler(&httpClient::on_handshake, shared_from_this()));
    }

    void httpClient::on_handshake(beast::error_code ec) {
        if (ec)
            return fail_http(ec, "handshake");

        // Set a timeout on the operation
        beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

        // Send the HTTP request to the remote host
        std::cout << "Sending " << req_ << std::endl;
        http::async_write(
            stream_, req_,
            beast::bind_front_handler(&httpClient::on_write, shared_from_this()));
    }

    void httpClient::on_write(beast::error_code ec, size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        if (ec)
            return fail_http(ec, "write");

        // Receive the HTTP response
        http::async_read(
            stream_, buffer_, res_,
            beast::bind_front_handler(&httpClient::on_read, shared_from_this()));
    }

    void httpClient::on_read(beast::error_code ec, size_t bytes_transferred) {
        boost::ignore_unused(bytes_transferred);

        if (ec)
            return fail_http(ec, "read");

        // Write the message to standard out
        std::cout << res_ << std::endl;

        // Set a timeout on the operation
        beast::get_lowest_layer(stream_).expires_after(std::chrono::seconds(30));

        // Gracefully close the stream
        stream_.async_shutdown(
            beast::bind_front_handler(&httpClient::on_shutdown, shared_from_this()));
    }

    void httpClient::on_shutdown(beast::error_code ec) {
        if (ec == net::error::eof) {
            ec = {};
        }
        if (ec)
            return fail_http(ec, "shutdown");
    }

    static void get_server_time(net::io_context& ioc, ssl::context& ctx) {
        static boost::url_view const uri{"https://api.binance.com/api/v3/time"};

        std::make_shared<httpClient>(net::make_strand(ioc), ctx)
            ->run(uri, http::verb::get);
    }
}} // namespace binapi::AsyncRest

int main() {
    net::io_context                ioc;

    // The SSL context is required, and holds certificates
    ssl::context ctx{ssl::context::tlsv12_client};

    // Verify the remote server's certificate
    ctx.set_verify_mode(ssl::verify_peer);
    ctx.set_default_verify_paths();

    binapi::AsyncRest::get_server_time(ioc, ctx);

    ioc.run();
}

Now we know that res_ is beast::http::response<beast::http::string_body>. So, if you only want to print the body, print that:

    std::cout << res_.body() << std::endl;

Prints

Sending GET /api/v3/time HTTP/1.1
Host: api.binance.com
User-Agent: Boost.Beast/330


{"serverTime":1652476115413}
shutdown: stream truncated

To only print the time:

    static constexpr boost::gregorian::date s_epoch{1970, 1, 1};

    auto epoch_seconds = json::parse(res_.body()).at("serverTime").as_int64();
    ptime serverTime(s_epoch, boost::posix_time::milliseconds(epoch_seconds));

    std::cout << serverTime << std::endl;

Prints

2022-May-13 21:38:55.982000

Summary/Notes

What it really looks like you're after is a function like

 ptime server_time();

Or

 void async_server_time(auto completionToken);

And you'd probably want to share the client class instance instead of reconnecting for each call.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 sehe