mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-10 10:47:16 +00:00
Merge branch 'main' into release/v2.4
This commit is contained in:
@@ -220,7 +220,9 @@ if (MSVC)
|
|||||||
# /bigobj (Increase Number of Sections in .Obj file)
|
# /bigobj (Increase Number of Sections in .Obj file)
|
||||||
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
|
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
|
||||||
# Generate symbols at every build target, even for the release.
|
# Generate symbols at every build target, even for the release.
|
||||||
add_compile_options(-bigobj -Zm520 /Zi)
|
# -Zm520 fixes error C3859 but forces the compiler to pre-allocate that memory for every translation unit regardless
|
||||||
|
# combining /Zi with /FS frees up a significant amount of memory pressure across all parallel compile jobs and makes /MP faster overall.
|
||||||
|
add_compile_options(-bigobj /Zi /FS)
|
||||||
# Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17.
|
# Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17.
|
||||||
#FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules.
|
#FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules.
|
||||||
add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
|
add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
|
||||||
@@ -231,6 +233,11 @@ if (MSVC)
|
|||||||
# Disable warnings on comparison of unsigned and signed
|
# Disable warnings on comparison of unsigned and signed
|
||||||
# C4018: signed/unsigned mismatch
|
# C4018: signed/unsigned mismatch
|
||||||
add_compile_options(/wd4018)
|
add_compile_options(/wd4018)
|
||||||
|
# Prevent linker restart when TBB (or other deps) built with /GL are linked
|
||||||
|
# Make your full (clean) build slower because it enables link-time code generation across all translation units.
|
||||||
|
# helps incremental builds by preventing the double-link restart from TBB
|
||||||
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LTCG")
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /LTCG")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang" AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15)
|
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang" AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15)
|
||||||
|
|||||||
@@ -28,17 +28,34 @@ function(encoding_check TARGET)
|
|||||||
# Obtain target source files
|
# Obtain target source files
|
||||||
get_target_property(T_SOURCES ${TARGET} SOURCES)
|
get_target_property(T_SOURCES ${TARGET} SOURCES)
|
||||||
|
|
||||||
# Define top-level encoding check target for this ${TARGET}
|
set(STAMP_FILE "${CMAKE_CURRENT_BINARY_DIR}/encoding-check-${TARGET}.stamp")
|
||||||
add_custom_target(encoding-check-${TARGET}
|
set(RSP_FILE "${CMAKE_CURRENT_BINARY_DIR}/encoding-check-${TARGET}.rsp")
|
||||||
DEPENDS encoding-check ${T_SOURCES}
|
|
||||||
COMMAND $<TARGET_FILE:encoding-check> ${TARGET} ${T_SOURCES}
|
# Resolve absolute paths
|
||||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
set(ABS_SOURCES "")
|
||||||
|
foreach(file ${T_SOURCES})
|
||||||
|
if(IS_ABSOLUTE "${file}")
|
||||||
|
list(APPEND ABS_SOURCES "${file}")
|
||||||
|
else()
|
||||||
|
list(APPEND ABS_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/${file}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Write response file at configure time
|
||||||
|
string(REPLACE ";" "\n" RSP_CONTENT "${ABS_SOURCES}")
|
||||||
|
file(WRITE "${RSP_FILE}" "${RSP_CONTENT}")
|
||||||
|
|
||||||
|
# Single process call via response file — no command line length limit
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${STAMP_FILE}"
|
||||||
|
DEPENDS encoding-check ${ABS_SOURCES}
|
||||||
COMMENT "Checking source files encodings for target ${TARGET}"
|
COMMENT "Checking source files encodings for target ${TARGET}"
|
||||||
|
COMMAND $<TARGET_FILE:encoding-check> ${TARGET} "@${RSP_FILE}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E touch "${STAMP_FILE}"
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_custom_target(encoding-check-${TARGET} DEPENDS "${STAMP_FILE}")
|
||||||
# This adds dependency on encoding-check-${TARGET} to ${TARET}
|
|
||||||
# via the global-encoding-check
|
|
||||||
add_dependencies(global-encoding-check encoding-check-${TARGET})
|
add_dependencies(global-encoding-check encoding-check-${TARGET})
|
||||||
add_dependencies(${TARGET} global-encoding-check)
|
add_dependencies(${TARGET} global-encoding-check)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The utf8_check() function scans the '\0'-terminated string starting
|
* The utf8_check() function scans the '\0'-terminated string starting
|
||||||
@@ -68,63 +68,97 @@ unsigned char *utf8_check(unsigned char *s)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ORCA
|
||||||
static const char* target;
|
bool check_file(const char* target, const char* filename)
|
||||||
|
|
||||||
void error_exit(const char* error, const char* filename)
|
|
||||||
{
|
|
||||||
std::cerr << "\n\tError: " << error << ": " << filename << "\n"
|
|
||||||
<< "\tTarget: " << target << "\n"
|
|
||||||
<< std::endl;
|
|
||||||
std::exit(-2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void utf8_check_file(const char* filename)
|
|
||||||
{
|
{
|
||||||
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
std::ifstream file(filename, std::ios::binary | std::ios::ate);
|
||||||
const auto size = file.tellg();
|
if (!file.is_open()) {
|
||||||
|
std::cerr << "\n\tError: Could not open file: " << filename
|
||||||
if (size == 0) {
|
<< "\n\tTarget: " << target << "\n" << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::streampos pos = file.tellg();
|
||||||
|
|
||||||
|
// Fix: tellg() returns -1 (cast to streampos) on failure.
|
||||||
|
// Also guard against implausibly large files before casting to size_t.
|
||||||
|
if (pos < 0) {
|
||||||
|
std::cerr << "\n\tError: Could not determine file size: " << filename
|
||||||
|
<< "\n\tTarget: " << target << "\n" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto size = static_cast<std::size_t>(pos);
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
file.seekg(0, std::ios::beg);
|
file.seekg(0, std::ios::beg);
|
||||||
std::vector<char> buffer(size);
|
std::vector<char> buffer(size);
|
||||||
|
|
||||||
if (file.read(buffer.data(), size)) {
|
// Fix: cast size to streamsize explicitly — streampos and streamsize are
|
||||||
buffer.push_back('\0');
|
// distinct types and passing streampos where streamsize is expected is
|
||||||
|
// implementation-defined behaviour on some platforms.
|
||||||
// Check UTF-8 validity
|
if (!file.read(buffer.data(), static_cast<std::streamsize>(size))) {
|
||||||
if (utf8_check(reinterpret_cast<unsigned char*>(buffer.data())) != nullptr) {
|
std::cerr << "\n\tError: Could not read source file: " << filename
|
||||||
error_exit("Source file does not contain (valid) UTF-8", filename);
|
<< "\n\tTarget: " << target << "\n" << std::endl;
|
||||||
}
|
return false;
|
||||||
|
|
||||||
// Check against a BOM mark
|
|
||||||
if (buffer.size() >= 3
|
|
||||||
&& buffer[0] == '\xef'
|
|
||||||
&& buffer[1] == '\xbb'
|
|
||||||
&& buffer[2] == '\xbf') {
|
|
||||||
error_exit("Source file is valid UTF-8 but contains a BOM mark", filename);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
error_exit("Could not read source file", filename);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
buffer.push_back('\0');
|
||||||
|
|
||||||
|
if (utf8_check(reinterpret_cast<unsigned char*>(buffer.data())) != nullptr) {
|
||||||
|
std::cerr << "\n\tError: Source file does not contain (valid) UTF-8: " << filename
|
||||||
|
<< "\n\tTarget: " << target << "\n" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.size() >= 3
|
||||||
|
&& buffer[0] == '\xef'
|
||||||
|
&& buffer[1] == '\xbb'
|
||||||
|
&& buffer[2] == '\xbf') {
|
||||||
|
std::cerr << "\n\tError: Source file is valid UTF-8 but contains a BOM mark: " << filename
|
||||||
|
<< "\n\tTarget: " << target << "\n" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char const *argv[])
|
int main(int argc, char const *argv[])
|
||||||
{
|
{
|
||||||
if (argc < 3) {
|
if (argc < 3) {
|
||||||
std::cerr << "Usage: " << argv[0] << " <program/library> <file[s]>" << std::endl;
|
std::cerr << "Usage: " << argv[0] << " <target> <file|@responsefile> [...]" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
target = argv[1];
|
const char* target = argv[1];
|
||||||
|
|
||||||
|
// Collect all files — support @responsefile syntax
|
||||||
|
std::vector<std::string> files;
|
||||||
for (int i = 2; i < argc; i++) {
|
for (int i = 2; i < argc; i++) {
|
||||||
utf8_check_file(argv[i]);
|
if (argv[i][0] == '@') {
|
||||||
|
// Response file — read paths from it, one per line
|
||||||
|
std::ifstream rsp(argv[i] + 1);
|
||||||
|
if (!rsp.is_open()) {
|
||||||
|
std::cerr << "Could not open response file: " << (argv[i]+1) << std::endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(rsp, line)) {
|
||||||
|
if (!line.empty())
|
||||||
|
files.push_back(line);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files.push_back(argv[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
bool all_ok = true;
|
||||||
|
for (const auto& f : files) {
|
||||||
|
if (!check_file(target, f.c_str()))
|
||||||
|
all_ok = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_ok ? 0 : -2;
|
||||||
}
|
}
|
||||||
@@ -1564,21 +1564,24 @@ bool OrcaCloudServiceAgent::decode_jwt_expiry(const std::string& token, std::chr
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::refresh_now(const std::string& refresh_token, const std::string& reason, bool async)
|
RefreshResult OrcaCloudServiceAgent::refresh_now(const std::string& refresh_token, const std::string& reason, bool async)
|
||||||
{
|
{
|
||||||
if (refresh_token.empty()) return false;
|
if (refresh_token.empty()) return RefreshResult::AuthRejected; // nothing to refresh
|
||||||
|
|
||||||
bool expected = false;
|
bool expected = false;
|
||||||
if (!refresh_running.compare_exchange_strong(expected, true)) {
|
if (!refresh_running.compare_exchange_strong(expected, true)) {
|
||||||
BOOST_LOG_TRIVIAL(debug) << "OrcaCloudServiceAgent: refresh already running, skip (reason=" << reason << ")";
|
BOOST_LOG_TRIVIAL(debug) << "OrcaCloudServiceAgent: refresh already running, skip (reason=" << reason << ")";
|
||||||
return false;
|
// Another refresh is already in flight. Treat as transient so we keep the session
|
||||||
|
// instead of logging out: that in-flight refresh surfaces its own Success/AuthRejected,
|
||||||
|
// so a genuine rejection is only deferred to the next request, never lost.
|
||||||
|
return RefreshResult::Transient;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto worker = [this, refresh_token, reason]() {
|
auto worker = [this, refresh_token, reason]() {
|
||||||
(void) reason;
|
(void) reason;
|
||||||
bool ok = refresh_session_with_token(refresh_token);
|
RefreshResult r = refresh_session_with_token(refresh_token);
|
||||||
refresh_running.store(false);
|
refresh_running.store(false);
|
||||||
return ok;
|
return r;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (async) {
|
if (async) {
|
||||||
@@ -1586,13 +1589,15 @@ bool OrcaCloudServiceAgent::refresh_now(const std::string& refresh_token, const
|
|||||||
refresh_thread.join();
|
refresh_thread.join();
|
||||||
}
|
}
|
||||||
refresh_thread = std::thread([worker]() { worker(); });
|
refresh_thread = std::thread([worker]() { worker(); });
|
||||||
return true;
|
// Fire-and-forget: the outcome isn't known yet and no current caller consumes it.
|
||||||
|
// Return Transient (indeterminate) rather than implying a completed, successful refresh.
|
||||||
|
return RefreshResult::Transient;
|
||||||
}
|
}
|
||||||
|
|
||||||
return worker();
|
return worker();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::refresh_from_storage(const std::string& reason, bool async)
|
RefreshResult OrcaCloudServiceAgent::refresh_from_storage(const std::string& reason, bool async)
|
||||||
{
|
{
|
||||||
std::string refresh_token = get_refresh_token();
|
std::string refresh_token = get_refresh_token();
|
||||||
if (refresh_token.empty()) {
|
if (refresh_token.empty()) {
|
||||||
@@ -1600,7 +1605,7 @@ bool OrcaCloudServiceAgent::refresh_from_storage(const std::string& reason, bool
|
|||||||
}
|
}
|
||||||
if (refresh_token.empty()) {
|
if (refresh_token.empty()) {
|
||||||
BOOST_LOG_TRIVIAL(warning) << "OrcaCloudServiceAgent: no refresh token available for refresh (reason=" << reason << ")";
|
BOOST_LOG_TRIVIAL(warning) << "OrcaCloudServiceAgent: no refresh token available for refresh (reason=" << reason << ")";
|
||||||
return false;
|
return RefreshResult::AuthRejected; // no persisted token: nothing to preserve
|
||||||
}
|
}
|
||||||
|
|
||||||
return refresh_now(refresh_token, reason, async);
|
return refresh_now(refresh_token, reason, async);
|
||||||
@@ -1616,37 +1621,55 @@ bool OrcaCloudServiceAgent::refresh_if_expiring(std::chrono::seconds skew, const
|
|||||||
|
|
||||||
if (!needs_refresh) return true;
|
if (!needs_refresh) return true;
|
||||||
|
|
||||||
if (refresh_from_storage(reason, false)) return true;
|
if (refresh_from_storage(reason, false) == RefreshResult::Success) return true;
|
||||||
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(750));
|
std::this_thread::sleep_for(std::chrono::milliseconds(750));
|
||||||
return refresh_from_storage(reason + "_retry", false);
|
return refresh_from_storage(reason + "_retry", false) == RefreshResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::refresh_session_with_token(const std::string& refresh_token)
|
// Maps a token-refresh HTTP outcome to a RefreshResult. http_code == 0 means the
|
||||||
|
// server could not be reached; session_established is only meaningful for a 2xx body.
|
||||||
|
static RefreshResult classify_refresh_result(unsigned http_code, bool session_established)
|
||||||
|
{
|
||||||
|
if (http_code == 0)
|
||||||
|
return RefreshResult::Transient; // no response: network/transport failure
|
||||||
|
if (http_code == 400 || http_code == 401 || http_code == 403)
|
||||||
|
return RefreshResult::AuthRejected; // refresh token rejected
|
||||||
|
if (http_code >= 400)
|
||||||
|
return RefreshResult::Transient; // rate-limit (429), server error (5xx) or other 4xx: keep the session
|
||||||
|
return session_established ? RefreshResult::Success // 2xx with a usable session
|
||||||
|
: RefreshResult::Transient; // 2xx but unusable body
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshResult OrcaCloudServiceAgent::refresh_session_with_token(const std::string& refresh_token)
|
||||||
{
|
{
|
||||||
std::string body = "{\"refresh_token\":\"" + refresh_token + "\"}";
|
std::string body = "{\"refresh_token\":\"" + refresh_token + "\"}";
|
||||||
std::string url = auth_base_url + auth_constants::TOKEN_PATH + "?grant_type=refresh_token";
|
std::string url = auth_base_url + auth_constants::TOKEN_PATH + "?grant_type=refresh_token";
|
||||||
std::string response;
|
std::string response;
|
||||||
unsigned int http_code = 0;
|
unsigned int http_code = 0;
|
||||||
if (!http_post_token(body, &response, &http_code, url) || http_code >= 400) {
|
// http_post_token sets http_code to 0 when the server could not be reached.
|
||||||
|
http_post_token(body, &response, &http_code, url);
|
||||||
|
|
||||||
|
bool established = false;
|
||||||
|
if (http_code >= 200 && http_code < 300) {
|
||||||
|
if (session_handler) {
|
||||||
|
established = session_handler(response);
|
||||||
|
} else {
|
||||||
|
// No session handler set - parse the token response directly and establish the
|
||||||
|
// session, so OrcaCloudServiceAgent is self-contained without external setup.
|
||||||
|
try {
|
||||||
|
established = set_user_session(json::parse(response));
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: token refresh parse exception - " << e.what();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
std::string truncated_response = response.size() > 200 ? response.substr(0, 200) + "..." : response;
|
std::string truncated_response = response.size() > 200 ? response.substr(0, 200) + "..." : response;
|
||||||
BOOST_LOG_TRIVIAL(warning) << "OrcaCloudServiceAgent: token refresh failed - http_code=" << http_code
|
BOOST_LOG_TRIVIAL(warning) << "OrcaCloudServiceAgent: token refresh failed - http_code=" << http_code
|
||||||
<< ", response_body=" << truncated_response;
|
<< ", response_body=" << truncated_response;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (session_handler) {
|
return classify_refresh_result(http_code, established);
|
||||||
return session_handler(response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No session handler set - parse the token response directly and establish session
|
|
||||||
// This makes OrcaCloudServiceAgent self-contained without requiring external setup
|
|
||||||
try {
|
|
||||||
return set_user_session(json::parse(response));
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: token refresh parse exception - " << e.what();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -1759,15 +1782,20 @@ void OrcaCloudServiceAgent::clear_session()
|
|||||||
// HTTP Helpers
|
// HTTP Helpers
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::attempt_refresh_after_unauthorized(const std::string& reason)
|
RefreshResult OrcaCloudServiceAgent::attempt_refresh_after_unauthorized(const std::string& reason)
|
||||||
{
|
{
|
||||||
if (refresh_from_storage(reason, false)) return true;
|
RefreshResult r = refresh_from_storage(reason, false);
|
||||||
|
if (r != RefreshResult::Transient) return r; // Success or AuthRejected: decided, no retry
|
||||||
|
|
||||||
|
// Only a transient (network/server) failure is worth retrying.
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||||
if (refresh_from_storage(reason + "_retry", false)) return true;
|
r = refresh_from_storage(reason + "_retry", false);
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "[auth] event=refresh result=failure source=" << reason << " action=logout";
|
if (r == RefreshResult::Transient)
|
||||||
return false;
|
BOOST_LOG_TRIVIAL(warning) << "[auth] event=refresh result=transient source=" << reason << " action=keep_session";
|
||||||
|
else if (r == RefreshResult::AuthRejected)
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << "[auth] event=refresh result=rejected source=" << reason << " action=logout";
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<std::string, std::string> OrcaCloudServiceAgent::data_headers()
|
std::map<std::string, std::string> OrcaCloudServiceAgent::data_headers()
|
||||||
@@ -1780,6 +1808,24 @@ std::map<std::string, std::string> OrcaCloudServiceAgent::data_headers()
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool OrcaCloudServiceAgent::resolve_unauthorized(HttpResult& res,
|
||||||
|
const std::function<HttpResult()>& perform, const std::string& reason)
|
||||||
|
{
|
||||||
|
if (res.status != 401)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RefreshResult rr = attempt_refresh_after_unauthorized(reason);
|
||||||
|
if (rr == RefreshResult::Success) {
|
||||||
|
res = perform(); // refreshed: retry the original request with the new token
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transient (no connection / 5xx / 429 / ambiguous): keep the session and token,
|
||||||
|
// suppress the auth error so the GUI does not log the user out.
|
||||||
|
// AuthRejected (refresh token genuinely rejected): let the 401 surface -> logout.
|
||||||
|
return rr == RefreshResult::Transient;
|
||||||
|
}
|
||||||
|
|
||||||
int OrcaCloudServiceAgent::http_get(const std::string& path, std::string* response_body, unsigned int* http_code)
|
int OrcaCloudServiceAgent::http_get(const std::string& path, std::string* response_body, unsigned int* http_code)
|
||||||
{
|
{
|
||||||
std::string url = api_base_url + path;
|
std::string url = api_base_url + path;
|
||||||
@@ -1788,12 +1834,6 @@ int OrcaCloudServiceAgent::http_get(const std::string& path, std::string* respon
|
|||||||
if (!ensure_token_fresh("http_get_" + path))
|
if (!ensure_token_fresh("http_get_" + path))
|
||||||
BOOST_LOG_TRIVIAL(warning) << "ensure_token_fresh returned false";
|
BOOST_LOG_TRIVIAL(warning) << "ensure_token_fresh returned false";
|
||||||
|
|
||||||
struct HttpResult {
|
|
||||||
bool success{false};
|
|
||||||
unsigned int status{0};
|
|
||||||
std::string body;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto perform = [&]() {
|
auto perform = [&]() {
|
||||||
HttpResult result;
|
HttpResult result;
|
||||||
try {
|
try {
|
||||||
@@ -1832,20 +1872,16 @@ int OrcaCloudServiceAgent::http_get(const std::string& path, std::string* respon
|
|||||||
};
|
};
|
||||||
|
|
||||||
HttpResult res = perform();
|
HttpResult res = perform();
|
||||||
|
bool suppress = resolve_unauthorized(res, perform, "http_get_" + path);
|
||||||
// Single retry on 401 - no recursion
|
|
||||||
if (res.status == 401 && attempt_refresh_after_unauthorized("http_get_" + path)) {
|
|
||||||
res = perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response_body) *response_body = res.body;
|
if (response_body) *response_body = res.body;
|
||||||
if (http_code) *http_code = res.status;
|
if (http_code) *http_code = res.status;
|
||||||
|
|
||||||
if (!res.success || res.status >= 400) {
|
if (!suppress && (!res.success || res.status >= 400)) {
|
||||||
invoke_http_error_callback(res.status, res.body);
|
invoke_http_error_callback(res.status, res.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.success ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
return (res.success && !suppress) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code)
|
int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code)
|
||||||
@@ -1855,12 +1891,6 @@ int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string&
|
|||||||
|
|
||||||
ensure_token_fresh("http_post_" + path);
|
ensure_token_fresh("http_post_" + path);
|
||||||
|
|
||||||
struct HttpResult {
|
|
||||||
bool success{false};
|
|
||||||
unsigned int status{0};
|
|
||||||
std::string body;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto perform = [&]() {
|
auto perform = [&]() {
|
||||||
HttpResult result;
|
HttpResult result;
|
||||||
try {
|
try {
|
||||||
@@ -1902,20 +1932,16 @@ int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string&
|
|||||||
};
|
};
|
||||||
|
|
||||||
HttpResult res = perform();
|
HttpResult res = perform();
|
||||||
|
bool suppress = resolve_unauthorized(res, perform, "http_post_" + path);
|
||||||
// Single retry on 401 - no recursion
|
|
||||||
if (res.status == 401 && attempt_refresh_after_unauthorized("http_post_" + path)) {
|
|
||||||
res = perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response_body) *response_body = res.body;
|
if (response_body) *response_body = res.body;
|
||||||
if (http_code) *http_code = res.status;
|
if (http_code) *http_code = res.status;
|
||||||
|
|
||||||
if (!res.success || res.status >= 400) {
|
if (!suppress && (!res.success || res.status >= 400)) {
|
||||||
invoke_http_error_callback(res.status, res.body);
|
invoke_http_error_callback(res.status, res.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.success ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
return (res.success && !suppress) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code)
|
int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code)
|
||||||
@@ -1925,12 +1951,6 @@ int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string&
|
|||||||
|
|
||||||
ensure_token_fresh("http_put_" + path);
|
ensure_token_fresh("http_put_" + path);
|
||||||
|
|
||||||
struct HttpResult {
|
|
||||||
bool success{false};
|
|
||||||
unsigned int status{0};
|
|
||||||
std::string body;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto perform = [&]() {
|
auto perform = [&]() {
|
||||||
HttpResult result;
|
HttpResult result;
|
||||||
try {
|
try {
|
||||||
@@ -1959,7 +1979,7 @@ int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string&
|
|||||||
.on_error([&](std::string resp_body, std::string error, unsigned resp_status) {
|
.on_error([&](std::string resp_body, std::string error, unsigned resp_status) {
|
||||||
result.success = false;
|
result.success = false;
|
||||||
result.status = resp_status == 0 ? 404 : resp_status;
|
result.status = resp_status == 0 ? 404 : resp_status;
|
||||||
result.body = body;
|
result.body = resp_body;
|
||||||
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
|
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
|
||||||
})
|
})
|
||||||
.timeout_max(30)
|
.timeout_max(30)
|
||||||
@@ -1972,20 +1992,16 @@ int OrcaCloudServiceAgent::http_put(const std::string& path, const std::string&
|
|||||||
};
|
};
|
||||||
|
|
||||||
HttpResult res = perform();
|
HttpResult res = perform();
|
||||||
|
bool suppress = resolve_unauthorized(res, perform, "http_put_" + path);
|
||||||
// Single retry on 401 - no recursion
|
|
||||||
if (res.status == 401 && attempt_refresh_after_unauthorized("http_put_" + path)) {
|
|
||||||
res = perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response_body) *response_body = res.body;
|
if (response_body) *response_body = res.body;
|
||||||
if (http_code) *http_code = res.status;
|
if (http_code) *http_code = res.status;
|
||||||
|
|
||||||
if (!res.success || res.status >= 400) {
|
if (!suppress && (!res.success || res.status >= 400)) {
|
||||||
invoke_http_error_callback(res.status, res.body);
|
invoke_http_error_callback(res.status, res.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.success ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
return (res.success && !suppress) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
int OrcaCloudServiceAgent::http_delete(const std::string& path, std::string* response_body, unsigned int* http_code)
|
int OrcaCloudServiceAgent::http_delete(const std::string& path, std::string* response_body, unsigned int* http_code)
|
||||||
@@ -1995,12 +2011,6 @@ int OrcaCloudServiceAgent::http_delete(const std::string& path, std::string* res
|
|||||||
|
|
||||||
ensure_token_fresh("http_delete_" + path);
|
ensure_token_fresh("http_delete_" + path);
|
||||||
|
|
||||||
struct HttpResult {
|
|
||||||
bool success{false};
|
|
||||||
unsigned int status{0};
|
|
||||||
std::string body;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto perform = [&]() {
|
auto perform = [&]() {
|
||||||
HttpResult result;
|
HttpResult result;
|
||||||
try {
|
try {
|
||||||
@@ -2039,20 +2049,16 @@ int OrcaCloudServiceAgent::http_delete(const std::string& path, std::string* res
|
|||||||
};
|
};
|
||||||
|
|
||||||
HttpResult res = perform();
|
HttpResult res = perform();
|
||||||
|
bool suppress = resolve_unauthorized(res, perform, "http_delete_" + path);
|
||||||
// Single retry on 401 - no recursion
|
|
||||||
if (res.status == 401 && attempt_refresh_after_unauthorized("http_delete_" + path)) {
|
|
||||||
res = perform();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response_body) *response_body = res.body;
|
if (response_body) *response_body = res.body;
|
||||||
if (http_code) *http_code = res.status;
|
if (http_code) *http_code = res.status;
|
||||||
|
|
||||||
if (!res.success || res.status >= 400) {
|
if (!suppress && (!res.success || res.status >= 400)) {
|
||||||
invoke_http_error_callback(res.status, res.body);
|
invoke_http_error_callback(res.status, res.body);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.success ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
return (res.success && !suppress) ? BAMBU_NETWORK_SUCCESS : BAMBU_NETWORK_ERR_CONNECT_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OrcaCloudServiceAgent::http_post_token(const std::string& body, std::string* response_body, unsigned int* http_code, const std::string& custom_url)
|
bool OrcaCloudServiceAgent::http_post_token(const std::string& body, std::string* response_body, unsigned int* http_code, const std::string& custom_url)
|
||||||
@@ -2108,7 +2114,7 @@ bool OrcaCloudServiceAgent::http_post_token(const std::string& body, std::string
|
|||||||
})
|
})
|
||||||
.on_error([&](std::string body, std::string error, unsigned resp_status) {
|
.on_error([&](std::string body, std::string error, unsigned resp_status) {
|
||||||
success = false;
|
success = false;
|
||||||
status = resp_status == 0 ? 404 : resp_status;
|
status = resp_status; // keep 0 for "no response" so the refresh classifier sees a transport failure
|
||||||
resp_body = body;
|
resp_body = body;
|
||||||
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
|
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,6 +20,14 @@ namespace Slic3r {
|
|||||||
class AppConfig;
|
class AppConfig;
|
||||||
struct BundleMetadata;
|
struct BundleMetadata;
|
||||||
|
|
||||||
|
// Outcome of a token-refresh attempt: decides whether a 401 should log the user
|
||||||
|
// out (AuthRejected) or be treated as a recoverable condition (Transient).
|
||||||
|
enum class RefreshResult {
|
||||||
|
Success, // new tokens obtained
|
||||||
|
AuthRejected, // server definitively rejected the refresh token -> logout is correct
|
||||||
|
Transient // network/server problem -> keep the session and retry later
|
||||||
|
};
|
||||||
|
|
||||||
// Constants for OAuth loopback server
|
// Constants for OAuth loopback server
|
||||||
namespace auth_constants {
|
namespace auth_constants {
|
||||||
constexpr int LOOPBACK_PORT = 41172;
|
constexpr int LOOPBACK_PORT = 41172;
|
||||||
@@ -276,10 +284,10 @@ public:
|
|||||||
void clear_refresh_token();
|
void clear_refresh_token();
|
||||||
|
|
||||||
// Token refresh helpers
|
// Token refresh helpers
|
||||||
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
bool refresh_if_expiring(std::chrono::seconds skew, const std::string& reason);
|
||||||
bool refresh_from_storage(const std::string& reason, bool async = false);
|
RefreshResult refresh_from_storage(const std::string& reason, bool async = false);
|
||||||
bool refresh_now(const std::string& refresh_token, const std::string& reason, bool async = false);
|
RefreshResult refresh_now(const std::string& refresh_token, const std::string& reason, bool async = false);
|
||||||
bool refresh_session_with_token(const std::string& refresh_token);
|
RefreshResult refresh_session_with_token(const std::string& refresh_token);
|
||||||
|
|
||||||
// Session state helpers. nickname is the human-facing UI label after provider fallback resolution.
|
// Session state helpers. nickname is the human-facing UI label after provider fallback resolution.
|
||||||
bool set_user_session(const std::string& token,
|
bool set_user_session(const std::string& token,
|
||||||
@@ -299,13 +307,28 @@ private:
|
|||||||
std::function<void(int http_code, const std::string& error)> on_error
|
std::function<void(int http_code, const std::string& error)> on_error
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Shared result of one HTTP attempt by the data methods (get/post/put/delete).
|
||||||
|
struct HttpResult {
|
||||||
|
bool success{false};
|
||||||
|
unsigned int status{0};
|
||||||
|
std::string body;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Applies the "retry once on 401" policy for the data HTTP methods.
|
||||||
|
// `res` holds the first response; `perform` re-issues the request after a
|
||||||
|
// successful refresh. Returns true if the auth error should be SUPPRESSED
|
||||||
|
// (i.e. the session must be kept rather than logged out).
|
||||||
|
bool resolve_unauthorized(HttpResult& res,
|
||||||
|
const std::function<HttpResult()>& perform,
|
||||||
|
const std::string& reason);
|
||||||
|
|
||||||
// HTTP request helpers
|
// HTTP request helpers
|
||||||
int http_get(const std::string& path, std::string* response_body, unsigned int* http_code);
|
int http_get(const std::string& path, std::string* response_body, unsigned int* http_code);
|
||||||
int http_post(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
|
int http_post(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
|
||||||
int http_put(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
|
int http_put(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);
|
||||||
int http_delete(const std::string& path, std::string* response_body, unsigned int* http_code);
|
int http_delete(const std::string& path, std::string* response_body, unsigned int* http_code);
|
||||||
std::map<std::string, std::string> data_headers();
|
std::map<std::string, std::string> data_headers();
|
||||||
bool attempt_refresh_after_unauthorized(const std::string& reason);
|
RefreshResult attempt_refresh_after_unauthorized(const std::string& reason);
|
||||||
|
|
||||||
// Auth HTTP helpers
|
// Auth HTTP helpers
|
||||||
bool http_post_token(const std::string& body, std::string* response_body, unsigned int* http_code, const std::string& url = "");
|
bool http_post_token(const std::string& body, std::string* response_body, unsigned int* http_code, const std::string& url = "");
|
||||||
|
|||||||
Reference in New Issue
Block a user