Skip to content

Commit

Permalink
Improve network error messages (#4704)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nerixyz authored Jul 1, 2023
1 parent d2f1516 commit 22b290c
Show file tree
Hide file tree
Showing 15 changed files with 378 additions and 169 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- Bugfix: Fix visual glitches with smooth scrolling. (#4501)
- Bugfix: Fixed pings firing for the "Your username" highlight when not signed in. (#4698)
- Bugfix: Fixed partially broken filters on Qt 6 builds. (#4702)
- Bugfix: Fixed some network errors having `0` as their HTTP status. (#4704)
- Bugfix: Fixed crash that could occurr when closing the usercard too quickly after blocking or unblocking a user. (#4711)
- Dev: Added command to set Qt's logging filter/rules at runtime (`/c2-set-logging-rules`). (#4637)
- Dev: Added the ability to see & load custom themes from the Themes directory. No stable promises are made of this feature, changes might be made that breaks custom themes without notice. (#4570)
Expand Down
13 changes: 8 additions & 5 deletions src/common/NetworkPrivate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ void loadUncached(std::shared_ptr<NetworkData> &&data)
{
postToThread([data] {
data->onError_(NetworkResult(
{}, NetworkResult::timedoutStatus));
NetworkResult::NetworkError::TimeoutError, {},
{}));
});
}

Expand Down Expand Up @@ -218,8 +219,9 @@ void loadUncached(std::shared_ptr<NetworkData> &&data)
QString(data->payload_));
}
// TODO: Should this always be run on the GUI thread?
postToThread([data, code = status.toInt(), reply] {
data->onError_(NetworkResult(reply->readAll(), code));
postToThread([data, status, reply] {
data->onError_(NetworkResult(reply->error(), status,
reply->readAll()));
});
}

Expand All @@ -238,7 +240,7 @@ void loadUncached(std::shared_ptr<NetworkData> &&data)
auto status =
reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);

NetworkResult result(bytes, status.toInt());
NetworkResult result(reply->error(), status, bytes);

DebugCount::increase("http request success");
// log("starting {}", data->request_.url().toString());
Expand Down Expand Up @@ -337,7 +339,8 @@ void loadCached(std::shared_ptr<NetworkData> &&data)

// XXX: check if bytes is empty?
QByteArray bytes = cachedFile.readAll();
NetworkResult result(bytes, 200);
NetworkResult result(NetworkResult::NetworkError::NoError, QVariant(200),
bytes);

qCDebug(chatterinoHTTP)
<< QString("%1 [CACHED] 200 %2")
Expand Down
28 changes: 23 additions & 5 deletions src/common/NetworkResult.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
#include "common/QLogging.hpp"

#include <QJsonDocument>
#include <QMetaEnum>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>

namespace chatterino {

NetworkResult::NetworkResult(const QByteArray &data, int status)
: data_(data)
, status_(status)
NetworkResult::NetworkResult(NetworkError error, const QVariant &httpStatusCode,
QByteArray data)
: data_(std::move(data))
, error_(error)
{
if (httpStatusCode.isValid())
{
this->status_ = httpStatusCode.toInt();
}
}

QJsonObject NetworkResult::parseJson() const
Expand Down Expand Up @@ -59,9 +65,21 @@ const QByteArray &NetworkResult::getData() const
return this->data_;
}

int NetworkResult::status() const
QString NetworkResult::formatError() const
{
return this->status_;
if (this->status_)
{
return QString::number(*this->status_);
}

const auto *name =
QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(
this->error_);
if (name == nullptr)
{
return QStringLiteral("unknown error (%1)").arg(this->error_);
}
return name;
}

} // namespace chatterino
30 changes: 26 additions & 4 deletions src/common/NetworkResult.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

#include <QJsonArray>
#include <QJsonObject>
#include <QNetworkReply>
#include <rapidjson/document.h>

#include <optional>

namespace chatterino {

class NetworkResult
{
public:
NetworkResult(const QByteArray &data, int status);
using NetworkError = QNetworkReply::NetworkError;

NetworkResult(NetworkError error, const QVariant &httpStatusCode,
QByteArray data);

/// Parses the result as json and returns the root as an object.
/// Returns empty object if parsing failed.
Expand All @@ -20,13 +26,29 @@ class NetworkResult
/// Parses the result as json and returns the document.
rapidjson::Document parseRapidJson() const;
const QByteArray &getData() const;
int status() const;

static constexpr int timedoutStatus = -2;
/// The error code of the reply.
/// In case of a successful reply, this will be NoError (0)
NetworkError error() const
{
return this->error_;
}

/// The HTTP status code if a response was received.
std::optional<int> status() const
{
return this->status_;
}

/// Formats the error.
/// If a reply is received, returns the HTTP status otherwise, the network error.
QString formatError() const;

private:
QByteArray data_;
int status_;

NetworkError error_;
std::optional<int> status_;
};

} // namespace chatterino
4 changes: 2 additions & 2 deletions src/providers/IvrApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ void IvrApi::getSubage(QString userName, QString channelName,
})
.onError([failureCallback](auto result) {
qCWarning(chatterinoIvr)
<< "Failed IVR API Call!" << result.status()
<< "Failed IVR API Call!" << result.formatError()
<< QString(result.getData());
failureCallback();
})
Expand All @@ -51,7 +51,7 @@ void IvrApi::getBulkEmoteSets(QString emoteSetList,
})
.onError([failureCallback](auto result) {
qCWarning(chatterinoIvr)
<< "Failed IVR API Call!" << result.status()
<< "Failed IVR API Call!" << result.formatError()
<< QString(result.getData());
failureCallback();
})
Expand Down
9 changes: 6 additions & 3 deletions src/providers/RecentMessagesApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,20 @@ void RecentMessagesApi::loadRecentMessages(const QString &channelName,

return Success;
})
.onError([channelPtr, onError](NetworkResult result) {
.onError([channelPtr, onError](const NetworkResult &result) {
auto shared = channelPtr.lock();
if (!shared)
{
return;
}

qCDebug(chatterinoRecentMessages)
<< "Failed to load recent messages for" << shared->getName();

shared->addMessage(makeSystemMessage(
QString("Message history service unavailable (Error %1)")
.arg(result.status())));
QStringLiteral(
"Message history service unavailable (Error: %1)")
.arg(result.formatError())));

onError();
})
Expand Down
20 changes: 7 additions & 13 deletions src/providers/bttv/BttvEmotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,23 +259,17 @@ void BttvEmotes::loadChannel(std::weak_ptr<Channel> channel,
shared->addMessage(
makeSystemMessage(CHANNEL_HAS_NO_EMOTES));
}
else if (result.status() == NetworkResult::timedoutStatus)
{
// TODO: Auto retry in case of a timeout, with a delay
qCWarning(chatterinoBttv)
<< "Fetching BTTV emotes for channel" << channelId
<< "failed due to timeout";
shared->addMessage(makeSystemMessage(
"Failed to fetch BetterTTV channel emotes. (timed out)"));
}
else
{
// TODO: Auto retry in case of a timeout, with a delay
auto errorString = result.formatError();
qCWarning(chatterinoBttv)
<< "Error fetching BTTV emotes for channel" << channelId
<< ", error" << result.status();
shared->addMessage(
makeSystemMessage("Failed to fetch BetterTTV channel "
"emotes. (unknown error)"));
<< ", error" << errorString;
shared->addMessage(makeSystemMessage(
QStringLiteral("Failed to fetch BetterTTV channel "
"emotes. (Error: %1)")
.arg(errorString)));
}
})
.execute();
Expand Down
21 changes: 7 additions & 14 deletions src/providers/ffz/FfzEmotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,24 +273,17 @@ void FfzEmotes::loadChannel(
makeSystemMessage(CHANNEL_HAS_NO_EMOTES));
}
}
else if (result.status() == NetworkResult::timedoutStatus)
{
// TODO: Auto retry in case of a timeout, with a delay
qCWarning(chatterinoFfzemotes)
<< "Fetching FFZ emotes for channel" << channelID
<< "failed due to timeout";
shared->addMessage(
makeSystemMessage("Failed to fetch FrankerFaceZ channel "
"emotes. (timed out)"));
}
else
{
// TODO: Auto retry in case of a timeout, with a delay
auto errorString = result.formatError();
qCWarning(chatterinoFfzemotes)
<< "Error fetching FFZ emotes for channel" << channelID
<< ", error" << result.status();
shared->addMessage(
makeSystemMessage("Failed to fetch FrankerFaceZ channel "
"emotes. (unknown error)"));
<< ", error" << errorString;
shared->addMessage(makeSystemMessage(
QStringLiteral("Failed to fetch FrankerFaceZ channel "
"emotes. (Error: %1)")
.arg(errorString)));
}
})
.execute();
Expand Down
29 changes: 8 additions & 21 deletions src/providers/seventv/SeventvEmotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -386,23 +386,17 @@ void SeventvEmotes::loadChannelEmotes(
makeSystemMessage(CHANNEL_HAS_NO_EMOTES));
}
}
else if (result.status() == NetworkResult::timedoutStatus)
{
// TODO: Auto retry in case of a timeout, with a delay
qCWarning(chatterinoSeventv)
<< "Fetching 7TV emotes for channel" << channelId
<< "failed due to timeout";
shared->addMessage(makeSystemMessage(
"Failed to fetch 7TV channel emotes. (timed out)"));
}
else
{
// TODO: Auto retry in case of a timeout, with a delay
auto errorString = result.formatError();
qCWarning(chatterinoSeventv)
<< "Error fetching 7TV emotes for channel" << channelId
<< ", error" << result.status();
shared->addMessage(
makeSystemMessage("Failed to fetch 7TV channel "
"emotes. (unknown error)"));
<< ", error" << errorString;
shared->addMessage(makeSystemMessage(
QStringLiteral("Failed to fetch 7TV channel "
"emotes. (Error: %1)")
.arg(errorString)));
}
})
.execute();
Expand Down Expand Up @@ -502,14 +496,7 @@ void SeventvEmotes::getEmoteSet(
})
.onError([emoteSetId, callback = std::move(errorCallback)](
const NetworkResult &result) {
if (result.status() == NetworkResult::timedoutStatus)
{
callback("timed out");
}
else
{
callback(QString("status: %1").arg(result.status()));
}
callback(result.formatError());
})
.execute();
}
Expand Down
Loading

0 comments on commit 22b290c

Please sign in to comment.