/**************************************************************************** ** ** Copyright (C) 2017 Witekio. ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCoap module. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qcoapreply_p.h" #include "qcoapinternalreply_p.h" #include #include QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcCoapExchange) /*! \internal Constructor. */ QCoapReplyPrivate::QCoapReplyPrivate(const QCoapRequest &req) : request(req) { } /*! \internal Marks the reply as running, and sets the \a token and \a messageId of this exchange. \sa isRunning() */ void QCoapReplyPrivate::_q_setRunning(const QCoapToken &token, QCoapMessageId messageId) { request.setToken(token); request.setMessageId(messageId); isRunning = true; } /*! \internal Sets the reply as finished. */ void QCoapReplyPrivate::_q_setObserveCancelled() { Q_Q(QCoapReply); bool alreadyFinished = q->isFinished(); isFinished = true; isRunning = false; if (!alreadyFinished) emit q->finished(q); } /*! \internal Sets the message and response code of this reply, unless reply is already finished. */ void QCoapReplyPrivate::_q_setContent(const QHostAddress &, const QCoapMessage &msg, QtCoap::ResponseCode code) { Q_Q(QCoapReply); if (q->isFinished()) return; message = msg; responseCode = code; seekBuffer(0); if (QtCoap::isError(responseCode)) _q_setError(responseCode); } /*! \internal For an Observe request, notifies that a new message was received by emitting the notified() signal. If the reply is finished, no signal will be emitted. */ void QCoapReplyPrivate::_q_setNotified() { Q_Q(QCoapReply); if (!q->isFinished()) emit q->notified(q, message); } /*! \internal Sets the reply as finished, sending the finished() signal if it wasn't already. */ void QCoapReplyPrivate::_q_setFinished(QtCoap::Error newError) { Q_Q(QCoapReply); if (q->isFinished()) return; isFinished = true; isRunning = false; if (newError != QtCoap::Error::Ok) _q_setError(newError); emit q->finished(q); } /*! \internal Sets the error of the reply. \sa errorReceived() */ void QCoapReplyPrivate::_q_setError(QtCoap::Error newError) { Q_Q(QCoapReply); if (error == newError) return; error = newError; emit q->error(q, error); } /*! \internal Sets the error of the reply. */ void QCoapReplyPrivate::_q_setError(QtCoap::ResponseCode code) { _q_setError(QtCoap::responseCodeError(code)); } /*! \class QCoapReply \inmodule QtCoap \brief The QCoapReply class holds the data of a CoAP reply. \reentrant The QCoapReply contains data related to a request sent with the QCoapClient. The finished() signal is emitted when the response is fully received or when the request fails. For \e Observe requests specifically, the notified() signal is emitted whenever a notification is received. \sa QCoapClient, QCoapRequest, QCoapDiscoveryReply */ /*! \fn void QCoapReply::finished(QCoapReply* reply) This signal is emitted whenever the corresponding request finished, whether successfully or not. When a resource is observed, this signal will only be emitted once, when the observation ends. The \a reply parameter is the QCoapReply itself for convenience. \note If the QCoapReply is deleted while not finished, both aborted() and finished() signal will be emitted immediately before the QCoapReply is destroyed. Given the QCoapReply may have been deleted when receiving the signal, you should not rely on the \a reply to be still valid. \sa QCoapClient::finished(), isFinished(), notified(), aborted() */ /*! \fn void QCoapReply::notified(QCoapReply* reply, const QCoapMessage &message) This signal is emitted whenever a notification is received from an observed resource. Its \a message parameter is a QCoapMessage containing the payload and the message details. The \a reply parameter is the QCoapReply itself for convenience. \sa QCoapClient::finished(), isFinished(), finished(), notified() */ /*! \fn void QCoapReply::error(QCoapReply* reply, QtCoap::Error error) This signal is emitted whenever an error occurs and is followed by the finished() signal. Its \a reply parameters is the QCoapReply itself for convenience, and the \a error parameter is the error received. \sa finished(), aborted() */ /*! \fn void QCoapReply::aborted(const QCoapToken &token); This signal is emitted when the request is aborted or the reply is deleted. Its \a token parameter is the token of the exchange that has been aborted. \note If the QCoapReply is deleted while not finished, both aborted() and finished() signal will be emitted immediately before the QCoapReply is destroyed. Given the QCoapReply may have been deleted when receiving the signal, you should not rely on the sender() object to be still valid. \sa finished(), error() */ /*! Constructs a new CoAP reply for the \a request and sets \a parent as its parent. */ QCoapReply::QCoapReply(const QCoapRequest &request, QObject *parent) : QCoapReply(*new QCoapReplyPrivate(request), parent) { } /*! \internal Constructs a new CoAP reply with \a dd as the d_ptr. This constructor must be used when subclassing internally the QCoapReply class. */ QCoapReply::QCoapReply(QCoapReplyPrivate &dd, QObject *parent) : QIODevice(dd, parent) { open(QIODevice::ReadOnly); } /*! Destroys the QCoapReply and aborts the request if its response has not yet been received. */ QCoapReply::~QCoapReply() { abortRequest(); } /*! \internal \overload */ qint64 QCoapReply::readData(char *data, qint64 maxSize) { Q_D(QCoapReply); QByteArray payload = d->message.payload(); maxSize = qMin(maxSize, qint64(payload.size()) - pos()); if (maxSize <= 0) return qint64(0); // Explicitly account for platform size_t limitations size_t len = static_cast(maxSize); if (sizeof(qint64) > sizeof(size_t) && maxSize > static_cast(std::numeric_limits::max())) { qCWarning(lcCoapExchange) << "Cannot read more than" << std::numeric_limits::max() << "at a time"; len = std::numeric_limits::max(); } memcpy(data, payload.constData() + pos(), len); return static_cast(len); } /*! \internal \overload */ qint64 QCoapReply::writeData(const char *data, qint64 maxSize) { // The user cannot write to the reply Q_UNUSED(data); Q_UNUSED(maxSize); return -1; } /*! Returns the response code of the request. */ QtCoap::ResponseCode QCoapReply::responseCode() const { Q_D(const QCoapReply); return d->responseCode; } /*! Returns the contained message. */ QCoapMessage QCoapReply::message() const { Q_D(const QCoapReply); return d->message; } /*! Returns the associated request. */ QCoapRequest QCoapReply::request() const { Q_D(const QCoapReply); return d->request; } /*! Returns \c true if the request is finished. \sa finished() */ bool QCoapReply::isFinished() const { Q_D(const QCoapReply); return d->isFinished || d->isAborted; } /*! Returns \c true if the request is running. */ bool QCoapReply::isRunning() const { Q_D(const QCoapReply); return d->isRunning && !isFinished(); } /*! Returns \c true if the request has been aborted. */ bool QCoapReply::isAborted() const { Q_D(const QCoapReply); return d->isAborted; } /*! Returns \c true if the request finished with no error. */ bool QCoapReply::isSuccessful() const { Q_D(const QCoapReply); return d->isFinished && !QtCoap::isError(d->responseCode) && d->error == QtCoap::Error::Ok; } /*! Returns the target uri of the associated request. */ QUrl QCoapReply::url() const { Q_D(const QCoapReply); return d->request.url(); } /*! Returns the method of the associated request. */ QtCoap::Method QCoapReply::method() const { Q_D(const QCoapReply); return d->request.method(); } /*! Returns the error of the reply or QCoapReply::NoError if there is no error. */ QtCoap::Error QCoapReply::errorReceived() const { Q_D(const QCoapReply); return d->error; } /*! Aborts the request immediately and emits the \l{QCoapReply::aborted(const QCoapToken &token)}{aborted(const QCoapToken &token)} signal if the request was not finished. */ void QCoapReply::abortRequest() { Q_D(QCoapReply); if (isFinished()) return; d->isAborted = true; d->isFinished = true; d->isRunning = false; emit aborted(request().token()); emit finished(this); } QT_END_NAMESPACE #include "moc_qcoapreply.cpp"