choke uploadProgress signals
[profile/ivi/qtbase.git] / src / network / access / qhttpthreaddelegate.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 //#define QHTTPTHREADDELEGATE_DEBUG
43 #include "qhttpthreaddelegate_p.h"
44
45 #include <QThread>
46 #include <QTimer>
47 #include <QAuthenticator>
48 #include <QEventLoop>
49
50 #include "private/qhttpnetworkreply_p.h"
51 #include "private/qnetworkaccesscache_p.h"
52 #include "private/qnoncontiguousbytedevice_p.h"
53
54 #ifndef QT_NO_HTTP
55
56 QT_BEGIN_NAMESPACE
57
58 static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
59 {
60     QNetworkReply::NetworkError code;
61     // we've got an error
62     switch (httpStatusCode) {
63     case 401:               // Authorization required
64         code = QNetworkReply::AuthenticationRequiredError;
65         break;
66
67     case 403:               // Access denied
68         code = QNetworkReply::ContentOperationNotPermittedError;
69         break;
70
71     case 404:               // Not Found
72         code = QNetworkReply::ContentNotFoundError;
73         break;
74
75     case 405:               // Method Not Allowed
76         code = QNetworkReply::ContentOperationNotPermittedError;
77         break;
78
79     case 407:
80         code = QNetworkReply::ProxyAuthenticationRequiredError;
81         break;
82
83     case 418:               // I'm a teapot
84         code = QNetworkReply::ProtocolInvalidOperationError;
85         break;
86
87
88     default:
89         if (httpStatusCode > 500) {
90             // some kind of server error
91             code = QNetworkReply::ProtocolUnknownError;
92         } else if (httpStatusCode >= 400) {
93             // content error we did not handle above
94             code = QNetworkReply::UnknownContentError;
95         } else {
96             qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
97                      httpStatusCode, qPrintable(url.toString()));
98             code = QNetworkReply::ProtocolFailure;
99         }
100     }
101
102     return code;
103 }
104
105
106 static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
107 {
108     QString result;
109     QUrl copy = url;
110     bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
111     copy.setPort(copy.port(isEncrypted ? 443 : 80));
112     result = copy.toString(QUrl::RemoveUserInfo | QUrl::RemovePath |
113                            QUrl::RemoveQuery | QUrl::RemoveFragment | QUrl::FullyEncoded);
114
115 #ifndef QT_NO_NETWORKPROXY
116     if (proxy && proxy->type() != QNetworkProxy::NoProxy) {
117         QUrl key;
118
119         switch (proxy->type()) {
120         case QNetworkProxy::Socks5Proxy:
121             key.setScheme(QLatin1String("proxy-socks5"));
122             break;
123
124         case QNetworkProxy::HttpProxy:
125         case QNetworkProxy::HttpCachingProxy:
126             key.setScheme(QLatin1String("proxy-http"));
127             break;
128
129         default:
130             break;
131         }
132
133         if (!key.scheme().isEmpty()) {
134             key.setUserName(proxy->user());
135             key.setHost(proxy->hostName());
136             key.setPort(proxy->port());
137             key.setQuery(result);
138             result = key.toString(QUrl::FullyEncoded);
139         }
140     }
141 #else
142     Q_UNUSED(proxy)
143 #endif
144
145     return "http-connection:" + result.toLatin1();
146 }
147
148 class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
149                                       public QNetworkAccessCache::CacheableObject
150 {
151     // Q_OBJECT
152 public:
153 #ifdef QT_NO_BEARERMANAGEMENT
154     QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
155         : QHttpNetworkConnection(hostName, port, encrypt)
156 #else
157     QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt, QSharedPointer<QNetworkSession> networkSession)
158         : QHttpNetworkConnection(hostName, port, encrypt, /*parent=*/0, networkSession)
159 #endif
160     {
161         setExpires(true);
162         setShareable(true);
163     }
164
165     virtual void dispose()
166     {
167 #if 0  // sample code; do this right with the API
168         Q_ASSERT(!isWorking());
169 #endif
170         delete this;
171     }
172 };
173
174
175 QThreadStorage<QNetworkAccessCache *> QHttpThreadDelegate::connections;
176
177
178 QHttpThreadDelegate::~QHttpThreadDelegate()
179 {
180     // It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply
181     if (httpReply) {
182         delete httpReply;
183     }
184
185     // Get the object cache that stores our QHttpNetworkConnection objects
186     // and release the entry for this QHttpNetworkConnection
187     if (connections.hasLocalData() && !cacheKey.isEmpty()) {
188         connections.localData()->releaseEntry(cacheKey);
189     }
190 }
191
192
193 QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
194     QObject(parent)
195     , ssl(false)
196     , downloadBufferMaximumSize(0)
197     , readBufferMaxSize(0)
198     , bytesEmitted(0)
199     , pendingDownloadData(0)
200     , pendingDownloadProgress(0)
201     , synchronous(false)
202     , incomingStatusCode(0)
203     , isPipeliningUsed(false)
204     , incomingContentLength(-1)
205     , incomingErrorCode(QNetworkReply::NoError)
206     , downloadBuffer(0)
207     , httpConnection(0)
208     , httpReply(0)
209     , synchronousRequestLoop(0)
210 {
211 }
212
213 // This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread
214 void QHttpThreadDelegate::startRequestSynchronously()
215 {
216 #ifdef QHTTPTHREADDELEGATE_DEBUG
217     qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId();
218 #endif
219     synchronous = true;
220
221     QEventLoop synchronousRequestLoop;
222     this->synchronousRequestLoop = &synchronousRequestLoop;
223
224     // Worst case timeout
225     QTimer::singleShot(30*1000, this, SLOT(abortRequest()));
226
227     QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection);
228     synchronousRequestLoop.exec();
229
230     connections.localData()->releaseEntry(cacheKey);
231     connections.setLocalData(0);
232
233 #ifdef QHTTPTHREADDELEGATE_DEBUG
234     qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
235 #endif
236 }
237
238
239 // This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread
240 void QHttpThreadDelegate::startRequest()
241 {
242 #ifdef QHTTPTHREADDELEGATE_DEBUG
243     qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId();
244 #endif
245     // Check QThreadStorage for the QNetworkAccessCache
246     // If not there, create this connection cache
247     if (!connections.hasLocalData()) {
248         connections.setLocalData(new QNetworkAccessCache());
249     }
250
251     // check if we have an open connection to this host
252     QUrl urlCopy = httpRequest.url();
253     urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
254
255 #ifndef QT_NO_NETWORKPROXY
256     if (transparentProxy.type() != QNetworkProxy::NoProxy)
257         cacheKey = makeCacheKey(urlCopy, &transparentProxy);
258     else if (cacheProxy.type() != QNetworkProxy::NoProxy)
259         cacheKey = makeCacheKey(urlCopy, &cacheProxy);
260     else
261 #endif
262         cacheKey = makeCacheKey(urlCopy, 0);
263
264
265     // the http object is actually a QHttpNetworkConnection
266     httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections.localData()->requestEntryNow(cacheKey));
267     if (httpConnection == 0) {
268         // no entry in cache; create an object
269         // the http object is actually a QHttpNetworkConnection
270 #ifdef QT_NO_BEARERMANAGEMENT
271         httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
272 #else
273         httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, networkSession);
274 #endif
275 #ifndef QT_NO_SSL
276         // Set the QSslConfiguration from this QNetworkRequest.
277         if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {
278             httpConnection->setSslConfiguration(incomingSslConfiguration);
279         }
280 #endif
281
282 #ifndef QT_NO_NETWORKPROXY
283         httpConnection->setTransparentProxy(transparentProxy);
284         httpConnection->setCacheProxy(cacheProxy);
285 #endif
286
287         // cache the QHttpNetworkConnection corresponding to this cache key
288         connections.localData()->addEntry(cacheKey, httpConnection);
289     }
290
291
292     // Send the request to the connection
293     httpReply = httpConnection->sendRequest(httpRequest);
294     httpReply->setParent(this);
295
296     // Connect the reply signals that we need to handle and then forward
297     if (synchronous) {
298         connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
299         connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
300         connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),
301                 this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
302
303         connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
304                 this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
305         connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
306                 this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
307
308         // Don't care about ignored SSL errors for now in the synchronous HTTP case.
309     } else if (!synchronous) {
310         connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
311         connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
312         connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError, const QString)),
313                 this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
314         // some signals are only interesting when normal asynchronous style is used
315         connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
316         connect(httpReply,SIGNAL(dataReadProgress(qint64, qint64)), this, SLOT(dataReadProgressSlot(qint64,qint64)));
317 #ifndef QT_NO_SSL
318         connect(httpReply,SIGNAL(sslErrors(const QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
319 #endif
320
321         // In the asynchronous HTTP case we can just forward those signals
322         // Connect the reply signals that we can directly forward
323         connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
324                 this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
325         connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
326                 this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
327     }
328
329     connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
330             this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
331 }
332
333 // This gets called from the user thread or by the synchronous HTTP timeout timer
334 void QHttpThreadDelegate::abortRequest()
335 {
336 #ifdef QHTTPTHREADDELEGATE_DEBUG
337     qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
338 #endif
339     if (httpReply) {
340         delete httpReply;
341         httpReply = 0;
342     }
343
344     // Got aborted by the timeout timer
345     if (synchronous) {
346         incomingErrorCode = QNetworkReply::TimeoutError;
347         QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
348     } else {
349         //only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
350         this->deleteLater();
351     }
352 }
353
354 void QHttpThreadDelegate::readBufferSizeChanged(qint64 size)
355 {
356 #ifdef QHTTPTHREADDELEGATE_DEBUG
357     qDebug() << "QHttpThreadDelegate::readBufferSizeChanged() size " << size;
358 #endif
359     if (httpReply) {
360         httpReply->setDownstreamLimited(size > 0);
361         httpReply->setReadBufferSize(size);
362         readBufferMaxSize = size;
363     }
364 }
365
366 void QHttpThreadDelegate::readBufferFreed(qint64 size)
367 {
368     if (readBufferMaxSize) {
369         bytesEmitted -= size;
370
371         QMetaObject::invokeMethod(this, "readyReadSlot", Qt::QueuedConnection);
372     }
373 }
374
375 void QHttpThreadDelegate::readyReadSlot()
376 {
377     if (!httpReply)
378         return;
379
380     // Don't do in zerocopy case
381     if (!downloadBuffer.isNull())
382         return;
383
384     if (readBufferMaxSize) {
385         if (bytesEmitted < readBufferMaxSize) {
386             qint64 sizeEmitted = 0;
387             while (httpReply->readAnyAvailable() && (sizeEmitted < (readBufferMaxSize-bytesEmitted))) {
388                 if (httpReply->sizeNextBlock() > (readBufferMaxSize-bytesEmitted)) {
389                     sizeEmitted = readBufferMaxSize-bytesEmitted;
390                     bytesEmitted += sizeEmitted;
391                     pendingDownloadData->fetchAndAddRelease(1);
392                     emit downloadData(httpReply->read(sizeEmitted));
393                 } else {
394                     sizeEmitted = httpReply->sizeNextBlock();
395                     bytesEmitted += sizeEmitted;
396                     pendingDownloadData->fetchAndAddRelease(1);
397                     emit downloadData(httpReply->readAny());
398                 }
399             }
400         } else {
401             // We need to wait until we empty data from the read buffer in the reply.
402         }
403
404     } else {
405         while (httpReply->readAnyAvailable()) {
406             pendingDownloadData->fetchAndAddRelease(1);
407             emit downloadData(httpReply->readAny());
408         }
409     }
410 }
411
412 void QHttpThreadDelegate::finishedSlot()
413 {
414     if (!httpReply) {
415         qWarning("QHttpThreadDelegate::finishedSlot: HTTP reply had already been deleted, internal problem. Please report.");
416         return;
417     }
418 #ifdef QHTTPTHREADDELEGATE_DEBUG
419     qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
420 #endif
421
422     // If there is still some data left emit that now
423     while (httpReply->readAnyAvailable()) {
424         pendingDownloadData->fetchAndAddRelease(1);
425         emit downloadData(httpReply->readAny());
426     }
427
428 #ifndef QT_NO_SSL
429     if (ssl)
430         emit sslConfigurationChanged(httpReply->sslConfiguration());
431 #endif
432
433     if (httpReply->statusCode() >= 400) {
434             // it's an error reply
435             QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
436                                                           "Error downloading %1 - server replied: %2"));
437             msg = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
438             emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
439         }
440
441     emit downloadFinished();
442
443     QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
444     QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
445     httpReply = 0;
446 }
447
448 void QHttpThreadDelegate::synchronousFinishedSlot()
449 {
450 #ifdef QHTTPTHREADDELEGATE_DEBUG
451     qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
452 #endif
453     if (httpReply->statusCode() >= 400) {
454             // it's an error reply
455             QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
456                                                           "Error downloading %1 - server replied: %2"));
457             incomingErrorDetail = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
458             incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
459     }
460
461     synchronousDownloadData = httpReply->readAll();
462
463     QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
464     QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
465     httpReply = 0;
466 }
467
468 void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
469 {
470     if (!httpReply) {
471         qWarning("QHttpThreadDelegate::finishedWithErrorSlot: HTTP reply had already been deleted, internal problem. Please report.");
472         return;
473     }
474 #ifdef QHTTPTHREADDELEGATE_DEBUG
475     qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
476 #endif
477
478 #ifndef QT_NO_SSL
479     if (ssl)
480         emit sslConfigurationChanged(httpReply->sslConfiguration());
481 #endif
482     emit error(errorCode,detail);
483     emit downloadFinished();
484
485
486     QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
487     QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
488     httpReply = 0;
489 }
490
491
492 void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
493 {
494 #ifdef QHTTPTHREADDELEGATE_DEBUG
495     qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
496 #endif
497     incomingErrorCode = errorCode;
498     incomingErrorDetail = detail;
499
500     QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
501     QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
502     httpReply = 0;
503 }
504
505 static void downloadBufferDeleter(char *ptr)
506 {
507     delete[] ptr;
508 }
509
510 void QHttpThreadDelegate::headerChangedSlot()
511 {
512 #ifdef QHTTPTHREADDELEGATE_DEBUG
513     qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
514 #endif
515
516 #ifndef QT_NO_SSL
517     if (ssl)
518         emit sslConfigurationChanged(httpReply->sslConfiguration());
519 #endif
520
521     // Is using a zerocopy buffer allowed by user and possible with this reply?
522     if (httpReply->supportsUserProvidedDownloadBuffer()
523         && (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
524         QT_TRY {
525             char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
526             if (buf) {
527                 downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
528                 httpReply->setUserProvidedDownloadBuffer(buf);
529             }
530         } QT_CATCH(const std::bad_alloc &) {
531             // in out of memory situations, don't use downloadbuffer.
532         }
533     }
534
535     // We fetch this into our own
536     incomingHeaders = httpReply->header();
537     incomingStatusCode = httpReply->statusCode();
538     incomingReasonPhrase = httpReply->reasonPhrase();
539     isPipeliningUsed = httpReply->isPipeliningUsed();
540     incomingContentLength = httpReply->contentLength();
541
542     emit downloadMetaData(incomingHeaders,
543                           incomingStatusCode,
544                           incomingReasonPhrase,
545                           isPipeliningUsed,
546                           downloadBuffer,
547                           incomingContentLength);
548 }
549
550 void QHttpThreadDelegate::synchronousHeaderChangedSlot()
551 {
552 #ifdef QHTTPTHREADDELEGATE_DEBUG
553     qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
554 #endif
555     // Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
556     incomingHeaders = httpReply->header();
557     incomingStatusCode = httpReply->statusCode();
558     incomingReasonPhrase = httpReply->reasonPhrase();
559     isPipeliningUsed = httpReply->isPipeliningUsed();
560     incomingContentLength = httpReply->contentLength();
561 }
562
563
564 void QHttpThreadDelegate::dataReadProgressSlot(qint64 done, qint64 total)
565 {
566     // If we don't have a download buffer don't attempt to go this codepath
567     // It is not used by QNetworkAccessHttpBackend
568     if (downloadBuffer.isNull())
569         return;
570
571     pendingDownloadProgress->fetchAndAddRelease(1);
572     emit downloadProgress(done, total);
573 }
574
575 void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
576 {
577     authenticationManager->cacheCredentials(request.url(), authenticator);
578 }
579
580
581 #ifndef QT_NO_SSL
582 void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
583 {
584     emit sslConfigurationChanged(httpReply->sslConfiguration());
585
586     bool ignoreAll = false;
587     QList<QSslError> specificErrors;
588     emit sslErrors(errors, &ignoreAll, &specificErrors);
589     if (ignoreAll)
590         httpReply->ignoreSslErrors();
591     if (!specificErrors.isEmpty())
592         httpReply->ignoreSslErrors(specificErrors);
593 }
594 #endif
595
596 void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
597 {
598     Q_UNUSED(request);
599 #ifdef QHTTPTHREADDELEGATE_DEBUG
600     qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
601 #endif
602
603     // Ask the credential cache
604     QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
605     if (!credential.isNull()) {
606         a->setUser(credential.user);
607         a->setPassword(credential.password);
608     }
609
610     // Disconnect this connection now since we only want to ask the authentication cache once.
611     QObject::disconnect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
612         this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
613 }
614
615 #ifndef QT_NO_NETWORKPROXY
616 void  QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
617 {
618 #ifdef QHTTPTHREADDELEGATE_DEBUG
619     qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
620 #endif
621     // Ask the credential cache
622     QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
623     if (!credential.isNull()) {
624         a->setUser(credential.user);
625         a->setPassword(credential.password);
626     }
627
628     // Disconnect this connection now since we only want to ask the authentication cache once.
629     QObject::disconnect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
630         this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
631 }
632
633 #endif
634
635 #endif // QT_NO_HTTP
636
637 QT_END_NAMESPACE