481015e1c6
Some checks failed
android / build (push) Failing after 9s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Debug) (push) Failing after 48m20s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (Release) (push) Failing after 52m49s
linux-arm-gcc / linux-gcc-arm (Debug) (push) Failing after 25s
linux-arm-gcc / linux-gcc-arm (Release) (push) Failing after 24s
linux-arm-gcc / linux-gcc-armhf (Debug) (push) Failing after 38m9s
linux-arm-gcc / linux-gcc-armhf (Release) (push) Failing after 33m29s
linux-mips-gcc / linux-gcc-mipsel (Debug) (push) Failing after 9m34s
linux-mips-gcc / linux-gcc-mipsel (Release) (push) Failing after 12m14s
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Failing after 10m1s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Failing after 12m29s
linux-riscv64-gcc / linux-gcc-riscv64 (Debug) (push) Failing after 10m6s
linux-riscv64-gcc / linux-gcc-riscv64 (Release) (push) Failing after 11m7s
linux-x64-clang / linux-clang (Debug) (push) Failing after 30s
linux-x64-clang / linux-clang (Release) (push) Failing after 41s
linux-x64-gcc / linux-gcc (Debug) (push) Failing after 6m34s
linux-x64-gcc / linux-gcc (Release) (push) Failing after 10m11s
linux-x86-gcc / linux-gcc (Debug) (push) Failing after 6m30s
linux-x86-gcc / linux-gcc (Release) (push) Failing after 10m36s
533 lines
20 KiB
C++
533 lines
20 KiB
C++
#include "tile/net/http/http_client.h"
|
|
|
|
#include "tile/base/enum.h"
|
|
#include "tile/base/internal/background_task_host.h"
|
|
#include "tile/base/logging.h"
|
|
#include "tile/base/thread/latch.h"
|
|
#include "tile/net/internal/http_engine.h"
|
|
#include "tile/net/internal/http_task.h"
|
|
|
|
#include "curl/curl.h"
|
|
#include "gflags/gflags.h"
|
|
|
|
DEFINE_int32(tile_http_client_default_timeout_ms, 1000, "Default timeout of tile::HttpClient.");
|
|
|
|
namespace tile {
|
|
namespace {
|
|
HttpClient::ErrorCode
|
|
GetErrorCodeFromCurlCode(int c)
|
|
{
|
|
switch (c) {
|
|
case CURLE_UNSUPPORTED_PROTOCOL:
|
|
return HttpClient::ERROR_PROTOCOL_NOT_SUPPORTED;
|
|
case CURLE_URL_MALFORMAT:
|
|
return HttpClient::ERROR_INVALID_URI_ADDRESS;
|
|
case CURLE_COULDNT_RESOLVE_PROXY:
|
|
return HttpClient::ERROR_PROXY;
|
|
case CURLE_COULDNT_RESOLVE_HOST:
|
|
return HttpClient::ERROR_FAIL_TO_RESOLVE_ADDRESS;
|
|
case CURLE_COULDNT_CONNECT:
|
|
return HttpClient::ERROR_CONNECTION;
|
|
case CURLE_HTTP2:
|
|
case CURLE_HTTP2_STREAM:
|
|
return HttpClient::ERROR_HTTP2;
|
|
case CURLE_HTTP_RETURNED_ERROR:
|
|
case CURLE_HTTP_POST_ERROR:
|
|
TILE_LOG_WARNING_EVERY_SECOND("ERROR_CURL_HTTP_ERROR CURLcode {}", c);
|
|
return HttpClient::ERROR_INTERNAL_ERROR;
|
|
case CURLE_OPERATION_TIMEDOUT:
|
|
return HttpClient::ERROR_TIMEOUT;
|
|
case CURLE_SSL_CONNECT_ERROR:
|
|
case CURLE_SSL_ENGINE_NOTFOUND:
|
|
case CURLE_SSL_ENGINE_SETFAILED:
|
|
case CURLE_SSL_CERTPROBLEM:
|
|
case CURLE_SSL_CIPHER:
|
|
case CURLE_PEER_FAILED_VERIFICATION:
|
|
TILE_LOG_WARNING_EVERY_SECOND("ERROR_SSL CURLcode {}", c);
|
|
return HttpClient::ERROR_SSL;
|
|
case CURLE_SEND_ERROR:
|
|
case CURLE_RECV_ERROR:
|
|
return HttpClient::ERROR_IO;
|
|
case CURLE_TOO_MANY_REDIRECTS:
|
|
return HttpClient::ERROR_TOO_MANY_REDIRECTS;
|
|
case CURLE_GOT_NOTHING:
|
|
return HttpClient::ERROR_GET_NOTHING;
|
|
|
|
default: {
|
|
TILE_LOG_WARNING_EVERY_SECOND(
|
|
"ERROR_UNKNOWN CURLcode {}, curl msg: ", c, curl_easy_strerror(static_cast<CURLcode>(c)));
|
|
return HttpClient::ERROR_UNKNOWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
long
|
|
TileHttpVersionToCurlHttpVersion(HttpVersion v, bool no_automatic_upgrade)
|
|
{
|
|
if (no_automatic_upgrade) { return CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE; }
|
|
switch (v) {
|
|
case HttpVersion::Unspecified:
|
|
return CURL_HTTP_VERSION_NONE;
|
|
case HttpVersion::V_1_0:
|
|
return CURL_HTTP_VERSION_1_0;
|
|
case HttpVersion::V_1_1:
|
|
return CURL_HTTP_VERSION_1_1;
|
|
case HttpVersion::V_2:
|
|
return CURL_HTTP_VERSION_2_0;
|
|
case HttpVersion::V_3:
|
|
return CURL_HTTP_VERSION_3;
|
|
default:
|
|
TILE_CHECK(0, "Unknown flare http version");
|
|
}
|
|
return CURL_HTTP_VERSION_NONE;
|
|
}
|
|
|
|
HttpVersion
|
|
CurlHttpVersionToHttpVersion(long v)
|
|
{
|
|
switch (v) {
|
|
case CURL_HTTP_VERSION_NONE:
|
|
return HttpVersion::Unspecified;
|
|
case CURL_HTTP_VERSION_1_0:
|
|
return HttpVersion::V_1_0;
|
|
case CURL_HTTP_VERSION_1_1:
|
|
return HttpVersion::V_1_1;
|
|
case CURL_HTTP_VERSION_2_0:
|
|
return HttpVersion::V_2;
|
|
case CURL_HTTP_VERSION_3:
|
|
return HttpVersion::V_3;
|
|
default:
|
|
TILE_CHECK(0, "Unknown flare http version {}", v);
|
|
}
|
|
return HttpVersion::Unspecified;
|
|
}
|
|
|
|
void
|
|
FillResponseInfo(CURL *easy_handler, HttpClient::ResponseInfo *response_info)
|
|
{
|
|
TILE_CHECK(response_info);
|
|
TILE_CHECK(easy_handler);
|
|
|
|
{
|
|
char *effective_url = nullptr;
|
|
curl_easy_getinfo(easy_handler, CURLINFO_EFFECTIVE_URL, &effective_url);
|
|
response_info->effective_url = effective_url;
|
|
}
|
|
|
|
{
|
|
double transfer_time_in_seconds = 0;
|
|
curl_easy_getinfo(easy_handler, CURLINFO_TOTAL_TIME, &transfer_time_in_seconds);
|
|
response_info->total_time_transfer = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
|
std::chrono::duration<double>(transfer_time_in_seconds));
|
|
}
|
|
|
|
{
|
|
long http_version;
|
|
curl_easy_getinfo(easy_handler, CURLINFO_HTTP_VERSION, &http_version);
|
|
response_info->http_version = CurlHttpVersionToHttpVersion(http_version);
|
|
}
|
|
}
|
|
|
|
std::optional<std::string>
|
|
GetHttpRequestUriFromUrl(const std::string &url)
|
|
{
|
|
// http://example.com/xxx
|
|
// http://example.com
|
|
|
|
auto start_pos = url.find("://");
|
|
if (start_pos != std::string::npos) {
|
|
start_pos += 3;
|
|
|
|
auto end_pos = url.find("/", start_pos);
|
|
if (end_pos != std::string::npos) { return url.substr(end_pos); }
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::pair<std::string, std::function<void(bool)>>>
|
|
OverrideHost(const std::string &url, const std::string &override_host_nslb)
|
|
{
|
|
return std::make_pair(url, nullptr);
|
|
}
|
|
|
|
class HttpEngineWrapper : public detail::HttpChannel {
|
|
public:
|
|
static HttpEngineWrapper *Instance()
|
|
{
|
|
static NeverDestroyedSingleton<HttpEngineWrapper> engine;
|
|
return engine.Get();
|
|
}
|
|
|
|
internal::HttpTask GetHttpTask(const std::string &url,
|
|
const HttpClient::Options opts,
|
|
const HttpClient::RequestOptions &request_options)
|
|
{
|
|
internal::HttpTask task;
|
|
task.SetUrl(url);
|
|
task.SetTimeout(request_options.timeout);
|
|
CURL *h = task.GetNativeHandle();
|
|
if (opts.follow_redirects) {
|
|
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_FOLLOWLOCATION, 1L));
|
|
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_MAXREDIRS, request_options.max_redirection_count));
|
|
}
|
|
|
|
if (request_options.verbose) { TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_VERBOSE, 1L)); }
|
|
|
|
if (opts.use_bintin_compression) {
|
|
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_ACCEPT_ENCODING, "identity, gzip, deflate"));
|
|
}
|
|
|
|
TILE_CHECK_EQ(
|
|
CURLE_OK,
|
|
curl_easy_setopt(
|
|
h, CURLOPT_HTTP_VERSION,
|
|
TileHttpVersionToCurlHttpVersion(request_options.http_version, request_options.no_automatic_upgrade)));
|
|
|
|
for (auto &&s : request_options.headers) { task.AddHeader(s); }
|
|
|
|
if (!request_options.content_type.empty()) { task.AddHeader("Content-Type: " + request_options.content_type); }
|
|
|
|
if (!opts.verify_server_certificate) {
|
|
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 0));
|
|
TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_SSL_VERIFYHOST, 0));
|
|
}
|
|
|
|
if (!opts.proxy_from_env) { TILE_CHECK_EQ(CURLE_OK, curl_easy_setopt(h, CURLOPT_PROXY, opts.proxy.c_str())); }
|
|
|
|
return task;
|
|
}
|
|
|
|
void AsyncCallCallback(std::function<void(std::expected<HttpResponse, HttpClient::ErrorCode>)> done,
|
|
std::function<void(bool)> report_function,
|
|
std::expected<internal::HttpTaskCompletion, Status> completion,
|
|
HttpClient::ResponseInfo *response_info)
|
|
{
|
|
auto moved_report_function = MakeMoveOnCopy(report_function);
|
|
auto moved_done = MakeMoveOnCopy(done);
|
|
auto moved_completion = MakeMoveOnCopy(completion);
|
|
|
|
internal::BackgroundTaskHost::Instance()->Queue(
|
|
[moved_report_function, moved_completion, moved_done, response_info] {
|
|
auto &&report_function = moved_report_function.Ref();
|
|
auto &&completion = moved_completion.Ref();
|
|
auto &&done = moved_done.Ref();
|
|
if (report_function) { report_function(!!completion); }
|
|
|
|
std::optional<HttpResponse> opt_response;
|
|
if (completion) {
|
|
HttpResponse response;
|
|
response.set_version(completion->version());
|
|
response.set_status(completion->status());
|
|
response.set_body(std::move(*completion->body()));
|
|
CopyHeaders(*completion->headers(), response.headers());
|
|
opt_response = std::move(response);
|
|
}
|
|
|
|
if (completion && response_info) { FillResponseInfo(completion->GetNativeHandle(), response_info); }
|
|
|
|
if (!completion) {
|
|
done(std::make_unexpected(GetErrorCodeFromCurlCode(completion.error().code())));
|
|
} else {
|
|
TILE_CHECK(opt_response.has_value());
|
|
done(std::move(*opt_response));
|
|
}
|
|
});
|
|
}
|
|
|
|
template<class F>
|
|
void AsyncCall(HttpMethod method,
|
|
const std::string &url,
|
|
const HttpClient::Options &opts,
|
|
const HttpClient::RequestOptions &request_options,
|
|
HttpClient::ResponseInfo *response_info,
|
|
std::function<void(std::expected<HttpResponse, HttpClient::ErrorCode>)> &&done,
|
|
F &&callback)
|
|
{
|
|
auto &&override_host = OverrideHost(url, request_options.override_host_nslb);
|
|
if (!override_host) {
|
|
done(std::make_unexpected(HttpClient::ERROR_FAIL_TO_RESOLVE_ADDRESS));
|
|
return;
|
|
}
|
|
|
|
auto &&effective_url = override_host->first;
|
|
auto &&report_function = override_host->second;
|
|
auto task = GetHttpTask(effective_url, opts, request_options);
|
|
task.SetMethod(method);
|
|
std::unique_ptr<HttpRequest> request = make_unique<HttpRequest>();
|
|
request->set_version(request_options.http_version);
|
|
for (auto &&header : request_options.headers) {
|
|
auto pos = header.find(':');
|
|
if (pos != std::string::npos) {
|
|
request->headers()->Append(header.substr(0, pos), Trim(header.substr(pos + 1)));
|
|
}
|
|
}
|
|
|
|
callback(&task, request.get());
|
|
auto moved_report_function = MakeMoveOnCopy(report_function);
|
|
auto moved_done = MakeMoveOnCopy(done);
|
|
internal::HttpEngine::Instance()->StartTask(
|
|
std::move(task),
|
|
[this, moved_report_function, moved_done,
|
|
response_info](std::expected<internal::HttpTaskCompletion, Status> &&completion) mutable {
|
|
AsyncCallCallback(moved_done.Move(), moved_report_function.Move(), std::move(completion),
|
|
response_info);
|
|
});
|
|
}
|
|
|
|
void AsyncGet(const std::string &url,
|
|
const HttpClient::Options &opts,
|
|
const HttpClient::RequestOptions &request_options,
|
|
HttpClient::ResponseInfo *response_info,
|
|
std::function<void(std::expected<HttpResponse, HttpClient::ErrorCode>)> &&done) override
|
|
{
|
|
|
|
auto cb = [url](internal::HttpTask *, HttpRequest *p_request) {
|
|
if (p_request) {
|
|
p_request->set_method(HttpMethod::Get);
|
|
auto uri = GetHttpRequestUriFromUrl(url);
|
|
if (uri) { p_request->set_uri(*uri); }
|
|
}
|
|
};
|
|
return AsyncCall(HttpMethod::Get, url, opts, request_options, response_info, std::move(done), std::move(cb));
|
|
}
|
|
|
|
void AsyncPost(const std::string &url,
|
|
const HttpClient::Options &opts,
|
|
std::string data,
|
|
const HttpClient::RequestOptions &request_options,
|
|
HttpClient::ResponseInfo *response_info,
|
|
std::function<void(std::expected<HttpResponse, HttpClient::ErrorCode>)> &&done) override
|
|
{
|
|
auto moved_data = MakeMoveOnCopy(data);
|
|
|
|
auto cb = [url, moved_data](internal::HttpTask *p_task, HttpRequest *p_request) {
|
|
if (p_request) {
|
|
p_request->set_method(HttpMethod::Post);
|
|
auto uri = GetHttpRequestUriFromUrl(url);
|
|
if (uri) { p_request->set_uri(*uri); }
|
|
p_request->set_body(moved_data.Ref());
|
|
}
|
|
p_task->SetBody(moved_data.Move());
|
|
};
|
|
return AsyncCall(HttpMethod::Post, url, opts, request_options, response_info, std::move(done), std::move(cb));
|
|
}
|
|
|
|
void AsyncRequest(const std::string &protocol,
|
|
const std::string &host,
|
|
const HttpClient::Options &opts,
|
|
const HttpRequest &request,
|
|
const HttpClient::RequestOptions &request_options,
|
|
HttpClient::ResponseInfo *response_info,
|
|
std::function<void(std::expected<HttpResponse, HttpClient::ErrorCode>)> &&done) override
|
|
{
|
|
auto url = protocol + "://" + host + request.uri();
|
|
auto cb = [request](internal::HttpTask *p_task, HttpRequest *p_request) {
|
|
if (p_request) { *p_request = request; }
|
|
|
|
if (request.body_size() > 0) {
|
|
if (request.noncontiguous_body()) {
|
|
p_task->SetBody(*request.noncontiguous_body());
|
|
} else {
|
|
p_task->SetBody(*request.body());
|
|
}
|
|
}
|
|
|
|
for (auto header : *request.headers()) { p_task->AddHeader(header.first + ": " + header.second); }
|
|
TILE_CHECK(request.method() != HttpMethod::Unspecified, "You should specify http method.");
|
|
TILE_CHECK_EQ(
|
|
CURLE_OK,
|
|
curl_easy_setopt(p_task->GetNativeHandle(), CURLOPT_CUSTOMREQUEST, ToSlice(request.method()).data()));
|
|
};
|
|
return AsyncCall(request.method(), url, opts, request_options, response_info, std::move(done), std::move(cb));
|
|
}
|
|
|
|
private:
|
|
static void CopyHeaders(const std::vector<std::string> &from, HttpHeaders *to)
|
|
{
|
|
for (auto &&s : from) {
|
|
auto pos = s.find(":");
|
|
if (pos != std::string::npos) {
|
|
auto name = s.substr(0, pos);
|
|
auto value = s.substr(pos + 1);
|
|
TILE_CHECK(!EndsWith(value, "\r\n"));
|
|
to->Append(std::string(name), std::string(Trim(value)));
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
detail::HttpChannel *mock_channel = nullptr;
|
|
detail::HttpChannel *dry_run_channel = nullptr;
|
|
|
|
bool
|
|
IsMockAddress(const std::string &url_or_protocol, bool is_url)
|
|
{
|
|
if (is_url) { return StartsWith(url_or_protocol, "mock://"); }
|
|
return url_or_protocol == "mock";
|
|
}
|
|
|
|
detail::HttpChannel *
|
|
GetHttpChannel(const std::string &url_or_protocol, bool is_url = true)
|
|
{
|
|
if (TILE_UNLIKELY(IsMockAddress(url_or_protocol, is_url))) {
|
|
TILE_CHECK(mock_channel, "Mock channel has not been registered yet.");
|
|
return mock_channel;
|
|
}
|
|
|
|
return HttpEngineWrapper::Instance();
|
|
}
|
|
|
|
}// namespace
|
|
|
|
namespace detail {}// namespace detail
|
|
|
|
HttpClient::HttpClient(const Options &options) : options_(options) {}
|
|
|
|
const char *
|
|
HttpClient::ErrorCodeToString(int error_code)
|
|
{
|
|
switch (static_cast<ErrorCode>(error_code)) {
|
|
case ERROR_INVALID:
|
|
return "Invalid";
|
|
case ERROR_INVALID_URI_ADDRESS:
|
|
return "Invalid URI address";
|
|
case ERROR_FAIL_TO_RESOLVE_ADDRESS:
|
|
return "Failed to resolve address";
|
|
case ERROR_FAIL_TO_SEND_REQUEST:
|
|
return "Failed to send request";
|
|
case ERROR_FAIL_TO_GET_RESPONSE:
|
|
return "Failed to get response";
|
|
case ERROR_CONNECTION:
|
|
return "Connection io error";
|
|
case ERROR_TIMEOUT:
|
|
return "Response timeout";
|
|
case ERROR_PROXY:
|
|
return "ERROR_PROXY";
|
|
case ERROR_PARSE_RESPONSE:
|
|
return "Failed to parse response";
|
|
case ERROR_FAIL_TO_CONNECT_SERVER:
|
|
return "Failed to connect to server";
|
|
case ERROR_PROTOCOL_NOT_SUPPORTED:
|
|
return "Protocol is not supported";
|
|
case ERROR_TOO_MANY_REDIRECTS:
|
|
return "Too many redirections";
|
|
case ERROR_REDIRECT_LOCATION_NOT_FOUND:
|
|
return "redict location not found";
|
|
case ERROR_DECOMPRESS_RESPONSE:
|
|
return "Failed to decompress response";
|
|
case ERROR_HTTP2:
|
|
return "ERROR_HTTP2";
|
|
case ERROR_SSL:
|
|
return "ERROR_SSL";
|
|
case ERROR_IO:
|
|
return "ERROR_IO";
|
|
case ERROR_INTERNAL_ERROR:
|
|
return "ERROR_INTERNAL_ERROR";
|
|
case ERROR_DRY_RUN:
|
|
return "ERROR_DRY_RUN";
|
|
case ERROR_GET_NOTHING:
|
|
return "ERROR_GET_NOTHING";
|
|
case ERROR_UNKNOWN:
|
|
return "<Unknown>";
|
|
}
|
|
return "<Unknown>";
|
|
}
|
|
|
|
std::expected<HttpResponse, HttpClient::ErrorCode>
|
|
HttpClient::Get(const std::string &url, const RequestOptions &request_options, ResponseInfo *response_info)
|
|
{
|
|
Latch l(1);
|
|
std::expected<HttpResponse, HttpClient::ErrorCode> e;
|
|
GetHttpChannel(url)->AsyncGet(url, options_, request_options, response_info,
|
|
[&](std::expected<HttpResponse, HttpClient::ErrorCode> res) mutable {
|
|
e = std::move(res);
|
|
l.CountDown();
|
|
});
|
|
l.Wait();
|
|
return e;
|
|
}
|
|
|
|
std::expected<HttpResponse, HttpClient::ErrorCode>
|
|
HttpClient::Post(const std::string &url,
|
|
const std::string &data,
|
|
const RequestOptions &request_options,
|
|
ResponseInfo *response_info)
|
|
{
|
|
Latch l(1);
|
|
std::expected<HttpResponse, HttpClient::ErrorCode> e;
|
|
GetHttpChannel(url)->AsyncPost(url, options_, data, request_options, response_info,
|
|
[&](std::expected<HttpResponse, HttpClient::ErrorCode> res) mutable {
|
|
e = std::move(res);
|
|
l.CountDown();
|
|
});
|
|
l.Wait();
|
|
return e;
|
|
}
|
|
|
|
std::expected<HttpResponse, HttpClient::ErrorCode>
|
|
HttpClient::Request(const std::string &protocol,
|
|
const std::string &host,
|
|
const HttpRequest &request,
|
|
const RequestOptions &request_options,
|
|
ResponseInfo *response_info)
|
|
{
|
|
Latch l(1);
|
|
std::expected<HttpResponse, HttpClient::ErrorCode> e;
|
|
GetHttpChannel(protocol)->AsyncRequest(protocol, host, options_, request, request_options, response_info,
|
|
[&](std::expected<HttpResponse, HttpClient::ErrorCode> res) mutable {
|
|
e = std::move(res);
|
|
l.CountDown();
|
|
});
|
|
l.Wait();
|
|
return e;
|
|
}
|
|
|
|
Future<std::expected<HttpResponse, HttpClient::ErrorCode>>
|
|
HttpClient::AsyncGet(const std::string &url, const RequestOptions &request_options, ResponseInfo *response_info)
|
|
{
|
|
Promise<std::expected<HttpResponse, ErrorCode>> promise;
|
|
auto future = promise.GetFuture();
|
|
auto moved_promise = MakeMoveOnCopy(promise);
|
|
GetHttpChannel(url)->AsyncGet(url, options_, request_options, response_info,
|
|
[moved_promise](std::expected<HttpResponse, ErrorCode> res) mutable {
|
|
moved_promise->SetValue(std::move(res));
|
|
});
|
|
return future;
|
|
}
|
|
|
|
Future<std::expected<HttpResponse, HttpClient::ErrorCode>>
|
|
HttpClient::AsyncPost(const std::string &url,
|
|
const std::string &data,
|
|
const RequestOptions &request_options,
|
|
ResponseInfo *response_info)
|
|
{
|
|
Promise<std::expected<HttpResponse, ErrorCode>> promise;
|
|
auto future = promise.GetFuture();
|
|
auto moved_promise = MakeMoveOnCopy(promise);
|
|
GetHttpChannel(url)->AsyncPost(url, options_, data, request_options, response_info,
|
|
[moved_promise](std::expected<HttpResponse, ErrorCode> res) mutable {
|
|
moved_promise->SetValue(std::move(res));
|
|
});
|
|
return future;
|
|
}
|
|
|
|
Future<std::expected<HttpResponse, HttpClient::ErrorCode>>
|
|
HttpClient::AsyncRequest(const std::string &protocol,
|
|
const std::string &host,
|
|
const HttpRequest &request,
|
|
const RequestOptions &request_options,
|
|
ResponseInfo *response_info)
|
|
{
|
|
Promise<std::expected<HttpResponse, ErrorCode>> promise;
|
|
auto future = promise.GetFuture();
|
|
auto moved_promise = MakeMoveOnCopy(promise);
|
|
GetHttpChannel(protocol)->AsyncRequest(protocol, host, options_, request, request_options, response_info,
|
|
[moved_promise](std::expected<HttpResponse, ErrorCode> res) mutable {
|
|
moved_promise->SetValue(std::move(res));
|
|
});
|
|
return future;
|
|
}
|
|
|
|
}// namespace tile
|