Skip to content

17 ayonapiget does not check for failed httplib connections #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 37 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
97f77cf
All possible solutions - messy source file (tmp version)
tadeas-hejnic Feb 13, 2025
1299ab5
Test for https connection and playground src file - needs to be delet…
tadeas-hejnic Feb 13, 2025
303a4a2
Gitignore: ignore the env files for testing
tadeas-hejnic Mar 5, 2025
74e5261
Test: env variables are loaded from env file
tadeas-hejnic Mar 5, 2025
203e426
Test: env variables are loaded from env file
tadeas-hejnic Mar 5, 2025
be9d172
Test: new function for loading the variables from env file
tadeas-hejnic Mar 5, 2025
db95398
Test: new function implementation for loading the variables from env …
tadeas-hejnic Mar 5, 2025
ab5a26c
Build: the previus implementation didn't work on windows
tadeas-hejnic Mar 5, 2025
48f4d69
Build: the previus implementation didn't work on windows
tadeas-hejnic Mar 5, 2025
6bf6bc6
Https fix: implementation working on windows
tadeas-hejnic Mar 5, 2025
46b551a
Cross-platform: works on linux, should work on windows
tadeas-hejnic Mar 10, 2025
506f69e
Cross-platform: works on linux, should work on windows, cleaning up t…
tadeas-hejnic Mar 10, 2025
60ab691
Cross-platform: small edits
tadeas-hejnic Mar 10, 2025
a7eb204
Cross-platform: works on linux, should work on windows
tadeas-hejnic Mar 10, 2025
ed6515f
Cross-platform: adjustments for stop test server on windows
tadeas-hejnic Mar 10, 2025
0bcf40c
Cross-platform: adjustments of starting test server on windows
tadeas-hejnic Mar 10, 2025
c298526
Conflict local X head
tadeas-hejnic Mar 14, 2025
301c26f
Code hygiene
tadeas-hejnic Mar 14, 2025
193c6ef
Code hygiene
tadeas-hejnic Mar 14, 2025
db6521a
Test: delete of playground source file
tadeas-hejnic Mar 17, 2025
c249415
Constructor: The change that has not been included somehow - already …
tadeas-hejnic Mar 17, 2025
4a0f60d
Constructor: The change that has not been included somehow - already …
tadeas-hejnic Mar 17, 2025
f25eb33
Test: Run tests without printing results
tadeas-hejnic Mar 17, 2025
3f2730f
Test: new endpoint added - /api/info
tadeas-hejnic Mar 28, 2025
dd5c22a
Test: unnecessary arguments deleted
tadeas-hejnic Mar 28, 2025
f219106
Test: unnecessary arguments deleted
tadeas-hejnic Mar 28, 2025
6285410
Test: unnecessary arguments deleted
tadeas-hejnic Mar 28, 2025
a9c1ea1
Test: unnecessary arguments deleted
tadeas-hejnic Mar 28, 2025
47d2162
SSL: added an option for loading a cert file from the newcerts/ direc…
tadeas-hejnic Mar 28, 2025
7eca99b
SSL: default cert file
tadeas-hejnic Mar 28, 2025
12b8fce
Build: added line that fixes the issue when including cpp-api to othe…
tadeas-hejnic Mar 31, 2025
0a14d99
Test: uncomment of the function used in tests
tadeas-hejnic Apr 1, 2025
9d047fc
Log: change the category of the log msg, small adjustments in code fo…
tadeas-hejnic Apr 7, 2025
0132dde
Merge branch '17-ayonapiget-dose-not-check-for-failed-httplib-connect…
tadeas-hejnic Apr 7, 2025
e16655d
Changing the git pointer to get the bug fix with AyonLogger
tadeas-hejnic Apr 7, 2025
871ed1b
Fix: fix of hardcoded platform in GET
tadeas-hejnic Apr 7, 2025
92c0eef
Log: added missing log for one case of setting ca cert
tadeas-hejnic Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ test_logs/
AyonCppApi_CiCd/
AyonCppApi_cicd/
__pycache__
.env*
23 changes: 18 additions & 5 deletions AyonBuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
)
AyonCppApiPrj.add_stage(SetTestVars)

SetDefaultVars = Project.Stage("SetDefaultVars")
SetDefaultVars.add_funcs(
Project.Func("", AyonCppApiPrj.setVar, "AYON_CPP_API_ENALBE_GBENCH", "OFF"),
Project.Func("", AyonCppApiPrj.setVar, "AYON_CPP_API_ENALBE_GTEST", "OFF"),
Project.Func("", AyonCppApiPrj.setVar, "JTRACE", "0"),
Project.Func("", AyonCppApiPrj.setVar, "ReleaseType", "Release"),
)
AyonCppApiPrj.add_stage(SetDefaultVars)

CleanUpStage = Project.Stage("Cleanup")
binFoulder = os.path.join(os.getcwd(), "bin")
buildFoulder = os.path.join(os.getcwd(), "build")
Expand Down Expand Up @@ -151,13 +160,15 @@ def startTestServer():

def CheckTestServer():
import requests

response = requests.get("http://localhost:8003/")
print("Test Respone", response.text)


def stopTestServer():
ServerPocVar.kill()
if sys.platform == "win32":
ServerPocVar.join(timeout=2)
else:
ServerPocVar.kill()


SetupTestServer = Project.Stage("SetupTestServer")
Expand Down Expand Up @@ -195,11 +206,13 @@ def stopTestServer():
AyonCppApiPrj.creat_stage_group(
"CleanBuild",
CleanUpStage,
SetDefaultVars,
BuildStage,
)
AyonCppApiPrj.creat_stage_group(
"CleanBuildAndDocs",
CleanUpStage,
SetDefaultVars,
BuildStage,
DoxyGenStage,
)
Expand All @@ -222,15 +235,15 @@ def stopTestServer():
)

AyonCppApiPrj.creat_stage_group(
"BuildAndBnech",
"BuildAndBench",
SetTestVars,
BuildStage,
SetupTestServer,
BenchStage,
StopTestServer,
)
AyonCppApiPrj.creat_stage_group(
"CleanBuildAndBnech",
"CleanBuildAndBench",
CleanUpStage,
SetTestVars,
BuildStage,
Expand All @@ -239,7 +252,7 @@ def stopTestServer():
StopTestServer,
)
AyonCppApiPrj.creat_stage_group(
"CleanBuildAndBnechPlusTest",
"CleanBuildAndBenchPlusTest",
CleanUpStage,
SetTestVars,
BuildStage,
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,4 @@ endif()

add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/src/AyonCppApi")
include_directories("${CMAKE_CURRENT_SOURCE_DIR}/src/AyonCppApi")

target_include_directories(AyonCppApi PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/ext/ayon-cpp-dev-tools/src")
3,642 changes: 3,642 additions & 0 deletions certs/cacert.pem

Large diffs are not rendered by default.

216 changes: 186 additions & 30 deletions src/AyonCppApi/AyonCppApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,57 +32,202 @@
#include "backward.hpp"
#include "perfPrinter.h"

// TODO implement the better Crash hanlder
#ifdef _WIN32
#include <windows.h>
#include <wincrypt.h>
#endif

// TODO implement the better Crash handler
backward::StackTrace st;

AyonApi::AyonApi(const std::string &logFilePos,

// ------------------------------------------------
// helper functions for getting the ca cert path
// ------------------------------------------------
std::string parseOutput(std::string& output) {
// Parse the output to extract the directory path
std::string::size_type start = output.find('"');
std::string::size_type end = output.find('"', start + 1);
if (start != std::string::npos && end != std::string::npos) {
return output.substr(start + 1, end - start - 1);
} else {
throw std::runtime_error("Failed to parse OpenSSL directory from command output.");
}
}

std::string getOpenSSLDirByCLI() {
std::array<char, 128> buffer;
std::string result;
auto pipeDeleter = [](FILE* pipe) {
#ifdef _WIN32
_pclose(pipe);
#else
pclose(pipe);
#endif
};
std::unique_ptr<FILE, decltype(pipeDeleter)> pipe(
#ifdef _WIN32
_popen("openssl version -d", "r"),
#else
popen("openssl version -d", "r"),
#endif
pipeDeleter
);
if (!pipe) {
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr) {
result += buffer.data();
}

return parseOutput(result);
}


std::string getOpenSSLDir() {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // OpenSSL 1.1.0+
const char* sslVersion = OpenSSL_version(OPENSSL_DIR);
std::string sslVersionStr(sslVersion);
return parseOutput(sslVersionStr);
#else // OpenSSL 1.0.x
return parseOutput(SSLeay_version(SSLEAY_DIR));
#endif
}
// ------------------------------------------------


AyonApi::AyonApi(const std::optional<std::string> &logFilePos,
const std::string &authKey,
const std::string &serverUrl,
const std::string &ayonProjectName,
const std::string &siteId,
std::optional<int> concurrency):
m_num_threads(concurrency.value_or(std::max(int(std::thread::hardware_concurrency() / 2), 1))),
m_authKey(authKey),
m_serverUrl(serverUrl),
m_ayonProjectName(ayonProjectName),
m_siteId(siteId) {
std::optional<int> concurrency)
: m_num_threads(concurrency.value_or(std::max(int(std::thread::hardware_concurrency() / 2), 1))),
m_authKey(authKey),
m_serverUrl(serverUrl),
m_ayonProjectName(ayonProjectName),
m_siteId(siteId) {
PerfTimer("AyonApi::AyonApi");

// ----------- Init m_Logger
std::filesystem::path logFileName = "logFile.json";
std::filesystem::path basePath = logFilePos;
std::filesystem::path logFilePath = std::filesystem::absolute(basePath) / logFileName;
std::filesystem::path logPath;
if (logFilePos.has_value()) {
std::filesystem::path inPath(logFilePos.value());

if (inPath.is_relative()) {
logPath = std::filesystem::weakly_canonical(inPath);
}
if (!inPath.has_parent_path()) {
// if the input path is just an filename we will just throw it into tmp
logPath = std::filesystem::temp_directory_path() / inPath;
}
// we allways want the data to be a json, so we just enforce it.
logPath.replace_extension(".json");

if (std::filesystem::exists(logFilePath)) {
logFilePath = std::filesystem::canonical(logFilePath);
}
else {
std::filesystem::create_directories(logFilePath.parent_path());
}

m_Log = std::make_shared<AyonLogger>(AyonLogger::getInstance(logFilePath.string()));
if (std::filesystem::exists(logPath)) {
logPath = std::filesystem::canonical(logPath);
}
else {
std::filesystem::create_directories(logPath.parent_path());
}
}
m_Log = std::make_shared<AyonLogger>(AyonLogger::getInstance(logPath.string()));
m_Log->LogLevlWarn();

m_Log->info(m_Log->key("AyonApi"), "Init AyonServer httplib::Client");
m_AyonServer = std::make_unique<httplib::Client>(m_serverUrl);
m_AyonServer->set_bearer_token_auth(m_authKey);

if (isSSL()) {
m_headers = {
{"X-Api-Key", m_authKey},
};

try {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it might be best to have everything in the try in a separate function. Would make expanding the business logic a bit simpler.
You can just make it an inlined function if you are afraid of performance or any behavior changes.

std::string opensslDirCLI = getOpenSSLDirByCLI();

#ifdef _WIN32
std::string certFileCLI = opensslDirCLI + "\\cert.pem";
#else
std::string certFileCLI = opensslDirCLI + "/cert.pem";
#endif

if (std::filesystem::exists(certFileCLI)) {
m_Log->info("Using cert based on CLI var.");
m_AyonServer->set_ca_cert_path(certFileCLI.c_str());
} else {
std::string opensslDir = getOpenSSLDir();
#ifdef _WIN32
std::string certFile = opensslDir + "\\cert.pem";
#else
std::string certFile = opensslDir + "/cert.pem";
#endif

if (std::filesystem::exists(certFile)) {
m_Log->info("Using cert based on SSLEAY_DIR.");
m_AyonServer->set_ca_cert_path(certFile.c_str());
} else {
const char* envCertFile = getenv("SSL_CERT_FILE");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the environment variable should be the first option making use of an early return or just avoiding the other code.

I would argue that if you define a cert file explicitly, it should be the one to use if possible.
Maybe it should even be possible to enforce only to use the one provided there so that if a studio defines one explicitly, they can enforce it.

PS;: not saying the fallback idea is bad or anything still a great idea.

if (envCertFile) {
m_Log->info("Using cert based on env variable (SSL_CERT_PATH).");
m_AyonServer->set_ca_cert_path(envCertFile);
} else {
m_Log->info("Failed to determine the OpenSSL directory. Falling back to the default certificate file path.");
std::string certPath = (
std::filesystem::path(__FILE__).parent_path().parent_path().parent_path() / "certs" / "cacert.pem"
).string();
m_AyonServer->set_ca_cert_path(certPath);
}
}
}
} catch (const std::exception &e) {
m_Log->error("Failed to get OpenSSL directory: {}", e.what());
m_AyonServer->set_ca_cert_path(nullptr);
}

m_AyonServer->enable_server_certificate_verification(true);
} else {
m_AyonServer->set_bearer_token_auth(m_authKey);
m_headers = {};
}

auto res = m_AyonServer->Get("/api/info", m_headers);
if (!res) {
m_Log->error("Failed to connect to the Ayon server.");
} else if (res->status != 200) {
m_Log->warn("Connected to the Ayon server : {}", res->status);
} else {
m_Log->info("Connected to the Ayon server : {}", res->status);
}

m_Log->info(m_Log->key("AyonApi"), "Constructor Getting Site Roots");
getSiteRoots();
};
}

AyonApi::~AyonApi() {
m_Log->info(m_Log->key("AyonApi"), "AyonApi::~AyonApi()");
};

std::unordered_map<std::string, std::string>*
AyonApi::getSiteRoots() {
m_Log->info(m_Log->key("AyonApi"), "AyonApi::getSiteRoots()");
if (m_siteRoots.size() < 1) {
httplib::Headers headers = {{"X-ayon-site-id", m_siteId}};
nlohmann::json response
= GET(std::make_shared<std::string>("/api/projects/" + m_ayonProjectName + "/siteRoots"),
std::make_shared<httplib::Headers>(headers), 200);
m_siteRoots = response;
if (m_siteRoots.size() < 1) {
std::string platform;
#ifdef _WIN32
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be best to make this a macro and let the preprocessing place it in the right places. This would allow the compiler to collapse all the string stuff into a singe object.
You could also constexpression the thing.

Also if you make it a macro you can just use it anywhere.

platform = "windows";
#elif __linux__
platform = "linux";
#endif
nlohmann::json response = GET(std::make_shared<std::string>("/api/projects/" + m_ayonProjectName + "/siteRoots?platform=" + platform),
std::make_shared<httplib::Headers>(m_headers), 200);

if (response.empty()) {
m_Log->error("AyonApi::getSiteRoots response is empty");
return &m_siteRoots;
} else {
m_siteRoots = response;
}

}
if (m_Log->isKeyActive(m_Log->key("AyonApi"))) {
m_Log->info(m_Log->key("AyonApi"), "found site Roots: ");
Expand Down Expand Up @@ -116,7 +261,7 @@ AyonApi::rootReplace(const std::string &rootLessPath) {
return rootedPath;
}
catch (std::out_of_range &e) {
m_Log->warn("AyonApi::rootedPath error acured {}, list off available root replace str: ");
m_Log->warn("AyonApi::rootedPath error acured {}, list off available root replace str: ", e.what());
for (auto &g: m_siteRoots) {
m_Log->warn("Key: {}, replacement: {}", g.first, g.second);
}
Expand All @@ -141,6 +286,12 @@ AyonApi::GET(const std::shared_ptr<std::string> endPoint,
while (retryes <= m_maxCallRetrys) {
try {
response = m_AyonServer->Get(*endPoint, *headers);

if (response == nullptr) {
m_Log->warn("AyonApi::GET response is null: {}", httplib::to_string(response.error()));
return nlohmann::json();
}

responeStatus = response->status;
retryes++;

Expand All @@ -162,7 +313,7 @@ AyonApi::GET(const std::shared_ptr<std::string> endPoint,
}
} // TODO error reason not printed
catch (const httplib::Error &e) {
m_Log->warn("Request Failed because: {}");
m_Log->warn("Request Failed because: {}", httplib::to_string(e));
break;
}
m_Log->warn("The connection failed Rety now.");
Expand Down Expand Up @@ -279,9 +430,9 @@ AyonApi::batchResolvePath(std::vector<std::string> &uriPaths) {
{
PerfTimer("AyonApi::batchResolvePath::sanatizeVector");
std::set<std::string> s;
unsigned size = uriPaths.size();
size_t size = uriPaths.size();

for (unsigned i = 0; i < size; ++i) s.insert(uriPaths[i]);
for (size_t i = 0; i < size; ++i) s.insert(uriPaths[i]);
uriPaths.assign(s.begin(), s.end());
m_Log->info("Make sure that the vector has no duplicates. vecSize before: {} after: {}", size,
uriPaths.size());
Expand Down Expand Up @@ -587,3 +738,8 @@ AyonApi::logPointer() {
m_Log->info(m_Log->key("AyonApi"), "AyonApi::logPointer()");
return m_Log;
};

bool
AyonApi::isSSL() const {
return m_serverUrl.rfind("https://", 0) == 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doing this as a compare of a substring might be faster.
But rfind is very fast so you might end up with +- 0
Just FYI

}
Loading