1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtNetwork module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include <QtNetwork/private/qtnetworkglobal_p.h> |
41 | |
42 | #include "qnetworkaccessmanager.h" |
43 | #include "qnetworkaccessmanager_p.h" |
44 | #include "qnetworkrequest.h" |
45 | #include "qnetworkreply.h" |
46 | #include "qnetworkreply_p.h" |
47 | #include "qnetworkcookie.h" |
48 | #include "qnetworkcookiejar.h" |
49 | #include "qabstractnetworkcache.h" |
50 | #include "qhstspolicy.h" |
51 | #include "qhsts_p.h" |
52 | |
53 | #if QT_CONFIG(settings) |
54 | #include "qhstsstore_p.h" |
55 | #endif // QT_CONFIG(settings) |
56 | |
57 | #include "QtNetwork/qnetworksession.h" |
58 | #include "QtNetwork/private/qsharednetworksession_p.h" |
59 | |
60 | #if QT_CONFIG(ftp) |
61 | #include "qnetworkaccessftpbackend_p.h" |
62 | #endif |
63 | #include "qnetworkaccessfilebackend_p.h" |
64 | #include "qnetworkaccessdebugpipebackend_p.h" |
65 | #include "qnetworkaccesscachebackend_p.h" |
66 | #include "qnetworkreplydataimpl_p.h" |
67 | #include "qnetworkreplyfileimpl_p.h" |
68 | |
69 | #include "QtCore/qbuffer.h" |
70 | #include "QtCore/qurl.h" |
71 | #include "QtCore/qvector.h" |
72 | #include "QtNetwork/private/qauthenticator_p.h" |
73 | #include "QtNetwork/qsslconfiguration.h" |
74 | #include "QtNetwork/qnetworkconfigmanager.h" |
75 | #include "QtNetwork/private/http2protocol_p.h" |
76 | |
77 | #if QT_CONFIG(http) |
78 | #include "qhttpmultipart.h" |
79 | #include "qhttpmultipart_p.h" |
80 | #include "qnetworkreplyhttpimpl_p.h" |
81 | #endif |
82 | |
83 | #include "qthread.h" |
84 | |
85 | #include <QHostInfo> |
86 | |
87 | #if defined(Q_OS_MACOS) |
88 | #include <CoreServices/CoreServices.h> |
89 | #include <SystemConfiguration/SystemConfiguration.h> |
90 | #include <Security/SecKeychain.h> |
91 | #endif |
92 | #ifdef Q_OS_WASM |
93 | #include "qnetworkreplywasmimpl_p.h" |
94 | #endif |
95 | |
96 | #include "qnetconmonitor_p.h" |
97 | |
98 | QT_BEGIN_NAMESPACE |
99 | |
100 | Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend) |
101 | #if QT_CONFIG(ftp) |
102 | Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend) |
103 | #endif // QT_CONFIG(ftp) |
104 | |
105 | #ifdef QT_BUILD_INTERNAL |
106 | Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend) |
107 | #endif |
108 | |
109 | #if defined(Q_OS_MACX) |
110 | bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password) |
111 | { |
112 | OSStatus err; |
113 | SecKeychainItemRef itemRef; |
114 | bool retValue = false; |
115 | SecProtocolType protocolType = kSecProtocolTypeAny; |
116 | if (scheme.compare(QLatin1String("ftp" ),Qt::CaseInsensitive)==0) { |
117 | protocolType = kSecProtocolTypeFTPProxy; |
118 | } else if (scheme.compare(QLatin1String("http" ),Qt::CaseInsensitive)==0 |
119 | || scheme.compare(QLatin1String("preconnect-http" ),Qt::CaseInsensitive)==0) { |
120 | protocolType = kSecProtocolTypeHTTPProxy; |
121 | } else if (scheme.compare(QLatin1String("https" ),Qt::CaseInsensitive)==0 |
122 | || scheme.compare(QLatin1String("preconnect-https" ),Qt::CaseInsensitive)==0) { |
123 | protocolType = kSecProtocolTypeHTTPSProxy; |
124 | } |
125 | QByteArray proxyHostnameUtf8(proxyHostname.toUtf8()); |
126 | err = SecKeychainFindInternetPassword(NULL, |
127 | proxyHostnameUtf8.length(), proxyHostnameUtf8.constData(), |
128 | 0,NULL, |
129 | 0, NULL, |
130 | 0, NULL, |
131 | 0, |
132 | protocolType, |
133 | kSecAuthenticationTypeAny, |
134 | 0, NULL, |
135 | &itemRef); |
136 | if (err == noErr) { |
137 | |
138 | SecKeychainAttribute attr; |
139 | SecKeychainAttributeList attrList; |
140 | UInt32 length; |
141 | void *outData; |
142 | |
143 | attr.tag = kSecAccountItemAttr; |
144 | attr.length = 0; |
145 | attr.data = NULL; |
146 | |
147 | attrList.count = 1; |
148 | attrList.attr = &attr; |
149 | |
150 | if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData) == noErr) { |
151 | username = QString::fromUtf8((const char*)attr.data, attr.length); |
152 | password = QString::fromUtf8((const char*)outData, length); |
153 | SecKeychainItemFreeContent(&attrList,outData); |
154 | retValue = true; |
155 | } |
156 | CFRelease(itemRef); |
157 | } |
158 | return retValue; |
159 | } |
160 | #endif |
161 | |
162 | |
163 | |
164 | static void ensureInitialized() |
165 | { |
166 | #if QT_CONFIG(ftp) |
167 | (void) ftpBackend(); |
168 | #endif |
169 | |
170 | #ifdef QT_BUILD_INTERNAL |
171 | (void) debugpipeBackend(); |
172 | #endif |
173 | |
174 | // leave this one last since it will query the special QAbstractFileEngines |
175 | (void) fileBackend(); |
176 | } |
177 | |
178 | /*! |
179 | \class QNetworkAccessManager |
180 | \brief The QNetworkAccessManager class allows the application to |
181 | send network requests and receive replies. |
182 | \since 4.4 |
183 | |
184 | \ingroup network |
185 | \inmodule QtNetwork |
186 | \reentrant |
187 | |
188 | The Network Access API is constructed around one QNetworkAccessManager |
189 | object, which holds the common configuration and settings for the requests |
190 | it sends. It contains the proxy and cache configuration, as well as the |
191 | signals related to such issues, and reply signals that can be used to |
192 | monitor the progress of a network operation. One QNetworkAccessManager |
193 | instance should be enough for the whole Qt application. Since |
194 | QNetworkAccessManager is based on QObject, it can only be used from the |
195 | thread it belongs to. |
196 | |
197 | Once a QNetworkAccessManager object has been created, the application can |
198 | use it to send requests over the network. A group of standard functions |
199 | are supplied that take a request and optional data, and each return a |
200 | QNetworkReply object. The returned object is used to obtain any data |
201 | returned in response to the corresponding request. |
202 | |
203 | A simple download off the network could be accomplished with: |
204 | \snippet code/src_network_access_qnetworkaccessmanager.cpp 0 |
205 | |
206 | QNetworkAccessManager has an asynchronous API. |
207 | When the \tt replyFinished slot above is called, the parameter it |
208 | takes is the QNetworkReply object containing the downloaded data |
209 | as well as meta-data (headers, etc.). |
210 | |
211 | \note After the request has finished, it is the responsibility of the user |
212 | to delete the QNetworkReply object at an appropriate time. Do not directly |
213 | delete it inside the slot connected to finished(). You can use the |
214 | deleteLater() function. |
215 | |
216 | \note QNetworkAccessManager queues the requests it receives. The number |
217 | of requests executed in parallel is dependent on the protocol. |
218 | Currently, for the HTTP protocol on desktop platforms, 6 requests are |
219 | executed in parallel for one host/port combination. |
220 | |
221 | A more involved example, assuming the manager is already existent, |
222 | can be: |
223 | \snippet code/src_network_access_qnetworkaccessmanager.cpp 1 |
224 | |
225 | \sa QNetworkRequest, QNetworkReply, QNetworkProxy |
226 | */ |
227 | |
228 | /*! |
229 | \enum QNetworkAccessManager::Operation |
230 | |
231 | Indicates the operation this reply is processing. |
232 | |
233 | \value HeadOperation retrieve headers operation (created |
234 | with head()) |
235 | |
236 | \value GetOperation retrieve headers and download contents |
237 | (created with get()) |
238 | |
239 | \value PutOperation upload contents operation (created |
240 | with put()) |
241 | |
242 | \value PostOperation send the contents of an HTML form for |
243 | processing via HTTP POST (created with post()) |
244 | |
245 | \value DeleteOperation delete contents operation (created with |
246 | deleteResource()) |
247 | |
248 | \value CustomOperation custom operation (created with |
249 | sendCustomRequest()) \since 4.7 |
250 | |
251 | \omitvalue UnknownOperation |
252 | |
253 | \sa QNetworkReply::operation() |
254 | */ |
255 | |
256 | /*! |
257 | \enum QNetworkAccessManager::NetworkAccessibility |
258 | \obsolete |
259 | |
260 | Indicates whether the network is accessible via this network access manager. |
261 | |
262 | \value UnknownAccessibility The network accessibility cannot be determined. |
263 | \value NotAccessible The network is not currently accessible, either because there |
264 | is currently no network coverage or network access has been |
265 | explicitly disabled by a call to setNetworkAccessible(). |
266 | \value Accessible The network is accessible. |
267 | |
268 | \sa networkAccessible |
269 | */ |
270 | |
271 | /*! |
272 | \property QNetworkAccessManager::networkAccessible |
273 | \brief whether the network is currently accessible via this network access manager. |
274 | \obsolete |
275 | |
276 | \since 4.7 |
277 | |
278 | If the network is \l {NotAccessible}{not accessible} the network access manager will not |
279 | process any new network requests, all such requests will fail with an error. Requests with |
280 | URLs with the file:// scheme will still be processed. |
281 | |
282 | By default the value of this property reflects the physical state of the device. Applications |
283 | may override it to disable all network requests via this network access manager by calling |
284 | |
285 | \snippet code/src_network_access_qnetworkaccessmanager.cpp 4 |
286 | |
287 | Network requests can be re-enabled again, and this property will resume to |
288 | reflect the actual device state by calling |
289 | |
290 | \snippet code/src_network_access_qnetworkaccessmanager.cpp 5 |
291 | |
292 | \note Calling setNetworkAccessible() does not change the network state. |
293 | */ |
294 | |
295 | /*! |
296 | \fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible) |
297 | \obsolete |
298 | |
299 | This signal is emitted when the value of the \l networkAccessible property changes. |
300 | \a accessible is the new network accessibility. |
301 | */ |
302 | |
303 | /*! |
304 | \fn void QNetworkAccessManager::networkSessionConnected() |
305 | |
306 | \since 4.7 |
307 | \obsolete |
308 | |
309 | \internal |
310 | |
311 | This signal is emitted when the status of the network session changes into a usable (Connected) |
312 | state. It is used to signal to QNetworkReplys to start or migrate their network operation once |
313 | the network session has been opened or finished roaming. |
314 | */ |
315 | |
316 | /*! |
317 | \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) |
318 | |
319 | This signal is emitted whenever a proxy requests authentication |
320 | and QNetworkAccessManager cannot find a valid, cached |
321 | credential. The slot connected to this signal should fill in the |
322 | credentials for the proxy \a proxy in the \a authenticator object. |
323 | |
324 | QNetworkAccessManager will cache the credentials internally. The |
325 | next time the proxy requests authentication, QNetworkAccessManager |
326 | will automatically send the same credential without emitting the |
327 | proxyAuthenticationRequired signal again. |
328 | |
329 | If the proxy rejects the credentials, QNetworkAccessManager will |
330 | emit the signal again. |
331 | |
332 | \sa proxy(), setProxy(), authenticationRequired() |
333 | */ |
334 | |
335 | /*! |
336 | \fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator) |
337 | |
338 | This signal is emitted whenever a final server requests |
339 | authentication before it delivers the requested contents. The slot |
340 | connected to this signal should fill the credentials for the |
341 | contents (which can be determined by inspecting the \a reply |
342 | object) in the \a authenticator object. |
343 | |
344 | QNetworkAccessManager will cache the credentials internally and |
345 | will send the same values if the server requires authentication |
346 | again, without emitting the authenticationRequired() signal. If it |
347 | rejects the credentials, this signal will be emitted again. |
348 | |
349 | \note To have the request not send credentials you must not call |
350 | setUser() or setPassword() on the \a authenticator object. This |
351 | will result in the \l finished() signal being emitted with a |
352 | \l QNetworkReply with error \l {QNetworkReply::} {AuthenticationRequiredError}. |
353 | |
354 | \note It is not possible to use a QueuedConnection to connect to |
355 | this signal, as the connection will fail if the authenticator has |
356 | not been filled in with new information when the signal returns. |
357 | |
358 | \sa proxyAuthenticationRequired(), QAuthenticator::setUser(), QAuthenticator::setPassword() |
359 | */ |
360 | |
361 | /*! |
362 | \fn void QNetworkAccessManager::finished(QNetworkReply *reply) |
363 | |
364 | This signal is emitted whenever a pending network reply is |
365 | finished. The \a reply parameter will contain a pointer to the |
366 | reply that has just finished. This signal is emitted in tandem |
367 | with the QNetworkReply::finished() signal. |
368 | |
369 | See QNetworkReply::finished() for information on the status that |
370 | the object will be in. |
371 | |
372 | \note Do not delete the \a reply object in the slot connected to this |
373 | signal. Use deleteLater(). |
374 | |
375 | \sa QNetworkReply::finished(), QNetworkReply::error() |
376 | */ |
377 | |
378 | /*! |
379 | \fn void QNetworkAccessManager::encrypted(QNetworkReply *reply) |
380 | \since 5.1 |
381 | |
382 | This signal is emitted when an SSL/TLS session has successfully |
383 | completed the initial handshake. At this point, no user data |
384 | has been transmitted. The signal can be used to perform |
385 | additional checks on the certificate chain, for example to |
386 | notify users when the certificate for a website has changed. The |
387 | \a reply parameter specifies which network reply is responsible. |
388 | If the reply does not match the expected criteria then it should |
389 | be aborted by calling QNetworkReply::abort() by a slot connected |
390 | to this signal. The SSL configuration in use can be inspected |
391 | using the QNetworkReply::sslConfiguration() method. |
392 | |
393 | Internally, QNetworkAccessManager may open multiple connections |
394 | to a server, in order to allow it process requests in parallel. |
395 | These connections may be reused, which means that the encrypted() |
396 | signal would not be emitted. This means that you are only |
397 | guaranteed to receive this signal for the first connection to a |
398 | site in the lifespan of the QNetworkAccessManager. |
399 | |
400 | \sa QSslSocket::encrypted() |
401 | \sa QNetworkReply::encrypted() |
402 | */ |
403 | |
404 | /*! |
405 | \fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors) |
406 | |
407 | This signal is emitted if the SSL/TLS session encountered errors |
408 | during the set up, including certificate verification errors. The |
409 | \a errors parameter contains the list of errors and \a reply is |
410 | the QNetworkReply that is encountering these errors. |
411 | |
412 | To indicate that the errors are not fatal and that the connection |
413 | should proceed, the QNetworkReply::ignoreSslErrors() function should be called |
414 | from the slot connected to this signal. If it is not called, the |
415 | SSL session will be torn down before any data is exchanged |
416 | (including the URL). |
417 | |
418 | This signal can be used to display an error message to the user |
419 | indicating that security may be compromised and display the |
420 | SSL settings (see sslConfiguration() to obtain it). If the user |
421 | decides to proceed after analyzing the remote certificate, the |
422 | slot should call ignoreSslErrors(). |
423 | |
424 | \sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(), |
425 | QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors() |
426 | */ |
427 | |
428 | /*! |
429 | \fn void QNetworkAccessManager::preSharedKeyAuthenticationRequired(QNetworkReply *reply, QSslPreSharedKeyAuthenticator *authenticator) |
430 | \since 5.5 |
431 | |
432 | This signal is emitted if the SSL/TLS handshake negotiates a PSK |
433 | ciphersuite, and therefore a PSK authentication is then required. |
434 | The \a reply object is the QNetworkReply that is negotiating |
435 | such ciphersuites. |
436 | |
437 | When using PSK, the client must send to the server a valid identity and a |
438 | valid pre shared key, in order for the SSL handshake to continue. |
439 | Applications can provide this information in a slot connected to this |
440 | signal, by filling in the passed \a authenticator object according to their |
441 | needs. |
442 | |
443 | \note Ignoring this signal, or failing to provide the required credentials, |
444 | will cause the handshake to fail, and therefore the connection to be aborted. |
445 | |
446 | \note The \a authenticator object is owned by the reply and must not be |
447 | deleted by the application. |
448 | |
449 | \sa QSslPreSharedKeyAuthenticator |
450 | */ |
451 | |
452 | /*! |
453 | Constructs a QNetworkAccessManager object that is the center of |
454 | the Network Access API and sets \a parent as the parent object. |
455 | */ |
456 | QNetworkAccessManager::QNetworkAccessManager(QObject *parent) |
457 | : QObject(*new QNetworkAccessManagerPrivate, parent) |
458 | { |
459 | ensureInitialized(); |
460 | |
461 | qRegisterMetaType<QNetworkReply::NetworkError>(); |
462 | #ifndef QT_NO_NETWORKPROXY |
463 | qRegisterMetaType<QNetworkProxy>(); |
464 | #endif |
465 | #ifndef QT_NO_SSL |
466 | qRegisterMetaType<QList<QSslError> >(); |
467 | qRegisterMetaType<QSslConfiguration>(); |
468 | qRegisterMetaType<QSslPreSharedKeyAuthenticator *>(); |
469 | #endif |
470 | qRegisterMetaType<QList<QPair<QByteArray,QByteArray> > >(); |
471 | #if QT_CONFIG(http) |
472 | qRegisterMetaType<QHttpNetworkRequest>(); |
473 | #endif |
474 | qRegisterMetaType<QNetworkReply::NetworkError>(); |
475 | qRegisterMetaType<QSharedPointer<char> >(); |
476 | |
477 | Q_D(QNetworkAccessManager); |
478 | |
479 | if (QNetworkStatusMonitor::isEnabled()) { |
480 | d->statusMonitor = new QNetworkStatusMonitor(this); |
481 | connect(asender: d->statusMonitor, SIGNAL(onlineStateChanged(bool)), |
482 | SLOT(_q_onlineStateChanged(bool))); |
483 | #ifdef QT_NO_BEARERMANAGEMENT |
484 | d->networkAccessible = d->statusMonitor->isNetworkAccessible(); |
485 | #else |
486 | d->networkAccessible = d->statusMonitor->isNetworkAccessible() ? Accessible : NotAccessible; |
487 | } else { |
488 | // if a session is required, we track online state through |
489 | // the QNetworkSession's signals if a request is already made. |
490 | // we need to track current accessibility state by default |
491 | // |
492 | connect(asender: &d->networkConfigurationManager, SIGNAL(onlineStateChanged(bool)), |
493 | SLOT(_q_onlineStateChanged(bool))); |
494 | connect(asender: &d->networkConfigurationManager, SIGNAL(configurationChanged(QNetworkConfiguration)), |
495 | SLOT(_q_configurationChanged(QNetworkConfiguration))); |
496 | #endif // QT_NO_BEARERMANAGEMENT |
497 | } |
498 | } |
499 | |
500 | /*! |
501 | Destroys the QNetworkAccessManager object and frees up any |
502 | resources. Note that QNetworkReply objects that are returned from |
503 | this class have this object set as their parents, which means that |
504 | they will be deleted along with it if you don't call |
505 | QObject::setParent() on them. |
506 | */ |
507 | QNetworkAccessManager::~QNetworkAccessManager() |
508 | { |
509 | #ifndef QT_NO_NETWORKPROXY |
510 | delete d_func()->proxyFactory; |
511 | #endif |
512 | |
513 | // Delete the QNetworkReply children first. |
514 | // Else a QAbstractNetworkCache might get deleted in ~QObject |
515 | // before a QNetworkReply that accesses the QAbstractNetworkCache |
516 | // object in its destructor. |
517 | qDeleteAll(c: findChildren<QNetworkReply *>()); |
518 | // The other children will be deleted in this ~QObject |
519 | // FIXME instead of this "hack" make the QNetworkReplyImpl |
520 | // properly watch the cache deletion, e.g. via a QWeakPointer. |
521 | } |
522 | |
523 | #ifndef QT_NO_NETWORKPROXY |
524 | /*! |
525 | Returns the QNetworkProxy that the requests sent using this |
526 | QNetworkAccessManager object will use. The default value for the |
527 | proxy is QNetworkProxy::DefaultProxy. |
528 | |
529 | \sa setProxy(), setProxyFactory(), proxyAuthenticationRequired() |
530 | */ |
531 | QNetworkProxy QNetworkAccessManager::proxy() const |
532 | { |
533 | return d_func()->proxy; |
534 | } |
535 | |
536 | /*! |
537 | Sets the proxy to be used in future requests to be \a proxy. This |
538 | does not affect requests that have already been sent. The |
539 | proxyAuthenticationRequired() signal will be emitted if the proxy |
540 | requests authentication. |
541 | |
542 | A proxy set with this function will be used for all requests |
543 | issued by QNetworkAccessManager. In some cases, it might be |
544 | necessary to select different proxies depending on the type of |
545 | request being sent or the destination host. If that's the case, |
546 | you should consider using setProxyFactory(). |
547 | |
548 | \sa proxy(), proxyAuthenticationRequired() |
549 | */ |
550 | void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy) |
551 | { |
552 | Q_D(QNetworkAccessManager); |
553 | delete d->proxyFactory; |
554 | d->proxy = proxy; |
555 | d->proxyFactory = nullptr; |
556 | } |
557 | |
558 | /*! |
559 | \fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const |
560 | \since 4.5 |
561 | |
562 | Returns the proxy factory that this QNetworkAccessManager object |
563 | is using to determine the proxies to be used for requests. |
564 | |
565 | Note that the pointer returned by this function is managed by |
566 | QNetworkAccessManager and could be deleted at any time. |
567 | |
568 | \sa setProxyFactory(), proxy() |
569 | */ |
570 | QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const |
571 | { |
572 | return d_func()->proxyFactory; |
573 | } |
574 | |
575 | /*! |
576 | \since 4.5 |
577 | |
578 | Sets the proxy factory for this class to be \a factory. A proxy |
579 | factory is used to determine a more specific list of proxies to be |
580 | used for a given request, instead of trying to use the same proxy |
581 | value for all requests. |
582 | |
583 | All queries sent by QNetworkAccessManager will have type |
584 | QNetworkProxyQuery::UrlRequest. |
585 | |
586 | For example, a proxy factory could apply the following rules: |
587 | \list |
588 | \li if the target address is in the local network (for example, |
589 | if the hostname contains no dots or if it's an IP address in |
590 | the organization's range), return QNetworkProxy::NoProxy |
591 | \li if the request is FTP, return an FTP proxy |
592 | \li if the request is HTTP or HTTPS, then return an HTTP proxy |
593 | \li otherwise, return a SOCKSv5 proxy server |
594 | \endlist |
595 | |
596 | The lifetime of the object \a factory will be managed by |
597 | QNetworkAccessManager. It will delete the object when necessary. |
598 | |
599 | \note If a specific proxy is set with setProxy(), the factory will not |
600 | be used. |
601 | |
602 | \sa proxyFactory(), setProxy(), QNetworkProxyQuery |
603 | */ |
604 | void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory) |
605 | { |
606 | Q_D(QNetworkAccessManager); |
607 | delete d->proxyFactory; |
608 | d->proxyFactory = factory; |
609 | d->proxy = QNetworkProxy(); |
610 | } |
611 | #endif |
612 | |
613 | /*! |
614 | \since 4.5 |
615 | |
616 | Returns the cache that is used to store data obtained from the network. |
617 | |
618 | \sa setCache() |
619 | */ |
620 | QAbstractNetworkCache *QNetworkAccessManager::cache() const |
621 | { |
622 | Q_D(const QNetworkAccessManager); |
623 | return d->networkCache; |
624 | } |
625 | |
626 | /*! |
627 | \since 4.5 |
628 | |
629 | Sets the manager's network cache to be the \a cache specified. The cache |
630 | is used for all requests dispatched by the manager. |
631 | |
632 | Use this function to set the network cache object to a class that implements |
633 | additional features, like saving the cookies to permanent storage. |
634 | |
635 | \note QNetworkAccessManager takes ownership of the \a cache object. |
636 | |
637 | QNetworkAccessManager by default does not have a set cache. |
638 | Qt provides a simple disk cache, QNetworkDiskCache, which can be used. |
639 | |
640 | \sa cache(), QNetworkRequest::CacheLoadControl |
641 | */ |
642 | void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache) |
643 | { |
644 | Q_D(QNetworkAccessManager); |
645 | if (d->networkCache != cache) { |
646 | delete d->networkCache; |
647 | d->networkCache = cache; |
648 | if (d->networkCache) |
649 | d->networkCache->setParent(this); |
650 | } |
651 | } |
652 | |
653 | /*! |
654 | Returns the QNetworkCookieJar that is used to store cookies |
655 | obtained from the network as well as cookies that are about to be |
656 | sent. |
657 | |
658 | \sa setCookieJar() |
659 | */ |
660 | QNetworkCookieJar *QNetworkAccessManager::cookieJar() const |
661 | { |
662 | Q_D(const QNetworkAccessManager); |
663 | if (!d->cookieJar) |
664 | d->createCookieJar(); |
665 | return d->cookieJar; |
666 | } |
667 | |
668 | /*! |
669 | Sets the manager's cookie jar to be the \a cookieJar specified. |
670 | The cookie jar is used by all requests dispatched by the manager. |
671 | |
672 | Use this function to set the cookie jar object to a class that |
673 | implements additional features, like saving the cookies to permanent |
674 | storage. |
675 | |
676 | \note QNetworkAccessManager takes ownership of the \a cookieJar object. |
677 | |
678 | If \a cookieJar is in the same thread as this QNetworkAccessManager, |
679 | it will set the parent of the \a cookieJar |
680 | so that the cookie jar is deleted when this |
681 | object is deleted as well. If you want to share cookie jars |
682 | between different QNetworkAccessManager objects, you may want to |
683 | set the cookie jar's parent to 0 after calling this function. |
684 | |
685 | QNetworkAccessManager by default does not implement any cookie |
686 | policy of its own: it accepts all cookies sent by the server, as |
687 | long as they are well formed and meet the minimum security |
688 | requirements (cookie domain matches the request's and cookie path |
689 | matches the request's). In order to implement your own security |
690 | policy, override the QNetworkCookieJar::cookiesForUrl() and |
691 | QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those |
692 | functions are called by QNetworkAccessManager when it detects a |
693 | new cookie. |
694 | |
695 | \sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl() |
696 | */ |
697 | void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar) |
698 | { |
699 | Q_D(QNetworkAccessManager); |
700 | d->cookieJarCreated = true; |
701 | if (d->cookieJar != cookieJar) { |
702 | if (d->cookieJar && d->cookieJar->parent() == this) |
703 | delete d->cookieJar; |
704 | d->cookieJar = cookieJar; |
705 | if (cookieJar && thread() == cookieJar->thread()) |
706 | d->cookieJar->setParent(this); |
707 | } |
708 | } |
709 | |
710 | /*! |
711 | \since 5.9 |
712 | |
713 | If \a enabled is \c true, QNetworkAccessManager follows the HTTP Strict Transport |
714 | Security policy (HSTS, RFC6797). When processing a request, QNetworkAccessManager |
715 | automatically replaces the "http" scheme with "https" and uses a secure transport |
716 | for HSTS hosts. If it's set explicitly, port 80 is replaced by port 443. |
717 | |
718 | When HSTS is enabled, for each HTTP response containing HSTS header and |
719 | received over a secure transport, QNetworkAccessManager will update its HSTS |
720 | cache, either remembering a host with a valid policy or removing a host with |
721 | an expired or disabled HSTS policy. |
722 | |
723 | \sa isStrictTransportSecurityEnabled() |
724 | */ |
725 | void QNetworkAccessManager::setStrictTransportSecurityEnabled(bool enabled) |
726 | { |
727 | Q_D(QNetworkAccessManager); |
728 | d->stsEnabled = enabled; |
729 | } |
730 | |
731 | /*! |
732 | \since 5.9 |
733 | |
734 | Returns true if HTTP Strict Transport Security (HSTS) was enabled. By default |
735 | HSTS is disabled. |
736 | |
737 | \sa setStrictTransportSecurityEnabled() |
738 | */ |
739 | bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const |
740 | { |
741 | Q_D(const QNetworkAccessManager); |
742 | return d->stsEnabled; |
743 | } |
744 | |
745 | /*! |
746 | \since 5.10 |
747 | |
748 | If \a enabled is \c true, the internal HSTS cache will use a persistent store |
749 | to read and write HSTS policies. \a storeDir defines where this store will be |
750 | located. The default location is defined by QStandardPaths::CacheLocation. |
751 | If there is no writable QStandartPaths::CacheLocation and \a storeDir is an |
752 | empty string, the store will be located in the program's working directory. |
753 | |
754 | \note If HSTS cache already contains HSTS policies by the time persistent |
755 | store is enabled, these policies will be preserved in the store. In case both |
756 | cache and store contain the same known hosts, policies from cache are considered |
757 | to be more up-to-date (and thus will overwrite the previous values in the store). |
758 | If this behavior is undesired, enable HSTS store before enabling Strict Transport |
759 | Security. By default, the persistent store of HSTS policies is disabled. |
760 | |
761 | \sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(), |
762 | QStandardPaths::standardLocations() |
763 | */ |
764 | |
765 | void QNetworkAccessManager::enableStrictTransportSecurityStore(bool enabled, const QString &storeDir) |
766 | { |
767 | #if QT_CONFIG(settings) |
768 | Q_D(QNetworkAccessManager); |
769 | d->stsStore.reset(other: enabled ? new QHstsStore(storeDir) : nullptr); |
770 | d->stsCache.setStore(d->stsStore.data()); |
771 | #else |
772 | Q_UNUSED(enabled) Q_UNUSED(storeDir) |
773 | qWarning("HSTS permanent store requires the feature 'settings' enabled" ); |
774 | #endif // QT_CONFIG(settings) |
775 | } |
776 | |
777 | /*! |
778 | \since 5.10 |
779 | |
780 | Returns true if HSTS cache uses a permanent store to load and store HSTS |
781 | policies. |
782 | |
783 | \sa enableStrictTransportSecurityStore() |
784 | */ |
785 | |
786 | bool QNetworkAccessManager::isStrictTransportSecurityStoreEnabled() const |
787 | { |
788 | #if QT_CONFIG(settings) |
789 | Q_D(const QNetworkAccessManager); |
790 | return bool(d->stsStore.data()); |
791 | #else |
792 | return false; |
793 | #endif // QT_CONFIG(settings) |
794 | } |
795 | |
796 | /*! |
797 | \since 5.9 |
798 | |
799 | Adds HTTP Strict Transport Security policies into HSTS cache. |
800 | \a knownHosts contains the known hosts that have QHstsPolicy |
801 | information. |
802 | |
803 | \note An expired policy will remove a known host from the cache, if previously |
804 | present. |
805 | |
806 | \note While processing HTTP responses, QNetworkAccessManager can also update |
807 | the HSTS cache, removing or updating exitsting policies or introducing new |
808 | \a knownHosts. The current implementation thus is server-driven, client code |
809 | can provide QNetworkAccessManager with previously known or discovered |
810 | policies, but this information can be overridden by "Strict-Transport-Security" |
811 | response headers. |
812 | |
813 | \sa addStrictTransportSecurityHosts(), enableStrictTransportSecurityStore(), QHstsPolicy |
814 | */ |
815 | |
816 | void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts) |
817 | { |
818 | Q_D(QNetworkAccessManager); |
819 | d->stsCache.updateFromPolicies(hosts: knownHosts); |
820 | } |
821 | |
822 | /*! |
823 | \since 5.9 |
824 | |
825 | Returns the list of HTTP Strict Transport Security policies. This list can |
826 | differ from what was initially set via addStrictTransportSecurityHosts() if |
827 | HSTS cache was updated from a "Strict-Transport-Security" response header. |
828 | |
829 | \sa addStrictTransportSecurityHosts(), QHstsPolicy |
830 | */ |
831 | QVector<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const |
832 | { |
833 | Q_D(const QNetworkAccessManager); |
834 | return d->stsCache.policies(); |
835 | } |
836 | |
837 | /*! |
838 | Posts a request to obtain the network headers for \a request |
839 | and returns a new QNetworkReply object which will contain such headers. |
840 | |
841 | The function is named after the HTTP request associated (HEAD). |
842 | */ |
843 | QNetworkReply *QNetworkAccessManager::(const QNetworkRequest &request) |
844 | { |
845 | return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::HeadOperation, request)); |
846 | } |
847 | |
848 | /*! |
849 | Posts a request to obtain the contents of the target \a request |
850 | and returns a new QNetworkReply object opened for reading which emits the |
851 | \l{QIODevice::readyRead()}{readyRead()} signal whenever new data |
852 | arrives. |
853 | |
854 | The contents as well as associated headers will be downloaded. |
855 | |
856 | \sa post(), put(), deleteResource(), sendCustomRequest() |
857 | */ |
858 | QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request) |
859 | { |
860 | return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::GetOperation, request)); |
861 | } |
862 | |
863 | /*! |
864 | Sends an HTTP POST request to the destination specified by \a request |
865 | and returns a new QNetworkReply object opened for reading that will |
866 | contain the reply sent by the server. The contents of the \a data |
867 | device will be uploaded to the server. |
868 | |
869 | \a data must be open for reading and must remain valid until the |
870 | finished() signal is emitted for this reply. |
871 | |
872 | \note Sending a POST request on protocols other than HTTP and |
873 | HTTPS is undefined and will probably fail. |
874 | |
875 | \sa get(), put(), deleteResource(), sendCustomRequest() |
876 | */ |
877 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data) |
878 | { |
879 | return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::PostOperation, request, outgoingData: data)); |
880 | } |
881 | |
882 | /*! |
883 | \overload |
884 | |
885 | Sends the contents of the \a data byte array to the destination |
886 | specified by \a request. |
887 | */ |
888 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data) |
889 | { |
890 | QBuffer *buffer = new QBuffer; |
891 | buffer->setData(data); |
892 | buffer->open(openMode: QIODevice::ReadOnly); |
893 | |
894 | QNetworkReply *reply = post(request, data: buffer); |
895 | buffer->setParent(reply); |
896 | return reply; |
897 | } |
898 | |
899 | #if QT_CONFIG(http) |
900 | /*! |
901 | \since 4.8 |
902 | |
903 | \overload |
904 | |
905 | Sends the contents of the \a multiPart message to the destination |
906 | specified by \a request. |
907 | |
908 | This can be used for sending MIME multipart messages over HTTP. |
909 | |
910 | \sa QHttpMultiPart, QHttpPart, put() |
911 | */ |
912 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart) |
913 | { |
914 | QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart); |
915 | QIODevice *device = multiPart->d_func()->device; |
916 | QNetworkReply *reply = post(request: newRequest, data: device); |
917 | return reply; |
918 | } |
919 | |
920 | /*! |
921 | \since 4.8 |
922 | |
923 | \overload |
924 | |
925 | Sends the contents of the \a multiPart message to the destination |
926 | specified by \a request. |
927 | |
928 | This can be used for sending MIME multipart messages over HTTP. |
929 | |
930 | \sa QHttpMultiPart, QHttpPart, post() |
931 | */ |
932 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *multiPart) |
933 | { |
934 | QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart); |
935 | QIODevice *device = multiPart->d_func()->device; |
936 | QNetworkReply *reply = put(request: newRequest, data: device); |
937 | return reply; |
938 | } |
939 | #endif // QT_CONFIG(http) |
940 | |
941 | /*! |
942 | Uploads the contents of \a data to the destination \a request and |
943 | returns a new QNetworkReply object that will be open for reply. |
944 | |
945 | \a data must be opened for reading when this function is called |
946 | and must remain valid until the finished() signal is emitted for |
947 | this reply. |
948 | |
949 | Whether anything will be available for reading from the returned |
950 | object is protocol dependent. For HTTP, the server may send a |
951 | small HTML page indicating the upload was successful (or not). |
952 | Other protocols will probably have content in their replies. |
953 | |
954 | \note For HTTP, this request will send a PUT request, which most servers |
955 | do not allow. Form upload mechanisms, including that of uploading |
956 | files through HTML forms, use the POST mechanism. |
957 | |
958 | \sa get(), post(), deleteResource(), sendCustomRequest() |
959 | */ |
960 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data) |
961 | { |
962 | return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::PutOperation, request, outgoingData: data)); |
963 | } |
964 | |
965 | /*! |
966 | \overload |
967 | |
968 | Sends the contents of the \a data byte array to the destination |
969 | specified by \a request. |
970 | */ |
971 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data) |
972 | { |
973 | QBuffer *buffer = new QBuffer; |
974 | buffer->setData(data); |
975 | buffer->open(openMode: QIODevice::ReadOnly); |
976 | |
977 | QNetworkReply *reply = put(request, data: buffer); |
978 | buffer->setParent(reply); |
979 | return reply; |
980 | } |
981 | |
982 | /*! |
983 | \since 4.6 |
984 | |
985 | Sends a request to delete the resource identified by the URL of \a request. |
986 | |
987 | \note This feature is currently available for HTTP only, performing an |
988 | HTTP DELETE request. |
989 | |
990 | \sa get(), post(), put(), sendCustomRequest() |
991 | */ |
992 | QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request) |
993 | { |
994 | return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::DeleteOperation, request)); |
995 | } |
996 | |
997 | #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section |
998 | |
999 | /*! |
1000 | \since 4.7 |
1001 | \obsolete |
1002 | |
1003 | Sets the network configuration that will be used when creating the |
1004 | \l {QNetworkSession}{network session} to \a config. |
1005 | |
1006 | The network configuration is used to create and open a network session before any request that |
1007 | requires network access is process. If no network configuration is explicitly set via this |
1008 | function the network configuration returned by |
1009 | QNetworkConfigurationManager::defaultConfiguration() will be used. |
1010 | |
1011 | To restore the default network configuration set the network configuration to the value |
1012 | returned from QNetworkConfigurationManager::defaultConfiguration(). |
1013 | |
1014 | Setting a network configuration means that the QNetworkAccessManager instance will only |
1015 | be using the specified one. In particular, if the default network configuration changes |
1016 | (upon e.g. Wifi being available), this new configuration needs to be enabled |
1017 | manually if desired. |
1018 | |
1019 | \snippet code/src_network_access_qnetworkaccessmanager.cpp 2 |
1020 | |
1021 | If an invalid network configuration is set, a network session will not be created. In this |
1022 | case network requests will be processed regardless, but may fail. For example: |
1023 | |
1024 | \snippet code/src_network_access_qnetworkaccessmanager.cpp 3 |
1025 | |
1026 | \sa configuration(), QNetworkSession |
1027 | */ |
1028 | void QNetworkAccessManager::setConfiguration(const QNetworkConfiguration &config) |
1029 | { |
1030 | Q_D(QNetworkAccessManager); |
1031 | |
1032 | d->networkConfiguration = config; |
1033 | d->customNetworkConfiguration = true; |
1034 | d->createSession(config); |
1035 | } |
1036 | |
1037 | /*! |
1038 | \since 4.7 |
1039 | \obsolete |
1040 | |
1041 | Returns the network configuration that will be used to create the |
1042 | \l {QNetworkSession}{network session} which will be used when processing network requests. |
1043 | |
1044 | \sa setConfiguration(), activeConfiguration() |
1045 | */ |
1046 | QNetworkConfiguration QNetworkAccessManager::configuration() const |
1047 | { |
1048 | Q_D(const QNetworkAccessManager); |
1049 | |
1050 | QSharedPointer<QNetworkSession> session(d->getNetworkSession()); |
1051 | if (session && !d->statusMonitor->isEnabled()) { |
1052 | return session->configuration(); |
1053 | } else { |
1054 | return d->networkConfigurationManager.defaultConfiguration(); |
1055 | } |
1056 | } |
1057 | |
1058 | /*! |
1059 | \since 4.7 |
1060 | \obsolete |
1061 | |
1062 | Returns the current active network configuration. |
1063 | |
1064 | If the network configuration returned by configuration() is of type |
1065 | QNetworkConfiguration::ServiceNetwork this function will return the current active child |
1066 | network configuration of that configuration. Otherwise returns the same network configuration |
1067 | as configuration(). |
1068 | |
1069 | Use this function to return the actual network configuration currently in use by the network |
1070 | session. |
1071 | |
1072 | \sa configuration() |
1073 | */ |
1074 | QNetworkConfiguration QNetworkAccessManager::activeConfiguration() const |
1075 | { |
1076 | Q_D(const QNetworkAccessManager); |
1077 | |
1078 | QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession()); |
1079 | if (networkSession && !d->statusMonitor->isEnabled()) { |
1080 | return d->networkConfigurationManager.configurationFromIdentifier( |
1081 | identifier: networkSession->sessionProperty(key: QLatin1String("ActiveConfiguration" )).toString()); |
1082 | } else { |
1083 | return d->networkConfigurationManager.defaultConfiguration(); |
1084 | } |
1085 | } |
1086 | |
1087 | /*! |
1088 | \since 4.7 |
1089 | \obsolete |
1090 | |
1091 | Overrides the reported network accessibility. If \a accessible is NotAccessible the reported |
1092 | network accessiblity will always be NotAccessible. Otherwise the reported network |
1093 | accessibility will reflect the actual device state. |
1094 | */ |
1095 | void QNetworkAccessManager::setNetworkAccessible(QNetworkAccessManager::NetworkAccessibility accessible) |
1096 | { |
1097 | Q_D(QNetworkAccessManager); |
1098 | |
1099 | d->defaultAccessControl = accessible == NotAccessible ? false : true; |
1100 | |
1101 | if (d->networkAccessible != accessible) { |
1102 | QT_WARNING_PUSH |
1103 | QT_WARNING_DISABLE_DEPRECATED |
1104 | NetworkAccessibility previous = networkAccessible(); |
1105 | d->networkAccessible = accessible; |
1106 | NetworkAccessibility current = networkAccessible(); |
1107 | if (previous != current) |
1108 | emit networkAccessibleChanged(accessible: current); |
1109 | QT_WARNING_POP |
1110 | } |
1111 | } |
1112 | |
1113 | /*! |
1114 | \since 4.7 |
1115 | \obsolete |
1116 | |
1117 | Returns the current network accessibility. |
1118 | */ |
1119 | QNetworkAccessManager::NetworkAccessibility QNetworkAccessManager::networkAccessible() const |
1120 | { |
1121 | Q_D(const QNetworkAccessManager); |
1122 | |
1123 | if (d->statusMonitor->isEnabled()) { |
1124 | if (!d->statusMonitor->isMonitoring()) |
1125 | d->statusMonitor->start(); |
1126 | return d->networkAccessible; |
1127 | } |
1128 | |
1129 | if (d->customNetworkConfiguration && d->networkConfiguration.state().testFlag(flag: QNetworkConfiguration::Undefined)) |
1130 | return UnknownAccessibility; |
1131 | |
1132 | if (d->networkSessionRequired) { |
1133 | QSharedPointer<QNetworkSession> networkSession(d->getNetworkSession()); |
1134 | if (networkSession) { |
1135 | // d->online holds online/offline state of this network session. |
1136 | if (d->online) |
1137 | return d->networkAccessible; |
1138 | else |
1139 | return NotAccessible; |
1140 | } else { |
1141 | if (d->defaultAccessControl) { |
1142 | if (d->online) |
1143 | return d->networkAccessible; |
1144 | else |
1145 | return NotAccessible; |
1146 | } |
1147 | return (d->networkAccessible); |
1148 | } |
1149 | } else { |
1150 | if (d->online) |
1151 | return d->networkAccessible; |
1152 | else |
1153 | return NotAccessible; |
1154 | } |
1155 | } |
1156 | |
1157 | /*! |
1158 | \internal |
1159 | |
1160 | Returns the network session currently in use. |
1161 | This can be changed at any time, ownership remains with the QNetworkAccessManager |
1162 | */ |
1163 | const QWeakPointer<const QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession(const QNetworkAccessManager *q) |
1164 | { |
1165 | return q->d_func()->networkSessionWeakRef; |
1166 | } |
1167 | |
1168 | QSharedPointer<QNetworkSession> QNetworkAccessManagerPrivate::getNetworkSession() const |
1169 | { |
1170 | if (networkSessionStrongRef) |
1171 | return networkSessionStrongRef; |
1172 | return networkSessionWeakRef.toStrongRef(); |
1173 | } |
1174 | |
1175 | #endif // QT_NO_BEARERMANAGEMENT |
1176 | |
1177 | #ifndef QT_NO_SSL |
1178 | /*! |
1179 | \since 5.2 |
1180 | |
1181 | Initiates a connection to the host given by \a hostName at port \a port, using |
1182 | \a sslConfiguration. This function is useful to complete the TCP and SSL handshake |
1183 | to a host before the HTTPS request is made, resulting in a lower network latency. |
1184 | |
1185 | \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols() |
1186 | on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in |
1187 | the list of allowed protocols. When using SPDY, one single connection per host is |
1188 | enough, i.e. calling this method multiple times per host will not result in faster |
1189 | network transactions. |
1190 | |
1191 | \note This function has no possibility to report errors. |
1192 | |
1193 | \sa connectToHost(), get(), post(), put(), deleteResource() |
1194 | */ |
1195 | |
1196 | void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, |
1197 | const QSslConfiguration &sslConfiguration) |
1198 | { |
1199 | connectToHostEncrypted(hostName, port, sslConfiguration, peerName: QString()); |
1200 | } |
1201 | |
1202 | /*! |
1203 | \since 5.13 |
1204 | \overload |
1205 | |
1206 | Initiates a connection to the host given by \a hostName at port \a port, using |
1207 | \a sslConfiguration with \a peerName set to be the hostName used for certificate |
1208 | validation. This function is useful to complete the TCP and SSL handshake |
1209 | to a host before the HTTPS request is made, resulting in a lower network latency. |
1210 | |
1211 | \note Preconnecting a SPDY connection can be done by calling setAllowedNextProtocols() |
1212 | on \a sslConfiguration with QSslConfiguration::NextProtocolSpdy3_0 contained in |
1213 | the list of allowed protocols. When using SPDY, one single connection per host is |
1214 | enough, i.e. calling this method multiple times per host will not result in faster |
1215 | network transactions. |
1216 | |
1217 | \note This function has no possibility to report errors. |
1218 | |
1219 | \sa connectToHost(), get(), post(), put(), deleteResource() |
1220 | */ |
1221 | |
1222 | void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quint16 port, |
1223 | const QSslConfiguration &sslConfiguration, |
1224 | const QString &peerName) |
1225 | { |
1226 | QUrl url; |
1227 | url.setHost(host: hostName); |
1228 | url.setPort(port); |
1229 | url.setScheme(QLatin1String("preconnect-https" )); |
1230 | QNetworkRequest request(url); |
1231 | if (sslConfiguration != QSslConfiguration::defaultConfiguration()) |
1232 | request.setSslConfiguration(sslConfiguration); |
1233 | |
1234 | // There is no way to enable SPDY/HTTP2 via a request, so we need to check |
1235 | // the ssl configuration whether SPDY/HTTP2 is allowed here. |
1236 | if (sslConfiguration.allowedNextProtocols().contains(t: QSslConfiguration::ALPNProtocolHTTP2)) |
1237 | request.setAttribute(code: QNetworkRequest::Http2AllowedAttribute, value: true); |
1238 | else if (sslConfiguration.allowedNextProtocols().contains(t: QSslConfiguration::NextProtocolSpdy3_0)) |
1239 | request.setAttribute(code: QNetworkRequest::SpdyAllowedAttribute, value: true); |
1240 | |
1241 | request.setPeerVerifyName(peerName); |
1242 | get(request); |
1243 | } |
1244 | #endif |
1245 | |
1246 | /*! |
1247 | \since 5.2 |
1248 | |
1249 | Initiates a connection to the host given by \a hostName at port \a port. |
1250 | This function is useful to complete the TCP handshake |
1251 | to a host before the HTTP request is made, resulting in a lower network latency. |
1252 | |
1253 | \note This function has no possibility to report errors. |
1254 | |
1255 | \sa connectToHostEncrypted(), get(), post(), put(), deleteResource() |
1256 | */ |
1257 | void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port) |
1258 | { |
1259 | QUrl url; |
1260 | url.setHost(host: hostName); |
1261 | url.setPort(port); |
1262 | url.setScheme(QLatin1String("preconnect-http" )); |
1263 | QNetworkRequest request(url); |
1264 | get(request); |
1265 | } |
1266 | |
1267 | /*! |
1268 | \since 5.9 |
1269 | |
1270 | Sets the manager's redirect policy to be the \a policy specified. This policy |
1271 | will affect all subsequent requests created by the manager. |
1272 | |
1273 | Use this function to enable or disable HTTP redirects on the manager's level. |
1274 | |
1275 | \note When creating a request QNetworkRequest::RedirectAttributePolicy has |
1276 | the highest priority, next by priority is QNetworkRequest::FollowRedirectsAttribute. |
1277 | Finally, the manager's policy has the lowest priority. |
1278 | |
1279 | For backwards compatibility the default value is QNetworkRequest::ManualRedirectPolicy. |
1280 | This may change in the future and some type of auto-redirect policy will become |
1281 | the default; clients relying on manual redirect handling are encouraged to set |
1282 | this policy explicitly in their code. |
1283 | |
1284 | \sa redirectPolicy(), QNetworkRequest::RedirectPolicy, |
1285 | QNetworkRequest::FollowRedirectsAttribute |
1286 | */ |
1287 | void QNetworkAccessManager::setRedirectPolicy(QNetworkRequest::RedirectPolicy policy) |
1288 | { |
1289 | Q_D(QNetworkAccessManager); |
1290 | d->redirectPolicy = policy; |
1291 | } |
1292 | |
1293 | /*! |
1294 | \since 5.9 |
1295 | |
1296 | Returns the redirect policy that is used when creating new requests. |
1297 | |
1298 | \sa setRedirectPolicy(), QNetworkRequest::RedirectPolicy |
1299 | */ |
1300 | QNetworkRequest::RedirectPolicy QNetworkAccessManager::redirectPolicy() const |
1301 | { |
1302 | Q_D(const QNetworkAccessManager); |
1303 | return d->redirectPolicy; |
1304 | } |
1305 | |
1306 | /*! |
1307 | \since 4.7 |
1308 | |
1309 | Sends a custom request to the server identified by the URL of \a request. |
1310 | |
1311 | It is the user's responsibility to send a \a verb to the server that is valid |
1312 | according to the HTTP specification. |
1313 | |
1314 | This method provides means to send verbs other than the common ones provided |
1315 | via get() or post() etc., for instance sending an HTTP OPTIONS command. |
1316 | |
1317 | If \a data is not empty, the contents of the \a data |
1318 | device will be uploaded to the server; in that case, data must be open for |
1319 | reading and must remain valid until the finished() signal is emitted for this reply. |
1320 | |
1321 | \note This feature is currently available for HTTP(S) only. |
1322 | |
1323 | \sa get(), post(), put(), deleteResource() |
1324 | */ |
1325 | QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data) |
1326 | { |
1327 | QNetworkRequest newRequest(request); |
1328 | newRequest.setAttribute(code: QNetworkRequest::CustomVerbAttribute, value: verb); |
1329 | return d_func()->postProcess(reply: createRequest(op: QNetworkAccessManager::CustomOperation, request: newRequest, outgoingData: data)); |
1330 | } |
1331 | |
1332 | /*! |
1333 | \since 5.8 |
1334 | |
1335 | \overload |
1336 | |
1337 | Sends the contents of the \a data byte array to the destination |
1338 | specified by \a request. |
1339 | */ |
1340 | QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data) |
1341 | { |
1342 | QBuffer *buffer = new QBuffer; |
1343 | buffer->setData(data); |
1344 | buffer->open(openMode: QIODevice::ReadOnly); |
1345 | |
1346 | QNetworkReply *reply = sendCustomRequest(request, verb, data: buffer); |
1347 | buffer->setParent(reply); |
1348 | return reply; |
1349 | } |
1350 | |
1351 | #if QT_CONFIG(http) |
1352 | /*! |
1353 | \since 5.8 |
1354 | |
1355 | \overload |
1356 | |
1357 | Sends a custom request to the server identified by the URL of \a request. |
1358 | |
1359 | Sends the contents of the \a multiPart message to the destination |
1360 | specified by \a request. |
1361 | |
1362 | This can be used for sending MIME multipart messages for custom verbs. |
1363 | |
1364 | \sa QHttpMultiPart, QHttpPart, put() |
1365 | */ |
1366 | QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart) |
1367 | { |
1368 | QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart); |
1369 | QIODevice *device = multiPart->d_func()->device; |
1370 | QNetworkReply *reply = sendCustomRequest(request: newRequest, verb, data: device); |
1371 | return reply; |
1372 | } |
1373 | #endif // QT_CONFIG(http) |
1374 | |
1375 | /*! |
1376 | Returns a new QNetworkReply object to handle the operation \a op |
1377 | and request \a originalReq. The device \a outgoingData is always 0 |
1378 | for Get and Head requests, but is the value passed to post() and |
1379 | put() in those operations (the QByteArray variants will pass a QBuffer |
1380 | object). |
1381 | |
1382 | The default implementation calls QNetworkCookieJar::cookiesForUrl() |
1383 | on the cookie jar set with setCookieJar() to obtain the cookies to |
1384 | be sent to the remote server. |
1385 | |
1386 | The returned object must be in an open state. |
1387 | */ |
1388 | QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op, |
1389 | const QNetworkRequest &originalReq, |
1390 | QIODevice *outgoingData) |
1391 | { |
1392 | Q_D(QNetworkAccessManager); |
1393 | |
1394 | QNetworkRequest req(originalReq); |
1395 | if (redirectPolicy() != QNetworkRequest::ManualRedirectPolicy |
1396 | && req.attribute(code: QNetworkRequest::RedirectPolicyAttribute).isNull() |
1397 | && req.attribute(code: QNetworkRequest::FollowRedirectsAttribute).isNull()) { |
1398 | req.setAttribute(code: QNetworkRequest::RedirectPolicyAttribute, value: redirectPolicy()); |
1399 | } |
1400 | |
1401 | #if QT_CONFIG(http) || defined (Q_OS_WASM) |
1402 | if (!req.transferTimeout()) |
1403 | req.setTransferTimeout(transferTimeout()); |
1404 | #endif |
1405 | |
1406 | if (autoDeleteReplies() |
1407 | && req.attribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute).isNull()) { |
1408 | req.setAttribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, value: true); |
1409 | } |
1410 | |
1411 | bool isLocalFile = req.url().isLocalFile(); |
1412 | QString scheme = req.url().scheme(); |
1413 | |
1414 | |
1415 | // fast path for GET on file:// URLs |
1416 | // The QNetworkAccessFileBackend will right now only be used for PUT |
1417 | if (op == QNetworkAccessManager::GetOperation |
1418 | || op == QNetworkAccessManager::HeadOperation) { |
1419 | if (isLocalFile |
1420 | #ifdef Q_OS_ANDROID |
1421 | || scheme == QLatin1String("assets" ) |
1422 | #endif |
1423 | || scheme == QLatin1String("qrc" )) { |
1424 | return new QNetworkReplyFileImpl(this, req, op); |
1425 | } |
1426 | |
1427 | if (scheme == QLatin1String("data" )) |
1428 | return new QNetworkReplyDataImpl(this, req, op); |
1429 | |
1430 | // A request with QNetworkRequest::AlwaysCache does not need any bearer management |
1431 | QNetworkRequest::CacheLoadControl mode = |
1432 | static_cast<QNetworkRequest::CacheLoadControl>( |
1433 | req.attribute(code: QNetworkRequest::CacheLoadControlAttribute, |
1434 | defaultValue: QNetworkRequest::PreferNetwork).toInt()); |
1435 | if (mode == QNetworkRequest::AlwaysCache) { |
1436 | // FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106 |
1437 | QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); |
1438 | QNetworkReplyImplPrivate *priv = reply->d_func(); |
1439 | priv->manager = this; |
1440 | priv->backend = new QNetworkAccessCacheBackend(); |
1441 | priv->backend->manager = this->d_func(); |
1442 | priv->backend->setParent(reply); |
1443 | priv->backend->reply = priv; |
1444 | priv->setup(op, request: req, outgoingData); |
1445 | return reply; |
1446 | } |
1447 | } |
1448 | QNetworkRequest request = req; |
1449 | if (!request.header(header: QNetworkRequest::ContentLengthHeader).isValid() && |
1450 | outgoingData && !outgoingData->isSequential()) { |
1451 | // request has no Content-Length |
1452 | // but the data that is outgoing is random-access |
1453 | request.setHeader(header: QNetworkRequest::ContentLengthHeader, value: outgoingData->size()); |
1454 | } |
1455 | |
1456 | if (static_cast<QNetworkRequest::LoadControl> |
1457 | (request.attribute(code: QNetworkRequest::CookieLoadControlAttribute, |
1458 | defaultValue: QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) { |
1459 | if (d->cookieJar) { |
1460 | QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(url: request.url()); |
1461 | if (!cookies.isEmpty()) |
1462 | request.setHeader(header: QNetworkRequest::CookieHeader, value: QVariant::fromValue(value: cookies)); |
1463 | } |
1464 | } |
1465 | #ifdef Q_OS_WASM |
1466 | // Support http, https, and relative urls |
1467 | if (scheme == QLatin1String("http" ) || scheme == QLatin1String("https" ) || scheme.isEmpty()) { |
1468 | QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this); |
1469 | QNetworkReplyWasmImplPrivate *priv = reply->d_func(); |
1470 | priv->manager = this; |
1471 | priv->setup(op, request, outgoingData); |
1472 | return reply; |
1473 | } |
1474 | #endif |
1475 | |
1476 | #if QT_CONFIG(http) |
1477 | // Since Qt 5 we use the new QNetworkReplyHttpImpl |
1478 | if (scheme == QLatin1String("http" ) || scheme == QLatin1String("preconnect-http" ) |
1479 | #ifndef QT_NO_SSL |
1480 | || scheme == QLatin1String("https" ) || scheme == QLatin1String("preconnect-https" ) |
1481 | #endif |
1482 | ) { |
1483 | #ifndef QT_NO_SSL |
1484 | if (isStrictTransportSecurityEnabled() && d->stsCache.isKnownHost(url: request.url())) { |
1485 | QUrl stsUrl(request.url()); |
1486 | // RFC6797, 8.3: |
1487 | // The UA MUST replace the URI scheme with "https" [RFC2818], |
1488 | // and if the URI contains an explicit port component of "80", |
1489 | // then the UA MUST convert the port component to be "443", or |
1490 | // if the URI contains an explicit port component that is not |
1491 | // equal to "80", the port component value MUST be preserved; |
1492 | // otherwise, |
1493 | // if the URI does not contain an explicit port component, the UA |
1494 | // MUST NOT add one. |
1495 | if (stsUrl.port() == 80) |
1496 | stsUrl.setPort(443); |
1497 | stsUrl.setScheme(QLatin1String("https" )); |
1498 | request.setUrl(stsUrl); |
1499 | } |
1500 | #endif |
1501 | QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData); |
1502 | #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section |
1503 | if (!d->statusMonitor->isEnabled()) { |
1504 | connect(sender: this, SIGNAL(networkSessionConnected()), |
1505 | receiver: reply, SLOT(_q_networkSessionConnected())); |
1506 | } |
1507 | #endif |
1508 | return reply; |
1509 | } |
1510 | #endif // QT_CONFIG(http) |
1511 | |
1512 | // first step: create the reply |
1513 | QNetworkReplyImpl *reply = new QNetworkReplyImpl(this); |
1514 | #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section |
1515 | // NETMONTODO: network reply impl must be augmented to use the same monitoring |
1516 | // capabilities as http network reply impl does. Once it does: uncomment the condition below |
1517 | if (!isLocalFile /*&& !d->statusMonitor.isEnabled()*/) { |
1518 | connect(sender: this, SIGNAL(networkSessionConnected()), |
1519 | receiver: reply, SLOT(_q_networkSessionConnected())); |
1520 | } |
1521 | #endif |
1522 | QNetworkReplyImplPrivate *priv = reply->d_func(); |
1523 | priv->manager = this; |
1524 | |
1525 | // second step: fetch cached credentials |
1526 | // This is not done for the time being, we should use signal emissions to request |
1527 | // the credentials from cache. |
1528 | |
1529 | // third step: find a backend |
1530 | priv->backend = d->findBackend(op, request); |
1531 | |
1532 | if (priv->backend) { |
1533 | priv->backend->setParent(reply); |
1534 | priv->backend->reply = priv; |
1535 | } |
1536 | |
1537 | #ifndef QT_NO_SSL |
1538 | reply->setSslConfiguration(request.sslConfiguration()); |
1539 | #endif |
1540 | |
1541 | // fourth step: setup the reply |
1542 | priv->setup(op, request, outgoingData); |
1543 | |
1544 | return reply; |
1545 | } |
1546 | |
1547 | /*! |
1548 | \since 5.2 |
1549 | |
1550 | Lists all the URL schemes supported by the access manager. |
1551 | |
1552 | \sa supportedSchemesImplementation() |
1553 | */ |
1554 | QStringList QNetworkAccessManager::supportedSchemes() const |
1555 | { |
1556 | QStringList schemes; |
1557 | QNetworkAccessManager *self = const_cast<QNetworkAccessManager *>(this); // We know we call a const slot |
1558 | QMetaObject::invokeMethod(obj: self, member: "supportedSchemesImplementation" , Qt::DirectConnection, |
1559 | Q_RETURN_ARG(QStringList, schemes)); |
1560 | schemes.removeDuplicates(); |
1561 | return schemes; |
1562 | } |
1563 | |
1564 | /*! |
1565 | \since 5.2 |
1566 | |
1567 | Lists all the URL schemes supported by the access manager. |
1568 | |
1569 | You should not call this function directly; use |
1570 | QNetworkAccessManager::supportedSchemes() instead. |
1571 | |
1572 | Reimplement this slot to provide your own supported schemes |
1573 | in a QNetworkAccessManager subclass. It is for instance necessary |
1574 | when your subclass provides support for new protocols. |
1575 | |
1576 | Because of binary compatibility constraints, the supportedSchemes() |
1577 | method (introduced in Qt 5.2) is not virtual. Instead, supportedSchemes() |
1578 | will dynamically detect and call this slot. |
1579 | |
1580 | \sa supportedSchemes() |
1581 | */ |
1582 | QStringList QNetworkAccessManager::supportedSchemesImplementation() const |
1583 | { |
1584 | Q_D(const QNetworkAccessManager); |
1585 | |
1586 | QStringList schemes = d->backendSupportedSchemes(); |
1587 | // Those ones don't exist in backends |
1588 | #if QT_CONFIG(http) |
1589 | schemes << QStringLiteral("http" ); |
1590 | #ifndef QT_NO_SSL |
1591 | if (QSslSocket::supportsSsl()) |
1592 | schemes << QStringLiteral("https" ); |
1593 | #endif |
1594 | #endif |
1595 | schemes << QStringLiteral("data" ); |
1596 | return schemes; |
1597 | } |
1598 | |
1599 | /*! |
1600 | \since 5.0 |
1601 | |
1602 | Flushes the internal cache of authentication data and network connections. |
1603 | |
1604 | This function is useful for doing auto tests. |
1605 | |
1606 | \sa clearConnectionCache() |
1607 | */ |
1608 | void QNetworkAccessManager::clearAccessCache() |
1609 | { |
1610 | QNetworkAccessManagerPrivate::clearAuthenticationCache(manager: this); |
1611 | QNetworkAccessManagerPrivate::clearConnectionCache(manager: this); |
1612 | } |
1613 | |
1614 | /*! |
1615 | \since 5.9 |
1616 | |
1617 | Flushes the internal cache of network connections. |
1618 | In contrast to clearAccessCache() the authentication data |
1619 | is preserved. |
1620 | |
1621 | \sa clearAccessCache() |
1622 | */ |
1623 | void QNetworkAccessManager::clearConnectionCache() |
1624 | { |
1625 | QNetworkAccessManagerPrivate::clearConnectionCache(manager: this); |
1626 | } |
1627 | |
1628 | |
1629 | /*! |
1630 | \since 5.14 |
1631 | |
1632 | Returns the true if QNetworkAccessManager is currently configured |
1633 | to automatically delete QNetworkReplies, false otherwise. |
1634 | |
1635 | \sa setAutoDeleteReplies, |
1636 | QNetworkRequest::AutoDeleteReplyOnFinishAttribute |
1637 | */ |
1638 | bool QNetworkAccessManager::autoDeleteReplies() const |
1639 | { |
1640 | return d_func()->autoDeleteReplies; |
1641 | } |
1642 | |
1643 | /*! |
1644 | \since 5.14 |
1645 | |
1646 | Enables or disables automatic deletion of \l {QNetworkReply} {QNetworkReplies}. |
1647 | |
1648 | Setting \a shouldAutoDelete to true is the same as setting the |
1649 | QNetworkRequest::AutoDeleteReplyOnFinishAttribute attribute to |
1650 | true on all \e{future} \l {QNetworkRequest} {QNetworkRequests} |
1651 | passed to this instance of QNetworkAccessManager unless the |
1652 | attribute was already explicitly set on the QNetworkRequest. |
1653 | |
1654 | \sa autoDeleteReplies, |
1655 | QNetworkRequest::AutoDeleteReplyOnFinishAttribute |
1656 | */ |
1657 | void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete) |
1658 | { |
1659 | d_func()->autoDeleteReplies = shouldAutoDelete; |
1660 | } |
1661 | |
1662 | /*! |
1663 | \since 5.15 |
1664 | |
1665 | Returns the timeout used for transfers, in milliseconds. |
1666 | |
1667 | This timeout is zero if setTransferTimeout() hasn't been |
1668 | called, which means that the timeout is not used. |
1669 | */ |
1670 | int QNetworkAccessManager::transferTimeout() const |
1671 | { |
1672 | return d_func()->transferTimeout; |
1673 | } |
1674 | |
1675 | /*! |
1676 | \since 5.15 |
1677 | |
1678 | Sets \a timeout as the transfer timeout in milliseconds. |
1679 | |
1680 | Transfers are aborted if no bytes are transferred before |
1681 | the timeout expires. Zero means no timer is set. If no |
1682 | argument is provided, the timeout is |
1683 | QNetworkRequest::DefaultTransferTimeoutConstant. If this function |
1684 | is not called, the timeout is disabled and has the |
1685 | value zero. The request-specific non-zero timeouts set for |
1686 | the requests that are executed override this value. This means |
1687 | that if QNetworkAccessManager has an enabled timeout, it needs |
1688 | to be disabled to execute a request without a timeout. |
1689 | |
1690 | \sa transferTimeout() |
1691 | */ |
1692 | void QNetworkAccessManager::setTransferTimeout(int timeout) |
1693 | { |
1694 | d_func()->transferTimeout = timeout; |
1695 | } |
1696 | |
1697 | void QNetworkAccessManagerPrivate::_q_replyFinished(QNetworkReply *reply) |
1698 | { |
1699 | Q_Q(QNetworkAccessManager); |
1700 | |
1701 | emit q->finished(reply); |
1702 | if (reply->request().attribute(code: QNetworkRequest::AutoDeleteReplyOnFinishAttribute, defaultValue: false).toBool()) |
1703 | QMetaObject::invokeMethod(context: reply, function: [reply] { reply->deleteLater(); }, type: Qt::QueuedConnection); |
1704 | |
1705 | #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section |
1706 | // If there are no active requests, release our reference to the network session. |
1707 | // It will not be destroyed immediately, but rather when the connection cache is flushed |
1708 | // after 2 minutes. |
1709 | activeReplyCount--; |
1710 | if (networkSessionStrongRef && activeReplyCount == 0) |
1711 | networkSessionStrongRef.clear(); |
1712 | #endif |
1713 | } |
1714 | |
1715 | void QNetworkAccessManagerPrivate::_q_replyEncrypted(QNetworkReply *reply) |
1716 | { |
1717 | #ifndef QT_NO_SSL |
1718 | Q_Q(QNetworkAccessManager); |
1719 | emit q->encrypted(reply); |
1720 | #else |
1721 | Q_UNUSED(reply); |
1722 | #endif |
1723 | } |
1724 | |
1725 | void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors) |
1726 | { |
1727 | #ifndef QT_NO_SSL |
1728 | Q_Q(QNetworkAccessManager); |
1729 | QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: q->sender()); |
1730 | if (reply) |
1731 | emit q->sslErrors(reply, errors); |
1732 | #else |
1733 | Q_UNUSED(errors); |
1734 | #endif |
1735 | } |
1736 | |
1737 | void QNetworkAccessManagerPrivate::_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator) |
1738 | { |
1739 | #ifndef QT_NO_SSL |
1740 | Q_Q(QNetworkAccessManager); |
1741 | QNetworkReply *reply = qobject_cast<QNetworkReply *>(object: q->sender()); |
1742 | if (reply) |
1743 | emit q->preSharedKeyAuthenticationRequired(reply, authenticator); |
1744 | #else |
1745 | Q_UNUSED(authenticator); |
1746 | #endif |
1747 | } |
1748 | |
1749 | QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply) |
1750 | { |
1751 | Q_Q(QNetworkAccessManager); |
1752 | QNetworkReplyPrivate::setManager(reply, manager: q); |
1753 | q->connect(sender: reply, signal: &QNetworkReply::finished, context: reply, |
1754 | slot: [this, reply]() { _q_replyFinished(reply); }); |
1755 | #ifndef QT_NO_SSL |
1756 | /* In case we're compiled without SSL support, we don't have this signal and we need to |
1757 | * avoid getting a connection error. */ |
1758 | q->connect(sender: reply, signal: &QNetworkReply::encrypted, context: reply, |
1759 | slot: [this, reply]() { _q_replyEncrypted(reply); }); |
1760 | q->connect(asender: reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>))); |
1761 | q->connect(asender: reply, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)), SLOT(_q_replyPreSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*))); |
1762 | #endif |
1763 | #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section |
1764 | activeReplyCount++; |
1765 | #endif |
1766 | |
1767 | return reply; |
1768 | } |
1769 | |
1770 | void QNetworkAccessManagerPrivate::createCookieJar() const |
1771 | { |
1772 | if (!cookieJarCreated) { |
1773 | // keep the ugly hack in here |
1774 | QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this); |
1775 | that->cookieJar = new QNetworkCookieJar(that->q_func()); |
1776 | that->cookieJarCreated = true; |
1777 | } |
1778 | } |
1779 | |
1780 | void QNetworkAccessManagerPrivate::authenticationRequired(QAuthenticator *authenticator, |
1781 | QNetworkReply *reply, |
1782 | bool synchronous, |
1783 | QUrl &url, |
1784 | QUrl *urlForLastAuthentication, |
1785 | bool allowAuthenticationReuse) |
1786 | { |
1787 | Q_Q(QNetworkAccessManager); |
1788 | |
1789 | // don't try the cache for the same URL twice in a row |
1790 | // being called twice for the same URL means the authentication failed |
1791 | // also called when last URL is empty, e.g. on first call |
1792 | if (allowAuthenticationReuse && (urlForLastAuthentication->isEmpty() |
1793 | || url != *urlForLastAuthentication)) { |
1794 | // if credentials are included in the url, then use them, unless they were already used |
1795 | if (!url.userName().isEmpty() && !url.password().isEmpty() |
1796 | && (url.userName() != authenticator->user() |
1797 | || url.password() != authenticator->password())) { |
1798 | authenticator->setUser(url.userName(options: QUrl::FullyDecoded)); |
1799 | authenticator->setPassword(url.password(QUrl::FullyDecoded)); |
1800 | *urlForLastAuthentication = url; |
1801 | authenticationManager->cacheCredentials(url, auth: authenticator); |
1802 | return; |
1803 | } |
1804 | |
1805 | QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, auth: authenticator); |
1806 | if (!cred.isNull() |
1807 | && (cred.user != authenticator->user() || cred.password != authenticator->password())) { |
1808 | authenticator->setUser(cred.user); |
1809 | authenticator->setPassword(cred.password); |
1810 | *urlForLastAuthentication = url; |
1811 | return; |
1812 | } |
1813 | } |
1814 | |
1815 | // if we emit a signal here in synchronous mode, the user might spin |
1816 | // an event loop, which might recurse and lead to problems |
1817 | if (synchronous) |
1818 | return; |
1819 | |
1820 | *urlForLastAuthentication = url; |
1821 | emit q->authenticationRequired(reply, authenticator); |
1822 | if (allowAuthenticationReuse) |
1823 | authenticationManager->cacheCredentials(url, auth: authenticator); |
1824 | } |
1825 | |
1826 | #ifndef QT_NO_NETWORKPROXY |
1827 | void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(const QUrl &url, |
1828 | const QNetworkProxy &proxy, |
1829 | bool synchronous, |
1830 | QAuthenticator *authenticator, |
1831 | QNetworkProxy *lastProxyAuthentication) |
1832 | { |
1833 | Q_Q(QNetworkAccessManager); |
1834 | QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth&: *authenticator); |
1835 | if (proxy != *lastProxyAuthentication && (!priv || !priv->hasFailed)) { |
1836 | QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy); |
1837 | if (!cred.isNull()) { |
1838 | authenticator->setUser(cred.user); |
1839 | authenticator->setPassword(cred.password); |
1840 | return; |
1841 | } |
1842 | } |
1843 | |
1844 | #if defined(Q_OS_MACOS) |
1845 | //now we try to get the username and password from keychain |
1846 | //if not successful signal will be emitted |
1847 | QString username; |
1848 | QString password; |
1849 | if (getProxyAuth(proxy.hostName(), url.scheme(), username, password)) { |
1850 | // only cache the system credentials if they are correct (or if they have changed) |
1851 | // to not run into an endless loop in case they are wrong |
1852 | QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy); |
1853 | if (!priv->hasFailed || cred.user != username || cred.password != password) { |
1854 | authenticator->setUser(username); |
1855 | authenticator->setPassword(password); |
1856 | authenticationManager->cacheProxyCredentials(proxy, authenticator); |
1857 | return; |
1858 | } |
1859 | } |
1860 | #else |
1861 | Q_UNUSED(url); |
1862 | #endif |
1863 | |
1864 | // if we emit a signal here in synchronous mode, the user might spin |
1865 | // an event loop, which might recurse and lead to problems |
1866 | if (synchronous) |
1867 | return; |
1868 | |
1869 | *lastProxyAuthentication = proxy; |
1870 | emit q->proxyAuthenticationRequired(proxy, authenticator); |
1871 | authenticationManager->cacheProxyCredentials(proxy, auth: authenticator); |
1872 | } |
1873 | |
1874 | QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query) |
1875 | { |
1876 | QList<QNetworkProxy> proxies; |
1877 | if (proxyFactory) { |
1878 | proxies = proxyFactory->queryProxy(query); |
1879 | if (proxies.isEmpty()) { |
1880 | qWarning(msg: "QNetworkAccessManager: factory %p has returned an empty result set" , |
1881 | proxyFactory); |
1882 | proxies << QNetworkProxy::NoProxy; |
1883 | } |
1884 | } else if (proxy.type() == QNetworkProxy::DefaultProxy) { |
1885 | // no proxy set, query the application |
1886 | return QNetworkProxyFactory::proxyForQuery(query); |
1887 | } else { |
1888 | proxies << proxy; |
1889 | } |
1890 | |
1891 | return proxies; |
1892 | } |
1893 | #endif |
1894 | |
1895 | void QNetworkAccessManagerPrivate::clearAuthenticationCache(QNetworkAccessManager *manager) |
1896 | { |
1897 | manager->d_func()->authenticationManager->clearCache(); |
1898 | } |
1899 | |
1900 | void QNetworkAccessManagerPrivate::clearConnectionCache(QNetworkAccessManager *manager) |
1901 | { |
1902 | manager->d_func()->objectCache.clear(); |
1903 | manager->d_func()->destroyThread(); |
1904 | } |
1905 | |
1906 | QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate() |
1907 | { |
1908 | destroyThread(); |
1909 | } |
1910 | |
1911 | QThread * QNetworkAccessManagerPrivate::createThread() |
1912 | { |
1913 | if (!thread) { |
1914 | thread = new QThread; |
1915 | thread->setObjectName(QStringLiteral("QNetworkAccessManager thread" )); |
1916 | thread->start(); |
1917 | } |
1918 | Q_ASSERT(thread); |
1919 | return thread; |
1920 | } |
1921 | |
1922 | void QNetworkAccessManagerPrivate::destroyThread() |
1923 | { |
1924 | if (thread) { |
1925 | thread->quit(); |
1926 | thread->wait(deadline: QDeadlineTimer(5000)); |
1927 | if (thread->isFinished()) |
1928 | delete thread; |
1929 | else |
1930 | QObject::connect(sender: thread, SIGNAL(finished()), receiver: thread, SLOT(deleteLater())); |
1931 | thread = nullptr; |
1932 | } |
1933 | } |
1934 | |
1935 | #ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section |
1936 | void QNetworkAccessManagerPrivate::createSession(const QNetworkConfiguration &config) |
1937 | { |
1938 | Q_Q(QNetworkAccessManager); |
1939 | |
1940 | initializeSession = false; |
1941 | |
1942 | //resurrect weak ref if possible |
1943 | networkSessionStrongRef = networkSessionWeakRef.toStrongRef(); |
1944 | |
1945 | QSharedPointer<QNetworkSession> newSession; |
1946 | if (config.isValid()) |
1947 | newSession = QSharedNetworkSessionManager::getSession(config); |
1948 | |
1949 | QNetworkSession::State oldState = QNetworkSession::Invalid; |
1950 | if (networkSessionStrongRef) { |
1951 | //do nothing if new and old session are the same |
1952 | if (networkSessionStrongRef == newSession) |
1953 | return; |
1954 | //disconnect from old session |
1955 | QObject::disconnect(sender: networkSessionStrongRef.data(), SIGNAL(opened()), receiver: q, SIGNAL(networkSessionConnected())); |
1956 | QObject::disconnect(sender: networkSessionStrongRef.data(), SIGNAL(closed()), receiver: q, SLOT(_q_networkSessionClosed())); |
1957 | QObject::disconnect(sender: networkSessionStrongRef.data(), SIGNAL(stateChanged(QNetworkSession::State)), |
1958 | receiver: q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); |
1959 | QObject::disconnect(sender: networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)), |
1960 | receiver: q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); |
1961 | oldState = networkSessionStrongRef->state(); |
1962 | } |
1963 | |
1964 | //switch to new session (null if config was invalid) |
1965 | networkSessionStrongRef = newSession; |
1966 | networkSessionWeakRef = networkSessionStrongRef.toWeakRef(); |
1967 | |
1968 | if (!networkSessionStrongRef) { |
1969 | |
1970 | QT_WARNING_PUSH |
1971 | QT_WARNING_DISABLE_DEPRECATED |
1972 | if (networkAccessible == QNetworkAccessManager::NotAccessible || !online) |
1973 | emit q->networkAccessibleChanged(accessible: QNetworkAccessManager::NotAccessible); |
1974 | else |
1975 | emit q->networkAccessibleChanged(accessible: QNetworkAccessManager::UnknownAccessibility); |
1976 | QT_WARNING_POP |
1977 | |
1978 | return; |
1979 | } |
1980 | |
1981 | //connect to new session |
1982 | QObject::connect(sender: networkSessionStrongRef.data(), SIGNAL(opened()), receiver: q, SIGNAL(networkSessionConnected()), Qt::QueuedConnection); |
1983 | //QueuedConnection is used to avoid deleting the networkSession inside its closed signal |
1984 | QObject::connect(sender: networkSessionStrongRef.data(), SIGNAL(closed()), receiver: q, SLOT(_q_networkSessionClosed()), Qt::QueuedConnection); |
1985 | QObject::connect(sender: networkSessionStrongRef.data(), SIGNAL(stateChanged(QNetworkSession::State)), |
1986 | receiver: q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State)), Qt::QueuedConnection); |
1987 | QObject::connect(sender: networkSessionStrongRef.data(), SIGNAL(error(QNetworkSession::SessionError)), |
1988 | receiver: q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); |
1989 | |
1990 | const QNetworkSession::State newState = networkSessionStrongRef->state(); |
1991 | if (newState != oldState) { |
1992 | QMetaObject::invokeMethod(obj: q, member: "_q_networkSessionStateChanged" , type: Qt::QueuedConnection, |
1993 | Q_ARG(QNetworkSession::State, newState)); |
1994 | } |
1995 | } |
1996 | |
1997 | void QNetworkAccessManagerPrivate::_q_networkSessionClosed() |
1998 | { |
1999 | Q_Q(QNetworkAccessManager); |
2000 | QSharedPointer<QNetworkSession> networkSession(getNetworkSession()); |
2001 | if (networkSession) { |
2002 | networkConfiguration = networkSession->configuration(); |
2003 | |
2004 | //disconnect from old session |
2005 | QObject::disconnect(sender: networkSession.data(), SIGNAL(opened()), receiver: q, SIGNAL(networkSessionConnected())); |
2006 | QObject::disconnect(sender: networkSession.data(), SIGNAL(closed()), receiver: q, SLOT(_q_networkSessionClosed())); |
2007 | QObject::disconnect(sender: networkSession.data(), SIGNAL(stateChanged(QNetworkSession::State)), |
2008 | receiver: q, SLOT(_q_networkSessionStateChanged(QNetworkSession::State))); |
2009 | QObject::disconnect(sender: networkSession.data(), SIGNAL(error(QNetworkSession::SessionError)), |
2010 | receiver: q, SLOT(_q_networkSessionFailed(QNetworkSession::SessionError))); |
2011 | |
2012 | networkSessionStrongRef.clear(); |
2013 | networkSessionWeakRef.clear(); |
2014 | } |
2015 | } |
2016 | |
2017 | void QNetworkAccessManagerPrivate::_q_networkSessionStateChanged(QNetworkSession::State state) |
2018 | { |
2019 | Q_Q(QNetworkAccessManager); |
2020 | bool reallyOnline = false; |
2021 | //Do not emit the networkSessionConnected signal here, except for roaming -> connected |
2022 | //transition, otherwise it is emitted twice in a row when opening a connection. |
2023 | QT_WARNING_PUSH |
2024 | QT_WARNING_DISABLE_DEPRECATED |
2025 | if (state == QNetworkSession::Connected && lastSessionState != QNetworkSession::Roaming) |
2026 | emit q->networkSessionConnected(); |
2027 | lastSessionState = state; |
2028 | |
2029 | if (online && (state == QNetworkSession::Disconnected |
2030 | || state == QNetworkSession::NotAvailable)) { |
2031 | const auto cfgs = networkConfigurationManager.allConfigurations(); |
2032 | for (const QNetworkConfiguration &cfg : cfgs) { |
2033 | if (cfg.state().testFlag(flag: QNetworkConfiguration::Active)) { |
2034 | reallyOnline = true; |
2035 | } |
2036 | } |
2037 | } else if (state == QNetworkSession::Connected || state == QNetworkSession::Roaming) { |
2038 | reallyOnline = true; |
2039 | } |
2040 | online = reallyOnline; |
2041 | |
2042 | if (!reallyOnline) { |
2043 | if (state != QNetworkSession::Connected && state != QNetworkSession::Roaming) { |
2044 | if (networkAccessible != QNetworkAccessManager::NotAccessible) { |
2045 | networkAccessible = QNetworkAccessManager::NotAccessible; |
2046 | emit q->networkAccessibleChanged(accessible: networkAccessible); |
2047 | } |
2048 | } |
2049 | } else { |
2050 | if (defaultAccessControl) |
2051 | if (networkAccessible != QNetworkAccessManager::Accessible) { |
2052 | networkAccessible = QNetworkAccessManager::Accessible; |
2053 | emit q->networkAccessibleChanged(accessible: networkAccessible); |
2054 | } |
2055 | } |
2056 | if (online && (state != QNetworkSession::Connected && state != QNetworkSession::Roaming)) { |
2057 | _q_networkSessionClosed(); |
2058 | createSession(config: q->configuration()); |
2059 | } |
2060 | QT_WARNING_POP |
2061 | } |
2062 | |
2063 | void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) |
2064 | { |
2065 | Q_Q(QNetworkAccessManager); |
2066 | |
2067 | QT_WARNING_PUSH |
2068 | QT_WARNING_DISABLE_DEPRECATED |
2069 | |
2070 | if (statusMonitor->isEnabled()) { |
2071 | auto previous = networkAccessible; |
2072 | networkAccessible = isOnline ? QNetworkAccessManager::Accessible : QNetworkAccessManager::NotAccessible; |
2073 | QT_WARNING_PUSH |
2074 | QT_WARNING_DISABLE_DEPRECATED |
2075 | if (previous != networkAccessible) |
2076 | emit q->networkAccessibleChanged(accessible: networkAccessible); |
2077 | QT_WARNING_POP |
2078 | return; |
2079 | } |
2080 | |
2081 | // if the user set a config, we only care whether this one is active. |
2082 | // Otherwise, this QNAM is online if there is an online config. |
2083 | if (customNetworkConfiguration) { |
2084 | online = (networkConfiguration.state() & QNetworkConfiguration::Active); |
2085 | } else { |
2086 | if (online != isOnline) { |
2087 | online = isOnline; |
2088 | _q_networkSessionClosed(); |
2089 | createSession(config: q->configuration()); |
2090 | } |
2091 | } |
2092 | if (online) { |
2093 | if (defaultAccessControl) { |
2094 | if (networkAccessible != QNetworkAccessManager::Accessible) { |
2095 | networkAccessible = QNetworkAccessManager::Accessible; |
2096 | emit q->networkAccessibleChanged(accessible: networkAccessible); |
2097 | } |
2098 | } |
2099 | } else { |
2100 | if (networkAccessible != QNetworkAccessManager::NotAccessible) { |
2101 | networkAccessible = QNetworkAccessManager::NotAccessible; |
2102 | emit q->networkAccessibleChanged(accessible: networkAccessible); |
2103 | } |
2104 | } |
2105 | QT_WARNING_POP |
2106 | } |
2107 | |
2108 | void QNetworkAccessManagerPrivate::_q_configurationChanged(const QNetworkConfiguration &configuration) |
2109 | { |
2110 | if (statusMonitor->isEnabled()) |
2111 | return; |
2112 | |
2113 | const QString id = configuration.identifier(); |
2114 | if (configuration.state().testFlag(flag: QNetworkConfiguration::Active)) { |
2115 | if (!onlineConfigurations.contains(value: id)) { |
2116 | QSharedPointer<QNetworkSession> session(getNetworkSession()); |
2117 | if (session) { |
2118 | if (online && session->configuration().identifier() |
2119 | != networkConfigurationManager.defaultConfiguration().identifier()) { |
2120 | |
2121 | onlineConfigurations.insert(value: id); |
2122 | // CHECK: If it's having Active flag - why would it be disconnected ??? |
2123 | //this one disconnected but another one is online, |
2124 | // close and create new session |
2125 | _q_networkSessionClosed(); |
2126 | createSession(config: networkConfigurationManager.defaultConfiguration()); |
2127 | } |
2128 | } |
2129 | } |
2130 | |
2131 | } else if (onlineConfigurations.contains(value: id)) { |
2132 | //this one is disconnecting |
2133 | // CHECK: If it disconnected while we create a session over a down configuration ??? |
2134 | onlineConfigurations.remove(value: id); |
2135 | if (!onlineConfigurations.isEmpty()) { |
2136 | _q_networkSessionClosed(); |
2137 | createSession(config: configuration); |
2138 | } |
2139 | } |
2140 | } |
2141 | |
2142 | |
2143 | void QNetworkAccessManagerPrivate::_q_networkSessionFailed(QNetworkSession::SessionError) |
2144 | { |
2145 | if (statusMonitor->isEnabled()) |
2146 | return; |
2147 | |
2148 | const auto cfgs = networkConfigurationManager.allConfigurations(); |
2149 | for (const QNetworkConfiguration &cfg : cfgs) { |
2150 | if (cfg.state().testFlag(flag: QNetworkConfiguration::Active)) { |
2151 | online = true; |
2152 | _q_networkSessionClosed(); |
2153 | createSession(config: networkConfigurationManager.defaultConfiguration()); |
2154 | return; |
2155 | } |
2156 | } |
2157 | } |
2158 | |
2159 | #else |
2160 | |
2161 | void QNetworkAccessManagerPrivate::_q_onlineStateChanged(bool isOnline) |
2162 | { |
2163 | networkAccessible = isOnline; |
2164 | } |
2165 | |
2166 | #endif // QT_NO_BEARERMANAGEMENT |
2167 | |
2168 | #if QT_CONFIG(http) |
2169 | QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart) |
2170 | { |
2171 | // copy the request, we probably need to add some headers |
2172 | QNetworkRequest newRequest(request); |
2173 | |
2174 | // add Content-Type header if not there already |
2175 | if (!request.header(header: QNetworkRequest::ContentTypeHeader).isValid()) { |
2176 | QByteArray contentType; |
2177 | contentType.reserve(asize: 34 + multiPart->d_func()->boundary.count()); |
2178 | contentType += "multipart/" ; |
2179 | switch (multiPart->d_func()->contentType) { |
2180 | case QHttpMultiPart::RelatedType: |
2181 | contentType += "related" ; |
2182 | break; |
2183 | case QHttpMultiPart::FormDataType: |
2184 | contentType += "form-data" ; |
2185 | break; |
2186 | case QHttpMultiPart::AlternativeType: |
2187 | contentType += "alternative" ; |
2188 | break; |
2189 | default: |
2190 | contentType += "mixed" ; |
2191 | break; |
2192 | } |
2193 | // putting the boundary into quotes, recommended in RFC 2046 section 5.1.1 |
2194 | contentType += "; boundary=\"" + multiPart->d_func()->boundary + '"'; |
2195 | newRequest.setHeader(header: QNetworkRequest::ContentTypeHeader, value: QVariant(contentType)); |
2196 | } |
2197 | |
2198 | // add MIME-Version header if not there already (we must include the header |
2199 | // if the message conforms to RFC 2045, see section 4 of that RFC) |
2200 | QByteArray ("MIME-Version" ); |
2201 | if (!request.hasRawHeader(headerName: mimeHeader)) |
2202 | newRequest.setRawHeader(headerName: mimeHeader, value: QByteArray("1.0" )); |
2203 | |
2204 | QIODevice *device = multiPart->d_func()->device; |
2205 | if (!device->isReadable()) { |
2206 | if (!device->isOpen()) { |
2207 | if (!device->open(mode: QIODevice::ReadOnly)) |
2208 | qWarning(msg: "could not open device for reading" ); |
2209 | } else { |
2210 | qWarning(msg: "device is not readable" ); |
2211 | } |
2212 | } |
2213 | |
2214 | return newRequest; |
2215 | } |
2216 | #endif // QT_CONFIG(http) |
2217 | |
2218 | QT_END_NAMESPACE |
2219 | |
2220 | #include "moc_qnetworkaccessmanager.cpp" |
2221 | |