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