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

Last change on this file since 769 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

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