Skip to content

Instantly share code, notes, and snippets.

@rohith2506
Created November 4, 2023 08:24
Show Gist options
  • Save rohith2506/fc90430b377586f4ab8fb9b103229763 to your computer and use it in GitHub Desktop.
Save rohith2506/fc90430b377586f4ab8fb9b103229763 to your computer and use it in GitHub Desktop.
#include <thread>
#include <iostream>
#include <coroutine>
#include <optional>
#include <variant>
#include <vector>
#include <utility>
#include <string>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/json.hpp>
#include <boost/beast.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/coroutine/all.hpp>
#include <boost/beast/ssl/ssl_stream.hpp>
#include <boost/beast/core/tcp_stream.hpp>
#include <boost/beast/core/flat_static_buffer.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/verb.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/detached.hpp>
class http_client {
private:
using response = boost::beast::http::response<boost::beast::http::string_body>;
std::string http_hostname;
std::string ip_address;
boost::asio::ssl::context ssl_context;
boost::asio::ip::tcp::resolver hostname_resolver;
std::optional<boost::beast::ssl_stream<boost::beast::tcp_stream>> tcp_stream;
boost::beast::flat_static_buffer<4 * 1024 * 1024> receive_flat_buffer;
public:
http_client(const std::string& http_name, boost::asio::io_context& io_context) :
http_hostname(http_name),
ssl_context(boost::asio::ssl::context::tlsv12_client),
hostname_resolver(io_context),
tcp_stream(boost::beast::ssl_stream<boost::beast::tcp_stream>(io_context, ssl_context)),
receive_flat_buffer()
{
ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
ssl_context.set_options(
boost::asio::ssl::context::default_workarounds | boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 | boost::asio::ssl::context::single_dh_use);
if (!SSL_set_tlsext_host_name(tcp_stream->native_handle(), http_hostname.c_str())) {
boost::beast::error_code error_code{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
throw boost::beast::system_error{error_code};
}
auto const resolved_endpoint = hostname_resolver.resolve(http_hostname, "443");
ip_address = resolved_endpoint->endpoint().address().to_string();
boost::beast::get_lowest_layer(tcp_stream.value()).connect(resolved_endpoint);
boost::beast::get_lowest_layer(tcp_stream.value()).socket().set_option(boost::asio::socket_base::keep_alive(true));
boost::beast::get_lowest_layer(tcp_stream.value()).socket().set_option(boost::asio::ip::tcp::no_delay(true));
std::cout << "Connected to REST endpoint at IP address <" << ip_address << "> which was resolved from <" << resolved_endpoint->host_name() << std::endl;
tcp_stream->handshake(boost::asio::ssl::stream_base::client);
}
void send_request(boost::asio::io_context& io_context, const std::string& target, const std::function<void(response)>& callback) {
boost::asio::spawn(
io_context, [target = std::move(target), callback = std::move(callback), this](boost::asio::yield_context yield_context) mutable
{
boost::beast::http::request<boost::beast::http::string_body> http_request{
boost::beast::http::verb::get,
target,
11};
http_request.set(boost::beast::http::field::host, http_hostname);
http_request.set(boost::beast::http::field::content_type, "application/json");
http_request.set(boost::beast::http::field::connection, "Keep-Alive");
http_request.set(boost::beast::http::field::keep_alive, "timeout=86400");
http_request.keep_alive(true);
http_request.prepare_payload();
size_t bytes_transferred = boost::beast::http::async_write(tcp_stream.value(), http_request, yield_context);
response http_response;
boost::beast::http::async_read(tcp_stream.value(), receive_flat_buffer, http_response, yield_context);
callback(http_response);
}
);
}
};
int main() {
boost::asio::io_context io_context{};
std::string host_name{"fapi.binance.com"};
http_client client(host_name, io_context);
for (int i = 0; i < 100; i++) {
auto const stime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
client.send_request(io_context, "/fapi/v1/time", [&](boost::beast::http::response<boost::beast::http::string_body> const& http_response) {});
auto const etime = std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
std::cout << "time diff = " << etime - stime << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
io_context.run();
return 0;
}
@rohith2506
Copy link
Author

rohith2506 commented Nov 4, 2023

compiler flags: g++ -std=c++20 -O2 -flto -g http_client.cpp -I/home/dev/vcpkg/installed/x64-linux/include -L/home/dev/vcpkg/installed/x64-linux/lib -lboost_system -lboost_coroutine -lboost_thread -lboost_json -lssl -lcrypto -lboost_context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment