Make QRest* APIs non-owning and non-duplicating

Note: documentation will be updated in a follow-up commit

This commit makes QRestReply and QRestAccessManager
classes lighter, non-owning wrappers. Furthermore their
APIs don't duplicate the wrapped QNetwork* APIs.

This makes it easier to use / opt-in to these helpers
in pre-existing applications which are based on
QNetworkAccessManager and QNetworkReply.

Since APIs are no longer duplicated, the QRest
classes are more obviously a convenience _wrapper_,
as opposed to being an alternative vertical stack.

In practice this change consists of:
- QRestAM never instantiates QNetworkAccessManager,
  but accepts it via constructor. It does not take
  ownership of the QNetworkAccessManager.
- QRestReply accepts QNetworkReply via constructor. It
  does not take ownership of the QNetworkReply
- Signals and most duplicated functions are removed
  from both QRestAM and QRR.
- QRestReply is no longer a QObject
- Since QRestAM doesn't have much to report anymore,
  the debug operator is dropped.

Resulted from API-review

Change-Id: Ib62d9cc2df41cac631396a84bb7ec4d2d54b0c8c
Reviewed-by: Ivan Solovev <ivan.solovev@qt.io>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
(cherry picked from commit 9ba5c7ff6aa42c5701cf950d2137467a2d178833)
This commit is contained in:
Juha Vuolle 2024-01-19 11:33:39 +02:00
parent a57f89251c
commit afc1f2a6cc
7 changed files with 475 additions and 989 deletions

View File

@ -599,20 +599,13 @@ Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
\overload \overload
*/ */
/* QRestAccessManager::QRestAccessManager(QNetworkAccessManager *manager, QObject *parent)
Memory management/object ownership:
- QRestAM is parent of QNAM and QRestReplies
- QRestReplies are parents of QNetworkReplies
*/
/*!
Constructs a QRestAccessManager and sets \a parent as the parent object.
*/
QRestAccessManager::QRestAccessManager(QObject *parent)
: QObject(*new QRestAccessManagerPrivate, parent) : QObject(*new QRestAccessManagerPrivate, parent)
{ {
Q_D(QRestAccessManager); Q_D(QRestAccessManager);
d->ensureNetworkAccessManager(); d->qnam = manager;
if (!d->qnam)
qCWarning(lcQrest, "QRestAccessManager: QNetworkAccesManager is nullptr");
} }
/*! /*!
@ -622,95 +615,6 @@ QRestAccessManager::QRestAccessManager(QObject *parent)
QRestAccessManager::~QRestAccessManager() QRestAccessManager::~QRestAccessManager()
= default; = default;
/*!
Returns whether QRestAccessManager is currently configured to automatically
delete replies once they have finished. By default this is \c true.
\sa setDeletesRepliesOnFinished()
*/
bool QRestAccessManager::deletesRepliesOnFinished() const
{
Q_D(const QRestAccessManager);
return d->deletesRepliesOnFinished;
}
/*!
Enables or disables automatic deletion of QRestReply instances
once the request has finished, according to the provided
\a autoDelete parameter. The deletion is done with deleteLater()
so that using the replies in directly-connected slots or callbacks is safe.
\sa deletesRepliesOnFinished()
*/
void QRestAccessManager::setDeletesRepliesOnFinished(bool autoDelete)
{
Q_D(QRestAccessManager);
d->deletesRepliesOnFinished = autoDelete;
}
/*!
Aborts all unfinished network requests. Calling this function is same
as calling QRestReply::abort() for all individual unfinished requests.
\sa QRestReply::abort(), QNetworkReply::abort()
*/
void QRestAccessManager::abortRequests()
{
Q_D(QRestAccessManager);
// Make copy of the reply container, as it might get modified when
// aborting individual requests if they finish immediately
const auto requests = d->activeRequests;
for (const auto &[req, _] : requests.asKeyValueRange())
req->abort();
}
/*!
Sets \a timeout used for transfers.
\sa QNetworkAccessManager::setTransferTimeout(), transferTimeout(),
QNetworkRequestFactory::setTransferTimeout()
*/
void QRestAccessManager::setTransferTimeout(std::chrono::milliseconds timeout)
{
Q_D(QRestAccessManager);
d->qnam->setTransferTimeout(timeout);
}
/*!
Returns the timeout used for transfers.
\sa setTransferTimeout(), QNetworkAccessManager::transferTimeoutAsDuration(),
QNetworkRequestFactory::transferTimeout()
*/
std::chrono::milliseconds QRestAccessManager::transferTimeout() const
{
Q_D(const QRestAccessManager);
return d->qnam->transferTimeoutAsDuration();
}
#ifndef QT_NO_DEBUG_STREAM
/*!
\fn QDebug QRestAccessManager::operator<<(QDebug debug,
const QRestAccessManager &manager)
Writes \a manager into \a debug stream.
\sa {Debugging Techniques}
*/
QDebug operator<<(QDebug debug, const QRestAccessManager &manager)
{
const QDebugStateSaver saver(debug);
debug.resetFormat().nospace();
debug << "QRestAccessManager(deletesRepliesOnFinished = " << manager.deletesRepliesOnFinished()
<< ", transferTimeout = " << manager.transferTimeout()
<< ", active requests = " << manager.d_func()->activeRequests.size()
<< ")";
return debug;
}
#endif // QT_NO_DEBUG_STREAM
/*! /*!
Returns the underlying QNetworkAccessManager instance. The instance Returns the underlying QNetworkAccessManager instance. The instance
can be used for accessing less-frequently used features and configurations. can be used for accessing less-frequently used features and configurations.
@ -734,7 +638,7 @@ QRestAccessManagerPrivate::~QRestAccessManagerPrivate()
} }
} }
QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
const QJsonObject &data, const QObject *context, const QJsonObject &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -743,7 +647,7 @@ QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
const QJsonArray &data, const QObject *context, const QJsonArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -752,14 +656,14 @@ QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
const QVariantMap &data, const QObject *context, const QVariantMap &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
return postWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot); return postWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot);
} }
QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
const QByteArray &data, const QObject *context, const QByteArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -767,7 +671,7 @@ QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->post(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->post(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
QHttpMultiPart *data, const QObject *context, QHttpMultiPart *data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -775,7 +679,7 @@ QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->post(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->post(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
QIODevice *data, const QObject *context, QIODevice *data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -783,14 +687,14 @@ QRestReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->post(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->post(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
Q_D(QRestAccessManager); Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->get(request); }, context, slot); return d->executeRequest([&]() { return d->qnam->get(request); }, context, slot);
} }
QRestReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
const QByteArray &data, const QObject *context, const QByteArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -798,7 +702,7 @@ QRestReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->get(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->get(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
const QJsonObject &data, const QObject *context, const QJsonObject &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -807,7 +711,7 @@ QRestReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
QIODevice *data, const QObject *context, QIODevice *data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -815,21 +719,21 @@ QRestReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->get(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->get(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
Q_D(QRestAccessManager); Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->deleteResource(request); }, context, slot); return d->executeRequest([&]() { return d->qnam->deleteResource(request); }, context, slot);
} }
QRestReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
Q_D(QRestAccessManager); Q_D(QRestAccessManager);
return d->executeRequest([&]() { return d->qnam->head(request); }, context, slot); return d->executeRequest([&]() { return d->qnam->head(request); }, context, slot);
} }
QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
const QJsonObject &data, const QObject *context, const QJsonObject &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -838,7 +742,7 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
const QJsonArray &data, const QObject *context, const QJsonArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -847,14 +751,14 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
const QVariantMap &data, const QObject *context, const QVariantMap &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
return putWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot); return putWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot);
} }
QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
const QByteArray &data, const QObject *context, const QByteArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -862,7 +766,7 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
QHttpMultiPart *data, const QObject *context, QHttpMultiPart *data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -870,7 +774,7 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot); return d->executeRequest([&]() { return d->qnam->put(request, data); }, context, slot);
} }
QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data, QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
Q_D(QRestAccessManager); Q_D(QRestAccessManager);
@ -879,7 +783,7 @@ QRestReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
static const auto PATCH = "PATCH"_ba; static const auto PATCH = "PATCH"_ba;
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QJsonObject &data, const QObject *context, const QJsonObject &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -889,7 +793,7 @@ QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QJsonArray &data, const QObject *context, const QJsonArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -899,14 +803,14 @@ QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request
data, request, context, slot); data, request, context, slot);
} }
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QVariantMap &data, const QObject *context, const QVariantMap &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
return patchWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot); return patchWithDataImpl(request, QJsonObject::fromVariantMap(data), context, slot);
} }
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
const QByteArray &data, const QObject *context, const QByteArray &data, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
@ -915,7 +819,7 @@ QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request
context, slot); context, slot);
} }
QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data, QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
Q_D(QRestAccessManager); Q_D(QRestAccessManager);
@ -923,7 +827,7 @@ QRestReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request
context, slot); context, slot);
} }
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, const QByteArray &data, const QByteArray& method, const QByteArray &data,
const QObject *context, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
@ -933,7 +837,7 @@ QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &reques
context, slot); context, slot);
} }
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, QIODevice *data, const QByteArray& method, QIODevice *data,
const QObject *context, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
@ -943,7 +847,7 @@ QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &reques
context, slot); context, slot);
} }
QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request, QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
const QByteArray& method, QHttpMultiPart *data, const QByteArray& method, QHttpMultiPart *data,
const QObject *context, const QObject *context,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
@ -953,32 +857,31 @@ QRestReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &reques
context, slot); context, slot);
} }
QRestReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *networkReply, QNetworkReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *reply,
const QObject *contextObject, const QObject *contextObject,
QtPrivate::QSlotObjectBase *slot) QtPrivate::QSlotObjectBase *slot)
{ {
Q_Q(QRestAccessManager); Q_Q(QRestAccessManager);
Q_ASSERT(networkReply); Q_ASSERT(reply);
auto restReply = new QRestReply(networkReply, q);
QtPrivate::SlotObjSharedPtr slotPtr(QtPrivate::SlotObjUniquePtr{slot}); // adopts QtPrivate::SlotObjSharedPtr slotPtr(QtPrivate::SlotObjUniquePtr{slot}); // adopts
activeRequests.insert(restReply, CallerInfo{contextObject, slotPtr}); activeRequests.insert(reply, CallerInfo{contextObject, slotPtr});
// The signal connections below are made to 'q' to avoid stray signal
// handling upon its destruction while requests were still in progress
// If context object is provided, use it with connect => context object lifecycle is considered QObject::connect(reply, &QNetworkReply::finished, q, [reply, this]() {
const QObject *context = contextObject ? contextObject : q; handleReplyFinished(reply);
QObject::connect(networkReply, &QNetworkReply::finished, context, [restReply, this]() {
handleReplyFinished(restReply);
}); });
// Safe guard in case reply is destroyed before it's finished // Safe guard in case reply is destroyed before it's finished
QObject::connect(restReply, &QRestReply::destroyed, q, [restReply, this]() { QObject::connect(reply, &QObject::destroyed, q, [reply, this]() {
activeRequests.remove(restReply); activeRequests.remove(reply);
}); });
// If context object is destroyed, clean up any possible replies it had associated with it // If context object is destroyed, clean up any possible replies it had associated with it
if (contextObject) { if (contextObject) {
QObject::connect(contextObject, &QObject::destroyed, q, [restReply, this]() { QObject::connect(contextObject, &QObject::destroyed, q, [reply, this]() {
activeRequests.remove(restReply); activeRequests.remove(reply);
}); });
} }
return restReply; return reply;
} }
void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject) void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject)
@ -994,27 +897,17 @@ void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObjec
} }
} }
void QRestAccessManagerPrivate::ensureNetworkAccessManager() QNetworkReply* QRestAccessManagerPrivate::warnNoAccessManager()
{ {
Q_Q(QRestAccessManager); qCWarning(lcQrest, "QRestAccessManager: QNetworkAccessManager not set");
if (!qnam) { return nullptr;
qnam = new QNetworkAccessManager(q);
connect(qnam, &QNetworkAccessManager::authenticationRequired, this,
&QRestAccessManagerPrivate::handleAuthenticationRequired);
#ifndef QT_NO_NETWORKPROXY
QObject::connect(qnam, &QNetworkAccessManager::proxyAuthenticationRequired,
q, &QRestAccessManager::proxyAuthenticationRequired);
#endif
}
} }
void QRestAccessManagerPrivate::handleReplyFinished(QRestReply *restReply) void QRestAccessManagerPrivate::handleReplyFinished(QNetworkReply *reply)
{ {
Q_Q(QRestAccessManager); auto request = activeRequests.find(reply);
auto request = activeRequests.find(restReply);
if (request == activeRequests.end()) { if (request == activeRequests.end()) {
qCWarning(lcQrest, "Unexpected reply received, ignoring"); qCDebug(lcQrest, "QRestAccessManager: Unexpected reply received, ignoring");
return; return;
} }
@ -1022,41 +915,14 @@ void QRestAccessManagerPrivate::handleReplyFinished(QRestReply *restReply)
activeRequests.erase(request); activeRequests.erase(request);
if (caller.slot) { if (caller.slot) {
// Callback was provided. If we have context object, use it. // Callback was provided
// For clarity: being here with a context object means it has not been destroyed QRestReply restReply(reply);
// while the request has been in progress
void *argv[] = { nullptr, &restReply }; void *argv[] = { nullptr, &restReply };
// If we have context object, use it
QObject *context = caller.contextObject QObject *context = caller.contextObject
? const_cast<QObject*>(caller.contextObject) : nullptr; ? const_cast<QObject*>(caller.contextObject.get()) : nullptr;
caller.slot->call(context, argv); caller.slot->call(context, argv);
} }
if (restReply->hasError())
emit restReply->errorOccurred(restReply);
emit restReply->finished(restReply);
emit q->requestFinished(restReply);
if (deletesRepliesOnFinished)
restReply->deleteLater();
}
void QRestAccessManagerPrivate::handleAuthenticationRequired(QNetworkReply *networkReply,
QAuthenticator *authenticator)
{
Q_Q(QRestAccessManager);
QRestReply *restReply = restReplyFromNetworkReply(networkReply);
if (restReply)
emit q->authenticationRequired(restReply, authenticator);
else
qCWarning(lcQrest, "No matching QRestReply for authentication, ignoring.");
}
QRestReply *QRestAccessManagerPrivate::restReplyFromNetworkReply(QNetworkReply *networkReply)
{
for (const auto &[restReply,_] : activeRequests.asKeyValueRange()) {
if (restReply->networkReply() == networkReply)
return restReply;
}
return nullptr;
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -6,8 +6,6 @@
#include <QtNetwork/qnetworkaccessmanager.h> #include <QtNetwork/qnetworkaccessmanager.h>
#include <chrono>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QDebug; class QDebug;
@ -16,57 +14,57 @@ class QRestReply;
#define QREST_METHOD_WITH_DATA(METHOD, DATA) \ #define QREST_METHOD_WITH_DATA(METHOD, DATA) \
public: \ public: \
template <typename Functor, if_compatible_callback<Functor> = true> \ template <typename Functor, if_compatible_callback<Functor> = true> \
QRestReply *METHOD(const QNetworkRequest &request, DATA data, \ QNetworkReply *METHOD(const QNetworkRequest &request, DATA data, \
const ContextTypeForFunctor<Functor> *context, \ const ContextTypeForFunctor<Functor> *context, \
Functor &&callback) \ Functor &&callback) \
{ \ { \
return METHOD##WithDataImpl(request, data, context, \ return METHOD##WithDataImpl(request, data, context, \
QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
} \ } \
QRestReply *METHOD(const QNetworkRequest &request, DATA data) \ QNetworkReply *METHOD(const QNetworkRequest &request, DATA data) \
{ \ { \
return METHOD##WithDataImpl(request, data, nullptr, nullptr); \ return METHOD##WithDataImpl(request, data, nullptr, nullptr); \
} \ } \
private: \ private: \
QRestReply *METHOD##WithDataImpl(const QNetworkRequest &request, DATA data, \ QNetworkReply *METHOD##WithDataImpl(const QNetworkRequest &request, DATA data, \
const QObject *context, QtPrivate::QSlotObjectBase *slot); \ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
/* end */ /* end */
#define QREST_METHOD_NO_DATA(METHOD) \ #define QREST_METHOD_NO_DATA(METHOD) \
public: \ public: \
template <typename Functor, if_compatible_callback<Functor> = true> \ template <typename Functor, if_compatible_callback<Functor> = true> \
QRestReply *METHOD(const QNetworkRequest &request, \ QNetworkReply *METHOD(const QNetworkRequest &request, \
const ContextTypeForFunctor<Functor> *context, \ const ContextTypeForFunctor<Functor> *context, \
Functor &&callback) \ Functor &&callback) \
{ \ { \
return METHOD##NoDataImpl(request, context, \ return METHOD##NoDataImpl(request, context, \
QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
} \ } \
QRestReply *METHOD(const QNetworkRequest &request) \ QNetworkReply *METHOD(const QNetworkRequest &request) \
{ \ { \
return METHOD##NoDataImpl(request, nullptr, nullptr); \ return METHOD##NoDataImpl(request, nullptr, nullptr); \
} \ } \
private: \ private: \
QRestReply *METHOD##NoDataImpl(const QNetworkRequest &request, \ QNetworkReply *METHOD##NoDataImpl(const QNetworkRequest &request, \
const QObject *context, QtPrivate::QSlotObjectBase *slot); \ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
/* end */ /* end */
#define QREST_METHOD_CUSTOM_WITH_DATA(DATA) \ #define QREST_METHOD_CUSTOM_WITH_DATA(DATA) \
public: \ public: \
template <typename Functor, if_compatible_callback<Functor> = true> \ template <typename Functor, if_compatible_callback<Functor> = true> \
QRestReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data, \ QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data, \
const ContextTypeForFunctor<Functor> *context, \ const ContextTypeForFunctor<Functor> *context, \
Functor &&callback) \ Functor &&callback) \
{ \ { \
return customWithDataImpl(request, method, data, context, \ return customWithDataImpl(request, method, data, context, \
QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
} \ } \
QRestReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data) \ QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data) \
{ \ { \
return customWithDataImpl(request, method, data, nullptr, nullptr); \ return customWithDataImpl(request, method, data, nullptr, nullptr); \
} \ } \
private: \ private: \
QRestReply *customWithDataImpl(const QNetworkRequest& request, const QByteArray &method, \ QNetworkReply *customWithDataImpl(const QNetworkRequest& request, const QByteArray &method, \
DATA data, const QObject* context, \ DATA data, const QObject* context, \
QtPrivate::QSlotObjectBase *slot); \ QtPrivate::QSlotObjectBase *slot); \
/* end */ /* end */
@ -75,27 +73,18 @@ class QRestAccessManagerPrivate;
class Q_NETWORK_EXPORT QRestAccessManager : public QObject class Q_NETWORK_EXPORT QRestAccessManager : public QObject
{ {
Q_OBJECT Q_OBJECT
using CallbackPrototype = void(*)(QRestReply&);
using CallbackPrototype = void(*)(QRestReply*);
template <typename Functor> template <typename Functor>
using ContextTypeForFunctor = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType; using ContextTypeForFunctor = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType;
template <typename Functor> template <typename Functor>
using if_compatible_callback = std::enable_if_t< using if_compatible_callback = std::enable_if_t<
QtPrivate::AreFunctionsCompatible<CallbackPrototype, Functor>::value, bool>; QtPrivate::AreFunctionsCompatible<CallbackPrototype, Functor>::value, bool>;
public: public:
explicit QRestAccessManager(QObject *parent = nullptr); explicit QRestAccessManager(QNetworkAccessManager *manager, QObject *parent = nullptr);
~QRestAccessManager() override; ~QRestAccessManager() override;
QNetworkAccessManager *networkAccessManager() const; QNetworkAccessManager *networkAccessManager() const;
bool deletesRepliesOnFinished() const;
void setDeletesRepliesOnFinished(bool autoDelete);
void setTransferTimeout(std::chrono::milliseconds timeout);
std::chrono::milliseconds transferTimeout() const;
void abortRequests();
QREST_METHOD_NO_DATA(deleteResource) QREST_METHOD_NO_DATA(deleteResource)
QREST_METHOD_NO_DATA(head) QREST_METHOD_NO_DATA(head)
QREST_METHOD_NO_DATA(get) QREST_METHOD_NO_DATA(get)
@ -123,17 +112,7 @@ public:
QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *) QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *)
QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *) QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *)
Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
#endif
void authenticationRequired(QRestReply *reply, QAuthenticator *authenticator);
void requestFinished(QRestReply *reply);
private: private:
#ifndef QT_NO_DEBUG_STREAM
friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QRestAccessManager &manager);
#endif
Q_DECLARE_PRIVATE(QRestAccessManager) Q_DECLARE_PRIVATE(QRestAccessManager)
Q_DISABLE_COPY(QRestAccessManager) Q_DISABLE_COPY(QRestAccessManager)
}; };

View File

@ -34,30 +34,28 @@ public:
QRestAccessManagerPrivate(); QRestAccessManagerPrivate();
~QRestAccessManagerPrivate() override; ~QRestAccessManagerPrivate() override;
void ensureNetworkAccessManager(); QNetworkReply* createActiveRequest(QNetworkReply *reply, const QObject *contextObject,
QtPrivate::QSlotObjectBase *slot);
QRestReply *createActiveRequest(QNetworkReply *networkReply, const QObject *contextObject, void handleReplyFinished(QNetworkReply *reply);
QtPrivate::QSlotObjectBase *slot);
void removeActiveRequest(QRestReply *restReply);
void handleReplyFinished(QRestReply *restReply);
void handleAuthenticationRequired(QNetworkReply *networkReply, QAuthenticator *authenticator);
QRestReply *restReplyFromNetworkReply(QNetworkReply *networkReply);
template<typename Functor> template<typename Functor>
QRestReply *executeRequest(Functor requestOperation, QNetworkReply *executeRequest(Functor requestOperation,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
if (!qnam)
return warnNoAccessManager();
verifyThreadAffinity(context); verifyThreadAffinity(context);
QNetworkReply *reply = requestOperation(); QNetworkReply *reply = requestOperation();
return createActiveRequest(reply, context, slot); return createActiveRequest(reply, context, slot);
} }
template<typename Functor, typename Json> template<typename Functor, typename Json>
QRestReply *executeRequest(Functor requestOperation, Json jsonData, QNetworkReply *executeRequest(Functor requestOperation, Json jsonData,
const QNetworkRequest &request, const QNetworkRequest &request,
const QObject *context, QtPrivate::QSlotObjectBase *slot) const QObject *context, QtPrivate::QSlotObjectBase *slot)
{ {
if (!qnam)
return warnNoAccessManager();
verifyThreadAffinity(context); verifyThreadAffinity(context);
QNetworkRequest req(request); QNetworkRequest req(request);
if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) { if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
@ -70,12 +68,14 @@ public:
} }
void verifyThreadAffinity(const QObject *contextObject); void verifyThreadAffinity(const QObject *contextObject);
Q_DECL_COLD_FUNCTION
QNetworkReply* warnNoAccessManager();
struct CallerInfo { struct CallerInfo {
const QObject *contextObject = nullptr; QPointer<const QObject> contextObject = nullptr;
QtPrivate::SlotObjSharedPtr slot; QtPrivate::SlotObjSharedPtr slot;
}; };
QHash<QRestReply*, CallerInfo> activeRequests; QHash<QNetworkReply*, CallerInfo> activeRequests;
QNetworkAccessManager *qnam = nullptr; QNetworkAccessManager *qnam = nullptr;
bool deletesRepliesOnFinished = true; bool deletesRepliesOnFinished = true;

View File

@ -35,121 +35,27 @@ Q_DECLARE_LOGGING_CATEGORY(lcQrest)
\sa QRestAccessManager, QNetworkReply \sa QRestAccessManager, QNetworkReply
*/ */
/*! QRestReply::QRestReply(QNetworkReply *reply)
\fn void QRestReply::readyRead(QRestReply *reply) : wrapped(reply)
This signal is emitted when \a reply has received new data.
\sa body(), bytesAvailable(), isFinished()
*/
/*!
\fn void QRestReply::downloadProgress(qint64 bytesReceived,
qint64 bytesTotal,
QRestReply* reply)
This signal is emitted to indicate the progress of the download part of
this network \a reply.
The \a bytesReceived parameter indicates the number of bytes received,
while \a bytesTotal indicates the total number of bytes expected to be
downloaded. If the number of bytes to be downloaded is not known, for
instance due to a missing \c Content-Length header, \a bytesTotal
will be -1.
See \l QNetworkReply::downloadProgress() documentation for more details.
\sa bytesAvailable(), readyRead(), uploadProgress()
*/
/*!
\fn void QRestReply::uploadProgress(qint64 bytesSent, qint64 bytesTotal,
QRestReply* reply)
This signal is emitted to indicate the progress of the upload part of
\a reply.
The \a bytesSent parameter indicates the number of bytes already uploaded,
while \a bytesTotal indicates the total number of bytes still to upload.
If the number of bytes to upload is not known, \a bytesTotal will be -1.
See \l QNetworkReply::uploadProgress() documentation for more details.
\sa QNetworkReply::uploadProgress(), downloadProgress()
*/
/*!
\fn void QRestReply::finished(QRestReply *reply)
This signal is emitted when \a reply has finished processing. This
signal is emitted also in cases when the reply finished due to network
or protocol errors (the server did not reply with an HTTP status).
\sa isFinished(), httpStatus(), error()
*/
/*!
\fn void QRestReply::errorOccurred(QRestReply *reply)
This signal is emitted if, while processing \a reply, an error occurs that
is considered to be a network/protocol error. These errors are
disctinct from HTTP error responses such as \c {500 Internal Server Error}.
This signal is emitted together with the
finished() signal, and often connecting to that is sufficient.
\sa finished(), isFinished(), httpStatus(), error()
*/
QRestReply::QRestReply(QNetworkReply *reply, QObject *parent)
: QObject(*new QRestReplyPrivate, parent)
{ {
Q_D(QRestReply); if (!wrapped)
Q_ASSERT(reply); qCWarning(lcQrest, "QRestReply: QNetworkReply is nullptr");
d->networkReply = reply;
// Reparent so that destruction of QRestReply destroys QNetworkReply
reply->setParent(this);
QObject::connect(reply, &QNetworkReply::readyRead, this, [this] {
emit readyRead(this);
});
QObject::connect(reply, &QNetworkReply::downloadProgress, this,
[this](qint64 bytesReceived, qint64 bytesTotal) {
emit downloadProgress(bytesReceived, bytesTotal, this);
});
QObject::connect(reply, &QNetworkReply::uploadProgress, this,
[this] (qint64 bytesSent, qint64 bytesTotal) {
emit uploadProgress(bytesSent, bytesTotal, this);
});
} }
/*! /*!
Destroys this QRestReply object. Destroys this QRestReply object.
\sa abort()
*/ */
QRestReply::~QRestReply() QRestReply::~QRestReply()
= default; {
delete d;
}
/*! /*!
Returns a pointer to the underlying QNetworkReply wrapped by this object. Returns a pointer to the underlying QNetworkReply wrapped by this object.
*/ */
QNetworkReply *QRestReply::networkReply() const QNetworkReply *QRestReply::networkReply() const
{ {
Q_D(const QRestReply); return wrapped;
return d->networkReply;
}
/*!
Aborts the network operation immediately. The finished() signal
will be emitted.
\sa QRestAccessManager::abortRequests() QNetworkReply::abort()
*/
void QRestReply::abort()
{
Q_D(QRestReply);
d->networkReply->abort();
} }
/*! /*!
@ -167,19 +73,24 @@ void QRestReply::abort()
set to QJsonParseError::NoError to distinguish this case from an actual set to QJsonParseError::NoError to distinguish this case from an actual
error. error.
\sa body(), text(), finished(), isFinished() \sa body(), text()
*/ */
std::optional<QJsonDocument> QRestReply::json(QJsonParseError *error) std::optional<QJsonDocument> QRestReply::json(QJsonParseError *error)
{ {
Q_D(QRestReply); if (!wrapped) {
if (!isFinished()) { if (error)
*error = {0, QJsonParseError::ParseError::NoError};
return std::nullopt;
}
if (!wrapped->isFinished()) {
qCWarning(lcQrest, "Attempt to read json() of an unfinished reply, ignoring."); qCWarning(lcQrest, "Attempt to read json() of an unfinished reply, ignoring.");
if (error) if (error)
*error = {0, QJsonParseError::ParseError::NoError}; *error = {0, QJsonParseError::ParseError::NoError};
return std::nullopt; return std::nullopt;
} }
QJsonParseError parseError; QJsonParseError parseError;
const QByteArray data = d->networkReply->readAll(); const QByteArray data = wrapped->readAll();
const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError); const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
if (error) if (error)
*error = parseError; *error = parseError;
@ -199,8 +110,7 @@ std::optional<QJsonDocument> QRestReply::json(QJsonParseError *error)
*/ */
QByteArray QRestReply::body() QByteArray QRestReply::body()
{ {
Q_D(QRestReply); return wrapped ? wrapped->readAll() : QByteArray{};
return d->networkReply->readAll();
} }
/*! /*!
@ -221,15 +131,21 @@ QByteArray QRestReply::body()
*/ */
QString QRestReply::text() QString QRestReply::text()
{ {
Q_D(QRestReply);
QString result; QString result;
if (!wrapped)
return result;
QByteArray data = d->networkReply->readAll(); QByteArray data = wrapped->readAll();
if (data.isEmpty()) if (data.isEmpty())
return result; return result;
// Text decoding needs to persist decoding state across calls to this function,
// so allocate decoder if not yet allocated.
if (!d)
d = new QRestReplyPrivate;
if (!d->decoder) { if (!d->decoder) {
const QByteArray charset = d->contentCharset(); const QByteArray charset = QRestReplyPrivate::contentCharset(wrapped);
d->decoder = QStringDecoder(charset); d->decoder = QStringDecoder(charset);
if (!d->decoder->isValid()) { // the decoder may not support the mimetype's charset if (!d->decoder->isValid()) { // the decoder may not support the mimetype's charset
qCWarning(lcQrest, "text(): Charset \"%s\" is not supported", charset.constData()); qCWarning(lcQrest, "text(): Charset \"%s\" is not supported", charset.constData());
@ -259,8 +175,7 @@ QString QRestReply::text()
*/ */
int QRestReply::httpStatus() const int QRestReply::httpStatus() const
{ {
Q_D(const QRestReply); return wrapped ? wrapped->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() : 0;
return d->networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
} }
/*! /*!
@ -296,8 +211,16 @@ bool QRestReply::isHttpStatusSuccess() const
*/ */
bool QRestReply::hasError() const bool QRestReply::hasError() const
{ {
Q_D(const QRestReply); if (!wrapped)
return d->hasNonHttpError(); return false;
const int status = httpStatus();
if (status > 0) {
// The HTTP status is set upon receiving the response headers, but the
// connection might still fail later while receiving the body data.
return wrapped->error() == QNetworkReply::RemoteHostClosedError;
}
return wrapped->error() != QNetworkReply::NoError;
} }
/*! /*!
@ -309,10 +232,9 @@ bool QRestReply::hasError() const
*/ */
QNetworkReply::NetworkError QRestReply::error() const QNetworkReply::NetworkError QRestReply::error() const
{ {
Q_D(const QRestReply);
if (!hasError()) if (!hasError())
return QNetworkReply::NetworkError::NoError; return QNetworkReply::NetworkError::NoError;
return d->networkReply->error(); return wrapped->error();
} }
/*! /*!
@ -322,32 +244,11 @@ QNetworkReply::NetworkError QRestReply::error() const
*/ */
QString QRestReply::errorString() const QString QRestReply::errorString() const
{ {
Q_D(const QRestReply);
if (hasError()) if (hasError())
return d->networkReply->errorString(); return wrapped->errorString();
return {}; return {};
} }
/*!
Returns whether the network request has finished.
*/
bool QRestReply::isFinished() const
{
Q_D(const QRestReply);
return d->networkReply->isFinished();
}
/*!
Returns the number of bytes available.
\sa body
*/
qint64 QRestReply::bytesAvailable() const
{
Q_D(const QRestReply);
return d->networkReply->bytesAvailable();
}
QRestReplyPrivate::QRestReplyPrivate() QRestReplyPrivate::QRestReplyPrivate()
= default; = default;
@ -377,38 +278,37 @@ static QLatin1StringView operationName(QNetworkAccessManager::Operation operatio
} }
/*! /*!
\fn QDebug QRestReply::operator<<(QDebug debug, const QRestReply *reply) \fn QDebug QRestReply::operator<<(QDebug debug, const QRestReply &reply)
Writes the \a reply into the \a debug object for debugging purposes. Writes the \a reply into the \a debug object for debugging purposes.
\sa {Debugging Techniques} \sa {Debugging Techniques}
*/ */
QDebug operator<<(QDebug debug, const QRestReply *reply) QDebug operator<<(QDebug debug, const QRestReply &reply)
{ {
const QDebugStateSaver saver(debug); const QDebugStateSaver saver(debug);
debug.resetFormat().nospace(); debug.resetFormat().nospace();
if (!reply) { if (!reply.networkReply()) {
debug << "QRestReply(nullptr)"; debug << "QRestReply(no network reply)";
return debug; return debug;
} }
debug << "QRestReply(isSuccess = " << reply.isSuccess()
debug << "QRestReply(isSuccess = " << reply->isSuccess() << ", httpStatus = " << reply.httpStatus()
<< ", httpStatus = " << reply->httpStatus() << ", isHttpStatusSuccess = " << reply.isHttpStatusSuccess()
<< ", isHttpStatusSuccess = " << reply->isHttpStatusSuccess() << ", hasError = " << reply.hasError()
<< ", hasError = " << reply->hasError() << ", errorString = " << reply.errorString()
<< ", errorString = " << reply->errorString() << ", error = " << reply.error()
<< ", error = " << reply->error() << ", isFinished = " << reply.networkReply()->isFinished()
<< ", isFinished = " << reply->isFinished() << ", bytesAvailable = " << reply.networkReply()->bytesAvailable()
<< ", bytesAvailable = " << reply->bytesAvailable() << ", url " << reply.networkReply()->url()
<< ", url " << reply->networkReply()->url() << ", operation = " << operationName(reply.networkReply()->operation())
<< ", operation = " << operationName(reply->networkReply()->operation()) << ", reply headers = " << reply.networkReply()->rawHeaderPairs()
<< ", reply headers = " << reply->networkReply()->rawHeaderPairs()
<< ")"; << ")";
return debug; return debug;
} }
#endif // QT_NO_DEBUG_STREAM #endif // QT_NO_DEBUG_STREAM
QByteArray QRestReplyPrivate::contentCharset() const QByteArray QRestReplyPrivate::contentCharset(const QNetworkReply* reply)
{ {
// Content-type consists of mimetype and optional parameters, of which one may be 'charset' // Content-type consists of mimetype and optional parameters, of which one may be 'charset'
// Example values and their combinations below are all valid, see RFC 7231 section 3.1.1.5 // Example values and their combinations below are all valid, see RFC 7231 section 3.1.1.5
@ -418,10 +318,10 @@ QByteArray QRestReplyPrivate::contentCharset() const
// text/plain; charset=utf-8;version=1.7 // text/plain; charset=utf-8;version=1.7
// text/plain; charset = utf-8 // text/plain; charset = utf-8
// text/plain; charset ="utf-8" // text/plain; charset ="utf-8"
QByteArray contentTypeValue =
networkReply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toByteArray();
// Default to the most commonly used UTF-8. // Default to the most commonly used UTF-8.
QByteArray charset{"UTF-8"}; QByteArray charset{"UTF-8"};
const QByteArray contentTypeValue =
reply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toByteArray();
QList<QByteArray> parameters = contentTypeValue.split(';'); QList<QByteArray> parameters = contentTypeValue.split(';');
if (parameters.size() >= 2) { // Need at least one parameter in addition to the mimetype itself if (parameters.size() >= 2) { // Need at least one parameter in addition to the mimetype itself
@ -442,18 +342,6 @@ QByteArray QRestReplyPrivate::contentCharset() const
return charset; return charset;
} }
// Returns true if there's an error that isn't appropriately indicated by the HTTP status
bool QRestReplyPrivate::hasNonHttpError() const
{
const int status = networkReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (status > 0) {
// The HTTP status is set upon receiving the response headers, but the
// connection might still fail later while receiving the body data.
return networkReply->error() == QNetworkReply::RemoteHostClosedError;
}
return networkReply->error() != QNetworkReply::NoError;
}
QT_END_NAMESPACE QT_END_NAMESPACE
#include "moc_qrestreply.cpp" #include "moc_qrestreply.cpp"

View File

@ -6,7 +6,10 @@
#include <QtNetwork/qnetworkreply.h> #include <QtNetwork/qnetworkreply.h>
#include <QtCore/qpointer.h>
#include <optional> #include <optional>
#include <utility>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -17,53 +20,52 @@ class QJsonDocument;
class QString; class QString;
class QRestReplyPrivate; class QRestReplyPrivate;
class Q_NETWORK_EXPORT QRestReply : public QObject class QRestReply
{ {
Q_OBJECT
public: public:
~QRestReply() override; Q_NETWORK_EXPORT explicit QRestReply(QNetworkReply *reply);
Q_NETWORK_EXPORT ~QRestReply();
QNetworkReply *networkReply() const; QRestReply(QRestReply &&other) noexcept
: wrapped(std::move(other.wrapped)),
d(std::exchange(other.d, nullptr))
{
}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRestReply)
void swap(QRestReply &other) noexcept
{
wrapped.swap(other.wrapped);
std::swap(d, other.d);
}
std::optional<QJsonDocument> json(QJsonParseError *error = nullptr); Q_NETWORK_EXPORT QNetworkReply *networkReply() const;
QByteArray body();
QString text(); Q_NETWORK_EXPORT std::optional<QJsonDocument> json(QJsonParseError *error = nullptr);
Q_NETWORK_EXPORT QByteArray body();
Q_NETWORK_EXPORT QString text();
bool isSuccess() const bool isSuccess() const
{ {
return !hasError() && isHttpStatusSuccess(); return !hasError() && isHttpStatusSuccess();
} }
int httpStatus() const; Q_NETWORK_EXPORT int httpStatus() const;
bool isHttpStatusSuccess() const; Q_NETWORK_EXPORT bool isHttpStatusSuccess() const;
bool hasError() const; Q_NETWORK_EXPORT bool hasError() const;
QNetworkReply::NetworkError error() const; Q_NETWORK_EXPORT QNetworkReply::NetworkError error() const;
QString errorString() const; Q_NETWORK_EXPORT QString errorString() const;
bool isFinished() const;
qint64 bytesAvailable() const;
public Q_SLOTS:
void abort();
Q_SIGNALS:
void finished(QRestReply *reply);
void errorOccurred(QRestReply *reply);
void readyRead(QRestReply *reply);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal, QRestReply *reply);
void uploadProgress(qint64 bytesSent, qint64 bytesTotal, QRestReply* reply);
private: private:
friend class QRestAccessManagerPrivate;
#ifndef QT_NO_DEBUG_STREAM #ifndef QT_NO_DEBUG_STREAM
friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QRestReply *reply); friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QRestReply &reply);
#endif #endif
explicit QRestReply(QNetworkReply *reply, QObject *parent = nullptr); QPointer<QNetworkReply> wrapped;
Q_DECLARE_PRIVATE(QRestReply) QRestReplyPrivate *d = nullptr;
Q_DISABLE_COPY_MOVE(QRestReply) Q_DISABLE_COPY(QRestReply)
}; };
Q_DECLARE_SHARED(QRestReply)
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QRESTREPLY_H #endif // QRESTREPLY_H

View File

@ -15,27 +15,22 @@
// We mean it. // We mean it.
// //
#include "private/qobject_p.h"
#include <QtNetwork/qnetworkreply.h>
#include <QtCore/qjsondocument.h>
#include <optional> #include <optional>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class QNetworkReply;
class QStringDecoder; class QStringDecoder;
class QRestReplyPrivate : public QObjectPrivate class QRestReplyPrivate
{ {
public: public:
QRestReplyPrivate(); QRestReplyPrivate();
~QRestReplyPrivate() override; ~QRestReplyPrivate();
QNetworkReply *networkReply = nullptr;
std::optional<QStringDecoder> decoder; std::optional<QStringDecoder> decoder;
QByteArray contentCharset() const; static QByteArray contentCharset(const QNetworkReply *reply);
bool hasNonHttpError() const;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE