168 const std::vector<uint8_t> &body,
169 const std::map<std::string, std::string> &headers,
170 const std::map<std::string, std::string> &query)
174 if (url_result.is_err()) {
176 url_result.error().message);
179 auto url_info = std::move(url_result.value());
182 for (
const auto &[key, value] : query) {
183 url_info.query[key] = value;
187 if (url_info.scheme ==
"https") {
189 -1,
"HTTPS not supported yet. Use HTTP for now.");
193 auto http_request = build_request(method, url_info, body, headers);
199 auto client = std::make_shared<messaging_client>(
202 std::chrono::system_clock::now().time_since_epoch().count()));
205 std::vector<uint8_t> response_data;
206 std::mutex response_mutex;
207 std::condition_variable response_cv;
208 bool response_complete =
false;
209 bool has_error =
false;
210 std::string error_message;
213 client->set_receive_callback([&](
const std::vector<uint8_t> &data) {
214 std::lock_guard<std::mutex> lock(response_mutex);
215 response_data.insert(response_data.end(), data.begin(), data.end());
220 std::string_view response_str(
221 reinterpret_cast<const char *
>(response_data.data()),
222 response_data.size());
224 auto headers_end = response_str.find(
"\r\n\r\n");
225 if (headers_end != std::string_view::npos) {
227 auto headers_section = response_str.substr(0, headers_end);
230 auto cl_pos = headers_section.find(
"Content-Length:");
231 if (cl_pos == std::string_view::npos) {
232 cl_pos = headers_section.find(
"content-length:");
235 if (cl_pos != std::string_view::npos) {
236 auto cl_start = headers_section.find(
':', cl_pos) + 1;
237 auto cl_end = headers_section.find(
'\r', cl_start);
238 auto cl_str = headers_section.substr(cl_start, cl_end - cl_start);
241 while (!cl_str.empty() && std::isspace(cl_str.front()))
242 cl_str.remove_prefix(1);
243 while (!cl_str.empty() && std::isspace(cl_str.back()))
244 cl_str.remove_suffix(1);
247 std::size_t content_length = std::stoull(std::string(cl_str));
248 std::size_t body_start = headers_end + 4;
250 if (response_data.size() >= body_start + content_length) {
251 response_complete =
true;
252 response_cv.notify_one();
256 response_complete =
true;
257 response_cv.notify_one();
266 client->set_error_callback([&](std::error_code ec) {
267 std::lock_guard<std::mutex> lock(response_mutex);
268 if (!response_complete) {
270 if (!response_data.empty()) {
271 response_complete =
true;
274 error_message = ec.message();
276 response_cv.notify_one();
281 auto connect_result = client->start_client(url_info.host, url_info.port);
282 if (connect_result.is_err()) {
284 -1,
"Failed to connect to " + url_info.host +
":" +
285 std::to_string(url_info.port) +
": " +
286 connect_result.error().message);
290 std::this_thread::sleep_for(std::chrono::milliseconds(100));
293 auto send_result = client->send_packet(std::move(request_bytes));
294 if (send_result.is_err()) {
295 (void)client->stop_client();
297 send_result.error().message);
302 std::unique_lock<std::mutex> lock(response_mutex);
303 if (!response_cv.wait_for(lock, timeout_,
304 [&] { return response_complete || has_error; })) {
305 (void)client->stop_client();
311 (void)client->stop_client();
316 "Request failed: " + error_message);
321 if (response_result.is_err()) {
323 -1,
"Failed to parse response: " + response_result.error().message);
326 return response_result;
auto request(internal::http_method method, const std::string &url, const std::vector< uint8_t > &body, const std::map< std::string, std::string > &headers, const std::map< std::string, std::string > &query) -> Result< internal::http_response >
Perform generic HTTP request.