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