Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qnetworkrequestfactory.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
7
8#if QT_CONFIG(ssl)
9#include <QtNetwork/qsslconfiguration.h>
10#endif
11
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/qmap.h>
14
16
17QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QNetworkRequestFactoryPrivate)
18
19using namespace Qt::StringLiterals;
20
21Q_STATIC_LOGGING_CATEGORY(lcQrequestfactory, "qt.network.access.request.factory")
22
23/*!
24 \class QNetworkRequestFactory
25 \since 6.7
26 \ingroup shared
27 \inmodule QtNetwork
28
29 \brief Convenience class for grouping remote server endpoints that share
30 common network request properties.
31
32 REST servers often have endpoints that require the same headers and other data.
33 Grouping such endpoints with a QNetworkRequestFactory makes it more
34 convenient to issue requests to these endpoints; only the typically
35 varying parts such as \e path and \e query parameters are provided
36 when creating a new request.
37
38 Basic usage steps of QNetworkRequestFactory are as follows:
39 \list
40 \li Instantiation
41 \li Setting the data common to all requests
42 \li Issuing requests
43 \endlist
44
45 An example of usage:
46
47 \snippet code/src_network_access_qnetworkrequestfactory.cpp 0
48*/
49
50/*!
51 Creates a new QNetworkRequestFactory object.
52 Use setBaseUrl() to set a valid base URL for the requests.
53
54 \sa QNetworkRequestFactory(const QUrl &baseUrl), setBaseUrl()
55*/
56
57QNetworkRequestFactory::QNetworkRequestFactory()
58 : d(new QNetworkRequestFactoryPrivate)
59{
60}
61
62/*!
63 Creates a new QNetworkRequestFactory object, initializing the base URL to
64 \a baseUrl. The base URL is used to populate subsequent network
65 requests.
66
67 If the URL contains a \e path component, it will be extracted and used
68 as a base path in subsequent network requests. This means that any
69 paths provided when requesting individual requests will be appended
70 to this base path, as illustrated below:
71
72 \snippet code/src_network_access_qnetworkrequestfactory.cpp 1
73 */
74QNetworkRequestFactory::QNetworkRequestFactory(const QUrl &baseUrl)
75 : d(new QNetworkRequestFactoryPrivate(baseUrl))
76{
77}
78
79/*!
80 Destroys this QNetworkRequestFactory object.
81 */
83 = default;
84
85/*!
86 Creates a copy of \a other.
87 */
88QNetworkRequestFactory::QNetworkRequestFactory(const QNetworkRequestFactory &other)
89 = default;
90
91/*!
92 Creates a copy of \a other and returns a reference to this factory.
93 */
95 = default;
96
97/*!
98 \fn QNetworkRequestFactory::QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept
99
100 Move-constructs the factory from \a other.
101
102 \note The moved-from object \a other is placed in a
103 partially-formed state, in which the only valid operations are
104 destruction and assignment of a new value.
105*/
106
107/*!
108 \fn QNetworkRequestFactory &QNetworkRequestFactory::operator=(QNetworkRequestFactory &&other) noexcept
109
110 Move-assigns \a other and returns a reference to this factory.
111
112 \note The moved-from object \a other is placed in a
113 partially-formed state, in which the only valid operations are
114 destruction and assignment of a new value.
115 */
116
117/*!
118 \fn void QNetworkRequestFactory::swap(QNetworkRequestFactory &other)
119 \memberswap{factory}
120 */
121
122/*!
123 Returns the base URL used for the individual requests.
124
125 The base URL may contain a path component. This path is used
126 as path "prefix" for the paths that are provided when generating
127 individual requests.
128
129 \sa setBaseUrl()
130 */
131QUrl QNetworkRequestFactory::baseUrl() const
132{
133 return d->baseUrl;
134}
135
136/*!
137 Sets the base URL used in individual requests to \a url.
138
139 \sa baseUrl()
140 */
141void QNetworkRequestFactory::setBaseUrl(const QUrl &url)
142{
143 if (d->baseUrl == url)
144 return;
145
146 d.detach();
147 d->baseUrl = url;
148}
149
150#if QT_CONFIG(ssl)
151/*!
152 Returns the SSL configuration set to this factory. The SSL configuration
153 is set to each individual request.
154
155 \sa setSslConfiguration()
156 */
157QSslConfiguration QNetworkRequestFactory::sslConfiguration() const
158{
159 return d->sslConfig;
160}
161
162/*!
163 Sets the SSL configuration to \a configuration.
164
165 \sa sslConfiguration()
166 */
167void QNetworkRequestFactory::setSslConfiguration(const QSslConfiguration &configuration)
168{
169 if (d->sslConfig == configuration)
170 return;
171
172 d.detach();
173 d->sslConfig = configuration;
174}
175#endif
176
177/*!
178 Returns a QNetworkRequest.
179
180 The returned request is filled with the data that this factory
181 has been configured with.
182
183 \sa createRequest(const QUrlQuery&), createRequest(const QString&, const QUrlQuery&)
184*/
185
186QNetworkRequest QNetworkRequestFactory::createRequest() const
187{
188 return d->newRequest(d->requestUrl());
189}
190
191/*!
192 Returns a QNetworkRequest.
193
194 The returned request's URL is formed by appending the provided \a path
195 to the baseUrl (which may itself have a path component).
196
197 \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
198*/
199QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path) const
200{
201 return d->newRequest(d->requestUrl(&path));
202}
203
204/*!
205 Returns a QNetworkRequest.
206
207 The returned request's URL is formed by appending the provided \a query
208 to the baseUrl.
209
210 \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
211*/
212QNetworkRequest QNetworkRequestFactory::createRequest(const QUrlQuery &query) const
213{
214 return d->newRequest(d->requestUrl(nullptr, &query));
215}
216
217/*!
218 Returns a QNetworkRequest.
219
220 The returned requests URL is formed by appending the provided \a path
221 and \a query to the baseUrl (which may have a path component).
222
223 If the provided \a path contains query items, they will be combined
224 with the items in \a query.
225
226 \sa createRequest(const QUrlQuery&), createRequest(), baseUrl()
227 */
228QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path, const QUrlQuery &query) const
229{
230 return d->newRequest(d->requestUrl(&path, &query));
231}
232
233/*!
234 Sets \a headers that are common to all requests.
235
236 These headers are added to individual requests' headers.
237 This is a convenience mechanism for setting headers that
238 repeat across requests.
239
240 \sa commonHeaders(), clearCommonHeaders(), createRequest()
241 */
242void QNetworkRequestFactory::setCommonHeaders(const QHttpHeaders &headers)
243{
244 d.detach();
245 d->headers = headers;
246}
247
248/*!
249 Returns the currently set headers.
250
251 \sa setCommonHeaders(), clearCommonHeaders()
252 */
253QHttpHeaders QNetworkRequestFactory::commonHeaders() const
254{
255 return d->headers;
256}
257
258/*!
259 Clears current headers.
260
261 \sa commonHeaders(), setCommonHeaders()
262*/
264{
265 if (d->headers.isEmpty())
266 return;
267 d.detach();
268 d->headers.clear();
269}
270
271/*!
272 Returns the bearer token that has been set.
273
274 The bearer token, if present, is used to set the
275 \c {Authorization: Bearer my_token} header for requests. This is a common
276 authorization convention and is provided as an additional convenience.
277
278 The means to acquire the bearer token vary. Standard methods include \c OAuth2
279 and the service provider's website/dashboard. It is expected that the bearer
280 token changes over time. For example, when updated with a refresh token,
281 always setting the new token again ensures that subsequent requests have
282 the latest, valid token.
283
284 The presence of the bearer token does not impact the \l commonHeaders()
285 listing. If the \l commonHeaders() also lists \c Authorization header, it
286 will be overwritten.
287
288 \sa setBearerToken(), commonHeaders()
289 */
290QByteArray QNetworkRequestFactory::bearerToken() const
291{
292 return d->bearerToken;
293}
294
295/*!
296 Sets the bearer token to \a token.
297
298 \sa bearerToken(), clearBearerToken()
299*/
300void QNetworkRequestFactory::setBearerToken(const QByteArray &token)
301{
302 if (d->bearerToken == token)
303 return;
304
305 d.detach();
306 d->bearerToken = token;
307}
308
309/*!
310 Clears the bearer token.
311
312 \sa bearerToken()
313*/
315{
316 if (d->bearerToken.isEmpty())
317 return;
318
319 d.detach();
320 d->bearerToken.clear();
321}
322
323/*!
324 Returns the username set to this factory.
325
326 \sa setUserName(), clearUserName(), password()
327*/
328QString QNetworkRequestFactory::userName() const
329{
330 return d->userName;
331}
332
333/*!
334 Sets the username of this factory to \a userName.
335
336 The username is set in the request URL when \l createRequest() is called.
337 The QRestAccessManager / QNetworkAccessManager will attempt to use
338 these credentials when the server indicates that authentication
339 is required.
340
341 \sa userName(), clearUserName(), password()
342*/
343void QNetworkRequestFactory::setUserName(const QString &userName)
344{
345 if (d->userName == userName)
346 return;
347 d.detach();
348 d->userName = userName;
349}
350
351/*!
352 Clears the username set to this factory.
353*/
355{
356 if (d->userName.isEmpty())
357 return;
358 d.detach();
359 d->userName.clear();
360}
361
362/*!
363 Returns the password set to this factory.
364
365 \sa setPassword(), clearPassword(), userName()
366*/
367QString QNetworkRequestFactory::password() const
368{
369 return d->password;
370}
371
372/*!
373 Sets the password of this factory to \a password.
374
375 The password is set in the request URL when \l createRequest() is called.
376 The QRestAccessManager / QNetworkAccessManager will attempt to use
377 these credentials when the server indicates that authentication
378 is required.
379
380 \sa password(), clearPassword(), userName()
381*/
382void QNetworkRequestFactory::setPassword(const QString &password)
383{
384 if (d->password == password)
385 return;
386 d.detach();
387 d->password = password;
388}
389
390/*!
391 Clears the password set to this factory.
392
393 \sa password(), setPassword(), userName()
394*/
396{
397 if (d->password.isEmpty())
398 return;
399 d.detach();
400 d->password.clear();
401}
402
403/*!
404 Sets \a timeout used for transfers.
405
406 \sa transferTimeout(), QNetworkRequest::setTransferTimeout(),
407 QNetworkAccessManager::setTransferTimeout()
408*/
409void QNetworkRequestFactory::setTransferTimeout(std::chrono::milliseconds timeout)
410{
411 if (d->transferTimeout == timeout)
412 return;
413
414 d.detach();
415 d->transferTimeout = timeout;
416}
417
418/*!
419 Returns the timeout used for transfers.
420
421 \sa setTransferTimeout(), QNetworkRequest::transferTimeout(),
422 QNetworkAccessManager::transferTimeout()
423*/
424std::chrono::milliseconds QNetworkRequestFactory::transferTimeout() const
425{
426 return d->transferTimeout;
427}
428
429/*!
430 Returns query parameters that are added to individual requests' query
431 parameters. The query parameters are added to any potential query
432 parameters provided with the individual \l createRequest() calls.
433
434 Use cases for using repeating query parameters are server dependent,
435 but typical examples include language setting \c {?lang=en}, format
436 specification \c {?format=json}, API version specification
437 \c {?version=1.0} and API key authentication.
438
439 \sa setQueryParameters(), clearQueryParameters(), createRequest()
440*/
441QUrlQuery QNetworkRequestFactory::queryParameters() const
442{
443 return d->queryParameters;
444}
445
446/*!
447 Sets \a query parameters that are added to individual requests' query
448 parameters.
449
450 \sa queryParameters(), clearQueryParameters()
451 */
452void QNetworkRequestFactory::setQueryParameters(const QUrlQuery &query)
453{
454 if (d->queryParameters == query)
455 return;
456
457 d.detach();
458 d->queryParameters = query;
459}
460
461/*!
462 Clears the query parameters.
463
464 \sa queryParameters()
465*/
467{
468 if (d->queryParameters.isEmpty())
469 return;
470
471 d.detach();
472 d->queryParameters.clear();
473}
474
475/*!
476 \since 6.8
477
478 Sets the priority for any future requests created by this factory to
479 \a priority.
480
481 The default priority is \l QNetworkRequest::NormalPriority.
482
483 \sa priority(), QNetworkRequest::setPriority()
484*/
485void QNetworkRequestFactory::setPriority(QNetworkRequest::Priority priority)
486{
487 if (d->priority == priority)
488 return;
489 d.detach();
490 d->priority = priority;
491}
492
493/*!
494 \since 6.8
495
496 Returns the priority assigned to any future requests created by this
497 factory.
498
499 \sa setPriority(), QNetworkRequest::priority()
500*/
501QNetworkRequest::Priority QNetworkRequestFactory::priority() const
502{
503 return d->priority;
504}
505
506/*!
507 \since 6.8
508
509 Sets the value associated with \a attribute to \a value.
510 If the attribute is already set, the previous value is
511 replaced. The attributes are set to any future requests
512 created by this factory.
513
514 \sa attribute(), clearAttribute(), clearAttributes(),
515 QNetworkRequest::Attribute
516*/
517void QNetworkRequestFactory::setAttribute(QNetworkRequest::Attribute attribute,
518 const QVariant &value)
519{
520 if (attribute == QNetworkRequest::HttpStatusCodeAttribute
521 || attribute == QNetworkRequest::HttpReasonPhraseAttribute
522 || attribute == QNetworkRequest::RedirectionTargetAttribute
523 || attribute == QNetworkRequest::ConnectionEncryptedAttribute
524 || attribute == QNetworkRequest::SourceIsFromCacheAttribute
525 || attribute == QNetworkRequest::HttpPipeliningWasUsedAttribute
526 || attribute == QNetworkRequest::Http2WasUsedAttribute
527 || attribute == QNetworkRequest::OriginalContentLengthAttribute)
528 {
529 qCWarning(lcQrequestfactory, "%i is a reply-only attribute, ignoring.", attribute);
530 return;
531 }
532 d.detach();
533 d->attributes.insert(attribute, value);
534}
535
536/*!
537 \since 6.8
538
539 Returns the value associated with \a attribute. If the
540 attribute has not been set, returns a default-constructed \l QVariant.
541
542 \sa attribute(QNetworkRequest::Attribute, const QVariant &),
543 setAttribute(), clearAttributes(), QNetworkRequest::Attribute
544
545*/
546QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute) const
547{
548 return d->attributes.value(attribute);
549}
550
551/*!
552 \since 6.8
553
554 Returns the value associated with \a attribute. If the
555 attribute has not been set, returns \a defaultValue.
556
557 \sa attribute(), setAttribute(), clearAttributes(),
558 QNetworkRequest::Attribute
559*/
560QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute,
561 const QVariant &defaultValue) const
562{
563 return d->attributes.value(attribute, defaultValue);
564}
565
566/*!
567 \since 6.8
568
569 Clears \a attribute set to this factory.
570
571 \sa attribute(), setAttribute()
572*/
573void QNetworkRequestFactory::clearAttribute(QNetworkRequest::Attribute attribute)
574{
575 if (!d->attributes.contains(attribute))
576 return;
577 d.detach();
578 d->attributes.remove(attribute);
579}
580
581/*!
582 \since 6.8
583
584 Clears any attributes set to this factory.
585
586 \sa attribute(), setAttribute()
587*/
589{
590 if (d->attributes.isEmpty())
591 return;
592 d.detach();
593 d->attributes.clear();
594}
595
596QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate()
597 = default;
598
599QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate(const QUrl &baseUrl)
600 : baseUrl(baseUrl)
601{
602}
603
604QNetworkRequest QNetworkRequestFactoryPrivate::newRequest(const QUrl &url) const
605{
606 QNetworkRequest request;
607 request.setUrl(url);
608#if QT_CONFIG(ssl)
609 if (!sslConfig.isNull())
610 request.setSslConfiguration(sslConfig);
611#endif
612 auto h = headers;
613 constexpr char Bearer[] = "Bearer ";
614 if (!bearerToken.isEmpty())
615 h.replaceOrAppend(QHttpHeaders::WellKnownHeader::Authorization, Bearer + bearerToken);
616 request.setHeaders(std::move(h));
617
618 request.setTransferTimeout(transferTimeout);
619 request.setPriority(priority);
620
621 for (const auto &[attribute, value] : attributes.asKeyValueRange())
622 request.setAttribute(attribute, value);
623
624 return request;
625}
626
627QUrl QNetworkRequestFactoryPrivate::requestUrl(const QString *path,
628 const QUrlQuery *query) const
629{
630 const QUrl providedPath = path ? QUrl(*path) : QUrl{};
631 const QUrlQuery providedQuery = query ? *query : QUrlQuery();
632
633 if (!providedPath.scheme().isEmpty() || !providedPath.host().isEmpty()) {
634 qCWarning(lcQrequestfactory, "The provided path %ls may only contain path and query item "
635 "components, and other parts will be ignored. Set the baseUrl instead",
636 qUtf16Printable(providedPath.toDisplayString()));
637 }
638
639 QUrl resultUrl = baseUrl;
640 QUrlQuery resultQuery(providedQuery);
641 QString basePath = baseUrl.path(QUrl::ComponentFormattingOption::FullyEncoded);
642
643 resultUrl.setUserName(userName);
644 resultUrl.setPassword(password);
645
646 // Separate the path and query parameters components on the application-provided path
647 const QString requestPath{providedPath.path(QUrl::ComponentFormattingOption::FullyEncoded)};
648 const QUrlQuery pathQueryItems{providedPath};
649
650 if (!pathQueryItems.isEmpty()) {
651 // Add any query items provided as part of the path
652 const auto items = pathQueryItems.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
653 for (const auto &[key, value]: items)
654 resultQuery.addQueryItem(key, value);
655 }
656
657 if (!queryParameters.isEmpty()) {
658 // Add any query items set to this factory
659 const QList<std::pair<QString,QString>> items =
660 queryParameters.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
661 for (const auto &item: items)
662 resultQuery.addQueryItem(item.first, item.second);
663 }
664
665 if (!resultQuery.isEmpty())
666 resultUrl.setQuery(resultQuery);
667
668 if (requestPath.isEmpty())
669 return resultUrl;
670
671 // Ensure that the "base path" (the path that may be present
672 // in the baseUrl), and the request path are joined with one '/'
673 // If both have it, remove one, if neither has it, add one
674 if (basePath.endsWith(u'/') && requestPath.startsWith(u'/'))
675 basePath.chop(1);
676 else if (!requestPath.startsWith(u'/') && !basePath.endsWith(u'/'))
677 basePath.append(u'/');
678
679 resultUrl.setPath(basePath.append(requestPath), QUrl::StrictMode);
680
681 return resultUrl;
682}
683
684#ifndef QT_NO_DEBUG_STREAM
685/*!
686 \fn QDebug QNetworkRequestFactory::operator<<(QDebug debug,
687 const QNetworkRequestFactory &factory)
688
689 Writes \a factory into \a debug stream.
690
691 \sa {Debugging Techniques}
692*/
693QDebug operator<<(QDebug debug, const QNetworkRequestFactory &factory)
694{
695 const QDebugStateSaver saver(debug);
696 debug.resetFormat().nospace();
697
698 debug << "QNetworkRequestFactory(baseUrl = " << factory.baseUrl()
699 << ", headers = " << factory.commonHeaders()
700 << ", queryParameters = " << factory.queryParameters().queryItems()
701 << ", bearerToken = " << (factory.bearerToken().isEmpty() ? "(empty)" : "(is set)")
702 << ", transferTimeout = " << factory.transferTimeout()
703 << ", userName = " << (factory.userName().isEmpty() ? "(empty)" : "(is set)")
704 << ", password = " << (factory.password().isEmpty() ? "(empty)" : "(is set)")
705#if QT_CONFIG(ssl)
706 << ", SSL configuration"
707 << (factory.sslConfiguration().isNull() ? " is not set (default)" : " is set")
708#else
709 << ", no SSL support"
710#endif
711 << ")";
712 return debug;
713}
714#endif // QT_NO_DEBUG_STREAM
715
716QT_END_NAMESPACE
Convenience class for grouping remote server endpoints that share common network request properties.
Q_NETWORK_EXPORT void setQueryParameters(const QUrlQuery &query)
Sets query parameters that are added to individual requests' query parameters.
Q_NETWORK_EXPORT void clearUserName()
Clears the username set to this factory.
Q_NETWORK_EXPORT void setBaseUrl(const QUrl &url)
Sets the base URL used in individual requests to url.
Q_NETWORK_EXPORT ~QNetworkRequestFactory()
Destroys this QNetworkRequestFactory object.
Q_NETWORK_EXPORT void clearBearerToken()
Clears the bearer token.
Q_NETWORK_EXPORT void setBearerToken(const QByteArray &token)
Sets the bearer token to token.
Q_NETWORK_EXPORT void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value)
Q_NETWORK_EXPORT void clearQueryParameters()
Clears the query parameters.
Q_NETWORK_EXPORT void clearPassword()
Clears the password set to this factory.
Q_NETWORK_EXPORT void clearCommonHeaders()
Clears current headers.
Q_NETWORK_EXPORT void clearAttributes()
Q_NETWORK_EXPORT void setUserName(const QString &userName)
Sets the username of this factory to userName.
Q_NETWORK_EXPORT void setTransferTimeout(std::chrono::milliseconds timeout)
Sets timeout used for transfers.
Q_NETWORK_EXPORT void setPassword(const QString &password)
Sets the password of this factory to password.
Q_NETWORK_EXPORT void setPriority(QNetworkRequest::Priority priority)
Q_NETWORK_EXPORT void setCommonHeaders(const QHttpHeaders &headers)
Sets headers that are common to all requests.
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")