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 "qcoapclient_p.h"
6#include "qcoapprotocol_p.h"
7#include "qcoapreply.h"
8#include "qcoapresourcediscoveryreply.h"
9#include "qcoapnamespace.h"
10#include "qcoapsecurityconfiguration.h"
11#include "qcoapqudpconnection_p.h"
12#include "qcoaprequest_p.h"
13#include "qcoapreply_p.h"
14#include <QtCore/qiodevice.h>
15#include <QtCore/qurl.h>
16#include <QtCore/qloggingcategory.h>
17#include <QtNetwork/qudpsocket.h>
18
19QT_BEGIN_NAMESPACE
20
21Q_LOGGING_CATEGORY(lcCoapClient, "qt.coap.client")
22
23QCoapClientPrivate::QCoapClientPrivate(QCoapProtocol *protocol, QCoapConnection *connection)
24 : protocol(protocol)
25 , connection(connection)
26 , workerThread(new QThread)
27{
28 protocol->moveToThread(thread: workerThread);
29 connection->moveToThread(thread: workerThread);
30 workerThread->start();
31}
32
33QCoapClientPrivate::~QCoapClientPrivate()
34{
35 workerThread->quit();
36 workerThread->wait();
37 delete workerThread;
38 delete protocol;
39 delete connection;
40}
41
42/*!
43 \class QCoapClient
44 \inmodule QtCoap
45
46 \brief The QCoapClient class allows the application to
47 send CoAP requests and receive replies.
48
49 \reentrant
50
51 The QCoapClient class contains signals that get triggered when the
52 reply of a sent request has arrived.
53
54 The application can use a QCoapClient to send requests over a CoAP
55 network. It provides functions for standard requests: each returns a QCoapReply object,
56 to which the response data shall be delivered; this can be read when the finished()
57 signal arrives.
58
59 A simple request can be sent with:
60 \code
61 QCoapClient *client = new QCoapClient(this);
62 connect(client, &QCoapClient::finished, this, &TestClass::slotFinished);
63 client->get(QCoapRequest(Qurl("coap://coap.me/test")));
64 \endcode
65
66 \note After processing of the request has finished, it is the responsibility
67 of the user to delete the QCoapReply object at an appropriate time. Do not
68 directly delete it inside the slot connected to finished(). You can use the
69 deleteLater() function.
70
71 You can also use an \e observe request. This can be used as above, or more
72 conveniently with the QCoapReply::notified() signal:
73 \code
74 QCoapRequest request = QCoapRequest(Qurl("coap://coap.me/obs"));
75 QCoapReply *reply = client->observe(request);
76 connect(reply, &QCoapReply::notified, this, &TestClass::slotNotified);
77 \endcode
78
79 And the observation can be cancelled with:
80 \code
81 client->cancelObserve(reply);
82 \endcode
83
84 When a reply arrives, the QCoapClient emits a finished() signal.
85
86 \note For a discovery request, the returned object is a QCoapResourceDiscoveryReply.
87 It can be used the same way as a QCoapReply but contains also a list of
88 resources.
89
90 \sa QCoapRequest, QCoapReply, QCoapResourceDiscoveryReply
91*/
92
93/*!
94 \fn void QCoapClient::finished(QCoapReply *reply)
95
96 This signal is emitted along with the \l QCoapReply::finished() signal
97 whenever a CoAP reply is received, after either a success or an error.
98 The \a reply parameter will contain a pointer to the reply that has just
99 been received.
100
101 \sa error(), QCoapReply::finished(), QCoapReply::error()
102*/
103
104/*!
105 \fn void QCoapClient::responseToMulticastReceived(QCoapReply *reply,
106 const QCoapMessage &message,
107 const QHostAddress &sender)
108
109 This signal is emitted when a unicast response to a multicast request
110 arrives. The \a reply parameter contains a pointer to the reply that has just
111 been received, \a message contains the payload and the message details,
112 and \a sender contains the sender address.
113
114 \sa error(), QCoapReply::finished(), QCoapReply::error()
115*/
116
117/*!
118 \fn void QCoapClient::error(QCoapReply *reply, QtCoap::Error error)
119
120 This signal is emitted whenever an error occurs. The \a reply parameter
121 can be \nullptr if the error is not related to a specific QCoapReply. The
122 \a error parameter contains the error code.
123
124 \sa finished(), QCoapReply::error(), QCoapReply::finished()
125*/
126
127/*!
128 Constructs a QCoapClient object for the given \a securityMode and
129 sets \a parent as the parent object.
130
131 The default for \a securityMode is QtCoap::NoSecurity, which
132 disables security.
133
134 This connects using a QCoapQUdpConnection; to use a custom transport,
135 sub-class QCoapConnection and pass an instance to one of the other
136 constructors.
137*/
138QCoapClient::QCoapClient(QtCoap::SecurityMode securityMode, QObject *parent) :
139 QObject(*new QCoapClientPrivate(new QCoapProtocol, new QCoapQUdpConnection(securityMode)),
140 parent)
141{
142 Q_D(QCoapClient);
143
144 qRegisterMetaType<QCoapReply *>();
145 qRegisterMetaType<QCoapMessage>();
146 qRegisterMetaType<QPointer<QCoapReply>>();
147 qRegisterMetaType<QPointer<QCoapResourceDiscoveryReply>>();
148 qRegisterMetaType<QCoapConnection *>();
149 qRegisterMetaType<QtCoap::Error>();
150 qRegisterMetaType<QtCoap::ResponseCode>();
151 qRegisterMetaType<QtCoap::Method>();
152 qRegisterMetaType<QtCoap::SecurityMode>();
153 qRegisterMetaType<QtCoap::MulticastGroup>();
154 // Requires a name, as this is a typedef
155 qRegisterMetaType<QCoapToken>(typeName: "QCoapToken");
156 qRegisterMetaType<QCoapMessageId>(typeName: "QCoapMessageId");
157 qRegisterMetaType<QAbstractSocket::SocketOption>();
158
159 connect(sender: d->connection, signal: &QCoapConnection::readyRead, context: d->protocol,
160 slot: [this](const QByteArray &data, const QHostAddress &sender) {
161 Q_D(QCoapClient);
162 d->protocol->d_func()->onFrameReceived(data, sender);
163 });
164 connect(sender: d->connection, signal: &QCoapConnection::error, context: d->protocol,
165 slot: [this](QAbstractSocket::SocketError socketError) {
166 Q_D(QCoapClient);
167 d->protocol->d_func()->onConnectionError(error: socketError);
168 });
169
170 connect(sender: d->protocol, signal: &QCoapProtocol::finished,
171 context: this, slot: &QCoapClient::finished);
172 connect(sender: d->protocol, signal: &QCoapProtocol::responseToMulticastReceived,
173 context: this, slot: &QCoapClient::responseToMulticastReceived);
174 connect(sender: d->protocol, signal: &QCoapProtocol::error,
175 context: this, slot: &QCoapClient::error);
176}
177
178/*!
179 \internal
180
181 Sets the client's connection to \a customConnection.
182*/
183void QCoapClientPrivate::setConnection(QCoapConnection *customConnection)
184{
185 Q_Q(QCoapClient);
186
187 delete connection;
188 connection = customConnection;
189
190 q->connect(sender: connection, signal: &QCoapConnection::readyRead, context: protocol,
191 slot: [this](const QByteArray &data, const QHostAddress &sender) {
192 protocol->d_func()->onFrameReceived(data, sender);
193 });
194 q->connect(sender: connection, signal: &QCoapConnection::error, context: protocol,
195 slot: [this](QAbstractSocket::SocketError socketError) {
196 protocol->d_func()->onConnectionError(error: socketError);
197 });
198}
199
200/*!
201 Destroys the QCoapClient object and frees up any
202 resources. Note that QCoapReply objects that are returned from
203 this class have the QCoapClient set as their parents, which means that
204 they will be deleted along with it.
205*/
206QCoapClient::~QCoapClient()
207{
208 qDeleteAll(c: findChildren<QCoapReply *>(aName: QString(), options: Qt::FindDirectChildrenOnly));
209}
210
211/*!
212 Sends the \a request using the GET method and returns a new QCoapReply object.
213
214 \sa post(), put(), deleteResource(), observe(), discover()
215*/
216QCoapReply *QCoapClient::get(const QCoapRequest &request)
217{
218 Q_D(QCoapClient);
219
220 QCoapRequest copyRequest = QCoapRequestPrivate::createRequest(other: request, method: QtCoap::Method::Get,
221 isSecure: d->connection->isSecure());
222 return d->sendRequest(request: copyRequest);
223}
224
225/*!
226 \overload
227
228 Sends a GET request to \a url and returns a new QCoapReply object.
229
230 \sa post(), put(), deleteResource(), observe(), discover()
231*/
232QCoapReply *QCoapClient::get(const QUrl &url)
233{
234 QCoapRequest request(url);
235 return get(request);
236}
237
238/*!
239 Sends the \a request using the PUT method and returns a new QCoapReply
240 object. Uses \a data as the payload for this request. If \a data is empty,
241 the payload of the \a request will be used.
242
243 \sa get(), post(), deleteResource(), observe(), discover()
244*/
245QCoapReply *QCoapClient::put(const QCoapRequest &request, const QByteArray &data)
246{
247 Q_D(QCoapClient);
248
249 QCoapRequest copyRequest = QCoapRequestPrivate::createRequest(other: request, method: QtCoap::Method::Put,
250 isSecure: d->connection->isSecure());
251 if (!data.isEmpty())
252 copyRequest.setPayload(data);
253 return d->sendRequest(request: copyRequest);
254}
255
256/*!
257 \overload
258
259 Sends the \a request using the PUT method and returns a new QCoapReply
260 object. Uses \a device content as the payload for this request.
261 A null device is treated as empty content.
262
263 \note The device has to be open and readable before calling this function.
264
265 \sa get(), post(), deleteResource(), observe(), discover()
266*/
267QCoapReply *QCoapClient::put(const QCoapRequest &request, QIODevice *device)
268{
269 return put(request, data: device ? device->readAll() : QByteArray());
270}
271
272/*!
273 \overload
274
275 Sends a PUT request to \a url and returns a new QCoapReply object.
276 Uses \a data as the payload for this request.
277
278 \sa get(), post(), deleteResource(), observe(), discover()
279*/
280QCoapReply *QCoapClient::put(const QUrl &url, const QByteArray &data)
281{
282 return put(request: QCoapRequest(url), data);
283}
284
285/*!
286 Sends the \a request using the POST method and returns a new QCoapReply
287 object. Uses \a data as the payload for this request. If \a data is empty,
288 the payload of the \a request will be used.
289
290 \sa get(), put(), deleteResource(), observe(), discover()
291*/
292QCoapReply *QCoapClient::post(const QCoapRequest &request, const QByteArray &data)
293{
294 Q_D(QCoapClient);
295
296 QCoapRequest copyRequest = QCoapRequestPrivate::createRequest(other: request, method: QtCoap::Method::Post,
297 isSecure: d->connection->isSecure());
298 if (!data.isEmpty())
299 copyRequest.setPayload(data);
300 return d->sendRequest(request: copyRequest);
301}
302
303/*!
304 \overload
305
306 Sends the \a request using the POST method and returns a new QCoapReply
307 object. Uses \a device content as the payload for this request.
308 A null device is treated as empty content.
309
310 \note The device has to be open and readable before calling this function.
311
312 \sa get(), put(), deleteResource(), observe(), discover()
313*/
314QCoapReply *QCoapClient::post(const QCoapRequest &request, QIODevice *device)
315{
316 if (!device)
317 return nullptr;
318
319 return post(request, data: device->readAll());
320}
321
322/*!
323 \overload
324
325 Sends a POST request to \a url and returns a new QCoapReply object.
326 Uses \a data as the payload for this request.
327
328 \sa get(), put(), deleteResource(), observe(), discover()
329*/
330QCoapReply *QCoapClient::post(const QUrl &url, const QByteArray &data)
331{
332 return post(request: QCoapRequest(url), data);
333}
334
335/*!
336 Sends the \a request using the DELETE method and returns a new QCoapReply
337 object.
338
339 \sa get(), put(), post(), observe(), discover()
340 */
341QCoapReply *QCoapClient::deleteResource(const QCoapRequest &request)
342{
343 Q_D(QCoapClient);
344
345 QCoapRequest copyRequest = QCoapRequestPrivate::createRequest(other: request, method: QtCoap::Method::Delete,
346 isSecure: d->connection->isSecure());
347 return d->sendRequest(request: copyRequest);
348}
349
350/*!
351 \overload
352
353 Sends a DELETE request to the target \a url.
354
355 \sa get(), put(), post(), observe(), discover()
356 */
357QCoapReply *QCoapClient::deleteResource(const QUrl &url)
358{
359 return deleteResource(request: QCoapRequest(url));
360}
361
362/*!
363 \overload
364
365 Discovers the resources available at the endpoints which have joined
366 the \a group at the given \a port. Returns a new QCoapResourceDiscoveryReply
367 object which emits the \l QCoapResourceDiscoveryReply::discovered() signal whenever
368 a response arrives. The \a group is one of the CoAP multicast group addresses
369 and defaults to QtCoap::AllCoapNodesIPv4.
370
371 Discovery path defaults to "/.well-known/core", but can be changed
372 by passing a different path to \a discoveryPath. Discovery is described in
373 \l{https://tools.ietf.org/html/rfc6690#section-1.2.1}{RFC 6690}.
374
375 \sa get(), post(), put(), deleteResource(), observe()
376*/
377QCoapResourceDiscoveryReply *QCoapClient::discover(QtCoap::MulticastGroup group, int port,
378 const QString &discoveryPath)
379{
380 Q_D(QCoapClient);
381
382 QString base;
383 switch (group) {
384 case QtCoap::MulticastGroup::AllCoapNodesIPv4:
385 base = QStringLiteral("224.0.1.187");
386 break;
387 case QtCoap::MulticastGroup::AllCoapNodesIPv6LinkLocal:
388 base = QStringLiteral("ff02::fd");
389 break;
390 case QtCoap::MulticastGroup::AllCoapNodesIPv6SiteLocal:
391 base = QStringLiteral("ff05::fd");
392 break;
393 }
394
395 QUrl discoveryUrl;
396 discoveryUrl.setHost(host: base);
397 discoveryUrl.setPath(path: discoveryPath);
398 discoveryUrl.setPort(port);
399
400 QCoapRequest request = QCoapRequestPrivate::createRequest(other: QCoapRequest(discoveryUrl),
401 method: QtCoap::Method::Get,
402 isSecure: d->connection->isSecure());
403
404 return d->sendDiscovery(request);
405}
406
407/*!
408 Discovers the resources available at the given \a url and returns
409 a new QCoapResourceDiscoveryReply object which emits the
410 \l QCoapResourceDiscoveryReply::discovered() signal whenever the response
411 arrives.
412
413 Discovery path defaults to "/.well-known/core", but can be changed
414 by passing a different path to \a discoveryPath. Discovery is described in
415 \l{https://tools.ietf.org/html/rfc6690#section-1.2.1}{RFC 6690}.
416
417 \sa get(), post(), put(), deleteResource(), observe()
418*/
419QCoapResourceDiscoveryReply *QCoapClient::discover(const QUrl &url, const QString &discoveryPath)
420{
421 Q_D(QCoapClient);
422
423 QUrl discoveryUrl(url);
424 discoveryUrl.setPath(path: url.path() + discoveryPath);
425
426 QCoapRequest request = QCoapRequestPrivate::createRequest(other: QCoapRequest(discoveryUrl),
427 method: QtCoap::Method::Get,
428 isSecure: d->connection->isSecure());
429 return d->sendDiscovery(request);
430}
431
432/*!
433 Sends a request to observe the target \a request and returns
434 a new QCoapReply object which emits the \l QCoapReply::notified()
435 signal whenever a new notification arrives.
436
437 \sa cancelObserve(), get(), post(), put(), deleteResource(), discover()
438*/
439QCoapReply *QCoapClient::observe(const QCoapRequest &request)
440{
441 Q_D(QCoapClient);
442
443 QCoapRequest copyRequest = QCoapRequestPrivate::createRequest(other: request, method: QtCoap::Method::Get,
444 isSecure: d->connection->isSecure());
445 copyRequest.enableObserve();
446
447 return get(request: copyRequest);
448}
449
450/*!
451 \overload
452
453 Sends a request to observe the target \a url and returns
454 a new QCoapReply object which emits the \l QCoapReply::notified()
455 signal whenever a new notification arrives.
456
457 \sa cancelObserve(), get(), post(), put(), deleteResource(), discover()
458*/
459QCoapReply *QCoapClient::observe(const QUrl &url)
460{
461 return observe(request: QCoapRequest(url));
462}
463
464/*!
465 \overload
466
467 Cancels the observation of a resource using the reply \a notifiedReply returned by
468 the observe() method.
469
470 \sa observe()
471*/
472void QCoapClient::cancelObserve(QCoapReply *notifiedReply)
473{
474 Q_D(QCoapClient);
475 QMetaObject::invokeMethod(obj: d->protocol, member: "cancelObserve",
476 Q_ARG(QPointer<QCoapReply>, QPointer<QCoapReply>(notifiedReply)));
477}
478
479/*!
480 \overload
481
482 Cancels the observation of a resource identified by the \a url.
483
484 \sa observe()
485*/
486void QCoapClient::cancelObserve(const QUrl &url)
487{
488 Q_D(QCoapClient);
489 const auto adjustedUrl = QCoapRequestPrivate::adjustedUrl(url, secure: d->connection->isSecure());
490 QMetaObject::invokeMethod(obj: d->protocol, member: "cancelObserve", Q_ARG(QUrl, adjustedUrl));
491}
492
493/*!
494 Closes the open sockets and connections to free the transport.
495
496 \note In the secure mode this needs to be called before changing
497 the security configuration or connecting to another server.
498
499 \sa setSecurityConfiguration()
500*/
501void QCoapClient::disconnect()
502{
503 Q_D(QCoapClient);
504 QMetaObject::invokeMethod(obj: d->connection, member: "disconnect", c: Qt::QueuedConnection);
505}
506
507/*!
508 \internal
509
510 Sends the CoAP \a request to its own URL and returns a new QCoapReply
511 object.
512*/
513QCoapReply *QCoapClientPrivate::sendRequest(const QCoapRequest &request)
514{
515 Q_Q(QCoapClient);
516
517 // Prepare the reply
518 QCoapReply *reply = QCoapReplyPrivate::createCoapReply(request, parent: q);
519
520 if (!send(reply)) {
521 delete reply;
522 return nullptr;
523 }
524
525 return reply;
526}
527
528/*!
529 \internal
530
531 Sends the CoAP \a request to its own URL and returns a
532 new QCoapResourceDiscoveryReply object.
533*/
534QCoapResourceDiscoveryReply *QCoapClientPrivate::sendDiscovery(const QCoapRequest &request)
535{
536 Q_Q(QCoapClient);
537
538 // Prepare the reply
539 QCoapResourceDiscoveryReply *reply = new QCoapResourceDiscoveryReply(request, q);
540
541 if (!send(reply)) {
542 delete reply;
543 return nullptr;
544 }
545
546 return reply;
547}
548
549/*!
550 \internal
551
552 Connect to the reply and use the protocol to send it.
553*/
554bool QCoapClientPrivate::send(QCoapReply *reply)
555{
556 const auto scheme = connection->isSecure() ? QLatin1String("coaps") : QLatin1String("coap");
557 if (reply->request().url().scheme() != scheme) {
558 qCWarning(lcCoapClient, "Failed to send request, URL has an incorrect scheme.");
559 return false;
560 }
561
562 if (!QCoapRequestPrivate::isUrlValid(url: reply->request().url())) {
563 qCWarning(lcCoapClient, "Failed to send request for an invalid URL.");
564 return false;
565 }
566
567 // According to https://tools.ietf.org/html/rfc7252#section-8.1,
568 // multicast requests MUST be Non-confirmable.
569 if (QHostAddress(reply->url().host()).isMulticast()
570 && reply->request().type() == QCoapMessage::Type::Confirmable) {
571 qCWarning(lcCoapClient, "Failed to send request, "
572 "multicast requests must be non-confirmable.");
573 return false;
574 }
575
576 QMetaObject::invokeMethod(obj: protocol, member: "sendRequest", c: Qt::QueuedConnection,
577 Q_ARG(QPointer<QCoapReply>, QPointer<QCoapReply>(reply)),
578 Q_ARG(QCoapConnection *, connection));
579
580 return true;
581}
582
583/*!
584 Sets the security configuration parameters from \a configuration.
585 Configuration will be ignored if the QtCoap::NoSecurity mode is used.
586
587 \note This method must be called before the handshake starts. If you need
588 to change the security configuration after establishing a secure connection
589 with the server, the client needs to be disconnected first.
590
591 \sa disconnect()
592*/
593void QCoapClient::setSecurityConfiguration(const QCoapSecurityConfiguration &configuration)
594{
595 Q_D(QCoapClient);
596
597 QMetaObject::invokeMethod(obj: d->connection, member: "setSecurityConfiguration", c: Qt::QueuedConnection,
598 Q_ARG(QCoapSecurityConfiguration, configuration));
599}
600
601/*!
602 Sets the maximum block size used by the protocol to \a blockSize
603 when sending requests and receiving replies. The block size must be
604 a power of two.
605*/
606void QCoapClient::setBlockSize(quint16 blockSize)
607{
608 Q_D(QCoapClient);
609
610 QMetaObject::invokeMethod(obj: d->protocol, member: "setBlockSize", c: Qt::QueuedConnection,
611 Q_ARG(quint16, blockSize));
612}
613
614/*!
615 Sets the QUdpSocket socket \a option to \a value.
616*/
617void QCoapClient::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
618{
619 Q_D(QCoapClient);
620
621 QMetaObject::invokeMethod(obj: d->connection, member: "setSocketOption", c: Qt::QueuedConnection,
622 Q_ARG(QAbstractSocket::SocketOption, option),
623 Q_ARG(QVariant, value));
624}
625
626/*!
627 Sets the \c MAX_SERVER_RESPONSE_DELAY value to \a responseDelay in milliseconds.
628 The default is 250 seconds.
629
630 As defined in \l {RFC 7390 - Section 2.5}, \c MAX_SERVER_RESPONSE_DELAY is the expected
631 maximum response delay over all servers that the client can send a multicast request to.
632*/
633void QCoapClient::setMaximumServerResponseDelay(uint responseDelay)
634{
635 Q_D(QCoapClient);
636 QMetaObject::invokeMethod(obj: d->protocol, member: "setMaximumServerResponseDelay", c: Qt::QueuedConnection,
637 Q_ARG(uint, responseDelay));
638}
639
640/*!
641 Sets the \c ACK_TIMEOUT value defined in \l {RFC 7252 - Section 4.2} to
642 \a ackTimeout in milliseconds. The default is 2000 ms.
643
644 This timeout only applies to confirmable messages. The actual timeout for
645 reliable transmissions is a random value between \c ACK_TIMEOUT and
646 \c {ACK_TIMEOUT * ACK_RANDOM_FACTOR}.
647
648 \sa setAckRandomFactor()
649*/
650void QCoapClient::setAckTimeout(uint ackTimeout)
651{
652 Q_D(QCoapClient);
653 QMetaObject::invokeMethod(obj: d->protocol, member: "setAckTimeout", c: Qt::QueuedConnection,
654 Q_ARG(uint, ackTimeout));
655}
656
657/*!
658 Sets the \c ACK_RANDOM_FACTOR value defined in \l {RFC 7252 - Section 4.2},
659 to \a ackRandomFactor. This value should be greater than or equal to 1.
660 The default is 1.5.
661
662 \sa setAckTimeout()
663*/
664void QCoapClient::setAckRandomFactor(double ackRandomFactor)
665{
666 Q_D(QCoapClient);
667 QMetaObject::invokeMethod(obj: d->protocol, member: "setAckRandomFactor", c: Qt::QueuedConnection,
668 Q_ARG(double, ackRandomFactor));
669}
670
671/*!
672 Sets the \c MAX_RETRANSMIT value defined in \l {RFC 7252 - Section 4.2}
673 to \a maximumRetransmitCount. This value should be less than or equal to 25.
674 The default is 4.
675*/
676void QCoapClient::setMaximumRetransmitCount(uint maximumRetransmitCount)
677{
678 Q_D(QCoapClient);
679 QMetaObject::invokeMethod(obj: d->protocol, member: "setMaximumRetransmitCount", c: Qt::QueuedConnection,
680 Q_ARG(uint, maximumRetransmitCount));
681}
682
683/*!
684 Sets the minimum token size to \a tokenSize in bytes. For security reasons it is
685 recommended to use tokens with a length of at least 4 bytes. The default value for
686 this parameter is 4 bytes.
687*/
688void QCoapClient::setMinimumTokenSize(int tokenSize)
689{
690 Q_D(QCoapClient);
691 QMetaObject::invokeMethod(obj: d->protocol, member: "setMinimumTokenSize", c: Qt::QueuedConnection,
692 Q_ARG(int, tokenSize));
693}
694
695QT_END_NAMESPACE
696

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