From b14b093195d7cf51318663b3e13077c3ff2d7f22 Mon Sep 17 00:00:00 2001 From: April White Date: Sat, 1 Jun 2024 19:57:57 -0700 Subject: [PATCH] Add the apikey, and use the RAII pattern for curl --- lib/CreatureVoicesLib/src/CreatureVoices.cpp | 4 +-- lib/CreatureVoicesLib/src/CurlBase.cpp | 25 +++++++--------- lib/CreatureVoicesLib/src/CurlBase.h | 3 +- lib/CreatureVoicesLib/src/CurlHandle.cpp | 31 ++++++++++++++++++++ lib/CreatureVoicesLib/src/CurlHandle.h | 9 +++++- 5 files changed, 53 insertions(+), 19 deletions(-) diff --git a/lib/CreatureVoicesLib/src/CreatureVoices.cpp b/lib/CreatureVoicesLib/src/CreatureVoices.cpp index 5dbf4f2..1fdb18a 100644 --- a/lib/CreatureVoicesLib/src/CreatureVoices.cpp +++ b/lib/CreatureVoicesLib/src/CreatureVoices.cpp @@ -28,14 +28,12 @@ namespace creatures::voice { debug("Fetching available voices"); auto curlHandle = createCurlHandle(url); - auto res = performRequest(curlHandle, HttpMethod::GET, ""); + auto res = performRequest(curlHandle, apiKey, HttpMethod::GET, ""); if(!res.isSuccess()) { auto error = res.getError(); std::string errorMessage = fmt::format("Failed to fetch available voices: {}", error->getMessage()); - curl_easy_cleanup(curlHandle.get()); return VoiceResult>{VoiceError(error->getCode(), error->getMessage())}; } - curl_easy_cleanup(curlHandle.get()); auto httpResponse = res.getValue().value(); trace("httpResponse was: {}", httpResponse); diff --git a/lib/CreatureVoicesLib/src/CurlBase.cpp b/lib/CreatureVoicesLib/src/CurlBase.cpp index 7238dfe..fb4d20b 100644 --- a/lib/CreatureVoicesLib/src/CurlBase.cpp +++ b/lib/CreatureVoicesLib/src/CurlBase.cpp @@ -28,6 +28,7 @@ namespace creatures::voice { } VoiceResult CurlBase::performRequest(CurlHandle& curlHandle, + const std::string& apiKey, HttpMethod method, const std::string& data) { @@ -43,6 +44,11 @@ namespace creatures::voice { return VoiceResult{VoiceError(VoiceError::InternalError, errorMessage)}; } + // Set headers + std::string apiKeyHeader = fmt::format("xi-api-key: {}", apiKey); + curlHandle.addHeader(apiKeyHeader); + curlHandle.addHeader("Content-Type: application/json"); + curl_easy_setopt(curlHandle.get(), CURLOPT_WRITEDATA, &response); trace("CURL handle set up for writing"); @@ -64,7 +70,6 @@ namespace creatures::voice { default: errorMessage = fmt::format("Unknown HTTP method: {}", httpMethodToString(method)); error(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::InternalError, errorMessage)}; } @@ -73,7 +78,6 @@ namespace creatures::voice { if (res != CURLE_OK) { errorMessage = fmt::format("CURL request failed: {}", curl_easy_strerror(res)); error(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::InternalError, errorMessage)}; } @@ -89,39 +93,32 @@ namespace creatures::voice { break; case 301: case 302: - errorMessage = fmt::format("HTTP error: {} - redirect", http_code); + errorMessage = fmt::format("11labs API error: {} - redirect", http_code); warn(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::InvalidData, errorMessage)}; case 400: - errorMessage = fmt::format("HTTP error: {} - bad request", http_code); + errorMessage = fmt::format("11labs API error: {} - bad request", http_code); warn(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::InvalidData, errorMessage)}; case 401: case 403: - errorMessage = fmt::format("HTTP error: {} - unauthorized", http_code); + errorMessage = fmt::format("11labs API error: {} - unauthorized (make sure the server has a good apiKey!)", http_code); warn(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::InvalidApiKey, errorMessage)}; case 404: - errorMessage = fmt::format("HTTP error: {} - not found", http_code); + errorMessage = fmt::format("11labs API error: {} - not found", http_code); warn(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::NotFound, errorMessage)}; // Map everything else to an error default: - errorMessage = fmt::format("HTTP error: {}", http_code); + errorMessage = fmt::format("11labs API error: {} ({})", http_code, response); error(errorMessage); - curl_easy_cleanup(curlHandle.get()); return VoiceResult{VoiceError(VoiceError::InternalError, errorMessage)}; } // Looks good! We have good data - curl_easy_cleanup(curlHandle.get()); - debug("request successful!"); return VoiceResult{response}; } diff --git a/lib/CreatureVoicesLib/src/CurlBase.h b/lib/CreatureVoicesLib/src/CurlBase.h index 937c1e4..29f5baf 100644 --- a/lib/CreatureVoicesLib/src/CurlBase.h +++ b/lib/CreatureVoicesLib/src/CurlBase.h @@ -27,7 +27,8 @@ namespace creatures :: voice { protected: CurlHandle createCurlHandle(const std::string& url); - VoiceResult performRequest(CurlHandle& curlHandle, HttpMethod method, const std::string& data); + VoiceResult performRequest(CurlHandle& curlHandle, const std::string& apiKey, + HttpMethod method, const std::string& data); static size_t WriteCallback(char* ptr, size_t size, size_t nmemb, std::string* data); }; diff --git a/lib/CreatureVoicesLib/src/CurlHandle.cpp b/lib/CreatureVoicesLib/src/CurlHandle.cpp index 7071c88..5a4d505 100644 --- a/lib/CreatureVoicesLib/src/CurlHandle.cpp +++ b/lib/CreatureVoicesLib/src/CurlHandle.cpp @@ -26,13 +26,44 @@ namespace creatures :: voice { if (curl) { curl_easy_cleanup(curl); } + if (headers) { + curl_slist_free_all(headers); + } + spdlog::trace("CurlHandle destroyed"); + } + + + CurlHandle::CurlHandle(CurlHandle&& other) noexcept : curl(other.curl), headers(other.headers) { + other.curl = nullptr; + other.headers = nullptr; } + CurlHandle& CurlHandle::operator=(CurlHandle&& other) noexcept { + if (this != &other) { + if (curl) { + curl_easy_cleanup(curl); + } + if (headers) { + curl_slist_free_all(headers); + } + spdlog::trace("CurlHandle destroyed via move assignment"); + curl = other.curl; + headers = other.headers; + other.curl = nullptr; + other.headers = nullptr; + } + return *this; + } CURL* CurlHandle::get() const { return curl; } + void CurlHandle::addHeader(const std::string& header) { + headers = curl_slist_append(headers, header.c_str()); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + } + size_t CurlHandle::WriteCallback(char* ptr, size_t size, size_t nmemb, std::string* data) { data->append(ptr, size * nmemb); return size * nmemb; diff --git a/lib/CreatureVoicesLib/src/CurlHandle.h b/lib/CreatureVoicesLib/src/CurlHandle.h index 8108a86..3de09af 100644 --- a/lib/CreatureVoicesLib/src/CurlHandle.h +++ b/lib/CreatureVoicesLib/src/CurlHandle.h @@ -16,16 +16,23 @@ namespace creatures :: voice { public: CurlHandle(const std::string& url); ~CurlHandle(); - //CurlHandle(CurlHandle&& other) noexcept; + + // Allow move semantics + CurlHandle(CurlHandle&& other) noexcept; // Delete copy constructor and copy assignment to avoid double cleanup CurlHandle(const CurlHandle&) = delete; CurlHandle& operator=(const CurlHandle&) = delete; + CurlHandle& operator=(CurlHandle&& other) noexcept; + CURL* get() const; + void addHeader(const std::string& header); + private: CURL* curl = nullptr; + struct curl_slist* headers = nullptr; static size_t WriteCallback(char* ptr, size_t size, size_t nmemb, std::string* data); };