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 | |
12 | QT_BEGIN_NAMESPACE |
13 | |
14 | Q_DECLARE_LOGGING_CATEGORY(lcCoapExchange) |
15 | |
16 | /*! |
17 | \internal |
18 | Constructor. |
19 | */ |
20 | QCoapReplyPrivate::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 | */ |
32 | void 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 | */ |
44 | void 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 | */ |
63 | void 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 | */ |
86 | void 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 | */ |
100 | void 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 | */ |
123 | void 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 | */ |
138 | void 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 | */ |
225 | QCoapReply::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 | */ |
235 | QCoapReply::~QCoapReply() |
236 | { |
237 | abortRequest(); |
238 | } |
239 | |
240 | /*! |
241 | \internal |
242 | |
243 | \overload |
244 | */ |
245 | qint64 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 | */ |
275 | qint64 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 | */ |
286 | QtCoap::ResponseCode QCoapReply::responseCode() const |
287 | { |
288 | Q_D(const QCoapReply); |
289 | return d->responseCode; |
290 | } |
291 | |
292 | /*! |
293 | Returns the contained message. |
294 | */ |
295 | QCoapMessage QCoapReply::message() const |
296 | { |
297 | Q_D(const QCoapReply); |
298 | return d->message; |
299 | } |
300 | |
301 | /*! |
302 | Returns the associated request. |
303 | */ |
304 | QCoapRequest 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 | */ |
315 | bool 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 | */ |
324 | bool 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 | */ |
333 | bool 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 | */ |
342 | bool 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 | */ |
352 | QUrl 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 | */ |
361 | QtCoap::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 | */ |
370 | QtCoap::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 | */ |
381 | void 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 | */ |
400 | QCoapReply *QCoapReplyPrivate::createCoapReply(const QCoapRequest &request, QObject *parent) |
401 | { |
402 | return new QCoapReply(*new QCoapReplyPrivate(request), parent); |
403 | } |
404 | |
405 | QT_END_NAMESPACE |
406 | |
407 | #include "moc_qcoapreply.cpp" |
408 | |