diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a45b4014c..268d712c53 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -75,6 +75,8 @@ message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") find_package(Threads REQUIRED) # GLOG find_package(glog CONFIG REQUIRED) +# curl +find_package(CURL CONFIG REQUIRED) # diff --git a/examples/simple/main.cc b/examples/simple/main.cc index c3af257af1..88df546f22 100644 --- a/examples/simple/main.cc +++ b/examples/simple/main.cc @@ -1,9 +1,11 @@ #include #include "base/bind.h" +#include "base/bind_post_task.h" #include "base/callback.h" #include "base/init.h" #include "base/logging.h" +#include "base/net/simple_url_loader.h" #include "base/synchronization/auto_signaller.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" @@ -138,8 +140,61 @@ void ThreadPoolSingleThreadExample() { pool.Stop(); } +void NetExample() { + base::Thread thread{}; + thread.Start(); + + base::WaitableEvent finished_event{}; + auto response_callback = base::BindOnce( + [](base::WaitableEvent* event, base::net::ResourceResponse response) { + LOG(INFO) << "Result: " << static_cast(response.result); + LOG(INFO) << "HTTP code: " << response.code; + LOG(INFO) << "Final URL: " << response.final_url; + LOG(INFO) << "Downloaded " << response.data.size() << " bytes"; + LOG(INFO) << "Latency: " << response.timing_connect.InMilliseconds() + << "ms"; + LOG(INFO) << "Headers"; + for (const auto& [h, v] : response.headers) { + LOG(INFO) << " " << h << ": " << v; + } + LOG(INFO) << "Content:\n" + << std::string{response.data.begin(), response.data.end()}; + event->Signal(); + }, + &finished_event); + + // Try to download and signal on finish + base::net::SimpleUrlLoader::DownloadUnbounded( + base::net::ResourceRequest{ + "https://www.google.com/robots.txt", + base::net::kDefaultHeaders, + base::net::kNoTimeout, + base::net::kNoTimeout, + true, + true, + }, + base::BindPostTask(thread.TaskRunner(), std::move(response_callback), + FROM_HERE)); + + // Timeout = 5s + thread.TaskRunner()->PostDelayedTask( + FROM_HERE, + base::BindOnce( + [](base::WaitableEvent* event) { + LOG(WARNING) << "Failed to download in 5 seconds"; + event->Signal(); + }, + &finished_event), + base::Seconds(5)); + + finished_event.Wait(); +} + int main(int argc, char* argv[]) { - base::Initialize(argc, argv); + base::InitOptions init_options; + init_options.InitializeNetworking = true; + + base::Initialize(argc, argv, std::move(init_options)); const auto timer = base::ElapsedTimer{}; @@ -149,6 +204,8 @@ int main(int argc, char* argv[]) { ThreadPoolSequencedExample(); ThreadPoolSingleThreadExample(); + NetExample(); + LOG(INFO) << "Example finished in " << timer.Elapsed().InMillisecondsF() << "ms"; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b38937cc99..2a59658881 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,7 +32,8 @@ target_include_directories(libbase PUBLIC target_link_libraries(libbase PUBLIC ${LIBBASE_LINK_FLAGS} Threads::Threads - glog::glog) + glog::glog + CURL::libcurl) target_sources(libbase PRIVATE @@ -40,69 +41,78 @@ target_sources(libbase base/barrier_callback.h base/barrier_closure.cc base/barrier_closure.h - base/bind.h base/bind_internals.h base/bind_post_task.h - base/callback.h + base/bind.h base/callback_forward.h base/callback_helpers.cc base/callback_helpers.h base/callback_iface.h base/callback_internals.h + base/callback.h base/init.cc base/init.h base/logging.cc base/logging.h base/memory/weak_ptr.h - base/message_loop/message_loop.h base/message_loop/message_loop_impl.cc base/message_loop/message_loop_impl.h - base/message_loop/message_pump.h + base/message_loop/message_loop.h base/message_loop/message_pump_impl.cc base/message_loop/message_pump_impl.h + base/message_loop/message_pump.h + base/net/impl/net_thread_impl.cc + base/net/impl/net_thread_impl.h + base/net/impl/net_thread.cc + base/net/impl/net_thread.h + base/net/resource_request.h + base/net/resource_response.h + base/net/result.h + base/net/simple_url_loader.cc + base/net/simple_url_loader.h base/sequence_checker.cc base/sequence_checker.h base/sequence_id.cc base/sequence_id.h - base/sequenced_task_runner.h base/sequenced_task_runner_helpers.cc base/sequenced_task_runner_helpers.h base/sequenced_task_runner_internals.h + base/sequenced_task_runner.h base/single_thread_task_runner.h base/source_location.h base/synchronization/auto_signaller.cc base/synchronization/auto_signaller.h base/synchronization/waitable_event.cc base/synchronization/waitable_event.h + base/task_runner_internals.h base/task_runner.cc base/task_runner.h - base/task_runner_internals.h - base/threading/delayed_task_manager.cc - base/threading/delayed_task_manager.h base/threading/delayed_task_manager_shared_instance.cc base/threading/delayed_task_manager_shared_instance.h + base/threading/delayed_task_manager.cc + base/threading/delayed_task_manager.h base/threading/sequenced_task_runner_handle.cc base/threading/sequenced_task_runner_handle.h base/threading/task_runner_impl.cc base/threading/task_runner_impl.h - base/threading/thread.cc - base/threading/thread.h base/threading/thread_pool.cc base/threading/thread_pool.h - base/time/time.cc - base/time/time.h + base/threading/thread.cc + base/threading/thread.h base/time/time_delta.cc base/time/time_delta.h base/time/time_ticks.cc base/time/time_ticks.h + base/time/time.cc + base/time/time.h base/timer/elapsed_timer.cc base/timer/elapsed_timer.h base/trace_event/trace_argument_packer.h base/trace_event/trace_async.h base/trace_event/trace_complete.h base/trace_event/trace_counter.h - base/trace_event/trace_event.h base/trace_event/trace_event_register.h + base/trace_event/trace_event.h base/trace_event/trace_events.cc base/trace_event/trace_events.h base/trace_event/trace_flow.h diff --git a/src/base/init.cc b/src/base/init.cc index a42139aa59..a735f9fc9c 100644 --- a/src/base/init.cc +++ b/src/base/init.cc @@ -1,27 +1,51 @@ #include "base/init.h" #include "base/logging.h" +#include "base/net/impl/net_thread.h" namespace base { +namespace { +InitOptions g_init_options; +} + // NOLINTNEXTLINE(modernize-avoid-c-arrays) -void Initialize(int /*argc*/, char* argv[]) { - FLAGS_logtostderr = true; +void Initialize(int /*argc*/, char* argv[], InitOptions options) { + g_init_options = options; + + FLAGS_logtostderr = g_init_options.LogToStderr; google::InitGoogleLogging(argv[0]); google::InstallPrefixFormatter(&detail::LogFormatter); google::InstallFailureSignalHandler(); + + if (g_init_options.InitializeNetworking) { + // Initialize Networking thread + net::NetThread::GetInstance().Start(); + } } // NOLINTNEXTLINE(modernize-avoid-c-arrays) -void InitializeForTests(int /*argc*/, char* argv[]) { - FLAGS_logtostderr = true; +void InitializeForTests(int /*argc*/, char* argv[], InitOptions options) { + g_init_options = options; + + FLAGS_logtostderr = g_init_options.LogToStderr; google::InitGoogleLogging(argv[0]); google::InstallPrefixFormatter(&detail::LogFormatter); + + if (g_init_options.InitializeNetworking) { + // Initialize Networking thread + net::NetThread::GetInstance().Start(); + } } void Deinitialize() { + if (g_init_options.InitializeNetworking) { + // Deinitialize Networking thread + net::NetThread::GetInstance().Stop(); + } + google::ShutdownGoogleLogging(); } diff --git a/src/base/init.h b/src/base/init.h index f26a956b31..b401e85de4 100644 --- a/src/base/init.h +++ b/src/base/init.h @@ -2,10 +2,18 @@ namespace base { +struct InitOptions { + // Logging + bool LogToStderr = true; + + // Networking + bool InitializeNetworking = false; +}; + // NOLINTNEXTLINE(modernize-avoid-c-arrays) -void Initialize(int argc, char* argv[]); +void Initialize(int argc, char* argv[], InitOptions options); // NOLINTNEXTLINE(modernize-avoid-c-arrays) -void InitializeForTests(int argc, char* argv[]); +void InitializeForTests(int argc, char* argv[], InitOptions options); void Deinitialize(); } // namespace base diff --git a/src/base/net/impl/net_thread.cc b/src/base/net/impl/net_thread.cc new file mode 100644 index 0000000000..3a74b07e75 --- /dev/null +++ b/src/base/net/impl/net_thread.cc @@ -0,0 +1,40 @@ +#include "base/net/impl/net_thread.h" + +#include "base/logging.h" +#include "base/net/impl/net_thread_impl.h" + +namespace base { +namespace net { + +// static +NetThread& NetThread::GetInstance() { + static NetThread instance; + return instance; +} + +void NetThread::Start() { + DCHECK(!impl_); + + impl_ = std::make_unique(); + impl_->Start(); +} + +void NetThread::Stop() { + DCHECK(impl_); + + impl_->Stop(); + impl_.reset(); +} + +void NetThread::EnqueueDownload( + ResourceRequest request, + std::optional max_response_size_bytes, + OnceCallback on_done_callback) { + DCHECK(impl_); + + impl_->EnqueueDownload(std::move(request), std::move(max_response_size_bytes), + std::move(on_done_callback)); +} + +} // namespace net +} // namespace base diff --git a/src/base/net/impl/net_thread.h b/src/base/net/impl/net_thread.h new file mode 100644 index 0000000000..c498f6125c --- /dev/null +++ b/src/base/net/impl/net_thread.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +#include "base/callback.h" +#include "base/net/resource_request.h" +#include "base/net/resource_response.h" + +namespace base { +namespace net { + +class NetThread { + public: + static NetThread& GetInstance(); + + void Start(); + void Stop(); + + void EnqueueDownload(ResourceRequest request, + std::optional max_response_size_bytes, + OnceCallback on_done_callback); + + private: + class NetThreadImpl; + + std::unique_ptr impl_; +}; + +} // namespace net +} // namespace base diff --git a/src/base/net/impl/net_thread_impl.cc b/src/base/net/impl/net_thread_impl.cc new file mode 100644 index 0000000000..b1168c1692 --- /dev/null +++ b/src/base/net/impl/net_thread_impl.cc @@ -0,0 +1,302 @@ +#include "base/net/impl/net_thread_impl.h" + +namespace base { +namespace net { + +namespace { +Result CurlCodeToNetResult(CURLcode code) { + switch (code) { + case CURLE_OK: + return Result::kOk; + + case CURLE_ABORTED_BY_CALLBACK: + return Result::kAborted; + + default: + return Result::kError; + } +} +} // namespace + +struct NetThread::NetThreadImpl::DownloadInfo { + ResourceRequest request; + std::optional max_response_size_bytes; + + struct curl_slist* headers = nullptr; + + ResourceResponse response; + OnceCallback on_done_callback; +}; + +// +// Logic executed on main thread +// + +NetThread::NetThreadImpl::NetThreadImpl() + : not_quit_(), not_modified_(), multi_handle_(nullptr) { + curl_global_init(CURL_GLOBAL_DEFAULT); +} + +NetThread::NetThreadImpl::~NetThreadImpl() { + if (thread_) { + Stop(); + } + + curl_global_cleanup(); +} + +void NetThread::NetThreadImpl::Start() { + CHECK(!thread_); + + not_quit_.test_and_set(); + not_modified_.test_and_set(); + + multi_handle_ = curl_multi_init(); + thread_ = + std::make_unique(&NetThreadImpl::RunLoop_NetThread, this); +} + +void NetThread::NetThreadImpl::Stop() { + not_quit_.clear(); + curl_multi_wakeup(multi_handle_); + + thread_->join(); + thread_.reset(); + + curl_multi_cleanup(multi_handle_); + multi_handle_ = nullptr; +} + +void NetThread::NetThreadImpl::EnqueueDownload( + ResourceRequest request, + std::optional max_response_size_bytes, + OnceCallback on_done_callback) { + DCHECK(thread_); + DCHECK(on_done_callback); + + { + std::unique_lock lock(mutex_); + pending_add_downloads_.push_back(DownloadInfo{ + std::move(request), + std::move(max_response_size_bytes), + nullptr, + {}, + std::move(on_done_callback), + }); + } + + not_modified_.clear(); + curl_multi_wakeup(multi_handle_); +} + +// +// Logic executed on NetThread +// + +void NetThread::NetThreadImpl::RunLoop_NetThread() { + while (not_quit_.test_and_set()) { + ProcessPendingActions_NetThread(); + Perform_NetThread(); + ProcessCompleted_NetThread(); + Wait_NetThread(); + } + + AbortAllDownloads_NetThread(); +} + +void NetThread::NetThreadImpl::ProcessPendingActions_NetThread() { + if (!not_modified_.test_and_set()) { + std::unique_lock lock(mutex_); + for (DownloadInfo& download : pending_add_downloads_) { + EnqueueDownload_NetThread(download); + } + pending_add_downloads_.clear(); + // TODO: handle pending_cancel_downloads_ + } +} + +void NetThread::NetThreadImpl::Perform_NetThread() { + int active = 0; + CURLMcode result = CURLM_OK; + do { + result = curl_multi_perform(multi_handle_, &active); + } while (result == CURLM_CALL_MULTI_PERFORM); + + if (result != CURLM_OK) { + LOG(ERROR) << __FUNCTION__ << "() curl_multi_perform failed: " + << curl_multi_strerror(result); + AbortAllDownloads_NetThread(); + } +} + +void NetThread::NetThreadImpl::ProcessCompleted_NetThread() { + CURLMsg* msg = nullptr; + int msgs_left = 0; + while ((msg = curl_multi_info_read(multi_handle_, &msgs_left))) { + if (msg->msg == CURLMSG_DONE) { + DownloadFinished_NetThread(msg->easy_handle, + CurlCodeToNetResult(msg->data.result)); + } + } +} + +void NetThread::NetThreadImpl::Wait_NetThread() { + const auto kWaitTimeout = base::Milliseconds(1000); + + int numfds = 0; + CURLMcode result = + curl_multi_poll(multi_handle_, nullptr, 0, + static_cast(kWaitTimeout.InMilliseconds()), &numfds); + if (result != CURLM_OK) { + LOG(ERROR) << __FUNCTION__ + << "() curl_multi_poll failed: " << curl_multi_strerror(result); + } +} + +void NetThread::NetThreadImpl::EnqueueDownload_NetThread( + DownloadInfo& download_info) { + CURL* easy_handle = curl_easy_init(); + + // Set easy handle's options + curl_easy_setopt(easy_handle, CURLOPT_PRIVATE, this); + curl_easy_setopt(easy_handle, CURLOPT_URL, download_info.request.url.c_str()); + if (download_info.request.follow_redirects) { + curl_easy_setopt(easy_handle, CURLOPT_FOLLOWLOCATION, 1L); + } + if (download_info.request.headers_only) { + curl_easy_setopt(easy_handle, CURLOPT_NOBODY, 1L); + } + for (const auto& header : download_info.request.headers) { + download_info.headers = + curl_slist_append(download_info.headers, header.c_str()); + } + if (download_info.headers) { + curl_easy_setopt(easy_handle, CURLOPT_HTTPHEADER, download_info.headers); + } + if (!download_info.request.connect_timeout.IsZero()) { + curl_easy_setopt(easy_handle, CURLOPT_CONNECTTIMEOUT_MS, + download_info.request.connect_timeout.InMilliseconds()); + } + if (!download_info.request.timeout.IsZero()) { + curl_easy_setopt(easy_handle, CURLOPT_TIMEOUT_MS, + download_info.request.timeout.InMilliseconds()); + } + + // Save download request/response data + active_downloads_.insert({easy_handle, std::move(download_info)}); + const auto& inserted_info = active_downloads_[easy_handle]; + + // WARNING: Lambda has to be converted to function pointer or it will crash! + curl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, (void*)(&inserted_info)); + curl_easy_setopt( + easy_handle, CURLOPT_WRITEFUNCTION, + +[](char* data, size_t n, size_t l, void* userp) -> size_t { + auto* info = static_cast(userp); + if (info->max_response_size_bytes && + info->response.data.size() + (n * l) > + *info->max_response_size_bytes) { + // Force error + return 0; + } + info->response.data.insert(info->response.data.end(), + reinterpret_cast(data), + reinterpret_cast(data) + n * l); + return n * l; + }); + + curl_multi_add_handle(multi_handle_, easy_handle); +} + +void NetThread::NetThreadImpl::DownloadFinished_NetThread(CURL* finished_curl, + Result result) { + CHECK_GT(active_downloads_.count(finished_curl), 0); + + auto& download = active_downloads_[finished_curl]; + + // Result + download.response.result = result; + + // Response code + if (download.response.result == Result::kOk) { + long http_code = 0; + curl_easy_getinfo(finished_curl, CURLINFO_RESPONSE_CODE, &http_code); + download.response.code = static_cast(http_code); + } else { + download.response.code = -1; + } + + // Get the URL of the completed transfer + { + char* final_url = nullptr; + curl_easy_getinfo(finished_curl, CURLINFO_EFFECTIVE_URL, &final_url); + + download.response.final_url = final_url ? final_url : ""; + } + + // Get final headers of the completed transfer + { + struct curl_header* header = nullptr; + while ((header = + curl_easy_nextheader(finished_curl, CURLH_HEADER, 0, header))) { + download.response.headers.insert({header->name, header->value}); + } + } + + // Get timing data + { + curl_off_t time = {}; + if (curl_easy_getinfo(finished_curl, CURLINFO_QUEUE_TIME_T, &time) == + CURLE_OK) { + download.response.timing_queue = Microseconds(time); + } + if (curl_easy_getinfo(finished_curl, CURLINFO_CONNECT_TIME_T, &time) == + CURLE_OK) { + download.response.timing_connect = Microseconds(time); + } + if (curl_easy_getinfo(finished_curl, CURLINFO_STARTTRANSFER_TIME_T, + &time) == CURLE_OK) { + download.response.timing_start_transfer = Microseconds(time); + } + if (curl_easy_getinfo(finished_curl, CURLINFO_TOTAL_TIME_T, &time) == + CURLE_OK) { + download.response.timing_total = Microseconds(time); + } + } + + DCHECK(download.on_done_callback); + std::move(download.on_done_callback).Run(std::move(download.response)); + + RemoveDownload_NetThread(finished_curl); +} + +void NetThread::NetThreadImpl::RemoveDownload_NetThread(CURL* finished_curl) { + // Remove the completed request from multi-handle + curl_multi_remove_handle(multi_handle_, finished_curl); + if (active_downloads_.count(finished_curl) > 0) { + if (active_downloads_[finished_curl].headers) { + curl_slist_free_all(active_downloads_[finished_curl].headers); + } + } + curl_easy_cleanup(finished_curl); + + // Remove the handle from our list + active_downloads_.erase(finished_curl); +} + +void NetThread::NetThreadImpl::AbortAllDownloads_NetThread() { + for (auto& [easy_handle, info] : active_downloads_) { + std::move(info.on_done_callback) + .Run(ResourceResponse{Result::kAborted, -1, {}, {}, {}, {}, {}, {}}); + + curl_multi_remove_handle(multi_handle_, easy_handle); + if (info.headers) { + curl_slist_free_all(info.headers); + } + curl_easy_cleanup(easy_handle); + } + + active_downloads_.clear(); +} + +} // namespace net +} // namespace base diff --git a/src/base/net/impl/net_thread_impl.h b/src/base/net/impl/net_thread_impl.h new file mode 100644 index 0000000000..8bd5ff6865 --- /dev/null +++ b/src/base/net/impl/net_thread_impl.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include + +#include "base/callback.h" +#include "base/net/impl/net_thread.h" +#include "base/net/resource_request.h" +#include "base/net/resource_response.h" +#include "base/net/result.h" + +#include "curl/curl.h" + +namespace base { +namespace net { + +class NetThread::NetThreadImpl { + public: + NetThreadImpl(); + ~NetThreadImpl(); + + void Start(); + void Stop(); + + void EnqueueDownload(ResourceRequest request, + std::optional max_response_size_bytes, + OnceCallback on_done_callback); + + private: + struct DownloadInfo; + + void RunLoop_NetThread(); + + // Parts of RunLoop + void ProcessPendingActions_NetThread(); + void Perform_NetThread(); + void ProcessCompleted_NetThread(); + void Wait_NetThread(); + + void EnqueueDownload_NetThread(DownloadInfo& download_info); + void DownloadFinished_NetThread(CURL* finished_curl, Result result); + void RemoveDownload_NetThread(CURL* finished_curl); + void AbortAllDownloads_NetThread(); + + // Accessible anywhere + std::atomic_flag not_quit_; + std::atomic_flag not_modified_; + + // Accessible with mutex + std::mutex mutex_; + std::vector pending_add_downloads_; + // TODO: add pending_cancel_downloads_; + + // Accessible on `thread_` + CURLM* multi_handle_; + std::map active_downloads_; + + // Accessible on IoThread owner thread + std::unique_ptr thread_; +}; + +} // namespace net +} // namespace base diff --git a/src/base/net/resource_request.h b/src/base/net/resource_request.h new file mode 100644 index 0000000000..8a6fac80e6 --- /dev/null +++ b/src/base/net/resource_request.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +#include "base/time/time_delta.h" + +namespace base { +namespace net { + +const auto kDefaultHeaders = std::vector{}; +const auto kNoTimeout = base::TimeDelta{}; + +struct ResourceRequest { + std::string url; + std::vector headers = kDefaultHeaders; + + base::TimeDelta connect_timeout = kNoTimeout; + base::TimeDelta timeout = kNoTimeout; + bool follow_redirects = true; + bool headers_only = false; +}; + +} // namespace net +} // namespace base diff --git a/src/base/net/resource_response.h b/src/base/net/resource_response.h new file mode 100644 index 0000000000..b12ac21808 --- /dev/null +++ b/src/base/net/resource_response.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + +#include "base/net/result.h" +#include "base/time/time_delta.h" + +namespace base { +namespace net { + +struct ResourceResponse { + Result result = Result::kOk; + int code = -1; + std::string final_url; + std::map headers; + std::vector data; + + base::TimeDelta timing_queue = base::Seconds(-1); + base::TimeDelta timing_connect = base::Seconds(-1); + base::TimeDelta timing_start_transfer = base::Seconds(-1); + base::TimeDelta timing_total = base::Seconds(-1); +}; + +} // namespace net +} // namespace base diff --git a/src/base/net/result.h b/src/base/net/result.h new file mode 100644 index 0000000000..360ad63c5e --- /dev/null +++ b/src/base/net/result.h @@ -0,0 +1,18 @@ +#pragma once + +namespace base { +namespace net { + +enum class Result { + // + kOk, + + // + kError, + + // + kAborted, +}; + +} // namespace net +} // namespace base diff --git a/src/base/net/simple_url_loader.cc b/src/base/net/simple_url_loader.cc new file mode 100644 index 0000000000..3122c5f3d2 --- /dev/null +++ b/src/base/net/simple_url_loader.cc @@ -0,0 +1,24 @@ +#include "base/net/simple_url_loader.h" + +#include + +#include "base/net/impl/net_thread.h" + +namespace base { +namespace net { + +void SimpleUrlLoader::DownloadUnbounded(ResourceRequest request, + ResultCallback on_done_callback) { + NetThread::GetInstance().EnqueueDownload(request, std::nullopt, + std::move(on_done_callback)); +} + +void SimpleUrlLoader::DownloadLimited(ResourceRequest request, + size_t max_response_size_bytes, + ResultCallback on_done_callback) { + NetThread::GetInstance().EnqueueDownload(request, max_response_size_bytes, + std::move(on_done_callback)); +} + +} // namespace net +} // namespace base diff --git a/src/base/net/simple_url_loader.h b/src/base/net/simple_url_loader.h new file mode 100644 index 0000000000..6bbfd3e8ee --- /dev/null +++ b/src/base/net/simple_url_loader.h @@ -0,0 +1,25 @@ +#pragma once + +#include "base/callback.h" +#include "base/net/resource_request.h" +#include "base/net/resource_response.h" + +namespace base { +namespace net { + +class SimpleUrlLoader { + public: + using ResultCallback = base::OnceCallback; + + static void DownloadUnbounded(ResourceRequest request, + ResultCallback on_done_callback); + static void DownloadLimited(ResourceRequest request, + size_t max_response_size_bytes, + ResultCallback on_done_callback); + + private: + // +}; + +} // namespace net +} // namespace base diff --git a/tests/perf/main.cc b/tests/perf/main.cc index 60fad01d09..4204a60f37 100644 --- a/tests/perf/main.cc +++ b/tests/perf/main.cc @@ -5,7 +5,7 @@ // `BENCHMARK_MAIN()` macro with `base` module initialized at the beginning // and deinitialized at the end. int main(int argc, char** argv) { - base::Initialize(argc, argv); + base::Initialize(argc, argv, {}); ::benchmark::Initialize(&argc, argv); if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { diff --git a/tests/unit/main.cc b/tests/unit/main.cc index 1208b12d73..a2e1393465 100644 --- a/tests/unit/main.cc +++ b/tests/unit/main.cc @@ -2,7 +2,7 @@ #include "gtest/gtest.h" int main(int argc, char* argv[]) { - base::InitializeForTests(argc, argv); + base::InitializeForTests(argc, argv, {}); ::testing::InitGoogleTest(&argc, argv); const auto result = RUN_ALL_TESTS(); diff --git a/vcpkg.json b/vcpkg.json index e4dc6279af..044619a5f7 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -19,6 +19,12 @@ "features": [ "customprefix" ] + }, + { + "name": "curl", + "features": [ + "ssl" + ] } ], "features": {