1// Copyright (C) 2017 Witekio.
2// Copyright (C) 2018 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
4
5#include "qcoapreply_p.h"
6#include "qcoapinternalreply_p.h"
7#include "qcoapnamespace_p.h"
8
9#include <QtCore/qmath.h>
10#include <QtCore/qloggingcategory.h>
11
12QT_BEGIN_NAMESPACE
13
14Q_DECLARE_LOGGING_CATEGORY(lcCoapExchange)
15
16/*!
17 \internal
18 Constructor.
19*/
20QCoapReplyPrivate::QCoapReplyPrivate(const QCoapRequest &req) :
21 request(req)
22{
23}
24
25/*!
26 \internal
27 Marks the reply as running, and sets the \a token and \a messageId of this
28 exchange.
29
30 \sa isRunning()
31*/
32void QCoapReplyPrivate::_q_setRunning(const QCoapToken &token, QCoapMessageId messageId)
33{
34 request.setToken(token);
35 request.setMessageId(messageId);
36 isRunning = true;
37}
38
39/*!
40 \internal
41
42 Sets the reply as finished.
43*/
44void QCoapReplyPrivate::_q_setObserveCancelled()
45{
46 Q_Q(QCoapReply);
47
48 bool alreadyFinished = q->isFinished();
49
50 isFinished = true;
51 isRunning = false;
52
53 if (!alreadyFinished)
54 emit q->finished(reply: q);
55}
56
57/*!
58 \internal
59
60 Sets the message and response code of this reply, unless reply is
61 already finished.
62*/
63void QCoapReplyPrivate::_q_setContent(const QHostAddress &, const QCoapMessage &msg,
64 QtCoap::ResponseCode code)
65{
66 Q_Q(QCoapReply);
67
68 if (q->isFinished())
69 return;
70
71 message = msg;
72 responseCode = code;
73 seekBuffer(newPos: 0);
74
75 if (QtCoap::isError(code: responseCode))
76 _q_setError(code: responseCode);
77}
78
79/*!
80 \internal
81
82 For an Observe request, notifies that a new message was received
83 by emitting the notified() signal. If the reply is finished, no
84 signal will be emitted.
85*/
86void QCoapReplyPrivate::_q_setNotified()
87{
88 Q_Q(QCoapReply);
89
90 if (!q->isFinished())
91 emit q->notified(reply: q, message);
92}
93
94/*!
95 \internal
96
97 Sets the reply as finished, sending the finished() signal if it wasn't
98 already.
99*/
100void QCoapReplyPrivate::_q_setFinished(QtCoap::Error newError)
101{
102 Q_Q(QCoapReply);
103
104 if (q->isFinished())
105 return;
106
107 isFinished = true;
108 isRunning = false;
109
110 if (newError != QtCoap::Error::Ok)
111 _q_setError(newError);
112
113 emit q->finished(reply: q);
114}
115
116/*!
117 \internal
118
119 Sets the error of the reply.
120
121 \sa errorReceived()
122*/
123void QCoapReplyPrivate::_q_setError(QtCoap::Error newError)
124{
125 Q_Q(QCoapReply);
126 if (error == newError)
127 return;
128
129 error = newError;
130 emit q->error(reply: q, error);
131}
132
133/*!
134 \internal
135
136 Sets the error of the reply.
137*/
138void QCoapReplyPrivate::_q_setError(QtCoap::ResponseCode code)
139{
140 _q_setError(newError: QtCoap::errorForResponseCode(code));
141}
142
143/*!
144 \class QCoapReply
145 \inmodule QtCoap
146
147 \brief The QCoapReply class holds the data of a CoAP reply.
148
149 \reentrant
150
151 The QCoapReply contains data related to a request sent with the
152 QCoapClient.
153
154 The finished() signal is emitted when the response is fully
155 received or when the request fails.
156
157 For \e Observe requests specifically, the notified() signal is emitted
158 whenever a notification is received.
159
160 \sa QCoapClient, QCoapRequest, QCoapResourceDiscoveryReply
161*/
162
163/*!
164 \fn void QCoapReply::finished(QCoapReply* reply)
165
166 This signal is emitted whenever the corresponding request finished,
167 whether successfully or not. When a resource is observed, this signal
168 will only be emitted once, when the observation ends.
169
170 The \a reply parameter is the QCoapReply itself for convenience.
171
172 \note If the QCoapReply is deleted while not finished, both aborted() and
173 finished() signal will be emitted immediately before the QCoapReply is
174 destroyed. Given the QCoapReply may have been deleted when receiving the
175 signal, you should not rely on the \a reply to be still valid.
176
177 \sa QCoapClient::finished(), isFinished(), notified(), aborted()
178*/
179
180/*!
181 \fn void QCoapReply::notified(QCoapReply* reply, const QCoapMessage &message)
182
183 This signal is emitted whenever a notification is received from an observed
184 resource.
185
186 Its \a message parameter is a QCoapMessage containing the payload and the
187 message details. The \a reply parameter is the QCoapReply itself for
188 convenience.
189
190 \sa QCoapClient::finished(), isFinished(), finished(), notified()
191*/
192
193/*!
194 \fn void QCoapReply::error(QCoapReply* reply, QtCoap::Error error)
195
196 This signal is emitted whenever an error occurs and is followed by the
197 finished() signal.
198
199 Its \a reply parameters is the QCoapReply itself for convenience, and
200 the \a error parameter is the error received.
201
202 \sa finished(), aborted()
203*/
204
205/*!
206 \fn void QCoapReply::aborted(const QCoapToken &token);
207
208 This signal is emitted when the request is aborted or the reply is deleted.
209 Its \a token parameter is the token of the exchange that has been aborted.
210
211 \note If the QCoapReply is deleted while not finished, both aborted() and
212 finished() signal will be emitted immediately before the QCoapReply is
213 destroyed. Given the QCoapReply may have been deleted when receiving the
214 signal, you should not rely on the sender() object to be still valid.
215
216 \sa finished(), error()
217*/
218
219/*!
220 \internal
221 Constructs a new CoAP reply with \a dd as the d_ptr.
222 This constructor must be used when subclassing internally
223 the QCoapReply class.
224*/
225QCoapReply::QCoapReply(QCoapReplyPrivate &dd, QObject *parent) :
226 QIODevice(dd, parent)
227{
228 open(mode: QIODevice::ReadOnly);
229}
230
231/*!
232 Destroys the QCoapReply and aborts the request if its response has
233 not yet been received.
234*/
235QCoapReply::~QCoapReply()
236{
237 abortRequest();
238}
239
240/*!
241 \internal
242
243 \overload
244*/
245qint64 QCoapReply::readData(char *data, qint64 maxSize)
246{
247 Q_D(QCoapReply);
248
249 QByteArray payload = d->message.payload();
250
251 maxSize = qMin(a: maxSize, b: qint64(payload.size()) - pos());
252 if (maxSize <= 0)
253 return qint64(0);
254
255 // Explicitly account for platform size_t limitations
256 size_t len = static_cast<size_t>(maxSize);
257 if (sizeof(qint64) > sizeof(size_t)
258 && maxSize > static_cast<qint64>(std::numeric_limits<size_t>::max())) {
259 qCWarning(lcCoapExchange) << "Cannot read more than"
260 << std::numeric_limits<size_t>::max()
261 << "at a time";
262 len = std::numeric_limits<size_t>::max();
263 }
264
265 memcpy(dest: data, src: payload.constData() + pos(), n: len);
266
267 return static_cast<qint64>(len);
268}
269
270/*!
271 \internal
272
273 \overload
274*/
275qint64 QCoapReply::writeData(const char *data, qint64 maxSize)
276{
277 // The user cannot write to the reply
278 Q_UNUSED(data);
279 Q_UNUSED(maxSize);
280 return -1;
281}
282
283/*!
284 Returns the response code of the request.
285*/
286QtCoap::ResponseCode QCoapReply::responseCode() const
287{
288 Q_D(const QCoapReply);
289 return d->responseCode;
290}
291
292/*!
293 Returns the contained message.
294*/
295QCoapMessage QCoapReply::message() const
296{
297 Q_D(const QCoapReply);
298 return d->message;
299}
300
301/*!
302 Returns the associated request.
303*/
304QCoapRequest QCoapReply::request() const
305{
306 Q_D(const QCoapReply);
307 return d->request;
308}
309
310/*!
311 Returns \c true if the request is finished.
312
313 \sa finished()
314*/
315bool QCoapReply::isFinished() const
316{
317 Q_D(const QCoapReply);
318 return d->isFinished || d->isAborted;
319}
320
321/*!
322 Returns \c true if the request is running.
323*/
324bool QCoapReply::isRunning() const
325{
326 Q_D(const QCoapReply);
327 return d->isRunning && !isFinished();
328}
329
330/*!
331 Returns \c true if the request has been aborted.
332*/
333bool QCoapReply::isAborted() const
334{
335 Q_D(const QCoapReply);
336 return d->isAborted;
337}
338
339/*!
340 Returns \c true if the request finished with no error.
341*/
342bool QCoapReply::isSuccessful() const
343{
344 Q_D(const QCoapReply);
345 return d->isFinished && !QtCoap::isError(code: d->responseCode)
346 && d->error == QtCoap::Error::Ok;
347}
348
349/*!
350 Returns the target uri of the associated request.
351*/
352QUrl QCoapReply::url() const
353{
354 Q_D(const QCoapReply);
355 return d->request.url();
356}
357
358/*!
359 Returns the method of the associated request.
360*/
361QtCoap::Method QCoapReply::method() const
362{
363 Q_D(const QCoapReply);
364 return d->request.method();
365}
366
367/*!
368 Returns the error of the reply or QCoapReply::NoError if there is no error.
369*/
370QtCoap::Error QCoapReply::errorReceived() const
371{
372 Q_D(const QCoapReply);
373 return d->error;
374}
375
376/*!
377 Aborts the request immediately and emits the
378 \l{QCoapReply::aborted(const QCoapToken &token)}{aborted(const QCoapToken &token)}
379 signal if the request was not finished.
380*/
381void QCoapReply::abortRequest()
382{
383 Q_D(QCoapReply);
384
385 if (isFinished())
386 return;
387
388 d->isAborted = true;
389 d->isFinished = true;
390 d->isRunning = false;
391 emit aborted(token: request().token());
392 emit finished(reply: this);
393}
394
395/*!
396 \internal
397
398 Creates a new instance of QCoapReply and returns a pointer to it.
399*/
400QCoapReply *QCoapReplyPrivate::createCoapReply(const QCoapRequest &request, QObject *parent)
401{
402 return new QCoapReply(*new QCoapReplyPrivate(request), parent);
403}
404
405QT_END_NAMESPACE
406
407#include "moc_qcoapreply.cpp"
408

source code of qtcoap/src/coap/qcoapreply.cpp