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)
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),
"GET ");
39 fmt::format_to(fmt::appender(m_request),
"{}", c);
41 fmt::format_to(fmt::appender(m_request),
"%20");
44 fmt::appender(m_request),
49 "Connection: close\r\n\r\n",
53 void resolve(
const std::string& server,
const std::string& port)
55 m_resolver.async_resolve(
57 [self = this->shared_from_this()](
58 const boost::system::error_code& err,
59 const tcp::resolver::results_type& endpoints) {
60 self->handle_resolve(err, endpoints);
64 void close() { m_socket.close(); }
68 const boost::system::error_code& err,
const tcp::resolver::results_type& endpoints)
72 boost::asio::async_connect(
74 [self = shared_from_this()](
const boost::system::error_code& err,
auto&&...) {
75 self->handle_connect(err);
85 void handle_connect(
const boost::system::error_code& err)
89 boost::asio::const_buffer request(m_request.data(), m_request.size());
90 boost::asio::async_write(
92 [self = shared_from_this()](
93 const boost::system::error_code& err, std::size_t size) {
94 self->handle_write_request(err, size);
104 void handle_write_request(
const boost::system::error_code& err, std::size_t size)
108 boost::asio::async_read_until(
109 m_socket, m_response,
"\r\n",
110 [self = shared_from_this()](
111 const boost::system::error_code& err, std::size_t size) {
112 self->handle_read_status_line(err, size);
122 void handle_read_status_line(
const boost::system::error_code& err, std::size_t size)
127 std::istream response_stream(&m_response);
128 std::string http_version;
129 response_stream >> http_version;
130 unsigned int status_code;
131 response_stream >> status_code;
132 std::string status_message;
133 std::getline(response_stream, status_message);
134 if(!response_stream || http_version.substr(0, 5) !=
"HTTP/")
139 if(status_code != 200)
141 ossia::logger().error(
"HTTP Error: status code {}", status_code);
146 boost::asio::async_read_until(
147 m_socket, m_response,
"\r\n\r\n",
148 [self = shared_from_this()](
149 const boost::system::error_code& err, std::size_t size) {
150 self->handle_read_headers(err, size);
160 void handle_read_headers(
const boost::system::error_code& err, std::size_t size)
165 std::istream response_stream(&m_response);
167 while(std::getline(response_stream, header) && header !=
"\r")
169 if(header.starts_with(
"Content-Length: "))
171 std::string_view sz(header.begin() + strlen(
"Content-Length: "), header.end());
172 if(
auto num = ossia::parse_relax<int>(sz))
174 m_contentLength = *num;
178 ossia::logger().error(
"Invalid HTTP Content-length: {}", sz);
184 if(m_contentLength > 0)
186 if(m_contentLength == m_response.size())
188 finish_read(boost::asio::error::eof, size);
192 boost::asio::async_read(
193 m_socket, m_response, boost::asio::transfer_exactly(m_contentLength - m_response.size()),
194 [self = shared_from_this()](
195 const boost::system::error_code& err, std::size_t size) {
196 self->handle_read_content(err, size);
203 boost::asio::async_read(
204 m_socket, m_response, boost::asio::transfer_all(),
205 [self = shared_from_this()](
206 const boost::system::error_code& err, std::size_t size) {
207 self->handle_read_content(err, size);
218 void handle_read_content(
const boost::system::error_code& err, std::size_t size)
220 if(!err || err == boost::asio::error::eof)
221 finish_read(err, size);
229 void finish_read(
const boost::system::error_code& err, std::size_t size)
231 const auto& dat = m_response.data();
232 auto begin = boost::asio::buffers_begin(dat);
233 auto end = boost::asio::buffers_end(dat);
234 auto sz = end - begin;
236 str.reserve(sz + 16);
237 str.assign(begin, end);
242 tcp::resolver m_resolver;
243 tcp::socket m_socket;
244 boost::asio::streambuf m_response;
245 int m_contentLength{-1};
spdlog::logger & logger() noexcept
Where the errors will be logged. Default is stderr.
Definition context.cpp:118