2#include <ossia/detail/config.hpp> 
    4#include <ossia/detail/fmt.hpp> 
    6#include <ossia/detail/parse_relax.hpp> 
    8#include <boost/asio.hpp> 
    9#include <boost/asio/placeholders.hpp> 
   15using tcp = boost::asio::ip::tcp;
 
   17template <
typename Fun, 
typename Err>
 
   18class http_get_request : 
public std::enable_shared_from_this<http_get_request<Fun, Err>>
 
   20  fmt::memory_buffer m_request;
 
   23  using std::enable_shared_from_this<http_get_request<Fun, Err>>::shared_from_this;
 
   25      Fun f, Err err, boost::asio::io_context& ctx, 
const std::string& server,
 
   26      const std::string& path, std::string_view verb = 
"GET")
 
   30      , m_err{std::move(err)}
 
   32    m_request.reserve(100 + server.size() + path.size());
 
   33    m_response.prepare(Fun::reserve_expect);
 
   34    fmt::format_to(fmt::appender(m_request), 
"{} ", verb);
 
   39        fmt::format_to(fmt::appender(m_request), 
"{}", c);
 
   41        fmt::format_to(fmt::appender(m_request), 
"%20");
 
   44        fmt::appender(m_request),
 
   48        "Connection: close\r\n\r\n",
 
   52  void resolve(
const std::string& server, 
const std::string& port)
 
   54    m_resolver.async_resolve(
 
   56        [self = this->shared_from_this()](
 
   57            const boost::system::error_code& err,
 
   58            const tcp::resolver::results_type& endpoints) {
 
   59      self->handle_resolve(err, endpoints);
 
   63  void close() { m_socket.close(); }
 
   67      const boost::system::error_code& err, 
const tcp::resolver::results_type& endpoints)
 
   71      boost::asio::async_connect(
 
   73          [self = shared_from_this()](
const boost::system::error_code& err, 
auto&&...) {
 
   74        self->handle_connect(err);
 
   84  void handle_connect(
const boost::system::error_code& err)
 
   88      boost::asio::const_buffer request(m_request.data(), m_request.size());
 
   89      boost::asio::async_write(
 
   91          [self = shared_from_this()](
 
   92              const boost::system::error_code& err, std::size_t size) {
 
   93        self->handle_write_request(err, size);
 
  103  void handle_write_request(
const boost::system::error_code& err, std::size_t size)
 
  107      boost::asio::async_read_until(
 
  108          m_socket, m_response, 
"\r\n",
 
  109          [self = shared_from_this()](
 
  110              const boost::system::error_code& err, std::size_t size) {
 
  111        self->handle_read_status_line(err, size);
 
  121  void handle_read_status_line(
const boost::system::error_code& err, std::size_t size)
 
  126      std::istream response_stream(&m_response);
 
  127      std::string http_version;
 
  128      response_stream >> http_version;
 
  129      unsigned int status_code;
 
  130      response_stream >> status_code;
 
  131      std::string status_message;
 
  132      std::getline(response_stream, status_message);
 
  133      if(!response_stream || http_version.substr(0, 5) != 
"HTTP/")
 
  138      if(status_code != 200)
 
  140        ossia::logger().error(
"HTTP Error: status code {}", status_code);
 
  145      boost::asio::async_read_until(
 
  146          m_socket, m_response, 
"\r\n\r\n",
 
  147          [self = shared_from_this()](
 
  148              const boost::system::error_code& err, std::size_t size) {
 
  149        self->handle_read_headers(err, size);
 
  159  void handle_read_headers(
const boost::system::error_code& err, std::size_t size)
 
  164      std::istream response_stream(&m_response);
 
  166      while(std::getline(response_stream, header) && header != 
"\r")
 
  168        if(header.starts_with(
"Content-Length: "))
 
  170          std::string_view sz(header.begin() + strlen(
"Content-Length: "), header.end());
 
  171          if(
auto num = ossia::parse_relax<int>(sz))
 
  173            m_contentLength = *num;
 
  177            ossia::logger().error(
"Invalid HTTP Content-length: {}", sz);
 
  183      if(m_contentLength > 0)
 
  185        if(m_contentLength == m_response.size())
 
  187          finish_read(boost::asio::error::eof, size);
 
  191          boost::asio::async_read(
 
  192              m_socket, m_response, boost::asio::transfer_exactly(m_contentLength - m_response.size()),
 
  193              [self = shared_from_this()](
 
  194                  const boost::system::error_code& err, std::size_t size) {
 
  195            self->handle_read_content(err, size);
 
  202        boost::asio::async_read(
 
  203            m_socket, m_response, boost::asio::transfer_all(),
 
  204            [self = shared_from_this()](
 
  205                const boost::system::error_code& err, std::size_t size) {
 
  206          self->handle_read_content(err, size);
 
  217  void handle_read_content(
const boost::system::error_code& err, std::size_t size)
 
  219    if(!err || err == boost::asio::error::eof)
 
  220      finish_read(err, size);
 
  228  void finish_read(
const boost::system::error_code& err, std::size_t size)
 
  230    const auto& dat = m_response.data();
 
  231    auto begin = boost::asio::buffers_begin(dat);
 
  232    auto end = boost::asio::buffers_end(dat);
 
  233    auto sz = end - begin;
 
  235    str.reserve(sz + 16); 
 
  236    str.assign(begin, end);
 
  241  tcp::resolver m_resolver;
 
  242  tcp::socket m_socket;
 
  243  boost::asio::streambuf m_response;
 
  244  int m_contentLength{-1};
 
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition context.cpp:118