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_p.h"
6
7#include <QtCore/qmath.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtCore/QDebug>
11
12QT_BEGIN_NAMESPACE
13
14Q_DECLARE_LOGGING_CATEGORY(lcCoapExchange)
15
16namespace {
17const auto CoapScheme = QLatin1String("coap");
18const auto CoapSecureScheme = QLatin1String("coaps");
19}
20
21QCoapRequestPrivate::QCoapRequestPrivate(const QUrl &url, QCoapMessage::Type type,
22 const QUrl &proxyUrl) :
23 QCoapMessagePrivate(type),
24 proxyUri(proxyUrl)
25{
26 setUrl(url);
27}
28
29QCoapRequestPrivate::~QCoapRequestPrivate()
30{
31}
32
33QCoapRequestPrivate *QCoapRequestPrivate::clone() const
34{
35 return new QCoapRequestPrivate(*this);
36}
37
38/*!
39 \internal
40
41 \brief Sets the url after adjusting it, and asserting its validity.
42*/
43void QCoapRequestPrivate::setUrl(const QUrl &url)
44{
45 // Print no warning when clearing URL
46 if (url.isEmpty()) {
47 uri = url;
48 return;
49 }
50
51 // Make first checks before editing the URL, to avoid editing it
52 // in a wrong way (e.g. when adding the scheme)
53 if (!url.isValid()) {
54 qCWarning(lcCoapExchange) << "Invalid CoAP url" << url.toString();
55 return;
56 }
57
58 // If the port is unknown, try to set it based on scheme
59 QUrl finalizedUrl = url;
60 if (!url.scheme().isEmpty()) {
61 if (url.scheme() == CoapScheme) {
62 if (url.port() == -1)
63 finalizedUrl.setPort(QtCoap::DefaultPort);
64 } else if (url.scheme() == CoapSecureScheme) {
65 if (url.port() == -1)
66 finalizedUrl.setPort(QtCoap::DefaultSecurePort);
67 } else {
68 qCWarning(lcCoapExchange) << "QCoapRequest: Request URL's scheme" << url.scheme()
69 << "isn't valid for CoAP";
70 return;
71 }
72 }
73
74 uri = finalizedUrl;
75}
76
77/*!
78 \class QCoapRequest
79 \inmodule QtCoap
80
81 \brief The QCoapRequest class holds a CoAP request. This request
82 can be sent with QCoapClient.
83
84 \reentrant
85
86 The QCoapRequest contains data needed to make CoAP frames that can be
87 sent to the URL it holds.
88
89 \sa QCoapClient, QCoapReply, QCoapResourceDiscoveryReply
90*/
91
92/*!
93 Constructs a QCoapRequest object with the target \a url,
94 the proxy URL \a proxyUrl and the \a type of the message.
95*/
96QCoapRequest::QCoapRequest(const QUrl &url, Type type, const QUrl &proxyUrl) :
97 QCoapMessage(*new QCoapRequestPrivate(url, type, proxyUrl))
98{
99}
100
101/*!
102 Constructs a QCoapRequest from a string literal
103*/
104QCoapRequest::QCoapRequest(const char *url, Type type) :
105 QCoapMessage(*new QCoapRequestPrivate(QUrl(QString::fromUtf8(utf8: url)), type))
106{
107}
108
109/*!
110 Constructs a copy of the \a other QCoapRequest.
111*/
112QCoapRequest::QCoapRequest(const QCoapRequest &other) :
113 //! No private data sharing, as QCoapRequestPrivate!=QCoapMessagePrivate
114 //! and the d_ptr is a QSharedDataPointer<QCoapMessagePrivate>
115 QCoapMessage(*other.d_func()->clone())
116{
117}
118
119/*!
120 Destroys the QCoapRequest.
121*/
122QCoapRequest::~QCoapRequest()
123{
124}
125
126/*!
127 Returns the target URI of the request.
128
129 \sa setUrl()
130*/
131QUrl QCoapRequest::url() const
132{
133 Q_D(const QCoapRequest);
134 return d->uri;
135}
136
137/*!
138 Returns the proxy URI of the request.
139 The request shall be sent directly if this is invalid.
140
141 \sa setProxyUrl()
142*/
143QUrl QCoapRequest::proxyUrl() const
144{
145 Q_D(const QCoapRequest);
146 return d->proxyUri;
147}
148
149/*!
150 Returns the method of the request.
151*/
152QtCoap::Method QCoapRequest::method() const
153{
154 Q_D(const QCoapRequest);
155 return d->method;
156}
157
158/*!
159 Returns \c true if the request is an observe request.
160
161 \sa enableObserve()
162*/
163bool QCoapRequest::isObserve() const
164{
165 return hasOption(name: QCoapOption::Observe);
166}
167
168/*!
169 Sets the target URI of the request to the given \a url.
170
171 If not indicated, the scheme of the URL will default to 'coap', and its
172 port will default to 5683.
173
174 \sa url()
175*/
176void QCoapRequest::setUrl(const QUrl &url)
177{
178 Q_D(QCoapRequest);
179 d->setUrl(url);
180}
181
182/*!
183 Sets the proxy URI of the request to the given \a proxyUrl.
184
185 \sa proxyUrl()
186*/
187void QCoapRequest::setProxyUrl(const QUrl &proxyUrl)
188{
189 Q_D(QCoapRequest);
190 d->proxyUri = proxyUrl;
191}
192
193/*!
194 Sets the observe to \c true to make an observe request.
195
196 \sa isObserve()
197*/
198void QCoapRequest::enableObserve()
199{
200 if (isObserve())
201 return;
202
203 addOption(name: QCoapOption::Observe);
204}
205
206/*!
207 \internal
208
209 Adjusts the request URL by setting the correct default scheme and port
210 (if not indicated) based on the \a secure parameter.
211
212 In non-secure mode the scheme of request URL will default to \c coap, and
213 its port will default to \e 5683. In secure mode the scheme will default to
214 \c coaps, and the port will default to \e 5684.
215*/
216void QCoapRequestPrivate::adjustUrl(bool secure)
217{
218 uri = adjustedUrl(url: uri, secure);
219}
220
221/*!
222 Creates a copy of \a other.
223*/
224QCoapRequest &QCoapRequest::operator=(const QCoapRequest &other)
225{
226 d_ptr = other.d_ptr;
227 return *this;
228}
229
230/*!
231 \internal
232
233 Returns \c true if the \a url is a valid CoAP URL.
234*/
235bool QCoapRequestPrivate::isUrlValid(const QUrl &url)
236{
237 return (url.isValid() && !url.isLocalFile() && !url.isRelative()
238 && (url.scheme() == CoapScheme || url.scheme() == CoapSecureScheme)
239 && !url.hasFragment());
240}
241
242/*!
243 \internal
244
245 Adjusts the \a url by setting the correct default scheme and port
246 (if not indicated) based on the \a secure parameter. Returns the
247 adjusted URL.
248
249 In non-secure mode the scheme of request URL will default to \c coap, and
250 its port will default to \e 5683. In secure mode the scheme will default to
251 \c coaps, and the port will default to \e 5684.
252*/
253QUrl QCoapRequestPrivate::adjustedUrl(const QUrl &url, bool secure)
254{
255 if (url.isEmpty() || !url.isValid())
256 return QUrl();
257
258 QUrl finalizedUrl = url;
259 const auto scheme = secure ? CoapSecureScheme : CoapScheme;
260 if (url.host().isEmpty() && url.isRelative()) {
261 // In some cases host address is mistaken for part of the relative path,
262 // prepending the scheme fixes this.
263 finalizedUrl = url.toString().prepend(s: scheme + QLatin1String("://"));
264 } else if (url.scheme().isEmpty()) {
265 finalizedUrl.setScheme(scheme);
266 }
267
268 if (finalizedUrl.host().isEmpty()) {
269 qCWarning(lcCoapExchange) << "The requested URL" << url << "is not a valid CoAP URL.";
270 return QUrl();
271 }
272
273 if (url.port() == -1) {
274 const auto port = secure ? QtCoap::DefaultSecurePort : QtCoap::DefaultPort;
275 finalizedUrl.setPort(port);
276 }
277
278 return finalizedUrl;
279}
280
281/*!
282 \internal
283
284 For QSharedDataPointer.
285*/
286QCoapRequestPrivate* QCoapRequest::d_func()
287{
288 return static_cast<QCoapRequestPrivate*>(d_ptr.data());
289}
290
291/*!
292 \internal
293
294 Creates a copy of \a other request and sets \a method as its request method.
295 Adjusts the request URL based on \a isSecure parameter.
296*/
297QCoapRequest
298QCoapRequestPrivate::createRequest(const QCoapRequest &other, QtCoap::Method method, bool isSecure)
299{
300 QCoapRequest request(other);
301 request.d_func()->method = method;
302 request.d_func()->adjustUrl(secure: isSecure);
303 return request;
304}
305
306QT_END_NAMESPACE
307

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