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 "qcoaprequest.h" |
6 | #include "qcoapinternalrequest_p.h" |
7 | |
8 | #include <QtCore/qmath.h> |
9 | #include <QtCore/qrandom.h> |
10 | #include <QtCore/qregularexpression.h> |
11 | #include <QtCore/qloggingcategory.h> |
12 | #include <QtNetwork/QHostAddress> |
13 | |
14 | QT_BEGIN_NAMESPACE |
15 | |
16 | Q_DECLARE_LOGGING_CATEGORY(lcCoapExchange) |
17 | |
18 | /*! |
19 | \internal |
20 | |
21 | \class QCoapInternalRequest |
22 | \brief The QCoapInternalRequest class contains data related to |
23 | a message that needs to be sent. |
24 | |
25 | \reentrant |
26 | |
27 | \sa QCoapInternalMessage, QCoapInternalReply |
28 | */ |
29 | |
30 | /*! |
31 | \internal |
32 | Constructs a new QCoapInternalRequest object and sets \a parent as |
33 | the parent object. |
34 | */ |
35 | QCoapInternalRequest::QCoapInternalRequest(QObject *parent) : |
36 | QCoapInternalMessage(*new QCoapInternalRequestPrivate, parent) |
37 | { |
38 | Q_D(QCoapInternalRequest); |
39 | d->timeoutTimer = new QTimer(this); |
40 | connect(sender: d->timeoutTimer, signal: &QTimer::timeout, context: this, slot: [this]() { emit timeout(this); }); |
41 | |
42 | d->maxTransmitWaitTimer = new QTimer(this); |
43 | connect(sender: d->maxTransmitWaitTimer, signal: &QTimer::timeout, context: this, |
44 | slot: [this]() { emit maxTransmissionSpanReached(this); }); |
45 | |
46 | d->multicastExpireTimer = new QTimer(this); |
47 | connect(sender: d->multicastExpireTimer, signal: &QTimer::timeout, context: this, |
48 | slot: [this]() { emit multicastRequestExpired(this); }); |
49 | } |
50 | |
51 | /*! |
52 | \internal |
53 | Constructs a new QCoapInternalRequest object with the information of |
54 | \a request and sets \a parent as the parent object. |
55 | */ |
56 | QCoapInternalRequest::QCoapInternalRequest(const QCoapRequest &request, QObject *parent) : |
57 | QCoapInternalRequest(parent) |
58 | { |
59 | Q_D(QCoapInternalRequest); |
60 | d->message = request; |
61 | d->method = request.method(); |
62 | d->fullPayload = request.payload(); |
63 | |
64 | addUriOptions(uri: request.url(), proxyUri: request.proxyUrl()); |
65 | } |
66 | |
67 | /*! |
68 | \internal |
69 | Returns \c true if the request is considered valid. |
70 | */ |
71 | bool QCoapInternalRequest::isValid() const |
72 | { |
73 | Q_D(const QCoapInternalRequest); |
74 | return isUrlValid(url: d->targetUri) && d->method != QtCoap::Method::Invalid; |
75 | } |
76 | |
77 | /*! |
78 | \internal |
79 | Initialize parameters to transform the QCoapInternalRequest into an |
80 | empty message (RST or ACK) with the message id \a messageId. |
81 | |
82 | An empty message should contain only the \a messageId. |
83 | */ |
84 | void QCoapInternalRequest::initEmptyMessage(quint16 messageId, QCoapMessage::Type type) |
85 | { |
86 | Q_D(QCoapInternalRequest); |
87 | |
88 | Q_ASSERT(type == QCoapMessage::Type::Acknowledgment || type == QCoapMessage::Type::Reset); |
89 | |
90 | setMethod(QtCoap::Method::Invalid); |
91 | d->message.setType(type); |
92 | d->message.setMessageId(messageId); |
93 | d->message.setToken(QByteArray()); |
94 | d->message.setPayload(QByteArray()); |
95 | d->message.clearOptions(); |
96 | } |
97 | |
98 | /*! |
99 | \internal |
100 | Explicitly casts \a value to a char and appends it to the \a buffer. |
101 | */ |
102 | template<typename T> |
103 | static void appendByte(QByteArray *buffer, T value) { |
104 | buffer->append(c: static_cast<char>(value)); |
105 | } |
106 | |
107 | /*! |
108 | \internal |
109 | Returns the CoAP frame corresponding to the QCoapInternalRequest into |
110 | a QByteArray object. |
111 | |
112 | For more details, refer to section |
113 | \l{https://tools.ietf.org/html/rfc7252#section-3}{'Message format' of RFC 7252}. |
114 | */ |
115 | //! 0 1 2 3 |
116 | //! 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
117 | //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
118 | //! |Ver| T | TKL | Code | Message ID | |
119 | //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
120 | //! | Token (if any, TKL bytes) ... |
121 | //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
122 | //! | Options (if any) ... |
123 | //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
124 | //! |1 1 1 1 1 1 1 1| Payload (if any) ... |
125 | //! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
126 | QByteArray QCoapInternalRequest::toQByteArray() const |
127 | { |
128 | Q_D(const QCoapInternalRequest); |
129 | QByteArray pdu; |
130 | |
131 | // Insert header |
132 | appendByte(buffer: &pdu, value: (d->message.version() << 6) // CoAP version |
133 | | (static_cast<quint8>(d->message.type()) << 4) // Message type |
134 | | d->message.token().size()); // Token Length |
135 | appendByte(buffer: &pdu, value: static_cast<quint8>(d->method) & 0xFF); // Method code |
136 | appendByte(buffer: &pdu, value: (d->message.messageId() >> 8) & 0xFF); // Message ID |
137 | appendByte(buffer: &pdu, value: d->message.messageId() & 0xFF); |
138 | |
139 | // Insert Token |
140 | pdu.append(a: d->message.token()); |
141 | |
142 | // Insert Options |
143 | if (!d->message.options().isEmpty()) { |
144 | const auto options = d->message.options(); |
145 | |
146 | // Options should be sorted in order of their option numbers |
147 | Q_ASSERT(std::is_sorted(d->message.options().cbegin(), d->message.options().cend(), |
148 | [](const QCoapOption &a, const QCoapOption &b) -> bool { |
149 | return a.name() < b.name(); |
150 | })); |
151 | |
152 | quint8 lastOptionNumber = 0; |
153 | for (const QCoapOption &option : std::as_const(t: options)) { |
154 | |
155 | quint16 optionDelta = static_cast<quint16>(option.name()) - lastOptionNumber; |
156 | bool isOptionDeltaExtended = false; |
157 | quint8 optionDeltaExtended = 0; |
158 | |
159 | // Delta value > 12 : special values |
160 | if (optionDelta > 268) { |
161 | optionDeltaExtended = static_cast<quint8>(optionDelta - 269); |
162 | optionDelta = 14; |
163 | isOptionDeltaExtended = true; |
164 | } else if (optionDelta > 12) { |
165 | optionDeltaExtended = static_cast<quint8>(optionDelta - 13); |
166 | optionDelta = 13; |
167 | isOptionDeltaExtended = true; |
168 | } |
169 | |
170 | quint16 optionLength = static_cast<quint16>(option.length()); |
171 | bool isOptionLengthExtended = false; |
172 | quint8 optionLengthExtended = 0; |
173 | |
174 | // Length > 12 : special values |
175 | if (optionLength > 268) { |
176 | optionLengthExtended = static_cast<quint8>(optionLength - 269); |
177 | optionLength = 14; |
178 | isOptionLengthExtended = true; |
179 | } else if (optionLength > 12) { |
180 | optionLengthExtended = static_cast<quint8>(optionLength - 13); |
181 | optionLength = 13; |
182 | isOptionLengthExtended = true; |
183 | } |
184 | |
185 | appendByte(buffer: &pdu, value: (optionDelta << 4) | (optionLength & 0x0F)); |
186 | |
187 | if (isOptionDeltaExtended) |
188 | appendByte(buffer: &pdu, value: optionDeltaExtended); |
189 | if (isOptionLengthExtended) |
190 | appendByte(buffer: &pdu, value: optionLengthExtended); |
191 | |
192 | pdu.append(a: option.opaqueValue()); |
193 | |
194 | lastOptionNumber = option.name(); |
195 | } |
196 | } |
197 | |
198 | // Insert Payload |
199 | if (!d->message.payload().isEmpty()) { |
200 | appendByte(buffer: &pdu, value: 0xFF); |
201 | pdu.append(a: d->message.payload()); |
202 | } |
203 | |
204 | return pdu; |
205 | } |
206 | |
207 | /*! |
208 | \internal |
209 | Initializes block parameters and creates the options needed to request the |
210 | block \a blockNumber with a size of \a blockSize. |
211 | |
212 | \sa blockOption(), setToSendBlock() |
213 | */ |
214 | void QCoapInternalRequest::setToRequestBlock(uint blockNumber, uint blockSize) |
215 | { |
216 | Q_D(QCoapInternalRequest); |
217 | |
218 | if (!checkBlockNumber(blockNumber)) |
219 | return; |
220 | |
221 | d->message.removeOption(name: QCoapOption::Block1); |
222 | d->message.removeOption(name: QCoapOption::Block2); |
223 | |
224 | addOption(option: blockOption(name: QCoapOption::Block2, blockNumber, blockSize)); |
225 | } |
226 | |
227 | /*! |
228 | \internal |
229 | Initialize blocks parameters and creates the options needed to send the block with |
230 | the number \a blockNumber and with a size of \a blockSize. |
231 | |
232 | \sa blockOption(), setToRequestBlock() |
233 | */ |
234 | void QCoapInternalRequest::setToSendBlock(uint blockNumber, uint blockSize) |
235 | { |
236 | Q_D(QCoapInternalRequest); |
237 | |
238 | if (!checkBlockNumber(blockNumber)) |
239 | return; |
240 | |
241 | d->message.setPayload(d->fullPayload.mid(index: static_cast<int>(blockNumber * blockSize), |
242 | len: static_cast<int>(blockSize))); |
243 | d->message.removeOption(name: QCoapOption::Block1); |
244 | |
245 | addOption(option: blockOption(name: QCoapOption::Block1, blockNumber, blockSize)); |
246 | } |
247 | |
248 | /*! |
249 | \internal |
250 | Returns \c true if the block number is valid, \c false otherwise. |
251 | If the block number is not valid, logs a warning message. |
252 | */ |
253 | bool QCoapInternalRequest::checkBlockNumber(uint blockNumber) |
254 | { |
255 | if (blockNumber >> 20) { |
256 | qCWarning(lcCoapExchange) << "Block number" << blockNumber |
257 | << "is too large. It should fit in 20 bits." ; |
258 | return false; |
259 | } |
260 | |
261 | return true; |
262 | } |
263 | |
264 | /*! |
265 | \internal |
266 | Builds and returns a Block option. |
267 | |
268 | The \a blockSize should range from 16 to 1024 and be a power of 2, |
269 | computed as 2^(SZX + 4), with SZX ranging from 0 to 6. For more details, |
270 | refer to the \l{https://tools.ietf.org/html/rfc7959#section-2.2}{RFC 7959}. |
271 | */ |
272 | QCoapOption QCoapInternalRequest::blockOption(QCoapOption::OptionName name, uint blockNumber, uint blockSize) const |
273 | { |
274 | Q_D(const QCoapInternalRequest); |
275 | |
276 | Q_ASSERT((blockSize & (blockSize - 1)) == 0); // is a power of two |
277 | Q_ASSERT(!(blockSize >> 11)); // blockSize <= 1024 |
278 | |
279 | // NUM field: the relative number of the block within a sequence of blocks |
280 | // 4, 12 or 20 bits (as little as possible) |
281 | Q_ASSERT(!(blockNumber >> 20)); // Fits in 20 bits |
282 | quint32 optionData = (blockNumber << 4); |
283 | |
284 | // SZX field: the size of the block |
285 | // 3 bits, set to "log2(blockSize) - 4" |
286 | optionData |= (blockSize >> 7) |
287 | ? ((blockSize >> 10) ? 6 : (3 + (blockSize >> 8))) |
288 | : (blockSize >> 5); |
289 | |
290 | // M field: whether more blocks are following |
291 | // 1 bit |
292 | if (name == QCoapOption::Block1 |
293 | && static_cast<int>((blockNumber + 1) * blockSize) < d->fullPayload.size()) { |
294 | optionData |= 8; |
295 | } |
296 | |
297 | QByteArray optionValue; |
298 | Q_ASSERT(!(optionData >> 24)); |
299 | if (optionData > 0xFFFF) |
300 | appendByte(buffer: &optionValue, value: optionData >> 16); |
301 | if (optionData > 0xFF) |
302 | appendByte(buffer: &optionValue, value: (optionData >> 8) & 0xFF); |
303 | appendByte(buffer: &optionValue, value: optionData & 0xFF); |
304 | |
305 | return QCoapOption(name, optionValue); |
306 | } |
307 | |
308 | /*! |
309 | \internal |
310 | Sets the request's message id. |
311 | */ |
312 | void QCoapInternalRequest::setMessageId(quint16 id) |
313 | { |
314 | Q_D(QCoapInternalRequest); |
315 | d->message.setMessageId(id); |
316 | } |
317 | |
318 | /*! |
319 | \internal |
320 | Sets the request's token. |
321 | */ |
322 | void QCoapInternalRequest::setToken(const QCoapToken &token) |
323 | { |
324 | Q_D(QCoapInternalRequest); |
325 | d->message.setToken(token); |
326 | } |
327 | |
328 | /*! |
329 | \internal |
330 | Adds the given CoAP \a option and sets block parameters if needed. |
331 | */ |
332 | void QCoapInternalRequest::addOption(const QCoapOption &option) |
333 | { |
334 | if (option.name() == QCoapOption::Block1) |
335 | setFromDescriptiveBlockOption(option); |
336 | |
337 | QCoapInternalMessage::addOption(option); |
338 | } |
339 | |
340 | /*! |
341 | \internal |
342 | Adds the CoAP options related to the target and proxy with the given \a uri |
343 | and \a proxyUri. Returns \c true upon success, \c false if an error |
344 | occurred. |
345 | |
346 | Numbers refer to step numbers from CoAP |
347 | \l{RFC 7252}{https://tools.ietf.org/html/rfc7252#section-6.4}. |
348 | */ |
349 | bool QCoapInternalRequest::addUriOptions(QUrl uri, const QUrl &proxyUri) |
350 | { |
351 | Q_D(QCoapInternalRequest); |
352 | // Set to an invalid state |
353 | d->targetUri = QUrl(); |
354 | |
355 | // When using a proxy uri, we SHOULD NOT include Uri-Host/Port/Path/Query |
356 | // options. |
357 | if (!proxyUri.isEmpty()) { |
358 | if (!isUrlValid(url: proxyUri)) |
359 | return false; |
360 | |
361 | addOption(option: QCoapOption(QCoapOption::ProxyUri, proxyUri.toString())); |
362 | d->targetUri = proxyUri; |
363 | return true; |
364 | } |
365 | |
366 | uri = uri.adjusted(options: QUrl::NormalizePathSegments); |
367 | |
368 | // 1/3/4. Fails if URL is relative, has no 'coap' scheme or has a fragment |
369 | if (!isUrlValid(url: uri)) |
370 | return false; |
371 | |
372 | // 2. Ensure encoding matches CoAP standard (i.e. uri is in ASCII encoding) |
373 | const auto uriStr = uri.toString(); |
374 | bool isAscii = std::all_of(first: uriStr.cbegin(), last: uriStr.cend(), |
375 | pred: [](const QChar &ch) { |
376 | return (ch.unicode() < 128); |
377 | }); |
378 | if (!isAscii) |
379 | return false; |
380 | |
381 | // 5. Add Uri-Host option if not a plain IP |
382 | QCoapOption uriHost = uriHostOption(uri); |
383 | if (uriHost.isValid()) |
384 | addOption(option: uriHost); |
385 | |
386 | // 6. Port should be set at this point |
387 | Q_ASSERT(uri.port() != -1); |
388 | |
389 | // 7. Add port to options if it is not the default port |
390 | if (uri.port() != QtCoap::DefaultPort && uri.port() != QtCoap::DefaultSecurePort) |
391 | addOption(name: QCoapOption::UriPort, value: static_cast<quint32>(uri.port())); |
392 | |
393 | // 8. Add path segments to options |
394 | const auto path = uri.path(); |
395 | const auto listPath = QStringView{path}.split(sep: QLatin1Char('/')); |
396 | for (auto pathPart : listPath) { |
397 | if (!pathPart.isEmpty()) |
398 | addOption(option: QCoapOption(QCoapOption::UriPath, pathPart.toString())); |
399 | } |
400 | |
401 | // 9. Add queries to options |
402 | QString query = uri.query(); |
403 | const auto listQuery = QStringView{query}.split(sep: QLatin1Char('&')); |
404 | for (auto queryElement : listQuery) { |
405 | if (!queryElement.isEmpty()) |
406 | addOption(option: QCoapOption(QCoapOption::UriQuery, queryElement.toString())); |
407 | } |
408 | |
409 | d->targetUri = uri; |
410 | return true; |
411 | } |
412 | |
413 | /*! |
414 | \internal |
415 | Returns the token of the request. |
416 | */ |
417 | QCoapToken QCoapInternalRequest::token() const |
418 | { |
419 | return message()->token(); |
420 | } |
421 | |
422 | /*! |
423 | \internal |
424 | Used to mark the transmission as "in progress", when starting or retrying |
425 | to transmit a message. This method manages the retransmission counter, |
426 | the transmission timeout and the exchange timeout. |
427 | */ |
428 | void QCoapInternalRequest::restartTransmission() |
429 | { |
430 | Q_D(QCoapInternalRequest); |
431 | |
432 | if (!d->transmissionInProgress) { |
433 | d->transmissionInProgress = true; |
434 | d->maxTransmitWaitTimer->start(); |
435 | } else { |
436 | d->retransmissionCounter++; |
437 | d->timeout *= 2; |
438 | } |
439 | |
440 | if (d->timeout > 0) |
441 | d->timeoutTimer->start(msec: static_cast<int>(d->timeout)); |
442 | } |
443 | |
444 | /*! |
445 | \internal |
446 | |
447 | Starts the timer for keeping the multicast request \e alive. |
448 | */ |
449 | void QCoapInternalRequest::startMulticastTransmission() |
450 | { |
451 | Q_ASSERT(isMulticast()); |
452 | |
453 | Q_D(QCoapInternalRequest); |
454 | d->multicastExpireTimer->start(); |
455 | } |
456 | |
457 | /*! |
458 | \internal |
459 | Marks the transmission as not running, after a successful reception or an |
460 | error. It resets the retransmission count if needed and stops all timeout timers. |
461 | */ |
462 | void QCoapInternalRequest::stopTransmission() |
463 | { |
464 | Q_D(QCoapInternalRequest); |
465 | if (isMulticast()) { |
466 | d->multicastExpireTimer->stop(); |
467 | } else { |
468 | d->transmissionInProgress = false; |
469 | d->retransmissionCounter = 0; |
470 | d->maxTransmitWaitTimer->stop(); |
471 | d->timeoutTimer->stop(); |
472 | } |
473 | } |
474 | |
475 | /*! |
476 | \internal |
477 | Returns the target uri. |
478 | |
479 | \sa setTargetUri() |
480 | */ |
481 | QUrl QCoapInternalRequest::targetUri() const |
482 | { |
483 | Q_D(const QCoapInternalRequest); |
484 | return d->targetUri; |
485 | } |
486 | |
487 | /*! |
488 | \internal |
489 | Returns the connection used to send this request. |
490 | |
491 | \sa setConnection() |
492 | */ |
493 | QCoapConnection *QCoapInternalRequest::connection() const |
494 | { |
495 | Q_D(const QCoapInternalRequest); |
496 | return d->connection; |
497 | } |
498 | |
499 | /*! |
500 | \internal |
501 | Returns the method of the request. |
502 | |
503 | \sa setMethod() |
504 | */ |
505 | QtCoap::Method QCoapInternalRequest::method() const |
506 | { |
507 | Q_D(const QCoapInternalRequest); |
508 | return d->method; |
509 | } |
510 | |
511 | /*! |
512 | \internal |
513 | Returns true if the request is an Observe request. |
514 | |
515 | */ |
516 | bool QCoapInternalRequest::isObserve() const |
517 | { |
518 | Q_D(const QCoapInternalRequest); |
519 | return d->message.hasOption(name: QCoapOption::Observe); |
520 | } |
521 | |
522 | /*! |
523 | \internal |
524 | Returns true if the observe request needs to be cancelled. |
525 | |
526 | \sa setCancelObserve() |
527 | */ |
528 | bool QCoapInternalRequest::isObserveCancelled() const |
529 | { |
530 | Q_D(const QCoapInternalRequest); |
531 | return d->observeCancelled; |
532 | } |
533 | |
534 | /*! |
535 | \internal |
536 | |
537 | Returns \c true if the request is multicast, returns \c false otherwise. |
538 | */ |
539 | bool QCoapInternalRequest::isMulticast() const |
540 | { |
541 | const QHostAddress hostAddress(targetUri().host()); |
542 | return hostAddress.isMulticast(); |
543 | } |
544 | |
545 | /*! |
546 | \internal |
547 | Returns the value of the retransmission counter. |
548 | */ |
549 | uint QCoapInternalRequest::retransmissionCounter() const |
550 | { |
551 | Q_D(const QCoapInternalRequest); |
552 | return d->retransmissionCounter; |
553 | } |
554 | |
555 | /*! |
556 | \internal |
557 | Sets the method of the request to the given \a method. |
558 | |
559 | \sa method() |
560 | */ |
561 | void QCoapInternalRequest::setMethod(QtCoap::Method method) |
562 | { |
563 | Q_D(QCoapInternalRequest); |
564 | d->method = method; |
565 | } |
566 | |
567 | /*! |
568 | \internal |
569 | Sets the connection to use to send this request to the given \a connection. |
570 | |
571 | \sa connection() |
572 | */ |
573 | void QCoapInternalRequest::setConnection(QCoapConnection *connection) |
574 | { |
575 | Q_D(QCoapInternalRequest); |
576 | d->connection = connection; |
577 | } |
578 | |
579 | /*! |
580 | \internal |
581 | Marks the observe request as cancelled. |
582 | |
583 | \sa isObserveCancelled() |
584 | */ |
585 | void QCoapInternalRequest::setObserveCancelled() |
586 | { |
587 | Q_D(QCoapInternalRequest); |
588 | d->observeCancelled = true; |
589 | } |
590 | |
591 | /*! |
592 | \internal |
593 | Sets the target uri to the given \a targetUri. |
594 | |
595 | \sa targetUri() |
596 | */ |
597 | void QCoapInternalRequest::setTargetUri(QUrl targetUri) |
598 | { |
599 | Q_D(QCoapInternalRequest); |
600 | d->targetUri = targetUri; |
601 | } |
602 | |
603 | /*! |
604 | \internal |
605 | Sets the timeout to the given \a timeout value in milliseconds. Timeout is |
606 | used for reliable transmission of Confirmable messages. |
607 | |
608 | When such request times out, its timeout value will double. |
609 | */ |
610 | void QCoapInternalRequest::setTimeout(uint timeout) |
611 | { |
612 | Q_D(QCoapInternalRequest); |
613 | d->timeout = timeout; |
614 | } |
615 | |
616 | /*! |
617 | \internal |
618 | Sets the maximum transmission span for the request. If the request is |
619 | not finished at the end of the transmission span, the request will timeout. |
620 | */ |
621 | void QCoapInternalRequest::setMaxTransmissionWait(uint duration) |
622 | { |
623 | Q_D(QCoapInternalRequest); |
624 | d->maxTransmitWaitTimer->setInterval(static_cast<int>(duration)); |
625 | } |
626 | |
627 | /*! |
628 | \internal |
629 | |
630 | Sets the timeout interval in milliseconds for keeping the multicast request |
631 | \e alive. |
632 | |
633 | In the unicast case, receiving a response means that the request is finished. |
634 | In the multicast case it is not known how many responses will be received, so |
635 | the response, along with its token, will be kept for |
636 | NON_LIFETIME + MAX_LATENCY + MAX_SERVER_RESPONSE_DELAY time, as suggested |
637 | in \l {RFC 7390 - Section 2.5}. |
638 | */ |
639 | void QCoapInternalRequest::setMulticastTimeout(uint responseDelay) |
640 | { |
641 | Q_D(QCoapInternalRequest); |
642 | d->multicastExpireTimer->setInterval(static_cast<int>(responseDelay)); |
643 | } |
644 | |
645 | /*! |
646 | \internal |
647 | Decode the \a uri provided and returns a QCoapOption. |
648 | */ |
649 | QCoapOption QCoapInternalRequest::uriHostOption(const QUrl &uri) const |
650 | { |
651 | QHostAddress address(uri.host()); |
652 | |
653 | // No need for Uri-Host option with an IPv4 or IPv6 address |
654 | if (!address.isNull()) |
655 | return QCoapOption(); |
656 | |
657 | return QCoapOption(QCoapOption::UriHost, uri.host()); |
658 | } |
659 | |
660 | QT_END_NAMESPACE |
661 | |