Skip to content

Commit

Permalink
refactor: ♻️ remove iostream usage; recreate http client on request fail
Browse files Browse the repository at this point in the history
Will recreate/reattempt the failing request only once if it fails. Refactored HttpConnection::Impl members.
  • Loading branch information
jontitorr committed Dec 27, 2023
1 parent 89db50c commit cf3a2e7
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 36 deletions.
52 changes: 16 additions & 36 deletions src/http.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,62 +7,42 @@
#include <boost/url/parse.hpp>
#include <deque>
#include <ekizu/http.hpp>
#include <iostream>

namespace {
using boost::asio::ip::tcp;
namespace beast = boost::beast;
namespace ssl = boost::asio::ssl;

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

namespace ekizu::net {
struct HttpConnection::Impl {
Impl(asio::any_io_executor ex, ssl::context &ctx)
: m_resolver{ex}, m_stream{ex, ctx} {}

void run(std::string_view host, std::string_view port,
const asio::yield_context &yield) {
ekizu::Result<> run(std::string_view host, std::string_view port,
const asio::yield_context &yield) {
if (!SSL_set_tlsext_host_name(m_stream.native_handle(), host.data())) {
beast::error_code ec{static_cast<int>(::ERR_get_error()),
asio::error::get_ssl_category()};
std::cerr << ec.message() << "\n";
return;
return ec;
}

boost::system::error_code ec;
const auto results = m_resolver.async_resolve(host, port, yield[ec]);

if (ec) { return fail(ec, "resolve"); }
if (ec) { return ec; }

beast::get_lowest_layer(m_stream).expires_after(
std::chrono::seconds(30));
beast::get_lowest_layer(m_stream).async_connect(results, yield[ec]);

if (ec) { return fail(ec, "connect"); }
if (ec) { return ec; }

m_stream.async_handshake(ssl::stream_base::client, yield[ec]);

if (ec) { return fail(ec, "handshake"); }
}

void do_request(boost::asio::yield_context yield) {
boost::system::error_code ec;
auto cb = std::move(m_queue.front().second);
http::async_write(m_stream, m_queue.front().first, yield[ec]);

if (ec) { return fail(ec, "write"); }

http::async_read(m_stream, m_buffer, m_res, yield);
m_queue.pop_front();
if (ec) { return ec; }

if (ec) { return fail(ec, "read"); }
if (cb) { cb(m_res); }
if (!m_queue.empty()) { do_request(yield); }
return outcome::success();
}

Result<HttpResponse> request(const HttpRequest &req,
Expand All @@ -72,21 +52,18 @@ struct HttpConnection::Impl {

if (ec) { return ec; }

m_res = {};
http::async_read(m_stream, m_buffer, m_res, yield[ec]);
HttpResponse res;
http::async_read(m_stream, m_buffer, res, yield[ec]);

if (ec) { return ec; }

return m_res;
return res;
}

private:
asio::ip::tcp::resolver m_resolver;
beast::ssl_stream<beast::tcp_stream> m_stream;
beast::flat_buffer m_buffer;
HttpResponse m_res;
std::deque<std::pair<HttpRequest, std::function<void(HttpResponse)>>>
m_queue;
};

Result<HttpResponse> HttpConnection::request(const HttpRequest &req,
Expand All @@ -107,9 +84,9 @@ Result<HttpConnection> HttpConnection::connect(
}

ssl::context ctx{ssl::context::tlsv12_client};
boost::system::error_code ec;

if (ctx.set_verify_mode(ssl::context::verify_peer |
if (boost::system::error_code ec;
ctx.set_verify_mode(ssl::context::verify_peer |
ssl::context::verify_fail_if_no_peer_cert,
ec) ||
ctx.set_default_verify_paths(ec)) {
Expand All @@ -119,7 +96,10 @@ Result<HttpConnection> HttpConnection::connect(
boost::certify::enable_native_https_server_verification(ctx);

auto impl = std::make_shared<Impl>(yield.get_executor(), ctx);
impl->run(uri.host(), uri.scheme() == "http" ? "80" : "443", yield);

BOOST_OUTCOME_TRY(
auto r,
impl->run(uri.host(), uri.scheme() == "http" ? "80" : "443", yield));

return HttpConnection{std::move(impl)};
}
Expand Down
6 changes: 6 additions & 0 deletions src/http_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ Result<net::HttpResponse> HttpClient::send(net::HttpRequest req,
m_http = std::move(http);
}

if (auto res = m_http->request(req, yield); res) { return res; }

BOOST_OUTCOME_TRY(
auto http, net::HttpConnection::connect("https://discord.com", yield));
m_http = std::move(http);

return m_http->request(req, yield);
}
} // namespace ekizu

0 comments on commit cf3a2e7

Please sign in to comment.