Boost

How to get boost::beast HTTP response status code

// Read & parse the response
beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(stream, buffer, res);

// Print status code, e.g. 200
cout << res.result_int() << endl;

 

Posted by Uli Köhler in Boost, C/C++

How to iterate boost::beast HTTP response headers

// Read & parse the response
beast::flat_buffer buffer;
http::response<http::dynamic_body> res;
http::read(stream, buffer, res);

// Iterate response headers
for (auto it = res.begin(); it != res.end(); ++it) {
    std::cout << it->name_string() << ": " << it->value() << "\n";
}

// ... or get one specific header
cout << res["Content-Length"] << endl;

 

Posted by Uli Köhler in Boost, C/C++

How to enable TLS SNI using boost::beast

First include the required header:

#include <openssl/ssl.h>

Once you have initialized the boost::beast::ssl_stream, add the following code (with host being a std::string containing the hostname to connect to such as api.ipify.org):

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()};
    throw beast::system_error{ec};
}

Original source: boost::beast official HTTPS client example

Posted by Uli Köhler in Boost, C/C++

Minimal boost::beast HTTPS client JSON example

This example uses boost::beast to make a HTTPS request to ipify.org and parses the response using boost::json.

#include <iostream>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/version.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/json.hpp>
#include <openssl/ssl.h>

namespace beast = boost::beast;
namespace http = beast::http;
namespace net = boost::asio;
namespace ip = boost::asio::ip;
namespace ssl = boost::asio::ssl;
namespace json = boost::json;
using tcp = boost::asio::ip::tcp;

int main()
{
    std::string host = "api64.ipify.org";

    // Initialize IO context
    net::io_context ioc;
    ssl::context ctx(ssl::context::tlsv13_client);
    ctx.set_default_verify_paths();

    // Set up an SSL context
    beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
    stream.set_verify_mode(ssl::verify_none);
    stream.set_verify_callback([](bool preverified, ssl::verify_context& ctx) {
        return true; // Accept any certificate
    });
    // Enable SNI
    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()};
        throw beast::system_error{ec};
    }
    // Connect to the HTTPS server
    ip::tcp::resolver resolver(ioc);
    get_lowest_layer(stream).connect(resolver.resolve({host, "https"}));
    get_lowest_layer(stream).expires_after(std::chrono::seconds(30));

    // Construct request
    http::request<http::empty_body> req{http::verb::get, "/?format=json" , 11};
    req.set(http::field::host, host);
    req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);

    // Send the request
    stream.handshake(ssl::stream_base::client);
    http::write(stream, req);

    // Receive the response
    beast::flat_buffer buffer;
    http::response<http::dynamic_body> res;
    http::read(stream, buffer, res);
    
    // Parse the JSON response
    json::error_code err;
    json::value j = json::parse(buffers_to_string(res.body().data()), err);

    std::cout << "IP address: " << j.at("ip").as_string() << std::endl;

    if (err) {
        std::cerr << "Error parsing JSON: " << err.message() << std::endl;
    }

    // Cleanup
    beast::error_code ec;
    stream.shutdown(ec);

    if (ec == net::error::eof) {
        ec = {};
    }
    if (ec) {
        throw beast::system_error{ec};
    }

    return 0;
}

Compile using

g++ -g -o http-example http-example.cpp -lcrypto -lssl -lboost_json

Example output:

IP address: "2a01:c22:6f8c:ef00:f0bb:a8a8:9c00:ff49"

 

Posted by Uli Köhler in Boost, C/C++

How to fix boost::beast handshake: no protocols available (SSL routines)

Problem:

While running your boost::beast application, you see the following exception:

terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  handshake: no protocols available (SSL routines) [asio.ssl:167772351]

Solution:

You are connecting using a SSL or TLS version the server does not support.

The TLS/SSL version is selected while initializing the boost::asio::ssl::context:

namespace ssl = boost::asio::ssl;

ssl::context ctx(ssl::context::tlsv11_client);

In this case, we’re using the outdated TLSv1.1 protocol.

Change the line to

ssl::context ctx(ssl::context::tlsv13_client);

to use TLSv1.3 instead. Keep in mind that some older TLS/SSL versions are considered to be insecure.

Posted by Uli Köhler in Boost, C/C++

How to link boost::beast HTTP client library

boost::beast is a header-only library. While boost::beast heavily depends on boost::asio as a networking library, asio is also a header-only library.

Therefore, typically, you don’t need to link boost::beast at all.

The only exception is if you are using SSL/TLS functions for HTTPS connections, in which case you need to link the OpenSSL library which provides the SSL/TLS layer. In that case, link it using

-lcrypto -lssl

 

Posted by Uli Köhler in Boost, C/C++

Cross-compiling boost for OpenWRT

This post showcases how to cross-compile boost for OpenWRT. We’ll use the Teltonika RUTX10 SDK which is already pre-configured with the relevant OpenWRT settings for that router.

The prerequisite for this is that you have:

We assume the SDK is located in ~/rutos-ipq40xx-rutx-gpl.

Compiling boost

First, you need to consider that many boost libraries are header-only libraries. For those libraries, you don’t need to compile boost, but it might still be easier to prepare a complete boost folder for installation.

First, download boost and unpack it. Now run

./bootstrap.sh

to compile bjam.

 

Now, open project-config.jam inside the boost folder and add the following line at the top of the file:

using gcc : openwrt : arm-openwrt-linux-muslgnueabi-g++ ;

Now, inside the boost folder, run the following bash script:

export STAGING_DIR=~/rutos-ipq40xx-rutx-gpl/staging_dir
export TOOLCHAIN=${STAGING_DIR}/toolchain-arm_cortex-a7+neon-vfpv4_gcc-8.4.0_musl_eabi
export CC=${TOOLCHAIN}/bin/arm-openwrt-linux-muslgnueabi-gcc
export CPP=${TOOLCHAIN}/bin/arm-openwrt-linux-muslgnueabi-g++
export PATH=$PATH:${TOOLCHAIN}/bin/

# Insert at the top of project-config.jam
# using gcc : openwrt : arm-openwrt-linux-muslgnueabi-g++ ;

rm -rf ~/boost_rutx10
mkdir -p ~/boost_rutx10/
./b2 --prefix=$HOME/boost_rutx10 install toolset=gcc-openwrt -sNO_COMPRESSION=1

Now, boost is installed in ~/boost_rutx10, which has include and lib subdirectories.

Posted by Uli Köhler in Boost, OpenWRT

How to debug boost::beast with mitmproxy

mitmproxy is a valuable tool to debug HTTPS request made using boost::beast.

In order to use it, remember that mitmproxy listents to localhost on port 8080 and forwards requests based on the HTTP Host header.

First, you need to disable SSL certificate verification:

beast::ssl_stream<beast::tcp_stream> stream(ioc, ctx);
stream.set_verify_mode(ssl::verify_none);

Now, instead of connecting to an IP address based on DNS results such as

ip::tcp::resolver resolver(ioc);
get_lowest_layer(stream).connect(resolver.resolve({endpoint, "https"}));

directly connect to the 127.0.0.1:8080 endpoint:

boost::asio::ip::tcp::endpoint ep(
    boost::asio::ip::address::from_string("127.0.0.1"),
    8080
);
get_lowest_layer(stream).connect(ep);

Besides that, you can do anything as usual.

Posted by Uli Köhler in Boost, C/C++

How to initialize boost::asio::tcp::endpoint from IPv4 address string

This code will initialize a boost::asio::tcp::endpoint from a IPv4 address string ("127.0.0.1" as an exmple) and a port number (443 in this example)

boost::asio::ip::tcp::endpoint ep(
    boost::asio::ip::address::from_string("127.0.0.1"),
    443
);

 

Posted by Uli Köhler in Boost, C/C++

How to fix boost::beast system_error uninitialized (SSL routines)

Problem:

While running your boost::beast https client you see the following error message:

terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  uninitialized (SSL routines) [asio.ssl:167772436]

Solution:

You forgot to handshake before writing the HTTP request:

stream.handshake(ssl::stream_base::client);

 

Posted by Uli Köhler in Boost, C/C++

How to iterate JSON array in C++ using Boost.JSON

You can use a standard for iteration loon on j.as_array() in order to iterate all values in the given JSON array:

for (auto& value : json.as_array()) {
    std::cout << value.as_int64() << std::endl;
}

Full example:

#include <boost/json.hpp>
#include <iostream>

int main()
{
    const char* json_str = R"([1, 2, 3, 4, 5])";
    boost::json::value j = boost::json::parse(json_str);

    if (j.is_array()) {
        for (auto& value : j.as_array()) {
            std::cout << value.as_int64() << std::endl;
        }
    }

    return 0;
}

Compile using

g++ -o json json.cpp -lboost_json

This will print

1
2
3
4
5

 

Posted by Uli Köhler in Boost, C/C++

How to copy boost::urls::url in C++ (Boost.Url)

You can copy a boost::urls::url by using it copy constructor:

boost::urls::url url1 = boost::urls::url::parse("https://example.com/path1");
boost::urls::url url2(url1); // Copy url1 to url2 using the copy constructor

 

Posted by Uli Köhler in Boost, C/C++

How to access JSON nested value in C++ using Boost.JSON

#include <boost/json.hpp>

boost::json::value j = /* ... */;
// Access nested value with key "value"
j.at("value").as_string();

Full example:

#include <boost/json.hpp>
#include <iostream>

int main() {
    // Hardcoded JSON string
    const std::string json_str = R"(
        {
            "key": "1234",
            "value": "Hello, world!"
        }
    )";

    // Parse the JSON string
    boost::json::value j = boost::json::parse(json_str);

    // Access the "value" property and print it to stdout
    std::string value = j.at("value").as_string().c_str();
    std::cout << value << std::endl; // Prints "Hello, world!"

    return 0;
}

Compile using

g++ -o json json.cpp -lboost_json
Posted by Uli Köhler in Boost, C/C++

How to parse JSON from std::string in C++ using Boost.JSON

#include <boost/json.hpp>

boost::json::value j = boost::json::parse(json_str);

Full example:

#include <boost/json.hpp>
#include <iostream>

int main() {
    // Hardcoded JSON string
    const std::string json_str = R"(
        {
            "key": "1234",
            "value": "Hello, world!"
        }
    )";

    // Parse the JSON string
    boost::json::value j = boost::json::parse(json_str);

    // Access the "value" property and print it to stdout
    std::string value = j.at("value").as_string().c_str();
    std::cout << value << std::endl; // Prints "Hello, world!"

    return 0;
}

Compile using

g++ -o json json.cpp -lboost_json
Posted by Uli Köhler in Boost, C/C++

How to encode URL query parameters in C++ using Boost.URL

#include <iostream>
#include <boost/url.hpp>

using namespace boost::urls;

int main() {
    std::string username = "myusername";
    std::string password = "mypassword";
    url_view base_url = "https://example.com/api/login";

    url encoded_url(base_url);
    encoded_url.params().set("user name", username); // With space to test proper encoding
    encoded_url.params().set("password", password);

    // Prints "https://example.com/api/login?username=my%20user%20name&password=mypassword"
    std::cout << "Encoded URL: " << encoded_url << std::endl;

    return 0;
}

Compile using

g++ -o urlhost urlhost.cpp -lboost_url

 

Posted by Uli Köhler in Boost, C/C++

How to parse host from URL in C++ using Boost::URL

If you have an URL such as:

std::string myURL = "https://subdomain.example.com/api/test";

you can parse the hostname from it using Boost::URL (you need at least Boost 1.81.0, previous versions of boost don’t have Boost.URL) using

boost::urls::url_view url(myURL);
std::string host = url.host();

std::cout << host << std::endl; // Prints "subdomain.example.com"

Full example:

#include <string>
#include <iostream>

#include <boost/url.hpp>

int main() {
    std::string myURL = "https://subdomain.example.com/api/test";

    boost::urls::url_view url(myURL);
    std::string host = url.host();

    std::cout << host << std::endl; // Prints "subdomain.example.com"
}

Compile using

g++ -o urlhost urlhost.cpp -lboost_url

 

Posted by Uli Köhler in Boost, C/C++

Minimal boost::asio::serial_port read (read_some) example

The following example shows how to initialize a boost::asio serial_port , set its baud rate, parity & stop bits and then read data from it in a loop, printing the data to stdout as-is.

#include <boost/asio.hpp>
#include <iostream>

#define BUFSIZE 256

int main() {
    boost::asio::io_service io;
    // Open serial port
    boost::asio::serial_port serial(io, "/dev/ttyUSB0");

    // Configure basic serial port parameters: 115.2kBaud, 8N1
    serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
    serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
    serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

    // Read data in a loop and copy to stdout
    while(true) {
        char data[BUFSIZE];
        size_t n = serial.read_some(boost::asio::buffer(data, BUFSIZE));
        // Write data to stdout
        std::cout.write(data, n);
    }
}

For a more complete example that also shows how to open the serial port, see How to open & initialize boost::asio::serial_port

Posted by Uli Köhler in Boost, C/C++

How to set baud rate & parity on boost::asio::serial_port

This example shows how to baudrate, parity & stop bits:

// Configure serial port parameters: 115.2kBaud, 8N1
serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

For a more complete example that also shows how to open the serial port, see How to open & initialize boost::asio::serial_port or our Minimal boost::asio::serial_port read (read_some) example

Posted by Uli Köhler in Boost, C/C++

How to open & initialize boost::asio::serial_port

This example shows how to open a boost serial port and how to set the baudrate, parity & stop bits:

boost::asio::io_service io;
// Open serial port
boost::asio::serial_port serial(io, "/dev/ttyUSB0");

// Configure basic serial port parameters: 115.2kBaud, 8N1
serial.set_option(boost::asio::serial_port_base::baud_rate(115200));
serial.set_option(boost::asio::serial_port_base::character_size(8 /* data bits */));
serial.set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
serial.set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));

Also see our Minimal boost::asio::serial_port read (read_some) example

Posted by Uli Köhler in Boost, C/C++

How to split strings in C++ using boost::algorithm::split() – a minimal example

#include <boost/algorithm/string/split.hpp> // boost::algorithm::split
#include <boost/algorithm/string/classification.hpp> // boost::is_any_of
#include <string>
#include <iostream>
#include <vector>

int main() {
    std::string mystr = "foo-bar-x";
    // Split by "-"
    std::vector<std::string> splitResult;
    boost::algorithm::split(splitResult, mystr, boost::is_any_of("-"));
    // Print results, one line at a time
    // Prints:
    // foo
    // bar
    // x
    for(const std::string& s : splitResult) {
        std::cout << s << std::endl;
    }
}

 

Posted by Uli Köhler in Boost, C/C++