#pragma once #include #include // Хелперы для информативного логирования сетевых ошибок. Используются // и в NetworkService, и в DeviceScreener — категория сразу показывает // характер сбоя: timeout / refused / DNS / SSL / HTTP-уровень. namespace NetworkErrorUtils { // Удаляет схему+хост из URL, оставляет только путь (для логов/UI без секретов). inline QString sanitizePath(const QString &fullUrl) { const int schemeEnd = fullUrl.indexOf(QStringLiteral("://")); if (schemeEnd < 0) return fullUrl; const int pathStart = fullUrl.indexOf(QLatin1Char('/'), schemeEnd + 3); return pathStart >= 0 ? fullUrl.mid(pathStart) : QStringLiteral("/"); } // Категория ошибки в виде короткого ярлыка. См. QNetworkReply::NetworkError. inline QString networkErrorCategory(QNetworkReply::NetworkError err) { switch (err) { case QNetworkReply::NoError: return QStringLiteral("NO_ERROR"); case QNetworkReply::ConnectionRefusedError: return QStringLiteral("CONNECTION_REFUSED"); case QNetworkReply::RemoteHostClosedError: return QStringLiteral("REMOTE_CLOSED"); case QNetworkReply::HostNotFoundError: return QStringLiteral("DNS_FAIL"); case QNetworkReply::TimeoutError: return QStringLiteral("QT_TIMEOUT"); case QNetworkReply::OperationCanceledError: return QStringLiteral("CANCELLED"); case QNetworkReply::SslHandshakeFailedError: return QStringLiteral("SSL_HANDSHAKE"); case QNetworkReply::TemporaryNetworkFailureError: return QStringLiteral("TEMP_NET_FAIL"); case QNetworkReply::NetworkSessionFailedError: return QStringLiteral("NO_NETWORK_SESSION"); case QNetworkReply::BackgroundRequestNotAllowedError: return QStringLiteral("BG_NOT_ALLOWED"); case QNetworkReply::TooManyRedirectsError: return QStringLiteral("TOO_MANY_REDIRECTS"); case QNetworkReply::InsecureRedirectError: return QStringLiteral("INSECURE_REDIRECT"); case QNetworkReply::UnknownNetworkError: return QStringLiteral("UNKNOWN_NETWORK"); case QNetworkReply::ProxyConnectionRefusedError: return QStringLiteral("PROXY_REFUSED"); case QNetworkReply::ProxyConnectionClosedError: return QStringLiteral("PROXY_CLOSED"); case QNetworkReply::ProxyNotFoundError: return QStringLiteral("PROXY_NOT_FOUND"); case QNetworkReply::ProxyTimeoutError: return QStringLiteral("PROXY_TIMEOUT"); case QNetworkReply::ProxyAuthenticationRequiredError: return QStringLiteral("PROXY_AUTH_REQ"); case QNetworkReply::ContentAccessDenied: return QStringLiteral("HTTP_403"); case QNetworkReply::ContentOperationNotPermittedError:return QStringLiteral("OP_NOT_PERMITTED"); case QNetworkReply::ContentNotFoundError: return QStringLiteral("HTTP_404"); case QNetworkReply::AuthenticationRequiredError: return QStringLiteral("HTTP_401"); case QNetworkReply::ContentReSendError: return QStringLiteral("RESEND_REQUIRED"); case QNetworkReply::ContentConflictError: return QStringLiteral("HTTP_409"); case QNetworkReply::ContentGoneError: return QStringLiteral("HTTP_410"); case QNetworkReply::InternalServerError: return QStringLiteral("HTTP_500"); case QNetworkReply::OperationNotImplementedError: return QStringLiteral("HTTP_501"); case QNetworkReply::ServiceUnavailableError: return QStringLiteral("HTTP_503"); case QNetworkReply::UnknownServerError: return QStringLiteral("UNKNOWN_SERVER"); case QNetworkReply::ProtocolUnknownError: return QStringLiteral("PROTO_UNKNOWN"); case QNetworkReply::ProtocolInvalidOperationError: return QStringLiteral("PROTO_INVALID_OP"); case QNetworkReply::UnknownContentError: return QStringLiteral("UNKNOWN_CONTENT"); case QNetworkReply::ProtocolFailure: return QStringLiteral("PROTO_FAILURE"); default: return QStringLiteral("err_%1").arg(static_cast(err)); } } // Развёрнутое описание сетевой ошибки: HTTP-код + категория или просто // категория + Qt errorString. Эндпоинт — без хоста. inline QString describeNetError(QNetworkReply *reply, const QString &path) { const int httpCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); const QString endpoint = sanitizePath(path); const QString category = networkErrorCategory(reply->error()); if (httpCode > 0) { return QStringLiteral("HTTP %1 [%2] %3").arg(httpCode).arg(category, endpoint); } return QStringLiteral("[%1] %2 (qt: %3)").arg(category, endpoint, reply->errorString()); } // Случай нашего QTimer-таймаута (reply ещё не завершился). reply->error() // обычно ещё NoError — помечаем категорию явно как CLIENT_TIMEOUT_30s. inline QString describeClientTimeout(QNetworkReply *reply, const QString &path) { const QString endpoint = sanitizePath(path); const QString qtCategory = networkErrorCategory(reply->error()); return QStringLiteral("[CLIENT_TIMEOUT_30s] %1 (qt-state=%2 errorString=\"%3\")") .arg(endpoint, qtCategory, reply->errorString()); } } // namespace NetworkErrorUtils