cf92f55cdfa8b3dc026449bf2326a051240f0af4
[profile/ivi/qtbase.git] / src / network / access / qnetworkreplyhttpimpl.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 QNETWORKACCESSHTTPBACKEND_DEBUG
43
44 #include "qnetworkreplyhttpimpl_p.h"
45 #include "qnetworkaccessmanager_p.h"
46 #include "qnetworkaccesscache_p.h"
47 #include "qabstractnetworkcache.h"
48 #include "qnetworkrequest.h"
49 #include "qnetworkreply.h"
50 #include "qnetworkrequest_p.h"
51 #include "qnetworkcookie.h"
52 #include "qnetworkcookie_p.h"
53 #include "QtCore/qdatetime.h"
54 #include "QtCore/qelapsedtimer.h"
55 #include "QtNetwork/qsslconfiguration.h"
56 #include "qhttpthreaddelegate_p.h"
57 #include "qthread.h"
58 #include "QtCore/qcoreapplication.h"
59
60 #include "qnetworkcookiejar.h"
61
62 #ifndef QT_NO_HTTP
63
64 #include <string.h>             // for strchr
65
66 Q_DECLARE_METATYPE(QSharedPointer<char>)
67
68 QT_BEGIN_NAMESPACE
69
70 class QNetworkProxy;
71
72 static inline bool isSeparator(register char c)
73 {
74     static const char separators[] = "()<>@,;:\\\"/[]?={}";
75     return isLWS(c) || strchr(separators, c) != 0;
76 }
77
78 // ### merge with nextField in cookiejar.cpp
79 static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header)
80 {
81     // The HTTP header is of the form:
82     // header          = #1(directives)
83     // directives      = token | value-directive
84     // value-directive = token "=" (token | quoted-string)
85     QHash<QByteArray, QByteArray> result;
86
87     int pos = 0;
88     while (true) {
89         // skip spaces
90         pos = nextNonWhitespace(header, pos);
91         if (pos == header.length())
92             return result;      // end of parsing
93
94         // pos points to a non-whitespace
95         int comma = header.indexOf(',', pos);
96         int equal = header.indexOf('=', pos);
97         if (comma == pos || equal == pos)
98             // huh? Broken header.
99             return result;
100
101         // The key name is delimited by either a comma, an equal sign or the end
102         // of the header, whichever comes first
103         int end = comma;
104         if (end == -1)
105             end = header.length();
106         if (equal != -1 && end > equal)
107             end = equal;        // equal sign comes before comma/end
108         QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower();
109         pos = end + 1;
110
111         if (uint(equal) < uint(comma)) {
112             // case: token "=" (token | quoted-string)
113             // skip spaces
114             pos = nextNonWhitespace(header, pos);
115             if (pos == header.length())
116                 // huh? Broken header
117                 return result;
118
119             QByteArray value;
120             value.reserve(header.length() - pos);
121             if (header.at(pos) == '"') {
122                 // case: quoted-string
123                 // quoted-string  = ( <"> *(qdtext | quoted-pair ) <"> )
124                 // qdtext         = <any TEXT except <">>
125                 // quoted-pair    = "\" CHAR
126                 ++pos;
127                 while (pos < header.length()) {
128                     register char c = header.at(pos);
129                     if (c == '"') {
130                         // end of quoted text
131                         break;
132                     } else if (c == '\\') {
133                         ++pos;
134                         if (pos >= header.length())
135                             // broken header
136                             return result;
137                         c = header.at(pos);
138                     }
139
140                     value += c;
141                     ++pos;
142                 }
143             } else {
144                 // case: token
145                 while (pos < header.length()) {
146                     register char c = header.at(pos);
147                     if (isSeparator(c))
148                         break;
149                     value += c;
150                     ++pos;
151                 }
152             }
153
154             result.insert(key, value);
155
156             // find the comma now:
157             comma = header.indexOf(',', pos);
158             if (comma == -1)
159                 return result;  // end of parsing
160             pos = comma + 1;
161         } else {
162             // case: token
163             // key is already set
164             result.insert(key, QByteArray());
165         }
166     }
167 }
168
169 QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manager,
170                                              const QNetworkRequest& request,
171                                              QNetworkAccessManager::Operation& operation,
172                                              QIODevice* outgoingData)
173     : QNetworkReply(*new QNetworkReplyHttpImplPrivate, manager)
174 {
175     Q_D(QNetworkReplyHttpImpl);
176     d->manager = manager;
177     d->managerPrivate = manager->d_func();
178     d->request = request;
179     d->operation = operation;
180     d->outgoingData = outgoingData;
181     d->url = request.url();
182 #ifndef QT_NO_SSL
183     d->sslConfiguration = request.sslConfiguration();
184 #endif
185
186     // FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
187     QIODevice::open(QIODevice::ReadOnly);
188
189
190     // Internal code that does a HTTP reply for the synchronous Ajax
191     // in QtWebKit.
192     QVariant synchronousHttpAttribute = request.attribute(
193             static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
194     if (synchronousHttpAttribute.isValid()) {
195         d->synchronous = synchronousHttpAttribute.toBool();
196         if (d->synchronous && outgoingData) {
197             // The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
198             // Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
199             d->outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
200             qint64 previousDataSize = 0;
201             do {
202                 previousDataSize = d->outgoingDataBuffer->size();
203                 d->outgoingDataBuffer->append(d->outgoingData->readAll());
204             } while (d->outgoingDataBuffer->size() != previousDataSize);
205             d->_q_startOperation();
206             return;
207         }
208     }
209
210
211     if (outgoingData) {
212         // there is data to be uploaded, e.g. HTTP POST.
213
214         if (!d->outgoingData->isSequential()) {
215             // fixed size non-sequential (random-access)
216             // just start the operation
217             QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
218             // FIXME make direct call?
219         } else {
220             bool bufferingDisallowed =
221                     request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
222                                   false).toBool();
223
224             if (bufferingDisallowed) {
225                 // if a valid content-length header for the request was supplied, we can disable buffering
226                 // if not, we will buffer anyway
227                 if (request.header(QNetworkRequest::ContentLengthHeader).isValid()) {
228                     QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
229                     // FIXME make direct call?
230                 } else {
231                     d->state = d->Buffering;
232                     QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
233                 }
234             } else {
235                 // _q_startOperation will be called when the buffering has finished.
236                 d->state = d->Buffering;
237                 QMetaObject::invokeMethod(this, "_q_bufferOutgoingData", Qt::QueuedConnection);
238             }
239         }
240     } else {
241         // No outgoing data (POST, ..)
242         d->_q_startOperation();
243     }
244 }
245
246 QNetworkReplyHttpImpl::~QNetworkReplyHttpImpl()
247 {
248     // Most work is done in private destructor
249 }
250
251 void QNetworkReplyHttpImpl::close()
252 {
253     Q_D(QNetworkReplyHttpImpl);
254
255     if (d->state == QNetworkReplyHttpImplPrivate::Aborted ||
256         d->state == QNetworkReplyHttpImplPrivate::Finished)
257         return;
258
259     // According to the documentation close only stops the download
260     // by closing we can ignore the download part and continue uploading.
261     QNetworkReply::close();
262
263     // call finished which will emit signals
264     // FIXME shouldn't this be emitted Queued?
265     d->error(OperationCanceledError, tr("Operation canceled"));
266     d->finished();
267 }
268
269 void QNetworkReplyHttpImpl::abort()
270 {
271     Q_D(QNetworkReplyHttpImpl);
272     // FIXME
273     if (d->state == QNetworkReplyHttpImplPrivate::Finished || d->state == QNetworkReplyHttpImplPrivate::Aborted)
274         return;
275
276     QNetworkReply::close();
277
278     if (d->state != QNetworkReplyHttpImplPrivate::Finished) {
279         // call finished which will emit signals
280         // FIXME shouldn't this be emitted Queued?
281         d->error(OperationCanceledError, tr("Operation canceled"));
282         d->finished();
283     }
284
285     d->state = QNetworkReplyHttpImplPrivate::Aborted;
286
287     emit abortHttpRequest();
288 }
289
290 qint64 QNetworkReplyHttpImpl::bytesAvailable() const
291 {
292     Q_D(const QNetworkReplyHttpImpl);
293
294     // if we load from cache device
295     if (d->cacheLoadDevice) {
296         return QNetworkReply::bytesAvailable() + d->cacheLoadDevice->bytesAvailable() + d->downloadMultiBuffer.byteAmount();
297     }
298
299     // zerocopy buffer
300     if (d->downloadZerocopyBuffer) {
301         return QNetworkReply::bytesAvailable() + d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
302     }
303
304     // normal buffer
305     return QNetworkReply::bytesAvailable() + d->downloadMultiBuffer.byteAmount();
306 }
307
308 bool QNetworkReplyHttpImpl::isSequential () const
309 {
310     // FIXME In the cache of a cached load or the zero-copy buffer we could actually be non-sequential.
311     // FIXME however this requires us to implement stuff like seek() too.
312     return true;
313 }
314
315 qint64 QNetworkReplyHttpImpl::size() const
316 {
317     // FIXME At some point, this could return a proper value, e.g. if we're non-sequential.
318     return QNetworkReply::size();
319 }
320
321 qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
322 {
323     Q_D(QNetworkReplyHttpImpl);
324
325     // cacheload device
326     if (d->cacheLoadDevice) {
327         // FIXME bytesdownloaded, position etc?
328
329         // There is something already in the buffer we buffered before because the user did not read()
330         // anything, so we read there first:
331         if (!d->downloadMultiBuffer.isEmpty()) {
332             return d->downloadMultiBuffer.read(data, maxlen);
333         }
334
335         qint64 ret = d->cacheLoadDevice->read(data, maxlen);
336         return ret;
337     }
338
339     // zerocopy buffer
340     if (d->downloadZerocopyBuffer) {
341         // FIXME bytesdownloaded, position etc?
342
343         qint64 howMuch = qMin(maxlen, (d->downloadBufferCurrentSize - d->downloadBufferReadPosition));
344         memcpy(data, d->downloadZerocopyBuffer + d->downloadBufferReadPosition, howMuch);
345         d->downloadBufferReadPosition += howMuch;
346         return howMuch;
347
348     }
349
350     // normal buffer
351     if (d->downloadMultiBuffer.isEmpty()) {
352         if (d->state == d->Finished || d->state == d->Aborted)
353             return -1;
354         return 0;
355     }
356
357     if (maxlen == 1) {
358         // optimization for getChar()
359         *data = d->downloadMultiBuffer.getChar();
360         if (readBufferSize())
361             emit readBufferFreed(1);
362         return 1;
363     }
364
365     maxlen = qMin<qint64>(maxlen, d->downloadMultiBuffer.byteAmount());
366     qint64 bytesRead = d->downloadMultiBuffer.read(data, maxlen);
367     if (readBufferSize())
368         emit readBufferFreed(bytesRead);
369     return bytesRead;
370 }
371
372 void QNetworkReplyHttpImpl::setReadBufferSize(qint64 size)
373 {
374     QNetworkReply::setReadBufferSize(size);
375     emit readBufferSizeChanged(size);
376     return;
377 }
378
379 bool QNetworkReplyHttpImpl::canReadLine () const
380 {
381     Q_D(const QNetworkReplyHttpImpl);
382
383     if (QNetworkReply::canReadLine())
384         return true;
385
386     if (d->cacheLoadDevice)
387         return d->cacheLoadDevice->canReadLine() || d->downloadMultiBuffer.canReadLine();
388
389     if (d->downloadZerocopyBuffer)
390         return memchr(d->downloadZerocopyBuffer + d->downloadBufferReadPosition, '\n', d->downloadBufferCurrentSize - d->downloadBufferReadPosition);
391
392     return d->downloadMultiBuffer.canReadLine();
393 }
394
395 #ifndef QT_NO_SSL
396 void QNetworkReplyHttpImpl::ignoreSslErrors()
397 {
398     Q_D(QNetworkReplyHttpImpl);
399
400     d->pendingIgnoreAllSslErrors = true;
401 }
402
403 void QNetworkReplyHttpImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
404 {
405     Q_D(QNetworkReplyHttpImpl);
406
407     // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
408     // is called before QNetworkAccessManager::get() (or post(), etc.)
409     d->pendingIgnoreSslErrorsList = errors;
410 }
411
412 void QNetworkReplyHttpImpl::setSslConfigurationImplementation(const QSslConfiguration &newconfig)
413 {
414     // Setting a SSL configuration on a reply is not supported. The user needs to set
415     // her/his QSslConfiguration on the QNetworkRequest.
416     Q_UNUSED(newconfig);
417 }
418
419 void QNetworkReplyHttpImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
420 {
421     Q_D(const QNetworkReplyHttpImpl);
422     configuration = d->sslConfiguration;
423 }
424 #endif
425
426 QNetworkReplyHttpImplPrivate::QNetworkReplyHttpImplPrivate()
427     : QNetworkReplyPrivate()
428     , manager(0)
429     , managerPrivate(0)
430     , synchronous(false)
431     , state(Idle)
432     , statusCode(0)
433     , outgoingData(0)
434     , bytesUploaded(-1)
435     , cacheLoadDevice(0)
436     , loadingFromCache(false)
437     , cacheSaveDevice(0)
438     , cacheEnabled(false)
439     , resumeOffset(0)
440     , preMigrationDownloaded(-1)
441     , bytesDownloaded(0)
442     , downloadBufferReadPosition(0)
443     , downloadBufferCurrentSize(0)
444     , downloadZerocopyBuffer(0)
445     , pendingDownloadDataEmissions(new QAtomicInt())
446     , pendingDownloadProgressEmissions(new QAtomicInt())
447     #ifndef QT_NO_SSL
448     , pendingIgnoreAllSslErrors(false)
449     #endif
450
451 {
452 }
453
454 QNetworkReplyHttpImplPrivate::~QNetworkReplyHttpImplPrivate()
455 {
456     Q_Q(QNetworkReplyHttpImpl);
457     // This will do nothing if the request was already finished or aborted
458     emit q->abortHttpRequest();
459 }
460
461 /*
462     For a given httpRequest
463     1) If AlwaysNetwork, return
464     2) If we have a cache entry for this url populate headers so the server can return 304
465     3) Calculate if response_is_fresh and if so send the cache and set loadedFromCache to true
466  */
467 bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest)
468 {
469     QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
470         (QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
471     if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
472         // If the request does not already specify preferred cache-control
473         // force reload from the network and tell any caching proxy servers to reload too
474         if (!request.rawHeaderList().contains("Cache-Control")) {
475             httpRequest.setHeaderField("Cache-Control", "no-cache");
476             httpRequest.setHeaderField("Pragma", "no-cache");
477         }
478         return false;
479     }
480
481     // The disk cache API does not currently support partial content retrieval.
482     // That is why we don't use the disk cache for any such requests.
483     if (request.hasRawHeader("Range"))
484         return false;
485
486     QAbstractNetworkCache *nc = managerPrivate->networkCache;
487     if (!nc)
488         return false;                 // no local cache
489
490     QNetworkCacheMetaData metaData = nc->metaData(request.url());
491     if (!metaData.isValid())
492         return false;                 // not in cache
493
494     if (!metaData.saveToDisk())
495         return false;
496
497     QNetworkHeadersPrivate cacheHeaders;
498     QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
499     cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
500
501     it = cacheHeaders.findRawHeader("etag");
502     if (it != cacheHeaders.rawHeaders.constEnd())
503         httpRequest.setHeaderField("If-None-Match", it->second);
504
505     QDateTime lastModified = metaData.lastModified();
506     if (lastModified.isValid())
507         httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
508
509     it = cacheHeaders.findRawHeader("Cache-Control");
510     if (it != cacheHeaders.rawHeaders.constEnd()) {
511         QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
512         if (cacheControl.contains("must-revalidate"))
513             return false;
514     }
515
516     QDateTime currentDateTime = QDateTime::currentDateTime();
517     QDateTime expirationDate = metaData.expirationDate();
518
519     bool response_is_fresh;
520     if (!expirationDate.isValid()) {
521         /*
522          * age_value
523          *      is the value of Age: header received by the cache with
524          *              this response.
525          * date_value
526          *      is the value of the origin server's Date: header
527          * request_time
528          *      is the (local) time when the cache made the request
529          *              that resulted in this cached response
530          * response_time
531          *      is the (local) time when the cache received the
532          *              response
533          * now
534          *      is the current (local) time
535          */
536         int age_value = 0;
537         it = cacheHeaders.findRawHeader("age");
538         if (it != cacheHeaders.rawHeaders.constEnd())
539             age_value = it->second.toInt();
540
541         QDateTime dateHeader;
542         int date_value = 0;
543         it = cacheHeaders.findRawHeader("date");
544         if (it != cacheHeaders.rawHeaders.constEnd()) {
545             dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
546             date_value = dateHeader.toTime_t();
547         }
548
549         int now = currentDateTime.toUTC().toTime_t();
550         int request_time = now;
551         int response_time = now;
552
553         // Algorithm from RFC 2616 section 13.2.3
554         int apparent_age = qMax(0, response_time - date_value);
555         int corrected_received_age = qMax(apparent_age, age_value);
556         int response_delay = response_time - request_time;
557         int corrected_initial_age = corrected_received_age + response_delay;
558         int resident_time = now - response_time;
559         int current_age   = corrected_initial_age + resident_time;
560
561         // RFC 2616 13.2.4 Expiration Calculations
562         if (!expirationDate.isValid()) {
563             if (lastModified.isValid()) {
564                 int diff = currentDateTime.secsTo(lastModified);
565                 expirationDate = lastModified;
566                 expirationDate.addSecs(diff / 10);
567                 if (httpRequest.headerField("Warning").isEmpty()) {
568                     QDateTime dt;
569                     dt.setTime_t(current_age);
570                     if (dt.daysTo(currentDateTime) > 1)
571                         httpRequest.setHeaderField("Warning", "113");
572                 }
573             }
574         }
575
576         // the cache-saving code below sets the expirationDate with date+max_age
577         // if "max-age" is present, or to Expires otherwise
578         int freshness_lifetime = dateHeader.secsTo(expirationDate);
579         response_is_fresh = (freshness_lifetime > current_age);
580     } else {
581         // expiration date was calculated earlier (e.g. when storing object to the cache)
582         response_is_fresh = currentDateTime.secsTo(expirationDate) >= 0;
583     }
584
585     if (!response_is_fresh)
586         return false;
587
588 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
589     qDebug() << "response_is_fresh" << CacheLoadControlAttribute;
590 #endif
591     return sendCacheContents(metaData);
592 }
593
594 QHttpNetworkRequest::Priority QNetworkReplyHttpImplPrivate::convert(const QNetworkRequest::Priority& prio)
595 {
596     switch (prio) {
597     case QNetworkRequest::LowPriority:
598         return QHttpNetworkRequest::LowPriority;
599     case QNetworkRequest::HighPriority:
600         return QHttpNetworkRequest::HighPriority;
601     case QNetworkRequest::NormalPriority:
602     default:
603         return QHttpNetworkRequest::NormalPriority;
604     }
605 }
606
607 void QNetworkReplyHttpImplPrivate::postRequest()
608 {
609     Q_Q(QNetworkReplyHttpImpl);
610
611     QThread *thread = 0;
612     if (synchronous) {
613         // A synchronous HTTP request uses its own thread
614         thread = new QThread();
615         thread->setObjectName(QStringLiteral("httpReply"));
616         QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
617         thread->start();
618     } else if (!managerPrivate->httpThread) {
619         // We use the manager-global thread.
620         // At some point we could switch to having multiple threads if it makes sense.
621         managerPrivate->httpThread = new QThread();
622         managerPrivate->httpThread->setObjectName(QStringLiteral("httpThread"));
623         QObject::connect(managerPrivate->httpThread, SIGNAL(finished()), managerPrivate->httpThread, SLOT(deleteLater()));
624         managerPrivate->httpThread->start();
625
626         thread = managerPrivate->httpThread;
627     } else {
628         // Asynchronous request, thread already exists
629         thread = managerPrivate->httpThread;
630     }
631
632     QUrl url = request.url();
633     httpRequest.setUrl(url);
634
635     bool ssl = url.scheme().toLower() == QLatin1String("https");
636     q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
637     httpRequest.setSsl(ssl);
638
639
640 #ifndef QT_NO_NETWORKPROXY
641     QNetworkProxy transparentProxy, cacheProxy;
642
643     // FIXME the proxy stuff should be done in the HTTP thread
644     foreach (const QNetworkProxy &p, managerPrivate->queryProxy(QNetworkProxyQuery(request.url()))) {
645         // use the first proxy that works
646         // for non-encrypted connections, any transparent or HTTP proxy
647         // for encrypted, only transparent proxies
648         if (!ssl
649             && (p.capabilities() & QNetworkProxy::CachingCapability)
650             && (p.type() == QNetworkProxy::HttpProxy ||
651                 p.type() == QNetworkProxy::HttpCachingProxy)) {
652             cacheProxy = p;
653             transparentProxy = QNetworkProxy::NoProxy;
654             break;
655         }
656         if (p.isTransparentProxy()) {
657             transparentProxy = p;
658             cacheProxy = QNetworkProxy::NoProxy;
659             break;
660         }
661     }
662
663     // check if at least one of the proxies
664     if (transparentProxy.type() == QNetworkProxy::DefaultProxy &&
665         cacheProxy.type() == QNetworkProxy::DefaultProxy) {
666         // unsuitable proxies
667         QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
668                                   Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProxyNotFoundError),
669                                   Q_ARG(QString, q->tr("No suitable proxy found")));
670         QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
671         return;
672     }
673 #endif
674
675
676     bool loadedFromCache = false;
677     httpRequest.setPriority(convert(request.priority()));
678
679     switch (operation) {
680     case QNetworkAccessManager::GetOperation:
681         httpRequest.setOperation(QHttpNetworkRequest::Get);
682         loadedFromCache = loadFromCacheIfAllowed(httpRequest);
683         break;
684
685     case QNetworkAccessManager::HeadOperation:
686         httpRequest.setOperation(QHttpNetworkRequest::Head);
687         loadedFromCache = loadFromCacheIfAllowed(httpRequest);
688         break;
689
690     case QNetworkAccessManager::PostOperation:
691         invalidateCache();
692         httpRequest.setOperation(QHttpNetworkRequest::Post);
693         createUploadByteDevice();
694         break;
695
696     case QNetworkAccessManager::PutOperation:
697         invalidateCache();
698         httpRequest.setOperation(QHttpNetworkRequest::Put);
699         createUploadByteDevice();
700         break;
701
702     case QNetworkAccessManager::DeleteOperation:
703         invalidateCache();
704         httpRequest.setOperation(QHttpNetworkRequest::Delete);
705         break;
706
707     case QNetworkAccessManager::CustomOperation:
708         invalidateCache(); // for safety reasons, we don't know what the operation does
709         httpRequest.setOperation(QHttpNetworkRequest::Custom);
710         createUploadByteDevice();
711         httpRequest.setCustomVerb(request.attribute(
712                 QNetworkRequest::CustomVerbAttribute).toByteArray());
713         break;
714
715     default:
716         break;                  // can't happen
717     }
718
719     if (loadedFromCache) {
720         return;    // no need to send the request! :)
721     }
722
723     QList<QByteArray> headers = request.rawHeaderList();
724     if (resumeOffset != 0) {
725         if (headers.contains("Range")) {
726             // Need to adjust resume offset for user specified range
727
728             headers.removeOne("Range");
729
730             // We've already verified that requestRange starts with "bytes=", see canResume.
731             QByteArray requestRange = request.rawHeader("Range").mid(6);
732
733             int index = requestRange.indexOf('-');
734
735             quint64 requestStartOffset = requestRange.left(index).toULongLong();
736             quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
737
738             requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
739                            '-' + QByteArray::number(requestEndOffset);
740
741             httpRequest.setHeaderField("Range", requestRange);
742         } else {
743             httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
744         }
745     }
746
747     foreach (const QByteArray &header, headers)
748         httpRequest.setHeaderField(header, request.rawHeader(header));
749
750     if (request.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
751         httpRequest.setPipeliningAllowed(true);
752
753     if (static_cast<QNetworkRequest::LoadControl>
754         (request.attribute(QNetworkRequest::AuthenticationReuseAttribute,
755                              QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
756         httpRequest.setWithCredentials(false);
757
758
759     // Create the HTTP thread delegate
760     QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
761 #ifndef QT_NO_BEARERMANAGEMENT
762     delegate->networkSession = managerPrivate->getNetworkSession();
763 #endif
764
765     // For the synchronous HTTP, this is the normal way the delegate gets deleted
766     // For the asynchronous HTTP this is a safety measure, the delegate deletes itself when HTTP is finished
767     QObject::connect(thread, SIGNAL(finished()), delegate, SLOT(deleteLater()));
768
769     // Set the properties it needs
770     delegate->httpRequest = httpRequest;
771 #ifndef QT_NO_NETWORKPROXY
772     delegate->cacheProxy = cacheProxy;
773     delegate->transparentProxy = transparentProxy;
774 #endif
775     delegate->ssl = ssl;
776 #ifndef QT_NO_SSL
777     if (ssl)
778         delegate->incomingSslConfiguration = request.sslConfiguration();
779 #endif
780
781     // Do we use synchronous HTTP?
782     delegate->synchronous = synchronous;
783
784     // The authentication manager is used to avoid the BlockingQueuedConnection communication
785     // from HTTP thread to user thread in some cases.
786     delegate->authenticationManager = managerPrivate->authenticationManager;
787
788     if (!synchronous) {
789         // Tell our zerocopy policy to the delegate
790         QVariant downloadBufferMaximumSizeAttribute = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
791         if (downloadBufferMaximumSizeAttribute.isValid()) {
792             delegate->downloadBufferMaximumSize = downloadBufferMaximumSizeAttribute.toLongLong();
793         } else {
794             // If there is no MaximumDownloadBufferSizeAttribute set (which is for the majority
795             // of QNetworkRequest) then we can assume we'll do it anyway for small HTTP replies.
796             // This helps with performance and memory fragmentation.
797             delegate->downloadBufferMaximumSize = 128*1024;
798         }
799
800
801         // These atomic integers are used for signal compression
802         delegate->pendingDownloadData = pendingDownloadDataEmissions;
803         delegate->pendingDownloadProgress = pendingDownloadProgressEmissions;
804
805         // Connect the signals of the delegate to us
806         QObject::connect(delegate, SIGNAL(downloadData(QByteArray)),
807                 q, SLOT(replyDownloadData(QByteArray)),
808                 Qt::QueuedConnection);
809         QObject::connect(delegate, SIGNAL(downloadFinished()),
810                 q, SLOT(replyFinished()),
811                 Qt::QueuedConnection);
812         QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
813                 q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64)),
814                 Qt::QueuedConnection);
815         QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
816                 q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
817                 Qt::QueuedConnection);
818         QObject::connect(delegate, SIGNAL(error(QNetworkReply::NetworkError,QString)),
819                 q, SLOT(httpError(QNetworkReply::NetworkError, const QString)),
820                 Qt::QueuedConnection);
821 #ifndef QT_NO_SSL
822         QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
823                 q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
824                 Qt::QueuedConnection);
825 #endif
826         // Those need to report back, therefire BlockingQueuedConnection
827         QObject::connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
828                 q, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
829                 Qt::BlockingQueuedConnection);
830 #ifndef QT_NO_NETWORKPROXY
831         QObject::connect(delegate, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
832                  q, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
833                  Qt::BlockingQueuedConnection);
834 #endif
835 #ifndef QT_NO_SSL
836         QObject::connect(delegate, SIGNAL(sslErrors(QList<QSslError>,bool*,QList<QSslError>*)),
837                 q, SLOT(replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *)),
838                 Qt::BlockingQueuedConnection);
839 #endif
840         // This signal we will use to start the request.
841         QObject::connect(q, SIGNAL(startHttpRequest()), delegate, SLOT(startRequest()));
842         QObject::connect(q, SIGNAL(abortHttpRequest()), delegate, SLOT(abortRequest()));
843
844         // To throttle the connection.
845         QObject::connect(q, SIGNAL(readBufferSizeChanged(qint64)), delegate, SLOT(readBufferSizeChanged(qint64)));
846         QObject::connect(q, SIGNAL(readBufferFreed(qint64)), delegate, SLOT(readBufferFreed(qint64)));
847
848         if (uploadByteDevice) {
849             QNonContiguousByteDeviceThreadForwardImpl *forwardUploadDevice =
850                     new QNonContiguousByteDeviceThreadForwardImpl(uploadByteDevice->atEnd(), uploadByteDevice->size());
851             if (uploadByteDevice->isResetDisabled())
852                 forwardUploadDevice->disableReset();
853             forwardUploadDevice->setParent(delegate); // needed to make sure it is moved on moveToThread()
854             delegate->httpRequest.setUploadByteDevice(forwardUploadDevice);
855
856             // From main thread to user thread:
857             QObject::connect(q, SIGNAL(haveUploadData(QByteArray, bool, qint64)),
858                              forwardUploadDevice, SLOT(haveDataSlot(QByteArray, bool, qint64)), Qt::QueuedConnection);
859             QObject::connect(uploadByteDevice.data(), SIGNAL(readyRead()),
860                              forwardUploadDevice, SIGNAL(readyRead()),
861                              Qt::QueuedConnection);
862
863             // From http thread to user thread:
864             QObject::connect(forwardUploadDevice, SIGNAL(wantData(qint64)),
865                              q, SLOT(wantUploadDataSlot(qint64)));
866             QObject::connect(forwardUploadDevice, SIGNAL(processedData(qint64)),
867                              q, SLOT(sentUploadDataSlot(qint64)));
868             QObject::connect(forwardUploadDevice, SIGNAL(resetData(bool*)),
869                     q, SLOT(resetUploadDataSlot(bool*)),
870                     Qt::BlockingQueuedConnection); // this is the only one with BlockingQueued!
871         }
872     } else if (synchronous) {
873         QObject::connect(q, SIGNAL(startHttpRequestSynchronously()), delegate, SLOT(startRequestSynchronously()), Qt::BlockingQueuedConnection);
874
875         if (uploadByteDevice) {
876             // For the synchronous HTTP use case the use thread (this one here) is blocked
877             // so we cannot use the asynchronous upload architecture.
878             // We therefore won't use the QNonContiguousByteDeviceThreadForwardImpl but directly
879             // use the uploadByteDevice provided to us by the QNetworkReplyImpl.
880             // The code that is in start() makes sure it is safe to use from a thread
881             // since it only wraps a QRingBuffer
882             delegate->httpRequest.setUploadByteDevice(uploadByteDevice.data());
883         }
884     }
885
886
887     // Move the delegate to the http thread
888     delegate->moveToThread(thread);
889     // This call automatically moves the uploadDevice too for the asynchronous case.
890
891     // Start timer for progress notifications
892     downloadProgressSignalChoke.start();
893
894
895     // Send an signal to the delegate so it starts working in the other thread
896     if (synchronous) {
897         emit q->startHttpRequestSynchronously(); // This one is BlockingQueuedConnection, so it will return when all work is done
898
899         if (delegate->incomingErrorCode != QNetworkReply::NoError) {
900             replyDownloadMetaData
901                     (delegate->incomingHeaders,
902                      delegate->incomingStatusCode,
903                      delegate->incomingReasonPhrase,
904                      delegate->isPipeliningUsed,
905                      QSharedPointer<char>(),
906                      delegate->incomingContentLength);
907             replyDownloadData(delegate->synchronousDownloadData);
908             httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
909         } else {
910             replyDownloadMetaData
911                     (delegate->incomingHeaders,
912                      delegate->incomingStatusCode,
913                      delegate->incomingReasonPhrase,
914                      delegate->isPipeliningUsed,
915                      QSharedPointer<char>(),
916                      delegate->incomingContentLength);
917             replyDownloadData(delegate->synchronousDownloadData);
918         }
919
920         // End the thread. It will delete itself from the finished() signal
921         thread->quit();
922         thread->wait(5000);
923
924         finished();
925     } else {
926         emit q->startHttpRequest(); // Signal to the HTTP thread and go back to user.
927     }
928 }
929
930 void QNetworkReplyHttpImplPrivate::invalidateCache()
931 {
932     QAbstractNetworkCache *nc = managerPrivate->networkCache;
933     if (nc)
934         nc->remove(request.url());
935 }
936
937 void QNetworkReplyHttpImplPrivate::initCacheSaveDevice()
938 {
939     Q_Q(QNetworkReplyHttpImpl);
940
941     // The disk cache does not support partial content, so don't even try to
942     // save any such content into the cache.
943     if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
944         cacheEnabled = false;
945         return;
946     }
947
948     // save the meta data
949     QNetworkCacheMetaData metaData;
950     metaData.setUrl(url);
951     metaData = fetchCacheMetaData(metaData);
952
953     // save the redirect request also in the cache
954     QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
955     if (redirectionTarget.isValid()) {
956         QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
957         attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
958         metaData.setAttributes(attributes);
959     }
960
961     cacheSaveDevice = managerPrivate->networkCache->prepare(metaData);
962
963     if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
964         if (cacheSaveDevice && !cacheSaveDevice->isOpen())
965             qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
966                   "class %s probably needs to be fixed",
967                   managerPrivate->networkCache->metaObject()->className());
968
969         managerPrivate->networkCache->remove(url);
970         cacheSaveDevice = 0;
971         cacheEnabled = false;
972     }
973 }
974
975 void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
976 {
977     Q_Q(QNetworkReplyHttpImpl);
978
979     // If we're closed just ignore this data
980     if (!q->isOpen())
981         return;
982
983     int pendingSignals = (int)pendingDownloadDataEmissions->fetchAndAddAcquire(-1) - 1;
984
985     if (pendingSignals > 0) {
986         // Some more signal emissions to this slot are pending.
987         // Instead of writing the downstream data, we wait
988         // and do it in the next call we get
989         // (signal comppression)
990         pendingDownloadData.append(d);
991         return;
992     }
993
994     pendingDownloadData.append(d);
995     d.clear();
996     // We need to usa a copy for calling writeDownstreamData as we could
997     // possibly recurse into this this function when we call
998     // appendDownstreamDataSignalEmissions because the user might call
999     // processEvents() or spin an event loop when this occur.
1000     QByteDataBuffer pendingDownloadDataCopy = pendingDownloadData;
1001     pendingDownloadData.clear();
1002
1003     if (cacheEnabled && !cacheSaveDevice) {
1004         initCacheSaveDevice();
1005     }
1006
1007     qint64 bytesWritten = 0;
1008     for (int i = 0; i < pendingDownloadDataCopy.bufferCount(); i++) {
1009         QByteArray const &item = pendingDownloadDataCopy[i];
1010
1011         if (cacheSaveDevice)
1012             cacheSaveDevice->write(item.constData(), item.size());
1013         downloadMultiBuffer.append(item);
1014
1015         bytesWritten += item.size();
1016     }
1017     pendingDownloadDataCopy.clear();
1018
1019     bytesDownloaded += bytesWritten;
1020
1021
1022     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
1023     if (preMigrationDownloaded != Q_INT64_C(-1))
1024         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
1025
1026     emit q->readyRead();
1027     // emit readyRead before downloadProgress incase this will cause events to be
1028     // processed and we get into a recursive call (as in QProgressDialog).
1029     if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
1030         downloadProgressSignalChoke.restart();
1031         emit q->downloadProgress(bytesDownloaded,
1032                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
1033     }
1034
1035 }
1036
1037 void QNetworkReplyHttpImplPrivate::replyFinished()
1038 {
1039     // We are already loading from cache, we still however
1040     // got this signal because it was posted already
1041     if (loadingFromCache)
1042         return;
1043
1044     finished();
1045 }
1046
1047 void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
1048 {
1049     Q_Q(QNetworkReplyHttpImpl);
1050     switch (statusCode) {
1051     case 301:                   // Moved Permanently
1052     case 302:                   // Found
1053     case 303:                   // See Other
1054     case 307:                   // Temporary Redirect
1055         // What do we do about the caching of the HTML note?
1056         // The response to a 303 MUST NOT be cached, while the response to
1057         // all of the others is cacheable if the headers indicate it to be
1058         QByteArray header = q->rawHeader("location");
1059         QUrl url = QUrl(QString::fromUtf8(header));
1060         if (!url.isValid())
1061             url = QUrl(QLatin1String(header));
1062         q->setAttribute(QNetworkRequest::RedirectionTargetAttribute, url);
1063     }
1064 }
1065
1066 void QNetworkReplyHttpImplPrivate::replyDownloadMetaData
1067         (QList<QPair<QByteArray,QByteArray> > hm,
1068          int sc,QString rp,bool pu,
1069          QSharedPointer<char> db,
1070          qint64 contentLength)
1071 {
1072     Q_Q(QNetworkReplyHttpImpl);
1073     Q_UNUSED(contentLength);
1074
1075     statusCode = sc;
1076     reasonPhrase = rp;
1077
1078     // Download buffer
1079     if (!db.isNull()) {
1080         downloadBufferPointer = db;
1081         downloadZerocopyBuffer = downloadBufferPointer.data();
1082         downloadBufferCurrentSize = 0;
1083         q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
1084     }
1085
1086     q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
1087
1088     // reconstruct the HTTP header
1089     QList<QPair<QByteArray, QByteArray> > headerMap = hm;
1090     QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
1091                                                         end = headerMap.constEnd();
1092     for (; it != end; ++it) {
1093         QByteArray value = q->rawHeader(it->first);
1094         if (!value.isEmpty()) {
1095             if (qstricmp(it->first.constData(), "set-cookie") == 0)
1096                 value += '\n';
1097             else
1098                 value += ", ";
1099         }
1100         value += it->second;
1101         q->setRawHeader(it->first, value);
1102     }
1103
1104     q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
1105     q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
1106
1107     // is it a redirection?
1108     checkForRedirect(statusCode);
1109
1110     if (statusCode >= 500 && statusCode < 600) {
1111         QAbstractNetworkCache *nc = managerPrivate->networkCache;
1112         if (nc) {
1113             QNetworkCacheMetaData metaData = nc->metaData(request.url());
1114             QNetworkHeadersPrivate cacheHeaders;
1115             cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
1116             QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
1117             it = cacheHeaders.findRawHeader("Cache-Control");
1118             bool mustReValidate = false;
1119             if (it != cacheHeaders.rawHeaders.constEnd()) {
1120                 QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
1121                 if (cacheControl.contains("must-revalidate"))
1122                     mustReValidate = true;
1123             }
1124             if (!mustReValidate && sendCacheContents(metaData))
1125                 return;
1126         }
1127     }
1128
1129     if (statusCode == 304) {
1130 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1131         qDebug() << "Received a 304 from" << url();
1132 #endif
1133         QAbstractNetworkCache *nc = managerPrivate->networkCache;
1134         if (nc) {
1135             QNetworkCacheMetaData oldMetaData = nc->metaData(request.url());
1136             QNetworkCacheMetaData metaData = fetchCacheMetaData(oldMetaData);
1137             if (oldMetaData != metaData)
1138                 nc->updateMetaData(metaData);
1139             if (sendCacheContents(metaData))
1140                 return;
1141         }
1142     }
1143
1144
1145     if (statusCode != 304 && statusCode != 303) {
1146         if (!isCachingEnabled())
1147             setCachingEnabled(true);
1148     }
1149
1150     metaDataChanged();
1151 }
1152
1153 void QNetworkReplyHttpImplPrivate::replyDownloadProgressSlot(qint64 bytesReceived,  qint64 bytesTotal)
1154 {
1155     Q_Q(QNetworkReplyHttpImpl);
1156
1157     // If we're closed just ignore this data
1158     if (!q->isOpen())
1159         return;
1160
1161     // we can be sure here that there is a download buffer
1162
1163     int pendingSignals = (int)pendingDownloadProgressEmissions->fetchAndAddAcquire(-1) - 1;
1164     if (pendingSignals > 0) {
1165         // Let's ignore this signal and look at the next one coming in
1166         // (signal comppression)
1167         return;
1168     }
1169
1170     if (!q->isOpen())
1171         return;
1172
1173     if (cacheEnabled && bytesReceived == bytesTotal) {
1174         // Write everything in one go if we use a download buffer. might be more performant.
1175         initCacheSaveDevice();
1176         // need to check again if cache enabled and device exists
1177         if (cacheSaveDevice && cacheEnabled)
1178             cacheSaveDevice->write(downloadZerocopyBuffer, bytesTotal);
1179         // FIXME where is it closed?
1180     }
1181
1182     bytesDownloaded = bytesReceived;
1183
1184     downloadBufferCurrentSize = bytesReceived;
1185
1186     // Only emit readyRead when actual data is there
1187     // emit readyRead before downloadProgress incase this will cause events to be
1188     // processed and we get into a recursive call (as in QProgressDialog).
1189     if (bytesDownloaded > 0)
1190         emit q->readyRead();
1191     if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
1192         downloadProgressSignalChoke.restart();
1193         emit q->downloadProgress(bytesDownloaded, bytesTotal);
1194     }
1195 }
1196
1197 void QNetworkReplyHttpImplPrivate::httpAuthenticationRequired(const QHttpNetworkRequest &request,
1198                                                            QAuthenticator *auth)
1199 {
1200     managerPrivate->authenticationRequired(auth, q_func(), synchronous, url, &urlForLastAuthentication, request.withCredentials());
1201 }
1202
1203 #ifndef QT_NO_NETWORKPROXY
1204 void QNetworkReplyHttpImplPrivate::proxyAuthenticationRequired(const QNetworkProxy &proxy,
1205                                                         QAuthenticator *authenticator)
1206 {
1207     managerPrivate->proxyAuthenticationRequired(proxy, synchronous, authenticator, &lastProxyAuthentication);
1208 }
1209 #endif
1210
1211 void QNetworkReplyHttpImplPrivate::httpError(QNetworkReply::NetworkError errorCode,
1212                                           const QString &errorString)
1213 {
1214 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1215     qDebug() << "http error!" << errorCode << errorString;
1216 #endif
1217
1218     // FIXME?
1219     error(errorCode, errorString);
1220 }
1221
1222 #ifndef QT_NO_SSL
1223 void QNetworkReplyHttpImplPrivate::replySslErrors(
1224         const QList<QSslError> &list, bool *ignoreAll, QList<QSslError> *toBeIgnored)
1225 {
1226     Q_Q(QNetworkReplyHttpImpl);
1227     emit q->sslErrors(list);
1228     // Check if the callback set any ignore and return this here to http thread
1229     if (pendingIgnoreAllSslErrors)
1230         *ignoreAll = true;
1231     if (!pendingIgnoreSslErrorsList.isEmpty())
1232         *toBeIgnored = pendingIgnoreSslErrorsList;
1233 }
1234
1235 void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfiguration &sslConfiguration)
1236 {
1237     // Receiving the used SSL configuration from the HTTP thread
1238     this->sslConfiguration = sslConfiguration;
1239 }
1240 #endif
1241
1242 // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1243 void QNetworkReplyHttpImplPrivate::resetUploadDataSlot(bool *r)
1244 {
1245     *r = uploadByteDevice->reset();
1246 }
1247
1248 // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1249 void QNetworkReplyHttpImplPrivate::sentUploadDataSlot(qint64 amount)
1250 {
1251     uploadByteDevice->advanceReadPointer(amount);
1252 }
1253
1254 // Coming from QNonContiguousByteDeviceThreadForwardImpl in HTTP thread
1255 void QNetworkReplyHttpImplPrivate::wantUploadDataSlot(qint64 maxSize)
1256 {
1257     Q_Q(QNetworkReplyHttpImpl);
1258
1259     // call readPointer
1260     qint64 currentUploadDataLength = 0;
1261     char *data = const_cast<char*>(uploadByteDevice->readPointer(maxSize, currentUploadDataLength));
1262     // Let's make a copy of this data
1263     QByteArray dataArray(data, currentUploadDataLength);
1264
1265     // Communicate back to HTTP thread
1266     emit q->haveUploadData(dataArray, uploadByteDevice->atEnd(), uploadByteDevice->size());
1267 }
1268
1269 /*
1270     A simple web page that can be used to test us: http://www.procata.com/cachetest/
1271  */
1272 bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData &metaData)
1273 {
1274     Q_Q(QNetworkReplyHttpImpl);
1275
1276     setCachingEnabled(false);
1277     if (!metaData.isValid())
1278         return false;
1279
1280     QAbstractNetworkCache *nc = managerPrivate->networkCache;
1281     Q_ASSERT(nc);
1282     QIODevice *contents = nc->data(url);
1283     if (!contents) {
1284 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1285         qDebug() << "Can not send cache, the contents are 0" << url;
1286 #endif
1287         return false;
1288     }
1289     contents->setParent(q);
1290
1291     QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
1292     int status = attributes.value(QNetworkRequest::HttpStatusCodeAttribute).toInt();
1293     if (status < 100)
1294         status = 200;           // fake it
1295
1296     q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, status);
1297     q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
1298     q->setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
1299
1300     QNetworkCacheMetaData::RawHeaderList rawHeaders = metaData.rawHeaders();
1301     QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
1302                                                        end = rawHeaders.constEnd();
1303     for ( ; it != end; ++it)
1304         setRawHeader(it->first, it->second);
1305
1306     checkForRedirect(status);
1307
1308     cacheLoadDevice = contents;
1309     q->connect(cacheLoadDevice, SIGNAL(readyRead()), SLOT(_q_cacheLoadReadyRead()));
1310     q->connect(cacheLoadDevice, SIGNAL(readChannelFinished()), SLOT(_q_cacheLoadReadyRead()));
1311
1312     // This needs to be emitted in the event loop because it can be reached at
1313     // the direct code path of qnam.get(...) before the user has a chance
1314     // to connect any signals.
1315     QMetaObject::invokeMethod(q, "metaDataChanged", Qt::QueuedConnection);
1316     QMetaObject::invokeMethod(q, "_q_cacheLoadReadyRead", Qt::QueuedConnection);
1317
1318
1319 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1320     qDebug() << "Successfully sent cache:" << url << contents->size() << "bytes";
1321 #endif
1322
1323     // Set the following flag so we can ignore some signals from HTTP thread
1324     // that would still come
1325     loadingFromCache = true;
1326     return true;
1327 }
1328
1329 QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
1330 {
1331     Q_Q(const QNetworkReplyHttpImpl);
1332
1333     QNetworkCacheMetaData metaData = oldMetaData;
1334
1335     QNetworkHeadersPrivate cacheHeaders;
1336     cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
1337     QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
1338
1339     QList<QByteArray> newHeaders = q->rawHeaderList();
1340     foreach (QByteArray header, newHeaders) {
1341         QByteArray originalHeader = header;
1342         header = header.toLower();
1343         bool hop_by_hop =
1344             (header == "connection"
1345              || header == "keep-alive"
1346              || header == "proxy-authenticate"
1347              || header == "proxy-authorization"
1348              || header == "te"
1349              || header == "trailers"
1350              || header == "transfer-encoding"
1351              || header ==  "upgrade");
1352         if (hop_by_hop)
1353             continue;
1354
1355         // for 4.6.0, we were planning to not store the date header in the
1356         // cached resource; through that we planned to reduce the number
1357         // of writes to disk when using a QNetworkDiskCache (i.e. don't
1358         // write to disk when only the date changes).
1359         // However, without the date we cannot calculate the age of the page
1360         // anymore.
1361         //if (header == "date")
1362             //continue;
1363
1364         // Don't store Warning 1xx headers
1365         if (header == "warning") {
1366             QByteArray v = q->rawHeader(header);
1367             if (v.length() == 3
1368                 && v[0] == '1'
1369                 && v[1] >= '0' && v[1] <= '9'
1370                 && v[2] >= '0' && v[2] <= '9')
1371                 continue;
1372         }
1373
1374         it = cacheHeaders.findRawHeader(header);
1375         if (it != cacheHeaders.rawHeaders.constEnd()) {
1376             // Match the behavior of Firefox and assume Cache-Control: "no-transform"
1377             if (header == "content-encoding"
1378                 || header == "content-range"
1379                 || header == "content-type")
1380                 continue;
1381
1382             // For MS servers that send "Content-Length: 0" on 304 responses
1383             // ignore this too
1384             if (header == "content-length")
1385                 continue;
1386         }
1387
1388 #if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
1389         QByteArray n = rawHeader(header);
1390         QByteArray o;
1391         if (it != cacheHeaders.rawHeaders.constEnd())
1392             o = (*it).second;
1393         if (n != o && header != "date") {
1394             qDebug() << "replacing" << header;
1395             qDebug() << "new" << n;
1396             qDebug() << "old" << o;
1397         }
1398 #endif
1399         cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
1400     }
1401     metaData.setRawHeaders(cacheHeaders.rawHeaders);
1402
1403     bool checkExpired = true;
1404
1405     QHash<QByteArray, QByteArray> cacheControl;
1406     it = cacheHeaders.findRawHeader("Cache-Control");
1407     if (it != cacheHeaders.rawHeaders.constEnd()) {
1408         cacheControl = parseHttpOptionHeader(it->second);
1409         QByteArray maxAge = cacheControl.value("max-age");
1410         if (!maxAge.isEmpty()) {
1411             checkExpired = false;
1412             QDateTime dt = QDateTime::currentDateTime();
1413             dt = dt.addSecs(maxAge.toInt());
1414             metaData.setExpirationDate(dt);
1415         }
1416     }
1417     if (checkExpired) {
1418         it = cacheHeaders.findRawHeader("expires");
1419         if (it != cacheHeaders.rawHeaders.constEnd()) {
1420             QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second);
1421             metaData.setExpirationDate(expiredDateTime);
1422         }
1423     }
1424
1425     it = cacheHeaders.findRawHeader("last-modified");
1426     if (it != cacheHeaders.rawHeaders.constEnd())
1427         metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second));
1428
1429     bool canDiskCache;
1430     // only cache GET replies by default, all other replies (POST, PUT, DELETE)
1431     //  are not cacheable by default (according to RFC 2616 section 9)
1432     if (httpRequest.operation() == QHttpNetworkRequest::Get) {
1433
1434         canDiskCache = true;
1435         // 14.32
1436         // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client
1437         // had sent "Cache-Control: no-cache".
1438         it = cacheHeaders.findRawHeader("pragma");
1439         if (it != cacheHeaders.rawHeaders.constEnd()
1440             && it->second == "no-cache")
1441             canDiskCache = false;
1442
1443         // HTTP/1.1. Check the Cache-Control header
1444         if (cacheControl.contains("no-cache"))
1445             canDiskCache = false;
1446         else if (cacheControl.contains("no-store"))
1447             canDiskCache = false;
1448
1449     // responses to POST might be cacheable
1450     } else if (httpRequest.operation() == QHttpNetworkRequest::Post) {
1451
1452         canDiskCache = false;
1453         // some pages contain "expires:" and "cache-control: no-cache" field,
1454         // so we only might cache POST requests if we get "cache-control: max-age ..."
1455         if (cacheControl.contains("max-age"))
1456             canDiskCache = true;
1457
1458     // responses to PUT and DELETE are not cacheable
1459     } else {
1460         canDiskCache = false;
1461     }
1462
1463     metaData.setSaveToDisk(canDiskCache);
1464     QNetworkCacheMetaData::AttributesMap attributes;
1465     if (statusCode != 304) {
1466         // update the status code
1467         attributes.insert(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
1468         attributes.insert(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
1469     } else {
1470         // this is a redirection, keep the attributes intact
1471         attributes = oldMetaData.attributes();
1472     }
1473     metaData.setAttributes(attributes);
1474     return metaData;
1475 }
1476
1477 bool QNetworkReplyHttpImplPrivate::canResume() const
1478 {
1479     Q_Q(const QNetworkReplyHttpImpl);
1480
1481     // Only GET operation supports resuming.
1482     if (operation != QNetworkAccessManager::GetOperation)
1483         return false;
1484
1485     // Can only resume if server/resource supports Range header.
1486     QByteArray acceptRangesheaderName("Accept-Ranges");
1487     if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
1488         return false;
1489
1490     // We only support resuming for byte ranges.
1491     if (request.hasRawHeader("Range")) {
1492         QByteArray range = request.rawHeader("Range");
1493         if (!range.startsWith("bytes="))
1494             return false;
1495     }
1496
1497     // If we're using a download buffer then we don't support resuming/migration
1498     // right now. Too much trouble.
1499     if (downloadZerocopyBuffer)
1500         return false;
1501
1502     return true;
1503 }
1504
1505 void QNetworkReplyHttpImplPrivate::setResumeOffset(quint64 offset)
1506 {
1507     resumeOffset = offset;
1508 }
1509
1510 /*!
1511     Starts the backend.  Returns true if the backend is started.  Returns false if the backend
1512     could not be started due to an unopened or roaming session.  The caller should recall this
1513     function once the session has been opened or the roaming process has finished.
1514 */
1515 bool QNetworkReplyHttpImplPrivate::start()
1516 {
1517 #ifndef QT_NO_BEARERMANAGEMENT
1518     QSharedPointer<QNetworkSession> networkSession(managerPrivate->getNetworkSession());
1519     if (!networkSession) {
1520 #endif
1521         postRequest();
1522         return true;
1523 #ifndef QT_NO_BEARERMANAGEMENT
1524     }
1525
1526     // This is not ideal.
1527     const QString host = url.host();
1528     if (host == QLatin1String("localhost") ||
1529         QHostAddress(host).isLoopback()) {
1530         // Don't need an open session for localhost access.
1531         postRequest();
1532         return true;
1533     }
1534
1535     if (networkSession->isOpen() &&
1536         networkSession->state() == QNetworkSession::Connected) {
1537         Q_Q(QNetworkReplyHttpImpl);
1538         QObject::connect(networkSession.data(), SIGNAL(usagePoliciesChanged(QNetworkSession::UsagePolicies)),
1539                             q, SLOT(_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies)));
1540         postRequest();
1541         return true;
1542     }
1543
1544     return false;
1545 #endif
1546 }
1547
1548 void QNetworkReplyHttpImplPrivate::_q_startOperation()
1549 {
1550     Q_Q(QNetworkReplyHttpImpl);
1551
1552     // ensure this function is only being called once
1553     if (state == Working) {
1554         qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
1555         return;
1556     }
1557     state = Working;
1558
1559 #ifndef QT_NO_BEARERMANAGEMENT
1560     // Do not start background requests if they are not allowed by session policy
1561     QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
1562     QVariant isBackground = request.attribute(QNetworkRequest::BackgroundRequestAttribute, QVariant::fromValue(false));
1563     if (isBackground.toBool() && session && session->usagePolicies().testFlag(QNetworkSession::NoBackgroundTrafficPolicy)) {
1564         QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
1565             Q_ARG(QNetworkReply::NetworkError, QNetworkReply::BackgroundRequestNotAllowedError),
1566             Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "Background request not allowed.")));
1567         QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
1568         return;
1569     }
1570 #endif
1571
1572     if (!start()) {
1573 #ifndef QT_NO_BEARERMANAGEMENT
1574         // backend failed to start because the session state is not Connected.
1575         // QNetworkAccessManager will call reply->backend->start() again for us when the session
1576         // state changes.
1577         state = WaitingForSession;
1578
1579         if (session) {
1580             QObject::connect(session.data(), SIGNAL(error(QNetworkSession::SessionError)),
1581                              q, SLOT(_q_networkSessionFailed()), Qt::QueuedConnection);
1582
1583             if (!session->isOpen()) {
1584                 session->setSessionProperty(QStringLiteral("ConnectInBackground"), isBackground);
1585                 session->open();
1586             }
1587         } else {
1588             qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
1589             QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
1590                 Q_ARG(QNetworkReply::NetworkError, QNetworkReply::NetworkSessionFailedError),
1591                 Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "Network session error.")));
1592             QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
1593             return;
1594         }
1595 #else
1596         qWarning("Backend start failed");
1597         QMetaObject::invokeMethod(q, "_q_error", synchronous ? Qt::DirectConnection : Qt::QueuedConnection,
1598             Q_ARG(QNetworkReply::NetworkError, QNetworkReply::UnknownNetworkError),
1599             Q_ARG(QString, QCoreApplication::translate("QNetworkReply", "backend start error.")));
1600         QMetaObject::invokeMethod(q, "_q_finished", synchronous ? Qt::DirectConnection : Qt::QueuedConnection);
1601         return;
1602 #endif
1603     }
1604
1605     if (synchronous) {
1606         state = Finished;
1607         q_func()->setFinished(true);
1608     } else {
1609         if (state != Finished) {
1610
1611         }
1612     }
1613 }
1614
1615 void QNetworkReplyHttpImplPrivate::_q_cacheLoadReadyRead()
1616 {
1617     Q_Q(QNetworkReplyHttpImpl);
1618
1619     if (state != Working)
1620         return;
1621     if (!cacheLoadDevice || !q->isOpen() || !cacheLoadDevice->bytesAvailable())
1622         return;
1623
1624     // FIXME Optimize to use zerocopy download buffer if it is a QBuffer.
1625     // Needs to be done where sendCacheContents() (?) of HTTP is emitting
1626     // metaDataChanged ?
1627
1628
1629     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
1630
1631     // emit readyRead before downloadProgress incase this will cause events to be
1632     // processed and we get into a recursive call (as in QProgressDialog).
1633
1634     // This readyRead() goes to the user. The user then may or may not read() anything.
1635     emit q->readyRead();
1636     if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
1637         downloadProgressSignalChoke.restart();
1638         emit q->downloadProgress(bytesDownloaded,
1639                              totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
1640     }
1641
1642     // If there are still bytes available in the cacheLoadDevice then the user did not read
1643     // in response to the readyRead() signal. This means we have to load from the cacheLoadDevice
1644     // and buffer that stuff. This is needed to be able to properly emit finished() later.
1645     while (cacheLoadDevice->bytesAvailable()) {
1646         downloadMultiBuffer.append(cacheLoadDevice->readAll());
1647     }
1648
1649     if (cacheLoadDevice->isSequential()) {
1650         // check if end and we can read the EOF -1
1651         char c;
1652         qint64 actualCount = cacheLoadDevice->read(&c, 1);
1653         if (actualCount < 0) {
1654             cacheLoadDevice->deleteLater();
1655             cacheLoadDevice = 0;
1656             QMetaObject::invokeMethod(q, "_q_finished", Qt::QueuedConnection);
1657         } else if (actualCount == 1) {
1658             // This is most probably not happening since most QIODevice returned something proper for bytesAvailable()
1659             // and had already been "emptied".
1660             cacheLoadDevice->ungetChar(c);
1661         }
1662     } else if ((!cacheLoadDevice->isSequential() && cacheLoadDevice->atEnd())) {
1663         // This codepath is in case the cache device is a QBuffer, e.g. from QNetworkDiskCache.
1664         cacheLoadDevice->deleteLater();
1665         cacheLoadDevice = 0;
1666         QMetaObject::invokeMethod(q, "_q_finished", Qt::QueuedConnection);
1667     }
1668
1669 }
1670
1671
1672 void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingDataFinished()
1673 {
1674     Q_Q(QNetworkReplyHttpImpl);
1675
1676     // make sure this is only called once, ever.
1677     //_q_bufferOutgoingData may call it or the readChannelFinished emission
1678     if (state != Buffering)
1679         return;
1680
1681     // disconnect signals
1682     QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
1683     QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
1684
1685     // finally, start the request
1686     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1687 }
1688
1689 void QNetworkReplyHttpImplPrivate::_q_bufferOutgoingData()
1690 {
1691     Q_Q(QNetworkReplyHttpImpl);
1692
1693     if (!outgoingDataBuffer) {
1694         // first call, create our buffer
1695         outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
1696
1697         QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
1698         QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
1699     }
1700
1701     qint64 bytesBuffered = 0;
1702     qint64 bytesToBuffer = 0;
1703
1704     // read data into our buffer
1705     forever {
1706         bytesToBuffer = outgoingData->bytesAvailable();
1707         // unknown? just try 2 kB, this also ensures we always try to read the EOF
1708         if (bytesToBuffer <= 0)
1709             bytesToBuffer = 2*1024;
1710
1711         char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
1712         bytesBuffered = outgoingData->read(dst, bytesToBuffer);
1713
1714         if (bytesBuffered == -1) {
1715             // EOF has been reached.
1716             outgoingDataBuffer->chop(bytesToBuffer);
1717
1718             _q_bufferOutgoingDataFinished();
1719             break;
1720         } else if (bytesBuffered == 0) {
1721             // nothing read right now, just wait until we get called again
1722             outgoingDataBuffer->chop(bytesToBuffer);
1723
1724             break;
1725         } else {
1726             // don't break, try to read() again
1727             outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
1728         }
1729     }
1730 }
1731
1732 #ifndef QT_NO_BEARERMANAGEMENT
1733 void QNetworkReplyHttpImplPrivate::_q_networkSessionConnected()
1734 {
1735     Q_Q(QNetworkReplyHttpImpl);
1736
1737     if (!manager)
1738         return;
1739
1740     QSharedPointer<QNetworkSession> session = managerPrivate->getNetworkSession();
1741     if (!session)
1742         return;
1743
1744     if (session->state() != QNetworkSession::Connected)
1745         return;
1746
1747     switch (state) {
1748     case QNetworkReplyImplPrivate::Buffering:
1749     case QNetworkReplyImplPrivate::Working:
1750     case QNetworkReplyImplPrivate::Reconnecting:
1751         // Migrate existing downloads to new network connection.
1752         migrateBackend();
1753         break;
1754     case QNetworkReplyImplPrivate::WaitingForSession:
1755         // Start waiting requests.
1756         QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1757         break;
1758     default:
1759         ;
1760     }
1761 }
1762
1763 void QNetworkReplyHttpImplPrivate::_q_networkSessionFailed()
1764 {
1765     // Abort waiting and working replies.
1766     if (state == WaitingForSession || state == Working) {
1767         state = Working;
1768         QSharedPointer<QNetworkSession> session(manager->d_func()->getNetworkSession());
1769         QString errorStr;
1770         if (session)
1771             errorStr = session->errorString();
1772         else
1773             errorStr = QCoreApplication::translate("QNetworkReply", "Network session error.");
1774         error(QNetworkReplyImpl::NetworkSessionFailedError, errorStr);
1775         finished();
1776     }
1777 }
1778
1779 void QNetworkReplyHttpImplPrivate::_q_networkSessionUsagePoliciesChanged(QNetworkSession::UsagePolicies newPolicies)
1780 {
1781     if (request.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) {
1782         if (newPolicies & QNetworkSession::NoBackgroundTrafficPolicy) {
1783             // Abort waiting and working replies.
1784             if (state == WaitingForSession || state == Working) {
1785                 state = Working;
1786                 error(QNetworkReply::BackgroundRequestNotAllowedError,
1787                     QCoreApplication::translate("QNetworkReply", "Background request not allowed."));
1788                 finished();
1789             }
1790             // ### if canResume(), then we could resume automatically
1791         }
1792     }
1793
1794 }
1795 #endif
1796
1797
1798 // need to have this function since the reply is a private member variable
1799 // and the special backends need to access this.
1800 void QNetworkReplyHttpImplPrivate::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
1801 {
1802     Q_Q(QNetworkReplyHttpImpl);
1803     if (isFinished)
1804         return;
1805     emit q->uploadProgress(bytesSent, bytesTotal);
1806 }
1807
1808 QNonContiguousByteDevice* QNetworkReplyHttpImplPrivate::createUploadByteDevice()
1809 {
1810     Q_Q(QNetworkReplyHttpImpl);
1811
1812     if (outgoingDataBuffer)
1813         uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(outgoingDataBuffer));
1814     else if (outgoingData) {
1815         uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(outgoingData));
1816     } else {
1817         return 0;
1818     }
1819
1820     bool bufferDisallowed =
1821             request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
1822                           QVariant(false)) == QVariant(true);
1823     if (bufferDisallowed)
1824         uploadByteDevice->disableReset();
1825
1826     // We want signal emissions only for normal asynchronous uploads
1827     if (!synchronous)
1828         QObject::connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)),
1829                          q, SLOT(emitReplyUploadProgress(qint64,qint64)));
1830
1831     return uploadByteDevice.data();
1832 }
1833
1834 void QNetworkReplyHttpImplPrivate::_q_finished()
1835 {
1836     // This gets called queued, just forward to real call then
1837     finished();
1838 }
1839
1840 void QNetworkReplyHttpImplPrivate::finished()
1841 {
1842     Q_Q(QNetworkReplyHttpImpl);
1843
1844     if (state == Finished || state == Aborted || state == WaitingForSession)
1845         return;
1846
1847     QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
1848     if (preMigrationDownloaded != Q_INT64_C(-1))
1849         totalSize = totalSize.toLongLong() + preMigrationDownloaded;
1850
1851     if (manager) {
1852 #ifndef QT_NO_BEARERMANAGEMENT
1853         QSharedPointer<QNetworkSession> session = managerPrivate->getNetworkSession();
1854         if (session && session->state() == QNetworkSession::Roaming &&
1855             state == Working && errorCode != QNetworkReply::OperationCanceledError) {
1856             // only content with a known size will fail with a temporary network failure error
1857             if (!totalSize.isNull()) {
1858                 if (bytesDownloaded != totalSize) {
1859                     if (migrateBackend()) {
1860                         // either we are migrating or the request is finished/aborted
1861                         if (state == Reconnecting || state == WaitingForSession) {
1862                             return; // exit early if we are migrating.
1863                         }
1864                     } else {
1865                         error(QNetworkReply::TemporaryNetworkFailureError,
1866                               QNetworkReply::tr("Temporary network failure."));
1867                     }
1868                 }
1869             }
1870         }
1871 #endif
1872     }
1873
1874     state = Finished;
1875     q->setFinished(true);
1876
1877     if (totalSize.isNull() || totalSize == -1) {
1878         emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
1879     } else {
1880         emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
1881     }
1882
1883     if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
1884         emit q->uploadProgress(0, 0);
1885
1886     // if we don't know the total size of or we received everything save the cache
1887     if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
1888         completeCacheSave();
1889
1890     emit q->readChannelFinished();
1891     emit q->finished();
1892 }
1893
1894 void QNetworkReplyHttpImplPrivate::_q_error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
1895 {
1896     this->error(code, errorMessage);
1897 }
1898
1899
1900 void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
1901 {
1902     Q_Q(QNetworkReplyHttpImpl);
1903     // Can't set and emit multiple errors.
1904     if (errorCode != QNetworkReply::NoError) {
1905         qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
1906         return;
1907     }
1908
1909     errorCode = code;
1910     q->setErrorString(errorMessage);
1911
1912     // note: might not be a good idea, since users could decide to delete us
1913     // which would delete the backend too...
1914     // maybe we should protect the backend
1915     emit q->error(code);
1916 }
1917
1918 void QNetworkReplyHttpImplPrivate::metaDataChanged()
1919 {
1920     // FIXME merge this with replyDownloadMetaData(); ?
1921
1922     Q_Q(QNetworkReplyHttpImpl);
1923     // 1. do we have cookies?
1924     // 2. are we allowed to set them?
1925     if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && manager
1926         && (static_cast<QNetworkRequest::LoadControl>
1927             (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
1928                                QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
1929         QList<QNetworkCookie> cookies =
1930             qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
1931         QNetworkCookieJar *jar = manager->cookieJar();
1932         if (jar)
1933             jar->setCookiesFromUrl(cookies, url);
1934     }
1935     emit q->metaDataChanged();
1936 }
1937
1938 /*
1939     Migrates the backend of the QNetworkReply to a new network connection if required.  Returns
1940     true if the reply is migrated or it is not required; otherwise returns false.
1941 */
1942 bool QNetworkReplyHttpImplPrivate::migrateBackend()
1943 {
1944     Q_Q(QNetworkReplyHttpImpl);
1945
1946     // Network reply is already finished or aborted, don't need to migrate.
1947     if (state == Finished || state == Aborted)
1948         return true;
1949
1950     // Backend does not support resuming download.
1951     if (!canResume())
1952         return false;
1953
1954     // Request has outgoing data, not migrating.
1955     if (outgoingData)
1956         return false;
1957
1958     // Request is serviced from the cache, don't need to migrate.
1959     if (cacheLoadDevice)
1960         return true;
1961
1962     state = Reconnecting;
1963
1964     cookedHeaders.clear();
1965     rawHeaders.clear();
1966
1967     preMigrationDownloaded = bytesDownloaded;
1968
1969     setResumeOffset(bytesDownloaded);
1970
1971     emit q->abortHttpRequest();
1972
1973     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
1974
1975     return true;
1976 }
1977
1978
1979 void QNetworkReplyHttpImplPrivate::createCache()
1980 {
1981     // check if we can save and if we're allowed to
1982     if (!managerPrivate->networkCache
1983         || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
1984         return;
1985     cacheEnabled = true;
1986 }
1987
1988 bool QNetworkReplyHttpImplPrivate::isCachingEnabled() const
1989 {
1990     return (cacheEnabled && managerPrivate->networkCache != 0);
1991 }
1992
1993 void QNetworkReplyHttpImplPrivate::setCachingEnabled(bool enable)
1994 {
1995     if (!enable && !cacheEnabled)
1996         return;                 // nothing to do
1997     if (enable && cacheEnabled)
1998         return;                 // nothing to do either!
1999
2000     if (enable) {
2001         if (bytesDownloaded) {
2002             qDebug("setCachingEnabled: %lld bytesDownloaded", bytesDownloaded);
2003             // refuse to enable in this case
2004             qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
2005             return;
2006         }
2007
2008         createCache();
2009     } else {
2010         // someone told us to turn on, then back off?
2011         // ok... but you should make up your mind
2012         qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false)");
2013         managerPrivate->networkCache->remove(url);
2014         cacheSaveDevice = 0;
2015         cacheEnabled = false;
2016     }
2017 }
2018
2019 void QNetworkReplyHttpImplPrivate::completeCacheSave()
2020 {
2021     if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
2022         managerPrivate->networkCache->remove(url);
2023     } else if (cacheEnabled && cacheSaveDevice) {
2024         managerPrivate->networkCache->insert(cacheSaveDevice);
2025     }
2026     cacheSaveDevice = 0;
2027     cacheEnabled = false;
2028 }
2029
2030 QT_END_NAMESPACE
2031
2032 #endif // QT_NO_HTTP