From 20a95213df41cc4e9dd12bf5fafa101e4a5453b2 Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Sun, 29 May 2022 10:19:20 +0200 Subject: [PATCH 1/4] Remove libuv / simpler curl http --- CMakeLists.txt | 7 +- include/playapi/util/http.h | 19 +--- include/playapi/util/http_request_pool.h | 77 -------------- lib/playapi/util/http.cpp | 83 +++++++-------- lib/playapi/util/http_request_pool.cpp | 124 ----------------------- 5 files changed, 48 insertions(+), 262 deletions(-) delete mode 100644 include/playapi/util/http_request_pool.h delete mode 100644 lib/playapi/util/http_request_pool.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c210bfc..572c75f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,6 @@ find_package(Threads REQUIRED) find_package(ZLIB REQUIRED) find_package(CURL REQUIRED) find_package(Protobuf REQUIRED) -find_package(LibUV REQUIRED) if (NOT DEFINED Protobuf_LIBRARIES) set(Protobuf_LIBRARIES ${PROTOBUF_LIBRARIES}) @@ -19,10 +18,10 @@ endif() protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS proto/gsf.proto proto/play_common.proto proto/play_document.proto proto/play_respone.proto proto/play_settings.proto proto/play_toc.proto proto/play_download.proto proto/play_filter_rules.proto proto/play_ownership.proto proto/play_containers.proto proto/play_link.proto proto/play_device_config.proto proto/play_search.proto proto/play_browse.proto proto/play_details.proto) set(LIB_UTIL_SOURCE_FILES lib/playapi/util/http.cpp include/playapi/util/http.h lib/playapi/util/config.cpp include/playapi/util/config.h lib/playapi/util/rand.cpp include/playapi/util/rand.h lib/playapi/util/base64.cpp include/playapi/util/base64.h) -set(LIB_SOURCE_FILES lib/playapi/login.cpp include/playapi/login.h lib/playapi/device_info.cpp include/playapi/device_info.h lib/playapi/checkin.cpp include/playapi/checkin.h include/playapi/api.h lib/playapi/api.cpp lib/playapi/experiments.cpp include/playapi/experiments.h include/playapi/login_cache.h include/playapi/file_login_cache.h lib/playapi/file_login_cache.cpp include/playapi/task.h include/playapi/util/http_request_pool.h lib/playapi/util/http_request_pool.cpp include/playapi/http_task.h) +set(LIB_SOURCE_FILES lib/playapi/login.cpp include/playapi/login.h lib/playapi/device_info.cpp include/playapi/device_info.h lib/playapi/checkin.cpp include/playapi/checkin.h include/playapi/api.h lib/playapi/api.cpp lib/playapi/experiments.cpp include/playapi/experiments.h include/playapi/login_cache.h include/playapi/file_login_cache.h lib/playapi/file_login_cache.cpp include/playapi/task.h include/playapi/http_task.h) add_library(gplayapi STATIC ${LIB_SOURCE_FILES} ${LIB_UTIL_SOURCE_FILES} ${PROTO_SRCS}) -target_link_libraries(gplayapi ${CURL_LIBRARIES} ${ZLIB_LIBRARIES} ${Protobuf_LIBRARIES} ${LIBUV_LIBRARIES} Threads::Threads) -target_include_directories(gplayapi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CURL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS} ${Protobuf_INCLUDE_DIRS} ${LIBUV_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) +target_link_libraries(gplayapi ${CURL_LIBRARIES} ${ZLIB_LIBRARIES} ${Protobuf_LIBRARIES} Threads::Threads) +target_include_directories(gplayapi PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CURL_INCLUDE_DIR} ${ZLIB_INCLUDE_DIRS} ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR}) add_executable(gplaydl src/gplaydl.cpp src/common.cpp src/config.cpp src/config.h) target_link_libraries(gplaydl gplayapi) diff --git a/include/playapi/util/http.h b/include/playapi/util/http.h index 7bcafc2..af7a991 100644 --- a/include/playapi/util/http.h +++ b/include/playapi/util/http.h @@ -6,7 +6,6 @@ #include #include #include -#include "http_request_pool.h" namespace playapi { @@ -30,14 +29,13 @@ struct http_response { private: - CURL* curl; CURLcode curlCode; long statusCode; std::string body; public: - http_response(CURL* curl, CURLcode curlCode, long statusCode, std::string body); + http_response(CURLcode curlCode, long statusCode, std::string body); http_response(http_response&& r); ~http_response(); @@ -80,18 +78,6 @@ class http_request { static int curl_xferinfo(void* ptr, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); CURL* build(std::stringstream& output, bool copy_body = false); - - struct pool_entry : public http_request_pool::base_entry { - std::stringstream output; - std::function success; - std::function error; - progress_callback callback_progress; - output_callback callback_output; - - ~pool_entry() override = default; - void done(CURL* curl, CURLcode code) override; - }; - public: http_request() {} @@ -124,8 +110,7 @@ class http_request { http_response perform(); - CURL* perform(std::function success, std::function error, - http_request_pool& pool = http_request_pool::default_instance()); + void perform(std::function success, std::function error); }; diff --git a/include/playapi/util/http_request_pool.h b/include/playapi/util/http_request_pool.h deleted file mode 100644 index 556ec7c..0000000 --- a/include/playapi/util/http_request_pool.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace playapi { - -class http_request_pool { - -private: - struct socket_data { - http_request_pool* pool; - curl_socket_t socket; - uv_poll_t poll_handle; - - socket_data(http_request_pool* pool, curl_socket_t s); - - void delete_later(); - }; - - CURLM* curlm; - std::thread thread; - uv_loop_t loop; - uv_timer_t timeout_timer; - uv_async_t notify_handle; - std::mutex mutex; - bool stopping = false; - std::vector add_queue, remove_queue; - - void run(); - - void interrupt(); - - void handle_interrupt(); - - void handle_socket_action(curl_socket_t socket, int flags); - - static int curl_socket_func(CURL* curl, curl_socket_t s, int what, void* userp, void* socketp); - - static int curl_timer_func(CURLM* multi, long timeout_ms, void *userp); - -public: - static http_request_pool& default_instance() { - static http_request_pool pool; - return pool; - } - - struct base_entry { - virtual ~base_entry() = default; - virtual void done(CURL* curl, CURLcode code) = 0; - }; - - http_request_pool(); - ~http_request_pool(); - - CURLM* handle() { return curlm; } - - void add(CURL* curl) { - mutex.lock(); - add_queue.push_back(curl); - mutex.unlock(); - interrupt(); - } - - void remove(CURL* curl) { - mutex.lock(); - remove_queue.push_back(curl); - mutex.unlock(); - interrupt(); - } - -}; - -} \ No newline at end of file diff --git a/lib/playapi/util/http.cpp b/lib/playapi/util/http.cpp index 3c32283..208181a 100644 --- a/lib/playapi/util/http.cpp +++ b/lib/playapi/util/http.cpp @@ -1,7 +1,9 @@ #include #include +#include #include +#include using namespace playapi; @@ -35,32 +37,27 @@ std::string url_encoded_entity::encode() const { return std::move(ret); } -http_response::http_response(CURL* curl, CURLcode curlCode, long statusCode, std::string body) : - curl(curl), curlCode(curlCode), statusCode(statusCode), body(std::move(body)) { +http_response::http_response(CURLcode curlCode, long statusCode, std::string body) : + curlCode(curlCode), statusCode(statusCode), body(std::move(body)) { // } -http_response::http_response(http_response&& r) : curl(r.curl), curlCode(r.curlCode), statusCode(r.statusCode), +http_response::http_response(http_response&& r) : curlCode(r.curlCode), statusCode(r.statusCode), body(r.body) { - r.curl = nullptr; r.curlCode = CURLE_FAILED_INIT; r.statusCode = 0; r.body = std::string(); } http_response& http_response::operator=(http_response&& r) { - curl = r.curl; curlCode = r.curlCode; body = r.body; - r.curl = nullptr; r.curlCode = CURLE_FAILED_INIT; r.body = std::string(); return *this; } http_response::~http_response() { - curl_easy_cleanup(curl); - curl = nullptr; } void http_request::set_body(const url_encoded_entity& ent) { @@ -173,36 +170,42 @@ http_response http_request::perform() { #ifndef NDEBUG printf("http response body: %s\n", output.str().c_str()); #endif - return http_response(curl, ret, status, output.str()); -} - -CURL* http_request::perform(std::function success, std::function error, - http_request_pool& pool) { - pool_entry* ei = new pool_entry; - ei->success = success; - ei->error = error; - CURL* curl = build(ei->output, true); - if (callback_output) { - ei->callback_output = callback_output; - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ei->callback_output); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_func); - } - if (callback_progress) { - ei->callback_progress = callback_progress; - curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curl_xferinfo); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &ei->callback_progress); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); - } - curl_easy_setopt(curl, CURLOPT_PRIVATE, ei); - pool.add(curl); - return curl; + curl_easy_cleanup(curl); + return http_response(ret, status, output.str()); +} + +void http_request::perform(std::function success, std::function error) { + std::thread([req = *this, success, error] mutable { + std::stringstream output; + CURL* curl = req.build(output); + char errbuf[CURL_ERROR_SIZE]; + curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); + if (req.callback_output) { + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &req.callback_output); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_func); + } + if (req.callback_progress) { + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curl_xferinfo); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &req.callback_progress); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); + } + CURLcode curlerr = curl_easy_perform(curl); + if (curlerr == CURLE_OK) { + long status; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); + #ifndef NDEBUG + printf("http response body: %s\n", output.str().c_str()); + #endif + success(http_response(curlerr, status, output.str())); + } else { + std::stringstream errormsg; + errormsg << "Failed to perform http request to " << req.url << " : CURLcode " << curlerr << " Details: " << errbuf; + try { + throw std::runtime_error(errormsg.str().data()); + } catch (...) { + error(std::current_exception()); + } + } + curl_easy_cleanup(curl); + }).detach(); } - -void http_request::pool_entry::done(CURL* curl, CURLcode code) { - long status; - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); -#ifndef NDEBUG - printf("http response body: %s\n", output.str().c_str()); -#endif - success(http_response(curl, code, status, output.str())); -} \ No newline at end of file diff --git a/lib/playapi/util/http_request_pool.cpp b/lib/playapi/util/http_request_pool.cpp deleted file mode 100644 index 4612120..0000000 --- a/lib/playapi/util/http_request_pool.cpp +++ /dev/null @@ -1,124 +0,0 @@ -#include -#include -#include -#include - -using namespace playapi; - -http_request_pool::http_request_pool() { - curlm = curl_multi_init(); - - uv_loop_init(&loop); - uv_timer_init(&loop, &timeout_timer); - timeout_timer.data = this; - uv_async_init(&loop, ¬ify_handle, [](uv_async_t* handle) { - ((http_request_pool*) handle->data)->handle_interrupt(); - }); - notify_handle.data = this; - thread = std::thread(std::bind(&http_request_pool::run, this)); -} - -http_request_pool::~http_request_pool() { - mutex.lock(); - stopping = true; - mutex.unlock(); - interrupt(); - thread.join(); - uv_loop_close(&loop); -} - -void http_request_pool::interrupt() { - uv_async_send(¬ify_handle); -} - -void http_request_pool::handle_interrupt() { - std::unique_lock lock(mutex); - if (stopping) { - curl_multi_cleanup(curlm); - uv_close((uv_handle_t*) &timeout_timer, [](uv_handle_t*){}); - uv_close((uv_handle_t*) ¬ify_handle, [](uv_handle_t*){}); - uv_stop(&loop); - return; - } - for (CURL* handle : add_queue) - curl_multi_add_handle(curlm, handle); - for (CURL* handle : remove_queue) - curl_multi_remove_handle(curlm, handle); -} - -http_request_pool::socket_data::socket_data(http_request_pool* pool, curl_socket_t s) : pool(pool), socket(s) { - uv_poll_init_socket(&pool->loop, &poll_handle, s); - poll_handle.data = this; -} - -void http_request_pool::socket_data::delete_later() { - uv_close((uv_handle_t *) &poll_handle, [](uv_handle_t* handle) { - delete (http_request_pool::socket_data*) handle->data; - }); -} - -void http_request_pool::handle_socket_action(curl_socket_t socket, int flags) { - int handles; - curl_multi_socket_action(curlm, socket, flags, &handles); - - struct CURLMsg* m; - while ((m = curl_multi_info_read(curlm, &handles))) { - if (m->msg == CURLMSG_DONE) { - base_entry* entry; - curl_easy_getinfo(m->easy_handle, CURLINFO_PRIVATE, &entry); - curl_easy_setopt(m->easy_handle, CURLOPT_PRIVATE, NULL); - entry->done(m->easy_handle, m->data.result); - delete entry; - } - } -} - -int http_request_pool::curl_socket_func(CURL* curl, curl_socket_t s, int what, void* userp, void* socketp) { - socket_data* socketd = (socket_data*) socketp; - if (what == CURL_POLL_IN || what == CURL_POLL_OUT || what == CURL_POLL_INOUT) { - if (socketd == nullptr) { - socketd = new socket_data((http_request_pool*) userp, s); - curl_multi_assign(((http_request_pool*) userp)->curlm, s, socketd); - } - int mask = 0; - if (what == CURL_POLL_IN || what == CURL_POLL_INOUT) - mask |= UV_READABLE; - if (what == CURL_POLL_OUT || what == CURL_POLL_INOUT) - mask |= UV_WRITABLE; - uv_poll_start(&socketd->poll_handle, mask, [](uv_poll_t* handle, int status, int events) { - socket_data* data = (socket_data*) handle->data; - int flags = 0; - if (events & UV_READABLE) - flags |= CURL_CSELECT_IN; - if (events & UV_WRITABLE) - flags |= CURL_CSELECT_OUT; - data->pool->handle_socket_action(data->socket, flags); - }); - } else if (what == CURL_POLL_REMOVE && socketd) { - uv_poll_stop(&socketd->poll_handle); - socketd->delete_later(); - curl_multi_assign(((http_request_pool*) userp)->curlm, s, nullptr); - } - return 0; -} - -int http_request_pool::curl_timer_func(CURLM* multi, long timeout_ms, void* userp) { - if (timeout_ms <= 0) { - uv_timer_stop(&((http_request_pool*) userp)->timeout_timer); - if (timeout_ms == 0) - ((http_request_pool*) userp)->handle_socket_action(CURL_SOCKET_TIMEOUT, 0); - } else { - uv_timer_start(&((http_request_pool*) userp)->timeout_timer, [](uv_timer_t* handle) { - ((http_request_pool*) handle->data)->handle_socket_action(CURL_SOCKET_TIMEOUT, 0); - }, timeout_ms, 0); - } - return 0; -} - -void http_request_pool::run() { - curl_multi_setopt(curlm, CURLMOPT_SOCKETFUNCTION, curl_socket_func); - curl_multi_setopt(curlm, CURLMOPT_SOCKETDATA, this); - curl_multi_setopt(curlm, CURLMOPT_TIMERFUNCTION, curl_timer_func); - curl_multi_setopt(curlm, CURLMOPT_TIMERDATA, this); - uv_run(&loop, UV_RUN_DEFAULT); -} From 0a7e45ed40e50e5678618cf71b55109c189826be Mon Sep 17 00:00:00 2001 From: Christopher Homberger Date: Thu, 9 Jun 2022 21:42:15 +0200 Subject: [PATCH 2/4] Only use plain c++11 for http_request::perform --- lib/playapi/util/http.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/playapi/util/http.cpp b/lib/playapi/util/http.cpp index 208181a..a806306 100644 --- a/lib/playapi/util/http.cpp +++ b/lib/playapi/util/http.cpp @@ -175,18 +175,19 @@ http_response http_request::perform() { } void http_request::perform(std::function success, std::function error) { - std::thread([req = *this, success, error] mutable { + auto req = std::make_shared(*this); + std::thread([req, success, error] { std::stringstream output; - CURL* curl = req.build(output); + CURL* curl = req->build(output); char errbuf[CURL_ERROR_SIZE]; curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); - if (req.callback_output) { - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &req.callback_output); + if (req->callback_output) { + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &req->callback_output); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_func); } - if (req.callback_progress) { + if (req->callback_progress) { curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, curl_xferinfo); - curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &req.callback_progress); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &req->callback_progress); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); } CURLcode curlerr = curl_easy_perform(curl); @@ -199,7 +200,7 @@ void http_request::perform(std::function success, std::func success(http_response(curlerr, status, output.str())); } else { std::stringstream errormsg; - errormsg << "Failed to perform http request to " << req.url << " : CURLcode " << curlerr << " Details: " << errbuf; + errormsg << "Failed to perform http request to " << req->url << " : CURLcode " << curlerr << " Details: " << errbuf; try { throw std::runtime_error(errormsg.str().data()); } catch (...) { From d57d575089d8f8e104f14cecfb88ae37e1025a05 Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Sat, 11 Jun 2022 22:13:44 +0200 Subject: [PATCH 3/4] Add missing brackets --- lib/playapi/util/http.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/playapi/util/http.cpp b/lib/playapi/util/http.cpp index a806306..0ac073d 100644 --- a/lib/playapi/util/http.cpp +++ b/lib/playapi/util/http.cpp @@ -176,7 +176,7 @@ http_response http_request::perform() { void http_request::perform(std::function success, std::function error) { auto req = std::make_shared(*this); - std::thread([req, success, error] { + std::thread([req, success, error]() { std::stringstream output; CURL* curl = req->build(output); char errbuf[CURL_ERROR_SIZE]; From 7b01831fe1b0e35e94578e89aa4644023f97fa40 Mon Sep 17 00:00:00 2001 From: KanuX-14 Date: Sun, 12 Jun 2022 09:44:36 +0000 Subject: [PATCH 4/4] Include for returned error from request --- lib/playapi/util/http.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/playapi/util/http.cpp b/lib/playapi/util/http.cpp index 0ac073d..c138f8f 100644 --- a/lib/playapi/util/http.cpp +++ b/lib/playapi/util/http.cpp @@ -4,6 +4,7 @@ #include #include #include +#include using namespace playapi;