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 \fn QNetworkReply *QRestAccessManager::get(const QNetworkRequest &request)
183
184 Issues an \c {HTTP GET} based on \a request.
185
186 The optional \a callback and \a context object can be provided for
187 handling the request completion as illustrated below:
188
189 \snippet code/src_network_access_qrestaccessmanager.cpp 3
190
191 Alternatively the signals of the returned QNetworkReply* object can be
192 used. For further information see
193 \l {Issuing Network Requests and Handling Replies}.
194
195 \sa QRestReply
196*/
197
198/*!
199 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
200 const QNetworkRequest &request, const QByteArray &data,
201 const ContextTypeForFunctor<Functor> *context,
202 Functor &&callback)
203 \fn QNetworkReply *QRestAccessManager::get(const QNetworkRequest &request, const QByteArray &data)
204
205 Issues an \c {HTTP GET} based on \a request and provided \a data.
206
207 The optional \a callback and \a context object can be provided for
208 handling the request completion as illustrated below:
209
210 \snippet code/src_network_access_qrestaccessmanager.cpp 4
211
212 Alternatively the signals of the returned QNetworkReply* object can be
213 used. For further information see
214 \l {Issuing Network Requests and Handling Replies}.
215
216 \sa QRestReply
217*/
218
219/*!
220 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
221 const QNetworkRequest &request, const QJsonDocument &data,
222 const ContextTypeForFunctor<Functor> *context,
223 Functor &&callback)
224 \fn QNetworkReply *QRestAccessManager::get(const QNetworkRequest &request, const QJsonDocument &data)
225
226 \overload
227*/
228
229/*!
230 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
231 const QNetworkRequest &request, QIODevice *data,
232 const ContextTypeForFunctor<Functor> *context,
233 Functor &&callback)
234 \fn QNetworkReply *QRestAccessManager::get(const QNetworkRequest &request, QIODevice *data)
235
236 \overload
237*/
238
239/*!
240 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
241 const QNetworkRequest &request, const QJsonDocument &data,
242 const ContextTypeForFunctor<Functor> *context,
243 Functor &&callback)
244 \fn QNetworkReply *QRestAccessManager::post(const QNetworkRequest &request, const QJsonDocument &data)
245
246 Issues an \c {HTTP POST} based on \a request.
247
248 The optional \a callback and \a context object can be provided for
249 handling the request completion as illustrated below:
250
251 \snippet code/src_network_access_qrestaccessmanager.cpp 5
252
253 Alternatively, the signals of the returned QNetworkReply* object can be
254 used. For further information see
255 \l {Issuing Network Requests and Handling Replies}.
256
257 The \c post() method always requires \a data parameter. The following
258 data types are supported:
259 \list
260 \li QByteArray
261 \li QJsonDocument *)
262 \li QVariantMap **)
263 \li QHttpMultiPart*
264 \li QIODevice*
265 \endlist
266
267 *) Sent in \l QJsonDocument::Compact format, and the
268 \c Content-Type header is set to \c {application/json} if the
269 \c Content-Type header was not set
270 **) QVariantMap is converted to and treated as a QJsonObject
271
272 \sa QRestReply
273*/
274
275/*!
276
277 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
278 const QNetworkRequest &request, const QVariantMap &data,
279 const ContextTypeForFunctor<Functor> *context,
280 Functor &&callback)
281 \fn QNetworkReply *QRestAccessManager::post(const QNetworkRequest &request, const QVariantMap &data)
282
283 \overload
284*/
285
286/*!
287 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
288 const QNetworkRequest &request, const QByteArray &data,
289 const ContextTypeForFunctor<Functor> *context,
290 Functor &&callback)
291 \fn QNetworkReply *QRestAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
292
293 \overload
294*/
295
296/*!
297 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
298 const QNetworkRequest &request, QHttpMultiPart *data,
299 const ContextTypeForFunctor<Functor> *context,
300 Functor &&callback)
301 \fn QNetworkReply *QRestAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *data)
302
303 \overload
304*/
305
306/*!
307 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
308 const QNetworkRequest &request, QIODevice *data,
309 const ContextTypeForFunctor<Functor> *context,
310 Functor &&callback)
311 \fn QNetworkReply *QRestAccessManager::post(const QNetworkRequest &request, QIODevice *data)
312
313 \overload
314*/
315
316/*!
317 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
318 const QNetworkRequest &request, const QJsonDocument &data,
319 const ContextTypeForFunctor<Functor> *context,
320 Functor &&callback)
321 \fn QNetworkReply *QRestAccessManager::put(const QNetworkRequest &request, const QJsonDocument &data)
322
323 Issues an \c {HTTP PUT} based on \a request.
324
325 The optional \a callback and \a context object can be provided for
326 handling the request completion as illustrated below:
327
328 \snippet code/src_network_access_qrestaccessmanager.cpp 6
329
330 Alternatively the signals of the returned QNetworkReply* object can be
331 used. For further information see
332 \l {Issuing Network Requests and Handling Replies}.
333
334 The \c put() method always requires \a data parameter. The following
335 data types are supported:
336 \list
337 \li QByteArray
338 \li QJsonDocument *)
339 \li QVariantMap **)
340 \li QHttpMultiPart*
341 \li QIODevice*
342 \endlist
343
344 *) Sent in \l QJsonDocument::Compact format, and the
345 \c Content-Type header is set to \c {application/json} if the
346 \c Content-Type header was not set
347 **) QVariantMap is converted to and treated as a QJsonObject
348
349 \sa QRestReply
350*/
351
352/*!
353 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
354 const QNetworkRequest &request, const QVariantMap &data,
355 const ContextTypeForFunctor<Functor> *context,
356 Functor &&callback)
357 \fn QNetworkReply *QRestAccessManager::put(const QNetworkRequest &request, const QVariantMap &data)
358
359 \overload
360*/
361
362/*!
363 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
364 const QNetworkRequest &request, const QByteArray &data,
365 const ContextTypeForFunctor<Functor> *context,
366 Functor &&callback)
367 \fn QNetworkReply *QRestAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
368
369 \overload
370*/
371
372/*!
373 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
374 const QNetworkRequest &request, QHttpMultiPart *data,
375 const ContextTypeForFunctor<Functor> *context,
376 Functor &&callback)
377 \fn QNetworkReply *QRestAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *data)
378
379 \overload
380*/
381
382/*!
383 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
384 const QNetworkRequest &request, QIODevice *data,
385 const ContextTypeForFunctor<Functor> *context,
386 Functor &&callback)
387 \fn QNetworkReply *QRestAccessManager::put(const QNetworkRequest &request, QIODevice *data)
388
389 \overload
390*/
391
392/*!
393 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
394 const QNetworkRequest &request, const QJsonDocument &data,
395 const ContextTypeForFunctor<Functor> *context,
396 Functor &&callback)
397 \fn QNetworkReply *QRestAccessManager::patch(const QNetworkRequest &request, const QJsonDocument &data)
398
399 Issues an \c {HTTP PATCH} based on \a request.
400
401 The optional \a callback and \a context object can be provided for
402 handling the request completion as illustrated below:
403
404 \snippet code/src_network_access_qrestaccessmanager.cpp 10
405
406 Alternatively the signals of the returned QNetworkReply* object can be
407 used. For further information see
408 \l {Issuing Network Requests and Handling Replies}.
409
410 The \c patch() method always requires \a data parameter. The following
411 data types are supported:
412 \list
413 \li QByteArray
414 \li QJsonDocument *)
415 \li QVariantMap **)
416 \li QIODevice*
417 \endlist
418
419 *) Sent in \l QJsonDocument::Compact format, and the
420 \c Content-Type header is set to \c {application/json} if the
421 \c Content-Type header was not set
422 **) QVariantMap is converted to and treated as a QJsonObject
423
424 \sa QRestReply
425*/
426
427/*!
428 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
429 const QNetworkRequest &request, const QVariantMap &data,
430 const ContextTypeForFunctor<Functor> *context,
431 Functor &&callback)
432 \fn QNetworkReply *QRestAccessManager::patch(const QNetworkRequest &request, const QVariantMap &data)
433
434 \overload
435*/
436
437/*!
438 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
439 const QNetworkRequest &request, const QByteArray &data,
440 const ContextTypeForFunctor<Functor> *context,
441 Functor &&callback)
442 \fn QNetworkReply *QRestAccessManager::patch(const QNetworkRequest &request, const QByteArray &data)
443
444 \overload
445*/
446
447/*!
448 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
449 const QNetworkRequest &request, QIODevice *data,
450 const ContextTypeForFunctor<Functor> *context,
451 Functor &&callback)
452 \fn QNetworkReply *QRestAccessManager::patch(const QNetworkRequest &request, QIODevice *data)
453
454 \overload
455*/
456
457/*!
458 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::head(
459 const QNetworkRequest &request,
460 const ContextTypeForFunctor<Functor> *context,
461 Functor &&callback)
462 \fn QNetworkReply *QRestAccessManager::head(const QNetworkRequest &request)
463
464 Issues an \c {HTTP HEAD} based on \a request.
465
466 The optional \a callback and \a context object can be provided for
467 handling the request completion as illustrated below:
468
469 \snippet code/src_network_access_qrestaccessmanager.cpp 7
470
471 Alternatively the signals of the returned QNetworkReply* object can be
472 used. For further information see
473 \l {Issuing Network Requests and Handling Replies}.
474
475 \c head() request does not support providing data.
476
477 \sa QRestReply
478*/
479
480/*!
481 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::deleteResource(
482 const QNetworkRequest &request,
483 const ContextTypeForFunctor<Functor> *context,
484 Functor &&callback)
485 \fn QNetworkReply *QRestAccessManager::deleteResource(const QNetworkRequest &request)
486
487 Issues an \c {HTTP DELETE} based on \a request.
488
489 The optional \a callback and \a context object can be provided for
490 handling the request completion as illustrated below:
491
492 \snippet code/src_network_access_qrestaccessmanager.cpp 8
493
494 Alternatively the signals of the returned QNetworkReply* object can be
495 used. For further information see
496 \l {Issuing Network Requests and Handling Replies}.
497
498 \c deleteResource() request does not support providing data.
499
500 \sa QRestReply
501*/
502
503/*!
504 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
505 const QNetworkRequest& request, const QByteArray &method, const QByteArray &data,
506 const ContextTypeForFunctor<Functor> *context,
507 Functor &&callback)
508 \fn QNetworkReply *QRestAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &method, const QByteArray &data)
509
510 Issues \a request based HTTP request with custom \a method and the
511 provided \a data.
512
513 The optional \a callback and \a context object can be provided for
514 handling the request completion as illustrated below:
515
516 \snippet code/src_network_access_qrestaccessmanager.cpp 9
517
518 Alternatively the signals of the returned QNetworkReply* object can be
519 used. For further information see
520 \l {Issuing Network Requests and Handling Replies}.
521
522*/
523
524/*!
525 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
526 const QNetworkRequest& request, const QByteArray &method, QIODevice *data,
527 const ContextTypeForFunctor<Functor> *context,
528 Functor &&callback)
529 \fn QNetworkReply *QRestAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &method, QIODevice *data)
530
531 \overload
532*/
533
534/*!
535 \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
536 const QNetworkRequest& request, const QByteArray &method, QHttpMultiPart *data,
537 const ContextTypeForFunctor<Functor> *context,
538 Functor &&callback)
539 \fn QNetworkReply *QRestAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &method, QHttpMultiPart *data)
540
541 \overload
542*/
543
544/*!
545 Constructs a QRestAccessManager object and sets \a parent as the parent
546 object, and \a manager as the underlying QNetworkAccessManager which
547 is used for communication.
548
549 \sa networkAccessManager()
550*/
551
552QRestAccessManager::QRestAccessManager(QNetworkAccessManager *manager, QObject *parent)
553 : QObject(*new QRestAccessManagerPrivate, parent)
554{
555 Q_D(QRestAccessManager);
556 d->qnam = manager;
557 if (!d->qnam)
558 qCWarning(lcQrest, "QRestAccessManager: QNetworkAccesManager is nullptr");
559}
560
561/*!
562 Destroys the QRestAccessManager object.
563*/
564QRestAccessManager::~QRestAccessManager()
565 = default;
566
567/*!
568 Returns the underlying QNetworkAccessManager instance.
569
570 \sa QNetworkAccessManager
571*/
572QNetworkAccessManager *QRestAccessManager::networkAccessManager() const
573{
574 Q_D(const QRestAccessManager);
575 return d->qnam;
576}
577
579 = default;
580
582{
583 if (!activeRequests.isEmpty()) {
584 qCWarning(lcQrest, "Access manager destroyed while %lld requests were still in progress",
585 qlonglong(activeRequests.size()));
586 }
587}
588
589QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
590 const QJsonDocument &data, const QObject *context,
591 QtPrivate::QSlotObjectBase *slot)
592{
593 Q_D(QRestAccessManager);
594 return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->post(req, data); },
595 data, request, context, slot);
596}
597
598QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
599 const QVariantMap &data, const QObject *context,
600 QtPrivate::QSlotObjectBase *slot)
601{
602 return postWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
603}
604
605QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
606 const QByteArray &data, const QObject *context,
607 QtPrivate::QSlotObjectBase *slot)
608{
609 Q_D(QRestAccessManager);
610 return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
611}
612
613QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
614 QHttpMultiPart *data, const QObject *context,
615 QtPrivate::QSlotObjectBase *slot)
616{
617 Q_D(QRestAccessManager);
618 return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
619}
620
621QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
622 QIODevice *data, const QObject *context,
623 QtPrivate::QSlotObjectBase *slot)
624{
625 Q_D(QRestAccessManager);
626 return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
627}
628
629QNetworkReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request,
630 const QObject *context, QtPrivate::QSlotObjectBase *slot)
631{
632 Q_D(QRestAccessManager);
633 return d->executeRequest([&](auto qnam) { return qnam->get(request); }, context, slot);
634}
635
636QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
637 const QByteArray &data, const QObject *context,
638 QtPrivate::QSlotObjectBase *slot)
639{
640 Q_D(QRestAccessManager);
641 return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
642}
643
644QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
645 const QJsonDocument &data, const QObject *context,
646 QtPrivate::QSlotObjectBase *slot)
647{
648 Q_D(QRestAccessManager);
649 return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->get(req, data); },
650 data, request, context, slot);
651}
652
653QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
654 QIODevice *data, const QObject *context,
655 QtPrivate::QSlotObjectBase *slot)
656{
657 Q_D(QRestAccessManager);
658 return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
659}
660
661QNetworkReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request,
662 const QObject *context, QtPrivate::QSlotObjectBase *slot)
663{
664 Q_D(QRestAccessManager);
665 return d->executeRequest([&](auto qnam) { return qnam->deleteResource(request); }, context, slot);
666}
667
668QNetworkReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request,
669 const QObject *context, QtPrivate::QSlotObjectBase *slot)
670{
671 Q_D(QRestAccessManager);
672 return d->executeRequest([&](auto qnam) { return qnam->head(request); }, context, slot);
673}
674
675QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
676 const QJsonDocument &data, const QObject *context,
677 QtPrivate::QSlotObjectBase *slot)
678{
679 Q_D(QRestAccessManager);
680 return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->put(req, data); },
681 data, request, context, slot);
682}
683
684QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
685 const QVariantMap &data, const QObject *context,
686 QtPrivate::QSlotObjectBase *slot)
687{
688 return putWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
689}
690
691QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
692 const QByteArray &data, const QObject *context,
693 QtPrivate::QSlotObjectBase *slot)
694{
695 Q_D(QRestAccessManager);
696 return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
697}
698
699QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
700 QHttpMultiPart *data, const QObject *context,
701 QtPrivate::QSlotObjectBase *slot)
702{
703 Q_D(QRestAccessManager);
704 return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
705}
706
707QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data,
708 const QObject *context, QtPrivate::QSlotObjectBase *slot)
709{
710 Q_D(QRestAccessManager);
711 return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
712}
713
714static const QByteArray& PATCH()
715{
716 static auto patch = "PATCH"_ba;
717 return patch;
718}
719
720QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
721 const QJsonDocument &data, const QObject *context,
722 QtPrivate::QSlotObjectBase *slot)
723{
724 Q_D(QRestAccessManager);
725 return d->executeRequest(
726 [](auto qnam, auto req, auto data) { return qnam->sendCustomRequest(req, PATCH(), data); },
727 data, request, context, slot);
728}
729
730QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
731 const QVariantMap &data, const QObject *context,
732 QtPrivate::QSlotObjectBase *slot)
733{
734 return patchWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
735}
736
737QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
738 const QByteArray &data, const QObject *context,
739 QtPrivate::QSlotObjectBase *slot)
740{
741 Q_D(QRestAccessManager);
742 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH(), data); },
743 context, slot);
744}
745
746QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
747 const QObject *context, QtPrivate::QSlotObjectBase *slot)
748{
749 Q_D(QRestAccessManager);
750 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH(), data); },
751 context, slot);
752}
753
754QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
755 const QByteArray& method, const QByteArray &data,
756 const QObject *context,
757 QtPrivate::QSlotObjectBase *slot)
758{
759 Q_D(QRestAccessManager);
760 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
761 context, slot);
762}
763
764QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
765 const QByteArray& method, QIODevice *data,
766 const QObject *context,
767 QtPrivate::QSlotObjectBase *slot)
768{
769 Q_D(QRestAccessManager);
770 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
771 context, slot);
772}
773
774QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
775 const QByteArray& method, QHttpMultiPart *data,
776 const QObject *context,
777 QtPrivate::QSlotObjectBase *slot)
778{
779 Q_D(QRestAccessManager);
780 return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
781 context, slot);
782}
783
785 const QObject *contextObject,
786 QtPrivate::SlotObjUniquePtr slot)
787{
788 Q_Q(QRestAccessManager);
789 Q_ASSERT(reply);
790 [[maybe_unused]] const auto [_, inserted] =
791 activeRequests.try_emplace(reply, CallerInfo{contextObject, std::move(slot)});
792 Q_ASSERT(inserted); // if this fails, we have an ABA problem...
793
794 // The signal connections below are made to 'q' to avoid stray signal
795 // handling upon its destruction while requests were still in progress
796
797 QObject::connect(reply, &QNetworkReply::finished, q, [reply, this]() {
798 handleReplyFinished(reply);
799 });
800 // Safe guard in case reply is destroyed before it's finished
801 QObject::connect(reply, &QObject::destroyed, q, [reply, this]() {
802 activeRequests.remove(reply);
803 });
804 // If context object is destroyed, clean up any possible replies it had associated with it
805 if (contextObject) {
806 QObject::connect(contextObject, &QObject::destroyed, q, [reply, this]() {
807 activeRequests.remove(reply);
808 });
809 }
810 return reply;
811}
812
813void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject)
814{
815 Q_Q(QRestAccessManager);
816 if (QThread::currentThread() != q->thread()) {
817 qCWarning(lcQrest, "QRestAccessManager can only be called in the thread it belongs to");
818 Q_ASSERT(false);
819 }
820 if (contextObject && (contextObject->thread() != q->thread())) {
821 qCWarning(lcQrest, "QRestAccessManager: the context object must reside in the same thread");
822 Q_ASSERT(false);
823 }
824}
825
826Q_DECL_COLD_FUNCTION
827static QNetworkReply* warnNoAccessManager()
828{
829 qCWarning(lcQrest, "QRestAccessManager: QNetworkAccessManager not set");
830 return nullptr;
831}
832
833void QRestAccessManagerPrivate::handleReplyFinished(QNetworkReply *reply)
834{
835 auto request = activeRequests.find(reply);
836 if (request == activeRequests.end()) {
837 qCDebug(lcQrest, "QRestAccessManager: Unexpected reply received, ignoring");
838 return;
839 }
840
841 CallerInfo caller = std::move(request.value());
842 activeRequests.erase(request);
843
844 if (caller.slot) {
845 // Callback was provided
846 QRestReply restReply(reply);
847 void *argv[] = { nullptr, &restReply };
848 // If we have context object, use it
849 QObject *context = caller.contextObject
850 ? const_cast<QObject*>(caller.contextObject.get()) : nullptr;
851 caller.slot->call(context, argv);
852 }
853}
854
856QRestAccessManagerPrivate::executeRequest(ReqOpRef requestOperation, const QObject *context,
857 QtPrivate::QSlotObjectBase *rawSlot)
858{
859 QtPrivate::SlotObjUniquePtr slot(rawSlot);
860 if (!qnam)
861 return warnNoAccessManager();
862 verifyThreadAffinity(context);
863 QNetworkReply *reply = requestOperation(qnam);
864 return createActiveRequest(reply, context, std::move(slot));
865}
866
867QNetworkReply *
868QRestAccessManagerPrivate::executeRequest(ReqOpRefJson requestOperation, const QJsonDocument &jsonDoc,
869 const QNetworkRequest &request, const QObject *context,
870 QtPrivate::QSlotObjectBase *rawSlot)
871{
872 QtPrivate::SlotObjUniquePtr slot(rawSlot);
873 if (!qnam)
874 return warnNoAccessManager();
875 verifyThreadAffinity(context);
876 QNetworkRequest req(request);
877 auto h = req.headers();
878 if (!h.contains(QHttpHeaders::WellKnownHeader::ContentType)) {
879 h.append(QHttpHeaders::WellKnownHeader::ContentType,
880 QLatin1StringView{"application/json"});
881 }
882 req.setHeaders(std::move(h));
883 QNetworkReply *reply = requestOperation(qnam, req, jsonDoc.toJson(QJsonDocument::Compact));
884 return createActiveRequest(reply, context, std::move(slot));
885}
886
887QT_END_NAMESPACE
888
889#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()