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