aboutsummaryrefslogtreecommitdiffstats
path: root/src/coap/qcoapprotocol.cpp
diff options
context:
space:
mode:
authorSona Kurazyan <sona.kurazyan@qt.io>2019-02-11 17:35:15 +0100
committerSona Kurazyan <sona.kurazyan@qt.io>2019-02-21 16:17:51 +0000
commitc0c8dfbfebec7614cf52d21d2ea6523456912596 (patch)
tree2577597aaf3f65c3d15a2dbc8cc488c7f9e98ad9 /src/coap/qcoapprotocol.cpp
parent18cccdd7baa1939ab32eac86d4f6b88be59cec14 (diff)
Add support for multicast CoAP requests
Change-Id: I9cf6d4f97c863c232b17bc8e560c6b62c3f39624 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io> Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'src/coap/qcoapprotocol.cpp')
-rw-r--r--src/coap/qcoapprotocol.cpp78
1 files changed, 70 insertions, 8 deletions
diff --git a/src/coap/qcoapprotocol.cpp b/src/coap/qcoapprotocol.cpp
index 2edbf69..f2e430f 100644
--- a/src/coap/qcoapprotocol.cpp
+++ b/src/coap/qcoapprotocol.cpp
@@ -96,6 +96,19 @@ void QCoapProtocol::sendRequest(QPointer<QCoapReply> reply, QCoapConnection *con
internalRequest->setMaxTransmissionWait(maxTransmitWait());
connect(reply, &QCoapReply::finished, this, &QCoapProtocol::finished);
+ if (internalRequest->isMulticast()) {
+ connect(internalRequest.data(), &QCoapInternalRequest::multicastRequestExpired, this,
+ [this](QCoapInternalRequest *request) {
+ Q_D(QCoapProtocol);
+ d->onMulticastRequestExpired(request);
+ });
+ // The timeout interval is chosen based on
+ // https://tools.ietf.org/html/rfc7390#section-2.5
+ internalRequest->setMulticastTimeout(nonConfirmLifetime()
+ + static_cast<uint>(maxLatency())
+ + maxServerResponseDelay());
+ }
+
// Set a unique Message Id and Token
QCoapMessage *requestMessage = internalRequest->message();
internalRequest->setMessageId(d->generateUniqueMessageId());
@@ -136,9 +149,11 @@ void QCoapProtocol::sendRequest(QPointer<QCoapReply> reply, QCoapConnection *con
/*!
\internal
- Encodes and sends the given \a request to the server.
+ Encodes and sends the given \a request to the server. If \a host is not empty,
+ sends the request to \a host, instead of using the host address from the request.
+ The \a host parameter is relevant for multicast blockwise transfers.
*/
-void QCoapProtocolPrivate::sendRequest(QCoapInternalRequest *request) const
+void QCoapProtocolPrivate::sendRequest(QCoapInternalRequest *request, const QString& host) const
{
Q_Q(const QCoapProtocol);
Q_ASSERT(QThread::currentThread() == q->thread());
@@ -148,10 +163,15 @@ void QCoapProtocolPrivate::sendRequest(QCoapInternalRequest *request) const
return;
}
- request->restartTransmission();
+ if (request->isMulticast())
+ request->startMulticastTransmission();
+ else
+ request->restartTransmission();
+
QByteArray requestFrame = request->toQByteArray();
QUrl uri = request->targetUri();
- request->connection()->sendRequest(requestFrame, uri.host(), static_cast<quint16>(uri.port()));
+ const auto& hostAddress = host.isEmpty() ? uri.host() : host;
+ request->connection()->sendRequest(requestFrame, hostAddress, static_cast<quint16>(uri.port()));
}
/*!
@@ -194,6 +214,29 @@ void QCoapProtocolPrivate::onRequestMaxTransmissionSpanReached(QCoapInternalRequ
/*!
\internal
+ This slot is called when the multicast request expires, meaning that no
+ more responses are expected for the multicast \a request. As a result of this
+ call, the request token is \e {freed up} and the \l finished() signal is emitted.
+*/
+void QCoapProtocolPrivate::onMulticastRequestExpired(QCoapInternalRequest *request)
+{
+ Q_ASSERT(request->isMulticast());
+
+ request->stopTransmission();
+ QPointer<QCoapReply> userReply = userReplyForToken(request->token());
+ if (userReply) {
+ QMetaObject::invokeMethod(userReply, "_q_setFinished", Qt::QueuedConnection,
+ Q_ARG(QtCoap::Error, QtCoap::NoError));
+ } else {
+ qWarning().nospace() << "QtCoap: Reply for token '" << request->token()
+ << "' is not registered, reply is null.";
+ }
+ forgetExchange(request);
+}
+
+/*!
+ \internal
+
Method triggered when a request fails.
*/
void QCoapProtocolPrivate::onRequestError(QCoapInternalRequest *request, QCoapInternalReply *reply)
@@ -268,7 +311,8 @@ void QCoapProtocolPrivate::onFrameReceived(const QByteArray &data, const QHostAd
return;
}
- request->stopTransmission();
+ if (!request->isMulticast())
+ request->stopTransmission();
addReply(request->token(), reply);
if (QtCoap::isError(reply->responseCode())) {
@@ -293,9 +337,13 @@ void QCoapProtocolPrivate::onFrameReceived(const QByteArray &data, const QHostAd
} else if (reply->hasMoreBlocksToReceive()) {
request->setToRequestBlock(reply->currentBlockNumber() + 1, reply->blockSize());
request->setMessageId(generateUniqueMessageId());
- sendRequest(request);
+ // In case of multicast blockwise transfers, according to
+ // https://tools.ietf.org/html/rfc7959#section-2.8, further blocks should be retrieved
+ // via unicast requests. So instead of using the multicast request address, we need
+ // to use the sender address for getting the next blocks.
+ sendRequest(request, sender.toString());
} else {
- onLastMessageReceived(request);
+ onLastMessageReceived(request, sender);
}
}
@@ -395,7 +443,8 @@ QCoapInternalRequest *QCoapProtocolPrivate::findRequestByMessageId(quint16 messa
associated QCoapReply and emits the
\l{QCoapProtocol::finished(QCoapReply*)}{finished(QCoapReply*)} signal.
*/
-void QCoapProtocolPrivate::onLastMessageReceived(QCoapInternalRequest *request)
+void QCoapProtocolPrivate::onLastMessageReceived(QCoapInternalRequest *request,
+ const QHostAddress &sender)
{
Q_ASSERT(request);
if (!request || !isRequestRegistered(request))
@@ -423,6 +472,16 @@ void QCoapProtocolPrivate::onLastMessageReceived(QCoapInternalRequest *request)
// Merge payloads for blockwise transfers
if (replies.size() > 1) {
+
+ // In multicast case, multiple hosts will reply to the same multicast request.
+ // We are interested only in replies coming from the sender.
+ if (request->isMulticast()) {
+ replies.erase(std::remove_if(replies.begin(), replies.end(),
+ [sender](QSharedPointer<QCoapInternalReply> reply) {
+ return reply->senderAddress() != sender;
+ }), replies.end());
+ }
+
std::stable_sort(std::begin(replies), std::end(replies),
[](QSharedPointer<QCoapInternalReply> a, QSharedPointer<QCoapInternalReply> b) -> bool {
return (a->currentBlockNumber() < b->currentBlockNumber());
@@ -452,6 +511,9 @@ void QCoapProtocolPrivate::onLastMessageReceived(QCoapInternalRequest *request)
if (request->isObserve()) {
QMetaObject::invokeMethod(userReply, "_q_setNotified", Qt::QueuedConnection);
forgetExchangeReplies(request->token());
+ } else if (request->isMulticast()) {
+ Q_Q(QCoapProtocol);
+ emit q->responseToMulticastReceived(userReply, *lastReply->message());
} else {
QMetaObject::invokeMethod(userReply, "_q_setFinished", Qt::QueuedConnection,
Q_ARG(QtCoap::Error, QtCoap::NoError));