From ede75f556589ee36fdcd4df0e24908a678ddabe1 Mon Sep 17 00:00:00 2001 From: j842 Date: Mon, 9 Jun 2025 18:14:46 +1200 Subject: [PATCH] tidy --- .vscode/settings.json | 3 +++ CLAUDE.md | 18 ++++++++++++++- ipdemo/CMakeLists.txt | 6 ----- ipdemo/src/http_utils.cpp | 48 +++++++++++++++++++++++++++++++++++++++ ipdemo/src/http_utils.hpp | 17 ++++++++++++++ ipdemo/src/main.cpp | 43 +++++------------------------------ 6 files changed, 91 insertions(+), 44 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 ipdemo/src/http_utils.cpp create mode 100644 ipdemo/src/http_utils.hpp diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6c48881 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "cmake.sourceDirectory": "/home/j/code/dropshell-build/ipdemo" +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 42273e9..7d4e2d8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -114,4 +114,20 @@ Applications can use: - MariaDB via connector at `/usr/local/mariadb-connector-c/` - SQLite3 at `/usr/local/sqlite3/` -All database libraries are statically linked into the final binary. \ No newline at end of file +All database libraries are statically linked into the final binary. + +### HTTP Client Utilities + +The ipdemo includes a blocking HTTP client utility (`src/http_utils.hpp`) that wraps Drogon's asynchronous HttpClient: + +```cpp +#include "http_utils.hpp" + +// Make a blocking HTTP GET request +auto response = http_get("example.com", "/api/endpoint", true, 10.0); // HTTPS with 10s timeout +if (response.success && response.status_code == 200) { + std::cout << response.body << std::endl; +} +``` + +This utility handles the threading complexity and provides a simple synchronous interface for HTTP requests. \ No newline at end of file diff --git a/ipdemo/CMakeLists.txt b/ipdemo/CMakeLists.txt index 3b36690..66a181b 100644 --- a/ipdemo/CMakeLists.txt +++ b/ipdemo/CMakeLists.txt @@ -112,15 +112,9 @@ set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} set(JSONCPP_INCLUDE_DIRS /usr/local/jsoncpp/include) set(JSONCPP_LIBRARIES /usr/local/jsoncpp/lib/libjsoncpp.a) -########## -# If you include the drogon source code locally in your project, use this method to add drogon -# add_subdirectory(external/drogon) -# target_link_libraries(${PROJECT_NAME} PRIVATE drogon) -########## find_package(Drogon CONFIG REQUIRED) target_link_libraries(${PROJECT_NAME} PRIVATE Drogon::Drogon) - # Additional PostgreSQL libraries needed for static linking set(POSTGRESQL_EXTRA_LIBS /usr/local/pgsql/lib/libpgcommon.a diff --git a/ipdemo/src/http_utils.cpp b/ipdemo/src/http_utils.cpp new file mode 100644 index 0000000..a1812bf --- /dev/null +++ b/ipdemo/src/http_utils.cpp @@ -0,0 +1,48 @@ +#include "http_utils.hpp" + +HttpResponse http_get(const std::string& url, const std::string& path, bool use_https, double timeout_seconds) { + HttpResponse result{0, "", false}; + bool done = false; + std::mutex mtx; + std::condition_variable cv; + + std::thread worker([&]() { + trantor::EventLoop loop; + + auto client = drogon::HttpClient::newHttpClient( + (use_https ? "https://" : "http://") + url, + &loop + ); + + auto req = drogon::HttpRequest::newHttpRequest(); + req->setMethod(drogon::Get); + req->setPath(path); + + client->sendRequest(req, [&](drogon::ReqResult res, const drogon::HttpResponsePtr &resp) { + std::lock_guard lock(mtx); + if (res == drogon::ReqResult::Ok && resp) { + result.status_code = resp->statusCode(); + result.body = resp->body(); + result.success = true; + } else { + result.status_code = 0; + result.body = ""; + result.success = false; + } + done = true; + cv.notify_one(); + loop.quit(); + }, timeout_seconds); + + loop.loop(); + }); + + // Wait for completion + { + std::unique_lock lock(mtx); + cv.wait(lock, [&] { return done; }); + } + + worker.join(); + return result; +} \ No newline at end of file diff --git a/ipdemo/src/http_utils.hpp b/ipdemo/src/http_utils.hpp new file mode 100644 index 0000000..7fd8eb6 --- /dev/null +++ b/ipdemo/src/http_utils.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +struct HttpResponse { + int status_code; + std::string body; + bool success; +}; + +// Utility function for blocking HTTP GET requests +HttpResponse http_get(const std::string& url, const std::string& path, bool use_https = false, double timeout_seconds = 10.0); \ No newline at end of file diff --git a/ipdemo/src/main.cpp b/ipdemo/src/main.cpp index 5962106..88301d6 100644 --- a/ipdemo/src/main.cpp +++ b/ipdemo/src/main.cpp @@ -1,12 +1,9 @@ #include -#include -#include -#include #include -#include #include "version.hpp" #include "assert.hpp" +#include "http_utils.hpp" void crashy() { ASSERT(false, "SUCCESS!"); @@ -17,42 +14,14 @@ int main() { std::cout << std::endl; std::cout << "Retrieving IP address..." << std::endl; - // Initialize drogon app but don't run it - drogon::app().setLogLevel(trantor::Logger::kError); - - std::string ip_body; - int status = 0; - bool done = false; - - // Run in a separate thread to avoid event loop conflicts - std::thread worker([&]() { - trantor::EventLoop loop; - auto client = drogon::HttpClient::newHttpClient("http://ipinfo.io", &loop); - auto req = drogon::HttpRequest::newHttpRequest(); - req->setMethod(drogon::Get); - req->setPath("/ip"); - - client->sendRequest(req, [&](drogon::ReqResult result, const drogon::HttpResponsePtr &resp) { - if (result == drogon::ReqResult::Ok && resp) { - ip_body = resp->body(); - status = resp->statusCode(); - } else { - status = 0; - } - done = true; - loop.quit(); - }); - - loop.loop(); - }); - - worker.join(); + // Use the utility function for HTTP GET + auto response = http_get("ipinfo.io", "/ip", false, 10.0); - ASSERT(status == 200, "Failed to get IP"); + ASSERT(response.success && response.status_code == 200, "Failed to get IP"); nlohmann::json j; - j["ip"] = ip_body; - j["status"] = status; + j["ip"] = response.body; + j["status"] = response.status_code; std::cout << j.dump(4) << std::endl;