Fix deadlock

This commit is contained in:
2026-04-24 12:54:16 +10:00
parent e2bf17289d
commit fd2394a85a
3 changed files with 138 additions and 16 deletions

View File

@@ -62,6 +62,111 @@ namespace ANSCENTER
return oss.str();
}
// ────────────────────────────────────────────────────────────────────────
// SummarizeChilkatError: turn Chilkat's verbose multi-line lastErrorText()
// into a single diagnostic line suitable for _logger.LogError().
//
// Chilkat's log looks like:
// ChilkatLog: Connect: DllDate: ... ChilkatVersion: ... UnlockStatus: ...
// restConnect: domain_or_ip: s3.us-east-1.amazonaws.com
// socket2Connect: connect2: connectImplicitSsl: connectSocket_v2:
// connect_domain: ckDnsResolveDomainIPv4_n: ...
// clientHandshake2: readHandshakeMessages:
// Failed to read beginning of SSL/TLS record.
// readTlsRecord: Socket operation timeout.
// See https://cknotes.com/...
// ConnectFailReason: 103
// Failed.
//
// Diagnostics we care about:
// • the deepest concrete failure reason (the non-label leaf line)
// • ConnectFailReason: <N> if present
// • the target (domain_or_ip / ip_or_domain + port)
// Rest (DllDate, ChilkatVersion, Architecture, VerboseLogging, ...) is noise.
//
// Returns e.g.:
// "Connect failed [reason=103] s3.us-east-1.amazonaws.com:443 — Socket operation timeout (TLS handshake). See https://cknotes.com/failed-to-read-beginning-of-ssl-tls-record-..."
// ────────────────────────────────────────────────────────────────────────
static std::string SummarizeChilkatError(const char* raw) {
if (!raw || !*raw) return "(empty Chilkat log)";
std::string s(raw);
// Split into trimmed lines.
std::vector<std::string> lines;
{
std::istringstream iss(s);
std::string ln;
while (std::getline(iss, ln)) {
// Trim leading whitespace (Chilkat indents by level).
size_t a = ln.find_first_not_of(" \t\r\n");
if (a == std::string::npos) continue;
size_t b = ln.find_last_not_of(" \t\r\n");
lines.push_back(ln.substr(a, b - a + 1));
}
}
if (lines.empty()) return "(empty Chilkat log)";
// Lines that are pure labels ("foo:" or "--foo") are scaffolding.
auto isLabelLine = [](const std::string& ln) {
if (ln.empty()) return true;
if (ln.rfind("--", 0) == 0) return true; // "--foo" close-marker
// "label: value" — keep only if value is non-empty and line doesn't
// look like pure call-stack breadcrumb (trailing ':').
if (!ln.empty() && ln.back() == ':') return true;
return false;
};
// Fields we want to extract.
std::string target, failReason, tlsHint, leafError, seeUrl;
for (auto& ln : lines) {
if (ln.rfind("domain_or_ip:", 0) == 0 || ln.rfind("ip_or_domain:", 0) == 0) {
auto p = ln.find(':');
if (p != std::string::npos) target = ln.substr(p + 1);
} else if (ln.rfind("ConnectFailReason:", 0) == 0) {
auto p = ln.find(':');
if (p != std::string::npos) failReason = ln.substr(p + 1);
} else if (ln.rfind("port:", 0) == 0) {
auto p = ln.find(':');
if (p != std::string::npos) {
// Append port to target if we have one.
std::string port = ln.substr(p + 1);
// Trim leading space.
size_t a = port.find_first_not_of(" \t");
if (a != std::string::npos) port = port.substr(a);
if (!target.empty()) target += ":" + port;
}
} else if (ln.rfind("See http", 0) == 0 || ln.rfind("See https", 0) == 0) {
seeUrl = ln.substr(4); // drop "See "
} else if (ln.rfind("Failed to read beginning of SSL/TLS record", 0) == 0) {
tlsHint = "TLS handshake — server sent nothing (firewall/AV TLS inspection, proxy, or packet loss)";
} else if (!isLabelLine(ln)) {
// Candidate concrete message. Keep the LATEST one that's not a
// stack label; Chilkat's deepest concrete error is usually the
// most specific explanation.
leafError = ln;
}
}
// Trim leading space on captured fields.
auto trim = [](std::string& v) {
size_t a = v.find_first_not_of(" \t");
if (a == std::string::npos) { v.clear(); return; }
size_t b = v.find_last_not_of(" \t");
v = v.substr(a, b - a + 1);
};
trim(target); trim(failReason); trim(leafError); trim(seeUrl);
// Compose.
std::ostringstream out;
out << "Chilkat Connect failed";
if (!failReason.empty()) out << " [reason=" << failReason << "]";
if (!target.empty()) out << " " << target;
if (!leafError.empty()) out << "" << leafError;
if (!tlsHint.empty()) out << " (" << tlsHint << ")";
if (!seeUrl.empty()) out << " (" << seeUrl << ")";
return out.str();
}
// Private helper function to extract file name from a path
// Helper function to extract filename from path
std::string ANSAWSS3::ExtractFileName(const std::string& filePath) {
@@ -249,7 +354,9 @@ namespace ANSCENTER
// Connect
if (!conn->rest.Connect(_fullAWSURL.c_str(), _port, _bTls, _bAutoReconnect)) {
_logger.LogError("ANSAWSS3::CreateConnection", conn->rest.lastErrorText(), __FILE__, __LINE__);
const std::string summary = SummarizeChilkatError(conn->rest.lastErrorText());
_logger.LogError("ANSAWSS3::CreateConnection", summary, __FILE__, __LINE__);
ANS_DBG("AWSS3", "Connect failed: %s", summary.c_str());
return nullptr;
}
@@ -261,7 +368,9 @@ namespace ANSCENTER
conn->socket.put_HttpProxyPassword(_proxyPassword.c_str());
conn->socket.put_HttpProxyForHttp(_bProxy);
if (!conn->rest.UseConnection(conn->socket, true)) {
_logger.LogError("ANSAWSS3::CreateConnection - Proxy error", conn->rest.lastErrorText(), __FILE__, __LINE__);
const std::string summary = SummarizeChilkatError(conn->rest.lastErrorText());
_logger.LogError("ANSAWSS3::CreateConnection - Proxy error", summary, __FILE__, __LINE__);
ANS_DBG("AWSS3", "Proxy error: %s", summary.c_str());
return nullptr;
}
}
@@ -275,7 +384,9 @@ namespace ANSCENTER
if (!_bucketRegion.empty()) conn->authAws.put_Region(_bucketRegion.c_str());
}
if (!conn->rest.SetAuthAws(conn->authAws)) {
_logger.LogError("ANSAWSS3::CreateConnection - Auth error", conn->rest.lastErrorText(), __FILE__, __LINE__);
const std::string summary = SummarizeChilkatError(conn->rest.lastErrorText());
_logger.LogError("ANSAWSS3::CreateConnection - Auth error", summary, __FILE__, __LINE__);
ANS_DBG("AWSS3", "Auth error: %s", summary.c_str());
return nullptr;
}
}