Skip to content

Instantly share code, notes, and snippets.

@aromanro
Last active August 16, 2020 10:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aromanro/d053c1e66bbf29fa6b123186607158a2 to your computer and use it in GitHub Desktop.
Save aromanro/d053c1e66bbf29fa6b123186607158a2 to your computer and use it in GitHub Desktop.
How to use boost for a https api call
#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/beast.hpp>
#include <boost/format.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
namespace http = boost::beast::http; // from <boost/beast/http.hpp>
namespace beast = boost::beast; // from <boost/beast.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
//#define USE_WINGOZ 1
#ifdef USE_WINGOZ
#include "wincrypt.h"
inline void load_root_certificates(ssl::context& ctx, boost::system::error_code& ec)
{
HCERTSTORE hstore = CertOpenSystemStore(NULL, L"ROOT");
if (!hstore) return;
char* data = NULL;
PCCERT_CONTEXT pcert_context = NULL;
do
{
pcert_context = CertEnumCertificatesInStore(hstore, pcert_context);
if (!pcert_context) break;
X509* x509 = d2i_X509(NULL, const_cast<const BYTE**>(&pcert_context->pbCertEncoded), pcert_context->cbCertEncoded);
BIO* bio = BIO_new(BIO_s_mem());
if (PEM_write_bio_X509(bio, x509))
{
const long int len = BIO_get_mem_data(bio, &data);
ctx.add_certificate_authority(boost::asio::buffer(data, static_cast<std::size_t>(len)), ec);
}
BIO_free(bio);
X509_free(x509);
} while (!ec);
CertCloseStore(hstore, 0);
}
inline void load_root_certificates(ssl::context& ctx)
{
boost::system::error_code ec;
load_root_certificates(ctx, ec);
if (ec) throw boost::system::system_error{ ec };
}
#endif
int main()
{
try
{
boost::system::error_code ec;
const std::string hostName = "jsonplaceholder.typicode.com";
net::io_service io_ctxt;
// DNS stuff
tcp::resolver resolver(io_ctxt);
tcp::resolver::query query(hostName, "https");
auto res = resolver.resolve(query);
// now the certification stuff
ssl::context ctx(ssl::context::method::sslv23_client);
ctx.set_verify_mode(ssl::verify_peer);
#ifdef USE_WINGOZ
load_root_certificates(ctx); // if you want to use windows root certificates store
#else
ctx.load_verify_file("cacert.pem"); // taken from mozilla
#endif
ssl::stream<tcp::socket> ssock(io_ctxt, ctx); // secure socket stream
tcp::socket::lowest_layer_type& sock = ssock.lowest_layer(); // for convenience
connect(sock, res);
sock.set_option(tcp::no_delay(true));
// Perform SSL handshake and verify the remote host's
// certificate.
ssock.set_verify_mode(ssl::verify_peer);
ssock.set_verify_callback(ssl::rfc2818_verification(hostName));
// not all servers need it
if (!SSL_set_tlsext_host_name(ssock.native_handle(), hostName.c_str()))
{
beast::error_code ecssl{ static_cast<int>(::ERR_get_error()), net::error::get_ssl_category() };
throw beast::system_error{ ecssl };
}
ssock.handshake(ssl::stream_base::client);
// send request:
// Set up an HTTP GET request message
http::request<http::string_body> req{ http::verb::get, "/todos", 10 };
req.set(http::field::host, hostName);
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
// Send the HTTP request to the remote host
http::write(ssock, req);
// read response
// first, the header
// Read the response status line and headers
net::streambuf response;
const size_t nr = read_until(ssock, response, "\r\n\r\n");
// if you want to parse the header uncomment this:
/*
std::string istr((std::istreambuf_iterator<char>(&response)), std::istreambuf_iterator<char>());
// save what was read supplementary and is part of the body
std::string startBody = istr.substr(nr);
istr.resize(nr); // resize to contain only the headers
std::cout << istr;
// Declare a parser for an HTTP response
beast::http::response_parser<beast::http::string_body> parser;
parser.put(buffer(istr), ec);
if (ec)
{
std::cout << "Error parsing header: " << ec.message() << std::endl;
return EXIT_FAILURE;
}
*/
response.consume(nr);
do
{
read(ssock, response, ec);
} while (!ec);
// Gracefully close the stream
ssock.shutdown(ec);
if (ec == net::error::eof || ec == ssl::error::stream_truncated)
ec = {};
if (ec) throw beast::system_error{ ec };
// If we get here then the connection is closed gracefully
std::string respstr((std::istreambuf_iterator<char>(&response)), std::istreambuf_iterator<char>());
// uncomment this if you parse the header
//respstr = startBody + respstr;
// now the json stuff
boost::property_tree::ptree pt;
std::istringstream strstrm(respstr);
boost::property_tree::read_json(strstrm, pt);
// TODO: deal with it
int completed = 0;
int notCompleted = 0;
for (const auto& val : pt)
{
if (val.second.get_child("completed").data() == "true")
++completed;
else
++notCompleted;
}
std::cout << "Completed TODOs: " << completed << std::endl;
std::cout << "Not completed TODOs: " << notCompleted << std::endl;
}
catch (std::exception const& e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment