source: trunk/src/network/access/qnetworkaccessmanager.cpp@ 385

Last change on this file since 385 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 32.9 KB
Line 
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
62QT_BEGIN_NAMESPACE
63
64#ifndef QT_NO_HTTP
65Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
66#endif // QT_NO_HTTP
67Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
68Q_GLOBAL_STATIC(QNetworkAccessDataBackendFactory, dataBackend)
69#ifndef QT_NO_FTP
70Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
71#endif // QT_NO_FTP
72
73#ifdef QT_BUILD_INTERNAL
74Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
75#endif
76
77static 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
230class QNetworkAuthenticationCredential
231{
232public:
233 QString domain;
234 QString user;
235 QString password;
236};
237Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
238inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
239{ return t1.domain < t2; }
240
241class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
242 public QNetworkAccessCache::CacheableObject
243{
244public:
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
286static 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
324static 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*/
335QNetworkAccessManager::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*/
348QNetworkAccessManager::~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*/
363QNetworkProxy 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*/
382void 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*/
402QNetworkProxyFactory *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*/
436void 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*/
452QAbstractNetworkCache *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*/
474void 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*/
491QNetworkCookieJar *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*/
527void 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*/
545QNetworkReply *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*/
559QNetworkReply *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*/
581QNetworkReply *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*/
591QNetworkReply *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*/
622QNetworkReply *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*/
632QNetworkReply *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*/
656QNetworkReply *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
711void 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
719void 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
731QNetworkReply *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
745void 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
755void 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
781void 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
801void 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
837QNetworkAuthenticationCredential *
838QNetworkAccessManagerPrivate::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
869QList<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
890void 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*/
932QNetworkAuthenticationCredential *
933QNetworkAccessManagerPrivate::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
954void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
955{
956 manager->d_func()->cache.clear();
957}
958
959QT_END_NAMESPACE
960
961#include "moc_qnetworkaccessmanager.cpp"
Note: See TracBrowser for help on using the repository browser.