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
qrestaccessmanager.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#include "qrestreply.h"
8
9#include <QtNetwork/qhttpmultipart.h>
10#include <QtNetwork/qnetworkaccessmanager.h>
11#include <QtNetwork/qnetworkreply.h>
12
13#include <QtCore/qjsondocument.h>
14#include <QtCore/qjsonobject.h>
15#include <QtCore/qloggingcategory.h>
16#include <QtCore/qthread.h>
17
18QT_BEGIN_NAMESPACE
19
20using namespace Qt::StringLiterals;
21
22Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
23
24/*!
25
26 \class QRestAccessManager
27 \brief The QRestAccessManager is a convenience wrapper for
28 QNetworkAccessManager.
29 \since 6.7
30
31 \ingroup network
32 \inmodule QtNetwork
33 \reentrant
34
35 QRestAccessManager is a convenience wrapper on top of
36 QNetworkAccessManager. It amends datatypes and HTTP methods
37 that are useful for typical RESTful client applications.
38
39 The usual Qt networking features are accessible by configuring the
40 wrapped QNetworkAccessManager directly. QRestAccessManager does not
41 take ownership of the wrapped QNetworkAccessManager.
42
43 QRestAccessManager and related QRestReply classes can only be used in the
44 thread they live in. See \l {QObject#Thread Affinity}{QObject thread affinity}
45 for more information.
46
47 \section1 Issuing Network Requests and Handling Replies
48
49 Network requests are initiated with a function call corresponding to
50 the desired HTTP method, such as \c get() and \c post().
51
52 \section2 Using Signals and Slots
53
54 The function returns a QNetworkReply* object, whose signals can be used
55 to follow up on the completion of the request in a traditional
56 Qt-signals-and-slots way.
57
58 Here's an example of how you could send a GET request and handle the
59 response:
60
61 \snippet code/src_network_access_qrestaccessmanager.cpp 0
62
63 \section2 Using Callbacks and Context Objects
64
65 The functions also take a context object of QObject (subclass) type
66 and a callback function as parameters. The callback takes one QRestReply&
67 as a parameter. The callback can be any callable, including a
68 pointer-to-member-function.
69
70 These callbacks are invoked when the reply has finished processing
71 (also in the case the processing finished due to an error).
72
73 The context object can be \c nullptr, although, generally speaking,
74 this is discouraged. Using a valid context object ensures that if the
75 context object is destroyed during request processing, the callback will
76 not be called. Stray callbacks which access a destroyed context is a source
77 of application misbehavior.
78
79 Here's an example of how you could send a GET request and check the
80 response:
81
82 \snippet code/src_network_access_qrestaccessmanager.cpp 1
83
84 Many of the functions take in data for sending to a server. The data is
85 supplied as the second parameter after the request.
86
87 Here's an example of how you could send a POST request and check the
88 response:
89
90 \snippet code/src_network_access_qrestaccessmanager.cpp 2
91
92 The provided QRestReply& is valid only while the callback
93 executes. If you need it for longer, you can either move it
94 to another QRestReply, or construct a new one and initialize
95 it with the QNetworkReply (see QRestReply::networkReply()).
96
97 \section2 Supported data types
98
99 The following table summarizes the methods and the supported data types.
100 \c X means support.
101
102 \table
103 \header
104 \li Data type
105 \li \c get()
106 \li \c post()
107 \li \c put()
108 \li \c head()
109 \li \c patch()
110 \li \c deleteResource()
111 \li \c sendCustomRequest()
112 \row
113 \li No data
114 \li X
115 \li -
116 \li -
117 \li X
118 \li -
119 \li X
120 \li -
121 \row
122 \li QByteArray
123 \li X
124 \li X
125 \li X
126 \li -
127 \li X
128 \li -
129 \li X
130 \row
131 \li QJsonDocument *)
132 \li X
133 \li X
134 \li X
135 \li -
136 \li X
137 \li -
138 \li -
139 \row
140 \li QVariantMap **)
141 \li -
142 \li X
143 \li X
144 \li -
145 \li X
146 \li -
147 \li -
148 \row
149 \li QHttpMultiPart
150 \li -
151 \li X
152 \li X
153 \li -
154 \li -
155 \li -
156 \li X
157 \row
158 \li QIODevice
159 \li X
160 \li X
161 \li X
162 \li -
163 \li X
164 \li -
165 \li X
166 \endtable
167
168 *) QJsonDocument is sent in \l QJsonDocument::Compact format,
169 and the \c Content-Type header is set to \c {application/json} if the
170 \c Content-Type header was not set
171
172 **) QVariantMap is converted to and treated as a QJsonObject
173
174 \sa QRestReply, QNetworkRequestFactory, QNetworkAccessManager
175*/
176
177/*!
178 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
179 const QNetworkRequest &request,
180 const ContextTypeForFunctor<Functor> *context,
181 Functor &&callback)
182
183 Issues an \c {HTTP GET} based on \a request.
184
185 The optional \a callback and \a context object can be provided for
186 handling the request completion as illustrated below:
187
188 \snippet code/src_network_access_qrestaccessmanager.cpp 3
189
190 Alternatively the signals of the returned QNetworkReply* object can be
191 used. For further information see
192 \l {Issuing Network Requests and Handling Replies}.
193
194 \sa QRestReply
195*/
196
197/*!
198 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
199 const QNetworkRequest &request, const QByteArray &data,
200 const ContextTypeForFunctor<Functor> *context,
201 Functor &&callback)
202
203 Issues an \c {HTTP GET} based on \a request and provided \a data.
204
205 The optional \a callback and \a context object can be provided for
206 handling the request completion as illustrated below:
207
208 \snippet code/src_network_access_qrestaccessmanager.cpp 4
209
210 Alternatively the signals of the returned QNetworkReply* object can be
211 used. For further information see
212 \l {Issuing Network Requests and Handling Replies}.
213
214 \sa QRestReply
215*/
216
217/*!
218 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
219 const QNetworkRequest &request, const QJsonDocument &data,
220 const ContextTypeForFunctor<Functor> *context,
221 Functor &&callback)
222
223 \overload
224*/
225
226/*!
227 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
228 const QNetworkRequest &request, QIODevice *data,
229 const ContextTypeForFunctor<Functor> *context,
230 Functor &&callback)
231
232 \overload
233*/
234
235/*!
236 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
237 const QNetworkRequest &request, const QJsonDocument &data,
238 const ContextTypeForFunctor<Functor> *context,
239 Functor &&callback)
240
241 Issues an \c {HTTP POST} based on \a request.
242
243 The optional \a callback and \a context object can be provided for
244 handling the request completion as illustrated below:
245
246 \snippet code/src_network_access_qrestaccessmanager.cpp 5
247
248 Alternatively, the signals of the returned QNetworkReply* object can be
249 used. For further information see
250 \l {Issuing Network Requests and Handling Replies}.
251
252 The \c post() method always requires \a data parameter. The following
253 data types are supported:
254 \list
255 \li QByteArray
256 \li QJsonDocument *)
257 \li QVariantMap **)
258 \li QHttpMultiPart*
259 \li QIODevice*
260 \endlist
261
262 *) Sent in \l QJsonDocument::Compact format, and the
263 \c Content-Type header is set to \c {application/json} if the
264 \c Content-Type header was not set
265 **) QVariantMap is converted to and treated as a QJsonObject
266
267 \sa QRestReply
268*/
269
270/*!
271
272 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
273 const QNetworkRequest &request, const QVariantMap &data,
274 const ContextTypeForFunctor<Functor> *context,
275 Functor &&callback)
276
277 \overload
278*/
279
280/*!
281 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
282 const QNetworkRequest &request, const QByteArray &data,
283 const ContextTypeForFunctor<Functor> *context,
284 Functor &&callback)
285
286 \overload
287*/
288
289/*!
290 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
291 const QNetworkRequest &request, QHttpMultiPart *data,
292 const ContextTypeForFunctor<Functor> *context,
293 Functor &&callback)
294
295 \overload
296*/
297
298/*!
299 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
300 const QNetworkRequest &request, QIODevice *data,
301 const ContextTypeForFunctor<Functor> *context,
302 Functor &&callback)
303
304 \overload
305*/
306
307/*!
308 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
309 const QNetworkRequest &request, const QJsonDocument &data,
310 const ContextTypeForFunctor<Functor> *context,
311 Functor &&callback)
312
313 Issues an \c {HTTP PUT} based on \a request.
314
315 The optional \a callback and \a context object can be provided for
316 handling the request completion as illustrated below:
317
318 \snippet code/src_network_access_qrestaccessmanager.cpp 6
319
320 Alternatively the signals of the returned QNetworkReply* object can be
321 used. For further information see
322 \l {Issuing Network Requests and Handling Replies}.
323
324 The \c put() method always requires \a data parameter. The following
325 data types are supported:
326 \list
327 \li QByteArray
328 \li QJsonDocument *)
329 \li QVariantMap **)
330 \li QHttpMultiPart*
331 \li QIODevice*
332 \endlist
333
334 *) Sent in \l QJsonDocument::Compact format, and the
335 \c Content-Type header is set to \c {application/json} if the
336 \c Content-Type header was not set
337 **) QVariantMap is converted to and treated as a QJsonObject
338
339 \sa QRestReply
340*/
341
342/*!
343 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
344 const QNetworkRequest &request, const QVariantMap &data,
345 const ContextTypeForFunctor<Functor> *context,
346 Functor &&callback)
347
348 \overload
349*/
350
351/*!
352 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
353 const QNetworkRequest &request, const QByteArray &data,
354 const ContextTypeForFunctor<Functor> *context,
355 Functor &&callback)
356
357 \overload
358*/
359
360/*!
361 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
362 const QNetworkRequest &request, QHttpMultiPart *data,
363 const ContextTypeForFunctor<Functor> *context,
364 Functor &&callback)
365
366 \overload
367*/
368
369/*!
370 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
371 const QNetworkRequest &request, QIODevice *data,
372 const ContextTypeForFunctor<Functor> *context,
373 Functor &&callback)
374
375 \overload
376*/
377
378/*!
379 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
380 const QNetworkRequest &request, const QJsonDocument &data,
381 const ContextTypeForFunctor<Functor> *context,
382 Functor &&callback)
383
384 Issues an \c {HTTP PATCH} based on \a request.
385
386 The optional \a callback and \a context object can be provided for
387 handling the request completion as illustrated below:
388
389 \snippet code/src_network_access_qrestaccessmanager.cpp 10
390
391 Alternatively the signals of the returned QNetworkReply* object can be
392 used. For further information see
393 \l {Issuing Network Requests and Handling Replies}.
394
395 The \c patch() method always requires \a data parameter. The following
396 data types are supported:
397 \list
398 \li QByteArray
399 \li QJsonDocument *)
400 \li QVariantMap **)
401 \li QIODevice*
402 \endlist
403
404 *) Sent in \l QJsonDocument::Compact format, and the
405 \c Content-Type header is set to \c {application/json} if the
406 \c Content-Type header was not set
407 **) QVariantMap is converted to and treated as a QJsonObject
408
409 \sa QRestReply
410*/
411
412/*!
413 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
414 const QNetworkRequest &request, const QVariantMap &data,
415 const ContextTypeForFunctor<Functor> *context,
416 Functor &&callback)
417
418 \overload
419*/
420
421/*!
422 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
423 const QNetworkRequest &request, const QByteArray &data,
424 const ContextTypeForFunctor<Functor> *context,
425 Functor &&callback)
426
427 \overload
428*/
429
430/*!
431 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
432 const QNetworkRequest &request, QIODevice *data,
433 const ContextTypeForFunctor<Functor> *context,
434 Functor &&callback)
435
436 \overload
437*/
438
439/*!
440 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::head(
441 const QNetworkRequest &request,
442 const ContextTypeForFunctor<Functor> *context,
443 Functor &&callback)
444
445 Issues an \c {HTTP HEAD} based on \a request.
446
447 The optional \a callback and \a context object can be provided for
448 handling the request completion as illustrated below:
449
450 \snippet code/src_network_access_qrestaccessmanager.cpp 7
451
452 Alternatively the signals of the returned QNetworkReply* object can be
453 used. For further information see
454 \l {Issuing Network Requests and Handling Replies}.
455
456 \c head() request does not support providing data.
457
458 \sa QRestReply
459*/
460
461/*!
462 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::deleteResource(
463 const QNetworkRequest &request,
464 const ContextTypeForFunctor<Functor> *context,
465 Functor &&callback)
466
467 Issues an \c {HTTP DELETE} based on \a request.
468
469 The optional \a callback and \a context object can be provided for
470 handling the request completion as illustrated below:
471
472 \snippet code/src_network_access_qrestaccessmanager.cpp 8
473
474 Alternatively the signals of the returned QNetworkReply* object can be
475 used. For further information see
476 \l {Issuing Network Requests and Handling Replies}.
477
478 \c deleteResource() request does not support providing data.
479
480 \sa QRestReply
481*/
482
483/*!
484 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
485 const QNetworkRequest& request, const QByteArray &method, const QByteArray &data,
486 const ContextTypeForFunctor<Functor> *context,
487 Functor &&callback)
488
489 Issues \a request based HTTP request with custom \a method and the
490 provided \a data.
491
492 The optional \a callback and \a context object can be provided for
493 handling the request completion as illustrated below:
494
495 \snippet code/src_network_access_qrestaccessmanager.cpp 9
496
497 Alternatively the signals of the returned QNetworkReply* object can be
498 used. For further information see
499 \l {Issuing Network Requests and Handling Replies}.
500
501*/
502
503/*!
504 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
505 const QNetworkRequest& request, const QByteArray &method, QIODevice *data,
506 const ContextTypeForFunctor<Functor> *context,
507 Functor &&callback)
508
509 \overload
510*/
511
512/*!
513 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
514 const QNetworkRequest& request, const QByteArray &method, QHttpMultiPart *data,
515 const ContextTypeForFunctor<Functor> *context,
516 Functor &&callback)
517
518 \overload
519*/
520
521/*!
522 Constructs a QRestAccessManager object and sets \a parent as the parent
523 object, and \a manager as the underlying QNetworkAccessManager which
524 is used for communication.
525
526 \sa networkAccessManager()
527*/
528
529QRestAccessManager::QRestAccessManager(QNetworkAccessManager *manager, QObject *parent)
530 : QObject(*new QRestAccessManagerPrivate, parent)
531{
532 Q_D(QRestAccessManager);
533 d->qnam = manager;
534 if (!d->qnam)
535 qCWarning(lcQrest, "QRestAccessManager: QNetworkAccesManager is nullptr");
536}
537
538/*!
539 Destroys the QRestAccessManager object.
540*/
541QRestAccessManager::~QRestAccessManager()
542 = default;
543
544/*!
545 Returns the underlying QNetworkAccessManager instance.
546
547 \sa QNetworkAccessManager
548*/
549QNetworkAccessManager *QRestAccessManager::networkAccessManager() const
550{
551 Q_D(const QRestAccessManager);
552 return d->qnam;
553}
554
556 = default;
557
559{
560 if (!activeRequests.isEmpty()) {
561 qCWarning(lcQrest, "Access manager destroyed while %lld requests were still in progress",
562 qlonglong(activeRequests.size()));
563 }
564}
565
566QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
567 const QJsonDocument &data, const QObject *context,
568 QtPrivate::QSlotObjectBase *slot)
569{
570 Q_D(QRestAccessManager);
571 return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->post(req, data); },
572 data, request, context, slot);
573}
574
575QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
576 const QVariantMap &data, const QObject *context,
577 QtPrivate::QSlotObjectBase *slot)
578{
579 return postWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
580}
581
582QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
583 const QByteArray &data, const QObject *context,
584 QtPrivate::QSlotObjectBase *slot)
585{
586 Q_D(QRestAccessManager);
587 return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
588}
589
590QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
591 QHttpMultiPart *data, const QObject *context,
592 QtPrivate::QSlotObjectBase *slot)
593{
594 Q_D(QRestAccessManager);
595 return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
596}
597
598QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
599 QIODevice *data, const QObject *context,
600 QtPrivate::QSlotObjectBase *slot)
601{
602 Q_D(QRestAccessManager);
603 return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
604}
605
606QNetworkReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request,
607 const QObject *context, QtPrivate::QSlotObjectBase *slot)
608{
609 Q_D(QRestAccessManager);
610 return d->executeRequest([&](auto qnam) { return qnam->get(request); }, context, slot);
611}
612
613QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
614 const QByteArray &data, const QObject *context,
615 QtPrivate::QSlotObjectBase *slot)
616{
617 Q_D(QRestAccessManager);
618 return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
619}
620
621QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
622 const QJsonDocument &data, const QObject *context,
623 QtPrivate::QSlotObjectBase *slot)
624{
625 Q_D(QRestAccessManager);
626 return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->get(req, data); },
627 data, request, context, slot);
628}
629
630QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
631 QIODevice *data, const QObject *context,
632 QtPrivate::QSlotObjectBase *slot)
633{
634 Q_D(QRestAccessManager);
635 return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
636}
637
638QNetworkReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request,
639 const QObject *context, QtPrivate::QSlotObjectBase *slot)
640{
641 Q_D(QRestAccessManager);
642 return d->executeRequest([&](auto qnam) { return qnam->deleteResource(request); }, context, slot);
643}
644
645QNetworkReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request,
646 const QObject *context, QtPrivate::QSlotObjectBase *slot)
647{
648 Q_D(QRestAccessManager);
649 return d->executeRequest([&](auto qnam) { return qnam->head(request); }, context, slot);
650}
651
652QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
653 const QJsonDocument &data, const QObject *context,
654 QtPrivate::QSlotObjectBase *slot)
655{
656 Q_D(QRestAccessManager);
657 return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->put(req, data); },
658 data, request, context, slot);
659}
660
661QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
662 const QVariantMap &data, const QObject *context,
663 QtPrivate::QSlotObjectBase *slot)
664{
665 return putWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
666}
667
668QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
669 const QByteArray &data, const QObject *context,
670 QtPrivate::QSlotObjectBase *slot)
671{
672 Q_D(QRestAccessManager);
673 return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
674}
675
676QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
677 QHttpMultiPart *data, const QObject *context,
678 QtPrivate::QSlotObjectBase *slot)
679{
680 Q_D(QRestAccessManager);
681 return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
682}
683
684QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data,
685 const QObject *context, QtPrivate::QSlotObjectBase *slot)
686{
687 Q_D(QRestAccessManager);
688 return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
689}
690
691static const QByteArray& PATCH()
692{
693 static auto patch = "PATCH"_ba;
694 return patch;
695}
696
697QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
698 const QJsonDocument &data, const QObject *context,
699 QtPrivate::QSlotObjectBase *slot)
700{
701 Q_D(QRestAccessManager);
702 return d->executeRequest(
703 [](auto qnam, auto req, auto data) { return qnam->sendCustomRequest(req, PATCH(), data); },
704 data, request, context, slot);
705}
706
707QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
708 const QVariantMap &data, const QObject *context,
709 QtPrivate::QSlotObjectBase *slot)
710{
711 return patchWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
712}
713
714QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
715 const QByteArray &data, const QObject *context,
716 QtPrivate::QSlotObjectBase *slot)
717{
718 Q_D(QRestAccessManager);
719 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH(), data); },
720 context, slot);
721}
722
723QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
724 const QObject *context, QtPrivate::QSlotObjectBase *slot)
725{
726 Q_D(QRestAccessManager);
727 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH(), data); },
728 context, slot);
729}
730
731QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
732 const QByteArray& method, const QByteArray &data,
733 const QObject *context,
734 QtPrivate::QSlotObjectBase *slot)
735{
736 Q_D(QRestAccessManager);
737 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
738 context, slot);
739}
740
741QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
742 const QByteArray& method, QIODevice *data,
743 const QObject *context,
744 QtPrivate::QSlotObjectBase *slot)
745{
746 Q_D(QRestAccessManager);
747 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
748 context, slot);
749}
750
751QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
752 const QByteArray& method, QHttpMultiPart *data,
753 const QObject *context,
754 QtPrivate::QSlotObjectBase *slot)
755{
756 Q_D(QRestAccessManager);
757 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
758 context, slot);
759}
760
762 const QObject *contextObject,
763 QtPrivate::SlotObjUniquePtr slot)
764{
765 Q_Q(QRestAccessManager);
766 Q_ASSERT(reply);
767 [[maybe_unused]] const auto [_, inserted] =
768 activeRequests.try_emplace(reply, CallerInfo{contextObject, std::move(slot)});
769 Q_ASSERT(inserted); // if this fails, we have an ABA problem...
770
771 // The signal connections below are made to 'q' to avoid stray signal
772 // handling upon its destruction while requests were still in progress
773
774 QObject::connect(reply, &QNetworkReply::finished, q, [reply, this]() {
775 handleReplyFinished(reply);
776 });
777 // Safe guard in case reply is destroyed before it's finished
778 QObject::connect(reply, &QObject::destroyed, q, [reply, this]() {
779 activeRequests.remove(reply);
780 });
781 // If context object is destroyed, clean up any possible replies it had associated with it
782 if (contextObject) {
783 QObject::connect(contextObject, &QObject::destroyed, q, [reply, this]() {
784 activeRequests.remove(reply);
785 });
786 }
787 return reply;
788}
789
790void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject)
791{
792 Q_Q(QRestAccessManager);
793 if (QThread::currentThread() != q->thread()) {
794 qCWarning(lcQrest, "QRestAccessManager can only be called in the thread it belongs to");
795 Q_ASSERT(false);
796 }
797 if (contextObject && (contextObject->thread() != q->thread())) {
798 qCWarning(lcQrest, "QRestAccessManager: the context object must reside in the same thread");
799 Q_ASSERT(false);
800 }
801}
802
803Q_DECL_COLD_FUNCTION
804static QNetworkReply* warnNoAccessManager()
805{
806 qCWarning(lcQrest, "QRestAccessManager: QNetworkAccessManager not set");
807 return nullptr;
808}
809
810void QRestAccessManagerPrivate::handleReplyFinished(QNetworkReply *reply)
811{
812 auto request = activeRequests.find(reply);
813 if (request == activeRequests.end()) {
814 qCDebug(lcQrest, "QRestAccessManager: Unexpected reply received, ignoring");
815 return;
816 }
817
818 CallerInfo caller = std::move(request.value());
819 activeRequests.erase(request);
820
821 if (caller.slot) {
822 // Callback was provided
823 QRestReply restReply(reply);
824 void *argv[] = { nullptr, &restReply };
825 // If we have context object, use it
826 QObject *context = caller.contextObject
827 ? const_cast<QObject*>(caller.contextObject.get()) : nullptr;
828 caller.slot->call(context, argv);
829 }
830}
831
833QRestAccessManagerPrivate::executeRequest(ReqOpRef requestOperation, const QObject *context,
834 QtPrivate::QSlotObjectBase *rawSlot)
835{
836 QtPrivate::SlotObjUniquePtr slot(rawSlot);
837 if (!qnam)
838 return warnNoAccessManager();
839 verifyThreadAffinity(context);
840 QNetworkReply *reply = requestOperation(qnam);
841 return createActiveRequest(reply, context, std::move(slot));
842}
843
844QNetworkReply *
845QRestAccessManagerPrivate::executeRequest(ReqOpRefJson requestOperation, const QJsonDocument &jsonDoc,
846 const QNetworkRequest &request, const QObject *context,
847 QtPrivate::QSlotObjectBase *rawSlot)
848{
849 QtPrivate::SlotObjUniquePtr slot(rawSlot);
850 if (!qnam)
851 return warnNoAccessManager();
852 verifyThreadAffinity(context);
853 QNetworkRequest req(request);
854 auto h = req.headers();
855 if (!h.contains(QHttpHeaders::WellKnownHeader::ContentType)) {
856 h.append(QHttpHeaders::WellKnownHeader::ContentType,
857 QLatin1StringView{"application/json"});
858 }
859 req.setHeaders(std::move(h));
860 QNetworkReply *reply = requestOperation(qnam, req, jsonDoc.toJson(QJsonDocument::Compact));
861 return createActiveRequest(reply, context, std::move(slot));
862}
863
864QT_END_NAMESPACE
865
866#include "moc_qrestaccessmanager.cpp"
QNetworkReply * executeRequest(ReqOpRef requestOperation, const QObject *context, QtPrivate::QSlotObjectBase *rawSlot)
QNetworkReply * createActiveRequest(QNetworkReply *reply, const QObject *contextObject, QtPrivate::SlotObjUniquePtr slot)
void verifyThreadAffinity(const QObject *contextObject)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
static const QByteArray & PATCH()