1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** Contact: Qt Software Information ([email protected])
|
---|
5 | **
|
---|
6 | ** This file is part of the QtNetwork module of the Qt Toolkit.
|
---|
7 | **
|
---|
8 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
9 | ** Commercial Usage
|
---|
10 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
11 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
12 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
13 | ** a written agreement between you and Nokia.
|
---|
14 | **
|
---|
15 | ** GNU Lesser General Public License Usage
|
---|
16 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
17 | ** General Public License version 2.1 as published by the Free Software
|
---|
18 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
19 | ** packaging of this file. Please review the following information to
|
---|
20 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
21 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
22 | **
|
---|
23 | ** In addition, as a special exception, Nokia gives you certain
|
---|
24 | ** additional rights. These rights are described in the Nokia Qt LGPL
|
---|
25 | ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
|
---|
26 | ** package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you are unsure which license is appropriate for your use, please
|
---|
37 | ** contact the sales department at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
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 "qabstractnetworkcache.h"
|
---|
49 |
|
---|
50 | #include "qnetworkaccesshttpbackend_p.h"
|
---|
51 | #include "qnetworkaccessftpbackend_p.h"
|
---|
52 | #include "qnetworkaccessfilebackend_p.h"
|
---|
53 | #include "qnetworkaccessdatabackend_p.h"
|
---|
54 | #include "qnetworkaccessdebugpipebackend_p.h"
|
---|
55 |
|
---|
56 | #include "QtCore/qbuffer.h"
|
---|
57 | #include "QtCore/qurl.h"
|
---|
58 | #include "QtCore/qvector.h"
|
---|
59 | #include "QtNetwork/qauthenticator.h"
|
---|
60 | #include "QtNetwork/qsslconfiguration.h"
|
---|
61 |
|
---|
62 | QT_BEGIN_NAMESPACE
|
---|
63 |
|
---|
64 | #ifndef QT_NO_HTTP
|
---|
65 | Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
|
---|
66 | #endif // QT_NO_HTTP
|
---|
67 | Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
|
---|
68 | Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend)
|
---|
69 | #ifndef QT_NO_FTP
|
---|
70 | Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
|
---|
71 | #endif // QT_NO_FTP
|
---|
72 |
|
---|
73 | #ifdef QT_BUILD_INTERNAL
|
---|
74 | Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
|
---|
75 | #endif
|
---|
76 |
|
---|
77 | static void ensureInitialized()
|
---|
78 | {
|
---|
79 | #ifndef QT_NO_HTTP
|
---|
80 | (void) httpBackend();
|
---|
81 | #endif // QT_NO_HTTP
|
---|
82 | (void) dataBackend();
|
---|
83 | #ifndef QT_NO_FTP
|
---|
84 | (void) ftpBackend();
|
---|
85 | #endif
|
---|
86 |
|
---|
87 | #ifdef QT_BUILD_INTERNAL
|
---|
88 | (void) debugpipeBackend();
|
---|
89 | #endif
|
---|
90 |
|
---|
91 | // leave this one last since it will query the special QAbstractFileEngines
|
---|
92 | (void) fileBackend();
|
---|
93 | }
|
---|
94 |
|
---|
95 | /*!
|
---|
96 | \class QNetworkAccessManager
|
---|
97 | \brief The QNetworkAccessManager class allows the application to
|
---|
98 | post network requests and receive replies
|
---|
99 | \since 4.4
|
---|
100 |
|
---|
101 | \inmodule QtNetwork
|
---|
102 | \reentrant
|
---|
103 |
|
---|
104 | The Network Access API is constructed around one QNetworkAccessManager
|
---|
105 | object, which holds the common configuration and settings for the requests
|
---|
106 | it sends. It contains the proxy and cache configuration, as well as the
|
---|
107 | signals related to such issues, and reply signals that can be used to
|
---|
108 | monitor the progress of a network operation.
|
---|
109 |
|
---|
110 | Once a QNetworkAccessManager object has been created, the application can
|
---|
111 | use it to send requests over the network. A group of standard functions
|
---|
112 | are supplied that take a request and optional data, and each return a
|
---|
113 | QNetworkReply object. The returned object is used to obtain any data
|
---|
114 | returned in response to the corresponding request.
|
---|
115 | the reply to is where most of the signals as well
|
---|
116 | as the downloaded data are posted.
|
---|
117 |
|
---|
118 | A simple download off the network could be accomplished with:
|
---|
119 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 0
|
---|
120 |
|
---|
121 | When the \tt replyFinished slot above is called, the parameter it
|
---|
122 | takes is the QNetworkReply object containing the downloaded data
|
---|
123 | as well as meta-data (headers, etc.).
|
---|
124 |
|
---|
125 | \note The slot is responsible for deleting the object at that point.
|
---|
126 |
|
---|
127 | A more involved example, assuming the manager is already existent,
|
---|
128 | can be:
|
---|
129 | \snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 1
|
---|
130 |
|
---|
131 | \sa QNetworkRequest, QNetworkReply, QNetworkProxy
|
---|
132 | */
|
---|
133 |
|
---|
134 | /*!
|
---|
135 | \enum QNetworkAccessManager::Operation
|
---|
136 |
|
---|
137 | Indicates the operation this reply is processing.
|
---|
138 |
|
---|
139 | \value HeadOperation retrieve headers operation (created
|
---|
140 | with head())
|
---|
141 |
|
---|
142 | \value GetOperation retrieve headers and download contents
|
---|
143 | (created with get())
|
---|
144 |
|
---|
145 | \value PutOperation upload contents operation (created
|
---|
146 | with put())
|
---|
147 |
|
---|
148 | \value PostOperation send the contents of an HTML form for
|
---|
149 | processing via HTTP POST (created with post())
|
---|
150 |
|
---|
151 | \omitvalue UnknownOperation
|
---|
152 |
|
---|
153 | \sa QNetworkReply::operation()
|
---|
154 | */
|
---|
155 |
|
---|
156 | /*!
|
---|
157 | \fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
|
---|
158 |
|
---|
159 | This signal is emitted whenever a proxy requests authentication
|
---|
160 | and QNetworkAccessManager cannot find a valid, cached
|
---|
161 | credential. The slot connected to this signal should fill in the
|
---|
162 | credentials for the proxy \a proxy in the \a authenticator object.
|
---|
163 |
|
---|
164 | QNetworkAccessManager will cache the credentials internally. The
|
---|
165 | next time the proxy requests authentication, QNetworkAccessManager
|
---|
166 | will automatically send the same credential without emitting the
|
---|
167 | proxyAuthenticationRequired signal again.
|
---|
168 |
|
---|
169 | If the proxy rejects the credentials, QNetworkAccessManager will
|
---|
170 | emit the signal again.
|
---|
171 |
|
---|
172 | \sa proxy(), setProxy(), authenticationRequired()
|
---|
173 | */
|
---|
174 |
|
---|
175 | /*!
|
---|
176 | \fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
|
---|
177 |
|
---|
178 | This signal is emitted whenever a final server requests
|
---|
179 | authentication before it delivers the requested contents. The slot
|
---|
180 | connected to this signal should fill the credentials for the
|
---|
181 | contents (which can be determined by inspecting the \a reply
|
---|
182 | object) in the \a authenticator object.
|
---|
183 |
|
---|
184 | QNetworkAccessManager will cache the credentials internally and
|
---|
185 | will send the same values if the server requires authentication
|
---|
186 | again, without emitting the authenticationRequired() signal. If it
|
---|
187 | rejects the credentials, this signal will be emitted again.
|
---|
188 |
|
---|
189 | \sa proxyAuthenticationRequired()
|
---|
190 | */
|
---|
191 |
|
---|
192 | /*!
|
---|
193 | \fn void QNetworkAccessManager::finished(QNetworkReply *reply)
|
---|
194 |
|
---|
195 | This signal is emitted whenever a pending network reply is
|
---|
196 | finished. The \a reply parameter will contain a pointer to the
|
---|
197 | reply that has just finished. This signal is emitted in tandem
|
---|
198 | with the QNetworkReply::finished() signal.
|
---|
199 |
|
---|
200 | See QNetworkReply::finished() for information on the status that
|
---|
201 | the object will be in.
|
---|
202 |
|
---|
203 | \sa QNetworkReply::finished(), QNetworkReply::error()
|
---|
204 | */
|
---|
205 |
|
---|
206 | /*!
|
---|
207 | \fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
---|
208 |
|
---|
209 | This signal is emitted if the SSL/TLS session encountered errors
|
---|
210 | during the set up, including certificate verification errors. The
|
---|
211 | \a errors parameter contains the list of errors and \a reply is
|
---|
212 | the QNetworkReply that is encountering these errors.
|
---|
213 |
|
---|
214 | To indicate that the errors are not fatal and that the connection
|
---|
215 | should proceed, the QNetworkReply::ignoreSslErrors() function should be called
|
---|
216 | from the slot connected to this signal. If it is not called, the
|
---|
217 | SSL session will be torn down before any data is exchanged
|
---|
218 | (including the URL).
|
---|
219 |
|
---|
220 | This signal can be used to display an error message to the user
|
---|
221 | indicating that security may be compromised and display the
|
---|
222 | SSL settings (see sslConfiguration() to obtain it). If the user
|
---|
223 | decides to proceed after analyzing the remote certificate, the
|
---|
224 | slot should call ignoreSslErrors().
|
---|
225 |
|
---|
226 | \sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(),
|
---|
227 | QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
|
---|
228 | */
|
---|
229 |
|
---|
230 | class QNetworkAuthenticationCredential
|
---|
231 | {
|
---|
232 | public:
|
---|
233 | QString domain;
|
---|
234 | QString user;
|
---|
235 | QString password;
|
---|
236 | };
|
---|
237 | Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
|
---|
238 | inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
|
---|
239 | { return t1.domain < t2; }
|
---|
240 |
|
---|
241 | class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
|
---|
242 | public QNetworkAccessCache::CacheableObject
|
---|
243 | {
|
---|
244 | public:
|
---|
245 | QNetworkAuthenticationCache()
|
---|
246 | {
|
---|
247 | setExpires(false);
|
---|
248 | setShareable(true);
|
---|
249 | reserve(1);
|
---|
250 | }
|
---|
251 |
|
---|
252 | QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
|
---|
253 | {
|
---|
254 | iterator it = qLowerBound(begin(), end(), domain);
|
---|
255 | if (it == end() && !isEmpty())
|
---|
256 | --it;
|
---|
257 | if (it == end() || !domain.startsWith(it->domain))
|
---|
258 | return 0;
|
---|
259 | return &*it;
|
---|
260 | }
|
---|
261 |
|
---|
262 | void insert(const QString &domain, const QString &user, const QString &password)
|
---|
263 | {
|
---|
264 | QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
|
---|
265 | if (closestMatch && closestMatch->domain == domain) {
|
---|
266 | // we're overriding the current credentials
|
---|
267 | closestMatch->user = user;
|
---|
268 | closestMatch->password = password;
|
---|
269 | } else {
|
---|
270 | QNetworkAuthenticationCredential newCredential;
|
---|
271 | newCredential.domain = domain;
|
---|
272 | newCredential.user = user;
|
---|
273 | newCredential.password = password;
|
---|
274 |
|
---|
275 | if (closestMatch)
|
---|
276 | QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
|
---|
277 | else
|
---|
278 | QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
|
---|
279 | }
|
---|
280 | }
|
---|
281 |
|
---|
282 | virtual void dispose() { delete this; }
|
---|
283 | };
|
---|
284 |
|
---|
285 | #ifndef QT_NO_NETWORKPROXY
|
---|
286 | static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
|
---|
287 | {
|
---|
288 | QUrl key;
|
---|
289 |
|
---|
290 | switch (proxy.type()) {
|
---|
291 | case QNetworkProxy::Socks5Proxy:
|
---|
292 | key.setScheme(QLatin1String("proxy-socks5"));
|
---|
293 | break;
|
---|
294 |
|
---|
295 | case QNetworkProxy::HttpProxy:
|
---|
296 | case QNetworkProxy::HttpCachingProxy:
|
---|
297 | key.setScheme(QLatin1String("proxy-http"));
|
---|
298 | break;
|
---|
299 |
|
---|
300 | case QNetworkProxy::FtpCachingProxy:
|
---|
301 | key.setScheme(QLatin1String("proxy-ftp"));
|
---|
302 |
|
---|
303 | case QNetworkProxy::DefaultProxy:
|
---|
304 | case QNetworkProxy::NoProxy:
|
---|
305 | // shouldn't happen
|
---|
306 | return QByteArray();
|
---|
307 |
|
---|
308 | // no default:
|
---|
309 | // let there be errors if a new proxy type is added in the future
|
---|
310 | }
|
---|
311 |
|
---|
312 | if (key.scheme().isEmpty())
|
---|
313 | // proxy type not handled
|
---|
314 | return QByteArray();
|
---|
315 |
|
---|
316 | key.setUserName(proxy.user());
|
---|
317 | key.setHost(proxy.hostName());
|
---|
318 | key.setPort(proxy.port());
|
---|
319 | key.setFragment(realm);
|
---|
320 | return "auth:" + key.toEncoded();
|
---|
321 | }
|
---|
322 | #endif
|
---|
323 |
|
---|
324 | static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
|
---|
325 | {
|
---|
326 | QUrl copy = url;
|
---|
327 | copy.setFragment(realm);
|
---|
328 | return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
|
---|
329 | }
|
---|
330 |
|
---|
331 | /*!
|
---|
332 | Constructs a QNetworkAccessManager object that is the center of
|
---|
333 | the Network Access API and sets \a parent as the parent object.
|
---|
334 | */
|
---|
335 | QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
---|
336 | : QObject(*new QNetworkAccessManagerPrivate, parent)
|
---|
337 | {
|
---|
338 | ensureInitialized();
|
---|
339 | }
|
---|
340 |
|
---|
341 | /*!
|
---|
342 | Destroys the QNetworkAccessManager object and frees up any
|
---|
343 | resources. Note that QNetworkReply objects that are returned from
|
---|
344 | this class have this object set as their parents, which means that
|
---|
345 | they will be deleted along with it if you don't call
|
---|
346 | QObject::setParent() on them.
|
---|
347 | */
|
---|
348 | QNetworkAccessManager::~QNetworkAccessManager()
|
---|
349 | {
|
---|
350 | #ifndef QT_NO_NETWORKPROXY
|
---|
351 | delete d_func()->proxyFactory;
|
---|
352 | #endif
|
---|
353 | }
|
---|
354 |
|
---|
355 | #ifndef QT_NO_NETWORKPROXY
|
---|
356 | /*!
|
---|
357 | Returns the QNetworkProxy that the requests sent using this
|
---|
358 | QNetworkAccessManager object will use. The default value for the
|
---|
359 | proxy is QNetworkProxy::DefaultProxy.
|
---|
360 |
|
---|
361 | \sa setProxy(), setProxyFactory(), proxyAuthenticationRequired()
|
---|
362 | */
|
---|
363 | QNetworkProxy QNetworkAccessManager::proxy() const
|
---|
364 | {
|
---|
365 | return d_func()->proxy;
|
---|
366 | }
|
---|
367 |
|
---|
368 | /*!
|
---|
369 | Sets the proxy to be used in future requests to be \a proxy. This
|
---|
370 | does not affect requests that have already been sent. The
|
---|
371 | proxyAuthenticationRequired() signal will be emitted if the proxy
|
---|
372 | requests authentication.
|
---|
373 |
|
---|
374 | A proxy set with this function will be used for all requests
|
---|
375 | issued by QNetworkAccessManager. In some cases, it might be
|
---|
376 | necessary to select different proxies depending on the type of
|
---|
377 | request being sent or the destination host. If that's the case,
|
---|
378 | you should consider using setProxyFactory().
|
---|
379 |
|
---|
380 | \sa proxy(), proxyAuthenticationRequired()
|
---|
381 | */
|
---|
382 | void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy)
|
---|
383 | {
|
---|
384 | Q_D(QNetworkAccessManager);
|
---|
385 | delete d->proxyFactory;
|
---|
386 | d->proxy = proxy;
|
---|
387 | d->proxyFactory = 0;
|
---|
388 | }
|
---|
389 |
|
---|
390 | /*!
|
---|
391 | \fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
|
---|
392 | \since 4.5
|
---|
393 |
|
---|
394 | Returns the proxy factory that this QNetworkAccessManager object
|
---|
395 | is using to determine the proxies to be used for requests.
|
---|
396 |
|
---|
397 | Note that the pointer returned by this function is managed by
|
---|
398 | QNetworkAccessManager and could be deleted at any time.
|
---|
399 |
|
---|
400 | \sa setProxyFactory(), proxy()
|
---|
401 | */
|
---|
402 | QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
|
---|
403 | {
|
---|
404 | return d_func()->proxyFactory;
|
---|
405 | }
|
---|
406 |
|
---|
407 | /*!
|
---|
408 | \since 4.5
|
---|
409 |
|
---|
410 | Sets the proxy factory for this class to be \a factory. A proxy
|
---|
411 | factory is used to determine a more specific list of proxies to be
|
---|
412 | used for a given request, instead of trying to use the same proxy
|
---|
413 | value for all requests.
|
---|
414 |
|
---|
415 | All queries sent by QNetworkAccessManager will have type
|
---|
416 | QNetworkProxyQuery::UrlRequest.
|
---|
417 |
|
---|
418 | For example, a proxy factory could apply the following rules:
|
---|
419 | \list
|
---|
420 | \o if the target address is in the local network (for example,
|
---|
421 | if the hostname contains no dots or if it's an IP address in
|
---|
422 | the organization's range), return QNetworkProxy::NoProxy
|
---|
423 | \o if the request is FTP, return an FTP proxy
|
---|
424 | \o if the request is HTTP or HTTPS, then return an HTTP proxy
|
---|
425 | \o otherwise, return a SOCKSv5 proxy server
|
---|
426 | \endlist
|
---|
427 |
|
---|
428 | The lifetime of the object \a factory will be managed by
|
---|
429 | QNetworkAccessManager. It will delete the object when necessary.
|
---|
430 |
|
---|
431 | \note If a specific proxy is set with setProxy(), the factory will not
|
---|
432 | be used.
|
---|
433 |
|
---|
434 | \sa proxyFactory(), setProxy(), QNetworkProxyQuery
|
---|
435 | */
|
---|
436 | void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory)
|
---|
437 | {
|
---|
438 | Q_D(QNetworkAccessManager);
|
---|
439 | delete d->proxyFactory;
|
---|
440 | d->proxyFactory = factory;
|
---|
441 | d->proxy = QNetworkProxy();
|
---|
442 | }
|
---|
443 | #endif
|
---|
444 |
|
---|
445 | /*!
|
---|
446 | \since 4.5
|
---|
447 |
|
---|
448 | Returns the cache that is used to store data obtained from the network.
|
---|
449 |
|
---|
450 | \sa setCache()
|
---|
451 | */
|
---|
452 | QAbstractNetworkCache *QNetworkAccessManager::cache() const
|
---|
453 | {
|
---|
454 | Q_D(const QNetworkAccessManager);
|
---|
455 | return d->networkCache;
|
---|
456 | }
|
---|
457 |
|
---|
458 | /*!
|
---|
459 | \since 4.5
|
---|
460 |
|
---|
461 | Sets the manager's network cache to be the \a cache specified. The cache
|
---|
462 | is used for all requests dispatched by the manager.
|
---|
463 |
|
---|
464 | Use this function to set the network cache object to a class that implements
|
---|
465 | additional features, like saving the cookies to permanent storage.
|
---|
466 |
|
---|
467 | \note QNetworkAccessManager takes ownership of the \a cache object.
|
---|
468 |
|
---|
469 | QNetworkAccessManager by default does not have a set cache.
|
---|
470 | Qt provides a simple disk cache, QNetworkDiskCache, which can be used.
|
---|
471 |
|
---|
472 | \sa cache(), QNetworkRequest::CacheLoadControl
|
---|
473 | */
|
---|
474 | void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache)
|
---|
475 | {
|
---|
476 | Q_D(QNetworkAccessManager);
|
---|
477 | if (d->networkCache != cache) {
|
---|
478 | delete d->networkCache;
|
---|
479 | d->networkCache = cache;
|
---|
480 | d->networkCache->setParent(this);
|
---|
481 | }
|
---|
482 | }
|
---|
483 |
|
---|
484 | /*!
|
---|
485 | Returns the QNetworkCookieJar that is used to store cookies
|
---|
486 | obtained from the network as well as cookies that are about to be
|
---|
487 | sent.
|
---|
488 |
|
---|
489 | \sa setCookieJar()
|
---|
490 | */
|
---|
491 | QNetworkCookieJar *QNetworkAccessManager::cookieJar() const
|
---|
492 | {
|
---|
493 | Q_D(const QNetworkAccessManager);
|
---|
494 | if (!d->cookieJar)
|
---|
495 | d->createCookieJar();
|
---|
496 | return d->cookieJar;
|
---|
497 | }
|
---|
498 |
|
---|
499 | /*!
|
---|
500 | Sets the manager's cookie jar to be the \a cookieJar specified.
|
---|
501 | The cookie jar is used by all requests dispatched by the manager.
|
---|
502 |
|
---|
503 | Use this function to set the cookie jar object to a class that
|
---|
504 | implements additional features, like saving the cookies to permanent
|
---|
505 | storage.
|
---|
506 |
|
---|
507 | \note QNetworkAccessManager takes ownership of the \a cookieJar object.
|
---|
508 |
|
---|
509 | QNetworkAccessManager will set the parent of the \a cookieJar
|
---|
510 | passed to itself, so that the cookie jar is deleted when this
|
---|
511 | object is deleted as well. If you want to share cookie jars
|
---|
512 | between different QNetworkAccessManager objects, you may want to
|
---|
513 | set the cookie jar's parent to 0 after calling this function.
|
---|
514 |
|
---|
515 | QNetworkAccessManager by default does not implement any cookie
|
---|
516 | policy of its own: it accepts all cookies sent by the server, as
|
---|
517 | long as they are well formed and meet the minimum security
|
---|
518 | requirements (cookie domain matches the request's and cookie path
|
---|
519 | matches the request's). In order to implement your own security
|
---|
520 | policy, override the QNetworkCookieJar::cookiesForUrl() and
|
---|
521 | QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those
|
---|
522 | functions are called by QNetworkAccessManager when it detects a
|
---|
523 | new cookie.
|
---|
524 |
|
---|
525 | \sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl()
|
---|
526 | */
|
---|
527 | void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
|
---|
528 | {
|
---|
529 | Q_D(QNetworkAccessManager);
|
---|
530 | d->cookieJarCreated = true;
|
---|
531 | if (d->cookieJar != cookieJar) {
|
---|
532 | if (d->cookieJar && d->cookieJar->parent() == this)
|
---|
533 | delete d->cookieJar;
|
---|
534 | d->cookieJar = cookieJar;
|
---|
535 | d->cookieJar->setParent(this);
|
---|
536 | }
|
---|
537 | }
|
---|
538 |
|
---|
539 | /*!
|
---|
540 | This function is used to post a request to obtain the network
|
---|
541 | headers for \a request. It takes its name after the HTTP request
|
---|
542 | associated (HEAD). It returns a new QNetworkReply object which
|
---|
543 | will contain such headers.
|
---|
544 | */
|
---|
545 | QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
|
---|
546 | {
|
---|
547 | return d_func()->postProcess(createRequest(QNetworkAccessManager::HeadOperation, request));
|
---|
548 | }
|
---|
549 |
|
---|
550 | /*!
|
---|
551 | This function is used to post a request to obtain the contents of
|
---|
552 | the target \a request. It will cause the contents to be
|
---|
553 | downloaded, along with the headers associated with it. It returns
|
---|
554 | a new QNetworkReply object opened for reading which emits its
|
---|
555 | QIODevice::readyRead() signal whenever new data arrives.
|
---|
556 |
|
---|
557 | \sa post(), put()
|
---|
558 | */
|
---|
559 | QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
|
---|
560 | {
|
---|
561 | return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));
|
---|
562 | }
|
---|
563 |
|
---|
564 | /*!
|
---|
565 | This function is used to send an HTTP POST request to the
|
---|
566 | destination specified by \a request. The contents of the \a data
|
---|
567 | device will be uploaded to the server.
|
---|
568 |
|
---|
569 | \a data must be opened for reading when this function is called
|
---|
570 | and must remain valid until the finished() signal is emitted for
|
---|
571 | this reply.
|
---|
572 |
|
---|
573 | The returned QNetworkReply object will be open for reading and
|
---|
574 | will contain the reply sent by the server to the POST request.
|
---|
575 |
|
---|
576 | Note: sending a POST request on protocols other than HTTP and
|
---|
577 | HTTPS is undefined and will probably fail.
|
---|
578 |
|
---|
579 | \sa get(), put()
|
---|
580 | */
|
---|
581 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
|
---|
582 | {
|
---|
583 | return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data));
|
---|
584 | }
|
---|
585 |
|
---|
586 | /*!
|
---|
587 | \overload
|
---|
588 | This function sends the contents of the \a data byte array to the
|
---|
589 | destination specified by \a request.
|
---|
590 | */
|
---|
591 | QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
|
---|
592 | {
|
---|
593 | QBuffer *buffer = new QBuffer;
|
---|
594 | buffer->setData(data);
|
---|
595 | buffer->open(QIODevice::ReadOnly);
|
---|
596 |
|
---|
597 | QNetworkReply *reply = post(request, buffer);
|
---|
598 | buffer->setParent(reply);
|
---|
599 | return reply;
|
---|
600 | }
|
---|
601 |
|
---|
602 | /*!
|
---|
603 | This function is used to upload the contents of \a data to the
|
---|
604 | destination \a request.
|
---|
605 |
|
---|
606 | \a data must be opened for reading when this function is called
|
---|
607 | and must remain valid until the finished() signal is emitted for
|
---|
608 | this reply.
|
---|
609 |
|
---|
610 | The returned QNetworkReply object will be open for reply, but
|
---|
611 | whether anything will be available for reading is protocol
|
---|
612 | dependent. For HTTP, the server may send a small HTML page
|
---|
613 | indicating the upload was successful (or not). Other protocols
|
---|
614 | will probably have content in their replies.
|
---|
615 |
|
---|
616 | For HTTP, this request will send a PUT request, which most servers
|
---|
617 | do not allow. Form upload mechanisms, including that of uploading
|
---|
618 | files through HTML forms, use the POST mechanism.
|
---|
619 |
|
---|
620 | \sa get(), post()
|
---|
621 | */
|
---|
622 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
|
---|
623 | {
|
---|
624 | return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data));
|
---|
625 | }
|
---|
626 |
|
---|
627 | /*!
|
---|
628 | \overload
|
---|
629 | This function sends the contents of the \a data byte array to the
|
---|
630 | destination specified by \a request.
|
---|
631 | */
|
---|
632 | QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
|
---|
633 | {
|
---|
634 | QBuffer *buffer = new QBuffer;
|
---|
635 | buffer->setData(data);
|
---|
636 | buffer->open(QIODevice::ReadOnly);
|
---|
637 |
|
---|
638 | QNetworkReply *reply = put(request, buffer);
|
---|
639 | buffer->setParent(reply);
|
---|
640 | return reply;
|
---|
641 | }
|
---|
642 |
|
---|
643 | /*!
|
---|
644 | Returns a new QNetworkReply object to handle the operation \a op
|
---|
645 | and request \a req. The device \a outgoingData is always 0 for Get and
|
---|
646 | Head requests, but is the value passed to post() and put() in
|
---|
647 | those operations (the QByteArray variants will pass a QBuffer
|
---|
648 | object).
|
---|
649 |
|
---|
650 | The default implementation calls QNetworkCookieJar::cookiesForUrl()
|
---|
651 | on the cookie jar set with setCookieJar() to obtain the cookies to
|
---|
652 | be sent to the remote server.
|
---|
653 |
|
---|
654 | The returned object must be in an open state.
|
---|
655 | */
|
---|
656 | QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
|
---|
657 | const QNetworkRequest &req,
|
---|
658 | QIODevice *outgoingData)
|
---|
659 | {
|
---|
660 | Q_D(QNetworkAccessManager);
|
---|
661 | QNetworkRequest request = req;
|
---|
662 | if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
|
---|
663 | outgoingData && !outgoingData->isSequential()) {
|
---|
664 | // request has no Content-Length
|
---|
665 | // but the data that is outgoing is random-access
|
---|
666 | request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
|
---|
667 | }
|
---|
668 | if (d->cookieJar) {
|
---|
669 | QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
|
---|
670 | if (!cookies.isEmpty())
|
---|
671 | request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
|
---|
672 | }
|
---|
673 |
|
---|
674 | // first step: create the reply
|
---|
675 | QUrl url = request.url();
|
---|
676 | QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
|
---|
677 | QNetworkReplyImplPrivate *priv = reply->d_func();
|
---|
678 | priv->manager = this;
|
---|
679 |
|
---|
680 | // second step: fetch cached credentials
|
---|
681 | QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url);
|
---|
682 | if (cred) {
|
---|
683 | url.setUserName(cred->user);
|
---|
684 | url.setPassword(cred->password);
|
---|
685 | priv->urlForLastAuthentication = url;
|
---|
686 | }
|
---|
687 |
|
---|
688 | // third step: setup the reply
|
---|
689 | priv->setup(op, request, outgoingData);
|
---|
690 | if (request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt() !=
|
---|
691 | QNetworkRequest::AlwaysNetwork)
|
---|
692 | priv->setNetworkCache(d->networkCache);
|
---|
693 | #ifndef QT_NO_NETWORKPROXY
|
---|
694 | QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url()));
|
---|
695 | priv->proxyList = proxyList;
|
---|
696 | #endif
|
---|
697 |
|
---|
698 | // fourth step: find a backend
|
---|
699 | priv->backend = d->findBackend(op, request);
|
---|
700 | if (priv->backend) {
|
---|
701 | priv->backend->setParent(reply);
|
---|
702 | priv->backend->reply = priv;
|
---|
703 | }
|
---|
704 |
|
---|
705 | #ifndef QT_NO_OPENSSL
|
---|
706 | reply->setSslConfiguration(request.sslConfiguration());
|
---|
707 | #endif
|
---|
708 | return reply;
|
---|
709 | }
|
---|
710 |
|
---|
711 | void QNetworkAccessManagerPrivate::_q_replyFinished()
|
---|
712 | {
|
---|
713 | Q_Q(QNetworkAccessManager);
|
---|
714 | QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
|
---|
715 | if (reply)
|
---|
716 | emit q->finished(reply);
|
---|
717 | }
|
---|
718 |
|
---|
719 | void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors)
|
---|
720 | {
|
---|
721 | #ifndef QT_NO_OPENSSL
|
---|
722 | Q_Q(QNetworkAccessManager);
|
---|
723 | QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
|
---|
724 | if (reply)
|
---|
725 | emit q->sslErrors(reply, errors);
|
---|
726 | #else
|
---|
727 | Q_UNUSED(errors);
|
---|
728 | #endif
|
---|
729 | }
|
---|
730 |
|
---|
731 | QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
|
---|
732 | {
|
---|
733 | Q_Q(QNetworkAccessManager);
|
---|
734 | QNetworkReplyPrivate::setManager(reply, q);
|
---|
735 | q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished()));
|
---|
736 | #ifndef QT_NO_OPENSSL
|
---|
737 | /* In case we're compiled without SSL support, we don't have this signal and we need to
|
---|
738 | * avoid getting a connection error. */
|
---|
739 | q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
|
---|
740 | #endif
|
---|
741 |
|
---|
742 | return reply;
|
---|
743 | }
|
---|
744 |
|
---|
745 | void QNetworkAccessManagerPrivate::createCookieJar() const
|
---|
746 | {
|
---|
747 | if (!cookieJarCreated) {
|
---|
748 | // keep the ugly hack in here
|
---|
749 | QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this);
|
---|
750 | that->cookieJarCreated = true;
|
---|
751 | that->cookieJar = new QNetworkCookieJar(that->q_func());
|
---|
752 | }
|
---|
753 | }
|
---|
754 |
|
---|
755 | void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend *backend,
|
---|
756 | QAuthenticator *authenticator)
|
---|
757 | {
|
---|
758 | Q_Q(QNetworkAccessManager);
|
---|
759 |
|
---|
760 | // FIXME: Add support for domains (i.e., the leading path)
|
---|
761 | QUrl url = backend->reply->url;
|
---|
762 |
|
---|
763 | // don't try the cache for the same URL twice in a row
|
---|
764 | // being called twice for the same URL means the authentication failed
|
---|
765 | if (url != backend->reply->urlForLastAuthentication) {
|
---|
766 | QNetworkAuthenticationCredential *cred = fetchCachedCredentials(url, authenticator);
|
---|
767 | if (cred) {
|
---|
768 | authenticator->setUser(cred->user);
|
---|
769 | authenticator->setPassword(cred->password);
|
---|
770 | backend->reply->urlForLastAuthentication = url;
|
---|
771 | return;
|
---|
772 | }
|
---|
773 | }
|
---|
774 |
|
---|
775 | backend->reply->urlForLastAuthentication = url;
|
---|
776 | emit q->authenticationRequired(backend->reply->q_func(), authenticator);
|
---|
777 | addCredentials(url, authenticator);
|
---|
778 | }
|
---|
779 |
|
---|
780 | #ifndef QT_NO_NETWORKPROXY
|
---|
781 | void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend,
|
---|
782 | const QNetworkProxy &proxy,
|
---|
783 | QAuthenticator *authenticator)
|
---|
784 | {
|
---|
785 | Q_Q(QNetworkAccessManager);
|
---|
786 |
|
---|
787 | if (proxy != backend->reply->lastProxyAuthentication) {
|
---|
788 | QNetworkAuthenticationCredential *cred = fetchCachedCredentials(proxy);
|
---|
789 | if (cred) {
|
---|
790 | authenticator->setUser(cred->user);
|
---|
791 | authenticator->setPassword(cred->password);
|
---|
792 | return;
|
---|
793 | }
|
---|
794 | }
|
---|
795 |
|
---|
796 | backend->reply->lastProxyAuthentication = proxy;
|
---|
797 | emit q->proxyAuthenticationRequired(proxy, authenticator);
|
---|
798 | addCredentials(proxy, authenticator);
|
---|
799 | }
|
---|
800 |
|
---|
801 | void QNetworkAccessManagerPrivate::addCredentials(const QNetworkProxy &p,
|
---|
802 | const QAuthenticator *authenticator)
|
---|
803 | {
|
---|
804 | Q_ASSERT(authenticator);
|
---|
805 | Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
|
---|
806 | Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
|
---|
807 |
|
---|
808 | QString realm = authenticator->realm();
|
---|
809 | QNetworkProxy proxy = p;
|
---|
810 | proxy.setUser(authenticator->user());
|
---|
811 | // Set two credentials: one with the username and one without
|
---|
812 | do {
|
---|
813 | // Set two credentials actually: one with and one without the realm
|
---|
814 | do {
|
---|
815 | QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
|
---|
816 | if (cacheKey.isEmpty())
|
---|
817 | return; // should not happen
|
---|
818 |
|
---|
819 | QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
|
---|
820 | auth->insert(QString(), authenticator->user(), authenticator->password());
|
---|
821 | cache.addEntry(cacheKey, auth); // replace the existing one, if there's any
|
---|
822 |
|
---|
823 | if (realm.isEmpty()) {
|
---|
824 | break;
|
---|
825 | } else {
|
---|
826 | realm.clear();
|
---|
827 | }
|
---|
828 | } while (true);
|
---|
829 |
|
---|
830 | if (proxy.user().isEmpty())
|
---|
831 | break;
|
---|
832 | else
|
---|
833 | proxy.setUser(QString());
|
---|
834 | } while (true);
|
---|
835 | }
|
---|
836 |
|
---|
837 | QNetworkAuthenticationCredential *
|
---|
838 | QNetworkAccessManagerPrivate::fetchCachedCredentials(const QNetworkProxy &p,
|
---|
839 | const QAuthenticator *authenticator)
|
---|
840 | {
|
---|
841 | QNetworkProxy proxy = p;
|
---|
842 | if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
---|
843 | proxy = QNetworkProxy::applicationProxy();
|
---|
844 | }
|
---|
845 | if (!proxy.password().isEmpty())
|
---|
846 | return 0; // no need to set credentials if it already has them
|
---|
847 |
|
---|
848 | QString realm;
|
---|
849 | if (authenticator)
|
---|
850 | realm = authenticator->realm();
|
---|
851 |
|
---|
852 | QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
|
---|
853 | if (cacheKey.isEmpty())
|
---|
854 | return 0;
|
---|
855 | if (!cache.hasEntry(cacheKey))
|
---|
856 | return 0;
|
---|
857 |
|
---|
858 | QNetworkAuthenticationCache *auth =
|
---|
859 | static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey));
|
---|
860 | QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString());
|
---|
861 | cache.releaseEntry(cacheKey);
|
---|
862 |
|
---|
863 | // proxy cache credentials always have exactly one item
|
---|
864 | Q_ASSERT_X(cred, "QNetworkAccessManager",
|
---|
865 | "Internal inconsistency: found a cache key for a proxy, but it's empty");
|
---|
866 | return cred;
|
---|
867 | }
|
---|
868 |
|
---|
869 | QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
|
---|
870 | {
|
---|
871 | QList<QNetworkProxy> proxies;
|
---|
872 | if (proxyFactory) {
|
---|
873 | proxies = proxyFactory->queryProxy(query);
|
---|
874 | if (proxies.isEmpty()) {
|
---|
875 | qWarning("QNetworkAccessManager: factory %p has returned an empty result set",
|
---|
876 | proxyFactory);
|
---|
877 | proxies << QNetworkProxy::NoProxy;
|
---|
878 | }
|
---|
879 | } else if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
---|
880 | // no proxy set, query the application
|
---|
881 | return QNetworkProxyFactory::proxyForQuery(query);
|
---|
882 | } else {
|
---|
883 | proxies << proxy;
|
---|
884 | }
|
---|
885 |
|
---|
886 | return proxies;
|
---|
887 | }
|
---|
888 | #endif
|
---|
889 |
|
---|
890 | void QNetworkAccessManagerPrivate::addCredentials(const QUrl &url,
|
---|
891 | const QAuthenticator *authenticator)
|
---|
892 | {
|
---|
893 | Q_ASSERT(authenticator);
|
---|
894 | QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
|
---|
895 | QString realm = authenticator->realm();
|
---|
896 |
|
---|
897 | // Set two credentials actually: one with and one without the username in the URL
|
---|
898 | QUrl copy = url;
|
---|
899 | copy.setUserName(authenticator->user());
|
---|
900 | do {
|
---|
901 | QByteArray cacheKey = authenticationKey(copy, realm);
|
---|
902 | if (cache.hasEntry(cacheKey)) {
|
---|
903 | QNetworkAuthenticationCache *auth =
|
---|
904 | static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey));
|
---|
905 | auth->insert(domain, authenticator->user(), authenticator->password());
|
---|
906 | cache.releaseEntry(cacheKey);
|
---|
907 | } else {
|
---|
908 | QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
|
---|
909 | auth->insert(domain, authenticator->user(), authenticator->password());
|
---|
910 | cache.addEntry(cacheKey, auth);
|
---|
911 | }
|
---|
912 |
|
---|
913 | if (copy.userName().isEmpty()) {
|
---|
914 | break;
|
---|
915 | } else {
|
---|
916 | copy.setUserName(QString());
|
---|
917 | }
|
---|
918 | } while (true);
|
---|
919 | }
|
---|
920 |
|
---|
921 | /*!
|
---|
922 | Fetch the credential data from the credential cache.
|
---|
923 |
|
---|
924 | If auth is 0 (as it is when called from createRequest()), this will try to
|
---|
925 | look up with an empty realm. That fails in most cases for HTTP (because the
|
---|
926 | realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
|
---|
927 | never sends the credentials on the first attempt: it needs to find out what
|
---|
928 | authentication methods the server supports.
|
---|
929 |
|
---|
930 | For FTP, realm is always empty.
|
---|
931 | */
|
---|
932 | QNetworkAuthenticationCredential *
|
---|
933 | QNetworkAccessManagerPrivate::fetchCachedCredentials(const QUrl &url,
|
---|
934 | const QAuthenticator *authentication)
|
---|
935 | {
|
---|
936 | if (!url.password().isEmpty())
|
---|
937 | return 0; // no need to set credentials if it already has them
|
---|
938 |
|
---|
939 | QString realm;
|
---|
940 | if (authentication)
|
---|
941 | realm = authentication->realm();
|
---|
942 |
|
---|
943 | QByteArray cacheKey = authenticationKey(url, realm);
|
---|
944 | if (!cache.hasEntry(cacheKey))
|
---|
945 | return 0;
|
---|
946 |
|
---|
947 | QNetworkAuthenticationCache *auth =
|
---|
948 | static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey));
|
---|
949 | QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
|
---|
950 | cache.releaseEntry(cacheKey);
|
---|
951 | return cred;
|
---|
952 | }
|
---|
953 |
|
---|
954 | void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
|
---|
955 | {
|
---|
956 | manager->d_func()->cache.clear();
|
---|
957 | }
|
---|
958 |
|
---|
959 | QT_END_NAMESPACE
|
---|
960 |
|
---|
961 | #include "moc_qnetworkaccessmanager.cpp"
|
---|