source: trunk/src/network/access/qnetworkreplyimpl.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

File size: 30.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qnetworkreplyimpl_p.h"
43#include "qnetworkaccessbackend_p.h"
44#include "qnetworkcookie.h"
45#include "qabstractnetworkcache.h"
46#include "QtCore/qcoreapplication.h"
47#include "QtCore/qdatetime.h"
48#include "QtNetwork/qsslconfiguration.h"
49#include "QtNetwork/qnetworksession.h"
50#include "qnetworkaccesshttpbackend_p.h"
51#include "qnetworkaccessmanager_p.h"
52
53#include <QtCore/QCoreApplication>
54
55QT_BEGIN_NAMESPACE
56
57inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
58 : backend(0), outgoingData(0), outgoingDataBuffer(0),
59 copyDevice(0),
60 cacheEnabled(false), cacheSaveDevice(0),
61 notificationHandlingPaused(false),
62 bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
63 httpStatusCode(0),
64 state(Idle)
65{
66}
67
68void QNetworkReplyImplPrivate::_q_startOperation()
69{
70 // ensure this function is only being called once
71 if (state == Working) {
72 qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
73 return;
74 }
75 state = Working;
76
77 // note: if that method is called directly, it cannot happen that the backend is 0,
78 // because we just checked via a qobject_cast that we got a http backend (see
79 // QNetworkReplyImplPrivate::setup())
80 if (!backend) {
81 error(QNetworkReplyImpl::ProtocolUnknownError,
82 QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
83 finished();
84 return;
85 }
86
87#ifndef QT_NO_BEARERMANAGEMENT
88 if (!backend->start()) { // ### we should call that method even if bearer is not used
89 // backend failed to start because the session state is not Connected.
90 // QNetworkAccessManager will call reply->backend->start() again for us when the session
91 // state changes.
92 state = WaitingForSession;
93
94 QNetworkSession *session = manager->d_func()->networkSession.data();
95
96 if (session) {
97 Q_Q(QNetworkReplyImpl);
98
99 QObject::connect(session, SIGNAL(error(QNetworkSession::SessionError)),
100 q, SLOT(_q_networkSessionFailed()));
101
102 if (!session->isOpen())
103 session->open();
104 } else {
105 qWarning("Backend is waiting for QNetworkSession to connect, but there is none!");
106 }
107
108 return;
109 }
110#endif
111
112 if (backend && backend->isSynchronous()) {
113 state = Finished;
114 } else {
115 if (state != Finished) {
116 if (operation == QNetworkAccessManager::GetOperation)
117 pendingNotifications.append(NotifyDownstreamReadyWrite);
118
119 handleNotifications();
120 }
121 }
122}
123
124void QNetworkReplyImplPrivate::_q_copyReadyRead()
125{
126 Q_Q(QNetworkReplyImpl);
127 if (state != Working)
128 return;
129 if (!copyDevice || !q->isOpen())
130 return;
131
132 forever {
133 qint64 bytesToRead = nextDownstreamBlockSize();
134 if (bytesToRead == 0)
135 // we'll be called again, eventually
136 break;
137
138 bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
139 QByteArray byteData;
140 byteData.resize(bytesToRead);
141 qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
142 if (bytesActuallyRead == -1) {
143 byteData.clear();
144 backendNotify(NotifyCopyFinished);
145 break;
146 }
147
148 byteData.resize(bytesActuallyRead);
149 readBuffer.append(byteData);
150
151 if (!copyDevice->isSequential() && copyDevice->atEnd()) {
152 backendNotify(NotifyCopyFinished);
153 bytesDownloaded += bytesActuallyRead;
154 break;
155 }
156
157 bytesDownloaded += bytesActuallyRead;
158 }
159
160 if (bytesDownloaded == lastBytesDownloaded) {
161 // we didn't read anything
162 return;
163 }
164
165 lastBytesDownloaded = bytesDownloaded;
166 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
167 if (preMigrationDownloaded != Q_INT64_C(-1))
168 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
169 pauseNotificationHandling();
170 emit q->downloadProgress(bytesDownloaded,
171 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
172 emit q->readyRead();
173 resumeNotificationHandling();
174}
175
176void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
177{
178 _q_copyReadyRead();
179}
180
181void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
182{
183 Q_Q(QNetworkReplyImpl);
184
185 // make sure this is only called once, ever.
186 //_q_bufferOutgoingData may call it or the readChannelFinished emission
187 if (state != Buffering)
188 return;
189
190 // disconnect signals
191 QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
192 QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
193
194 // finally, start the request
195 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
196}
197
198void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
199{
200 Q_Q(QNetworkReplyImpl);
201
202 if (!outgoingDataBuffer) {
203 // first call, create our buffer
204 outgoingDataBuffer = new QRingBuffer();
205
206 QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
207 QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
208 }
209
210 qint64 bytesBuffered = 0;
211 qint64 bytesToBuffer = 0;
212
213 // read data into our buffer
214 forever {
215 bytesToBuffer = outgoingData->bytesAvailable();
216 // unknown? just try 2 kB, this also ensures we always try to read the EOF
217 if (bytesToBuffer <= 0)
218 bytesToBuffer = 2*1024;
219
220 char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
221 bytesBuffered = outgoingData->read(dst, bytesToBuffer);
222
223 if (bytesBuffered == -1) {
224 // EOF has been reached.
225 outgoingDataBuffer->chop(bytesToBuffer);
226
227 _q_bufferOutgoingDataFinished();
228 break;
229 } else if (bytesBuffered == 0) {
230 // nothing read right now, just wait until we get called again
231 outgoingDataBuffer->chop(bytesToBuffer);
232
233 break;
234 } else {
235 // don't break, try to read() again
236 outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
237 }
238 }
239}
240
241#ifndef QT_NO_BEARERMANAGEMENT
242void QNetworkReplyImplPrivate::_q_networkSessionConnected()
243{
244 Q_Q(QNetworkReplyImpl);
245
246 if (manager.isNull())
247 return;
248
249 QNetworkSession *session = manager->d_func()->networkSession.data();
250 if (!session)
251 return;
252
253 if (session->state() != QNetworkSession::Connected)
254 return;
255
256 switch (state) {
257 case QNetworkReplyImplPrivate::Buffering:
258 case QNetworkReplyImplPrivate::Working:
259 case QNetworkReplyImplPrivate::Reconnecting:
260 // Migrate existing downloads to new network connection.
261 migrateBackend();
262 break;
263 case QNetworkReplyImplPrivate::WaitingForSession:
264 // Start waiting requests.
265 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
266 break;
267 default:
268 ;
269 }
270}
271
272void QNetworkReplyImplPrivate::_q_networkSessionFailed()
273{
274 // Abort waiting and working replies.
275 if (state == WaitingForSession || state == Working) {
276 state = Working;
277 error(QNetworkReplyImpl::UnknownNetworkError,
278 QCoreApplication::translate("QNetworkReply", "Network session error."));
279 finished();
280 }
281}
282#endif
283
284void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
285 QIODevice *data)
286{
287 Q_Q(QNetworkReplyImpl);
288
289 outgoingData = data;
290 request = req;
291 url = request.url();
292 operation = op;
293
294 q->QIODevice::open(QIODevice::ReadOnly);
295 // Internal code that does a HTTP reply for the synchronous Ajax
296 // in QtWebKit.
297 QVariant synchronousHttpAttribute = req.attribute(
298 static_cast<QNetworkRequest::Attribute>(QNetworkRequest::DownloadBufferAttribute + 1));
299 if (backend && synchronousHttpAttribute.toBool()) {
300 backend->setSynchronous(true);
301 if (outgoingData && outgoingData->isSequential()) {
302 outgoingDataBuffer = new QRingBuffer();
303 QByteArray data;
304 do {
305 data = outgoingData->readAll();
306 if (data.isEmpty())
307 break;
308 outgoingDataBuffer->append(data);
309 } while (1);
310 }
311 }
312 if (outgoingData && backend && !backend->isSynchronous()) {
313 // there is data to be uploaded, e.g. HTTP POST.
314
315 if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
316 // backend does not need upload buffering or
317 // fixed size non-sequential
318 // just start the operation
319 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
320 } else {
321 bool bufferingDisallowed =
322 req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
323 false).toBool();
324
325 if (bufferingDisallowed) {
326 // if a valid content-length header for the request was supplied, we can disable buffering
327 // if not, we will buffer anyway
328 if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
329 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
330 } else {
331 state = Buffering;
332 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
333 }
334 } else {
335 // _q_startOperation will be called when the buffering has finished.
336 state = Buffering;
337 QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
338 }
339 }
340 } else {
341 // No outgoing data (e.g. HTTP GET request)
342 // or no backend
343 // if no backend, _q_startOperation will handle the error of this
344
345 // for HTTP, we want to send out the request as fast as possible to the network, without
346 // invoking methods in a QueuedConnection
347#ifndef QT_NO_HTTP
348 if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
349 _q_startOperation();
350 } else {
351 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
352 }
353#else
354 if (backend && backend->isSynchronous())
355 _q_startOperation();
356 else
357 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
358#endif // QT_NO_HTTP
359 }
360}
361
362void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
363{
364 Q_Q(QNetworkReplyImpl);
365 if (!pendingNotifications.contains(notification))
366 pendingNotifications.enqueue(notification);
367
368 if (pendingNotifications.size() == 1)
369 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
370}
371
372void QNetworkReplyImplPrivate::handleNotifications()
373{
374 if (notificationHandlingPaused)
375 return;
376
377 NotificationQueue current = pendingNotifications;
378 pendingNotifications.clear();
379
380 if (state != Working)
381 return;
382
383 while (state == Working && !current.isEmpty()) {
384 InternalNotifications notification = current.dequeue();
385 switch (notification) {
386 case NotifyDownstreamReadyWrite:
387 if (copyDevice)
388 _q_copyReadyRead();
389 else
390 backend->downstreamReadyWrite();
391 break;
392
393 case NotifyCloseDownstreamChannel:
394 backend->closeDownstreamChannel();
395 break;
396
397 case NotifyCopyFinished: {
398 QIODevice *dev = copyDevice;
399 copyDevice = 0;
400 backend->copyFinished(dev);
401 break;
402 }
403 }
404 }
405}
406
407// Do not handle the notifications while we are emitting downloadProgress
408// or readyRead
409void QNetworkReplyImplPrivate::pauseNotificationHandling()
410{
411 notificationHandlingPaused = true;
412}
413
414// Resume notification handling
415void QNetworkReplyImplPrivate::resumeNotificationHandling()
416{
417 Q_Q(QNetworkReplyImpl);
418 notificationHandlingPaused = false;
419 if (pendingNotifications.size() >= 1)
420 QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
421}
422
423QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
424{
425 if (!backend)
426 return 0;
427 return backend->networkCache();
428}
429
430void QNetworkReplyImplPrivate::createCache()
431{
432 // check if we can save and if we're allowed to
433 if (!networkCache()
434 || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
435 || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
436 QNetworkRequest::PreferNetwork).toInt()
437 == QNetworkRequest::AlwaysNetwork)
438 return;
439 cacheEnabled = true;
440}
441
442bool QNetworkReplyImplPrivate::isCachingEnabled() const
443{
444 return (cacheEnabled && networkCache() != 0);
445}
446
447void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
448{
449 if (!enable && !cacheEnabled)
450 return; // nothing to do
451 if (enable && cacheEnabled)
452 return; // nothing to do either!
453
454 if (enable) {
455 if (bytesDownloaded) {
456 // refuse to enable in this case
457 qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
458 return;
459 }
460
461 createCache();
462 } else {
463 // someone told us to turn on, then back off?
464 // ok... but you should make up your mind
465 qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
466 "backend %s probably needs to be fixed",
467 backend->metaObject()->className());
468 networkCache()->remove(url);
469 cacheSaveDevice = 0;
470 cacheEnabled = false;
471 }
472}
473
474void QNetworkReplyImplPrivate::completeCacheSave()
475{
476 if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
477 networkCache()->remove(url);
478 } else if (cacheEnabled && cacheSaveDevice) {
479 networkCache()->insert(cacheSaveDevice);
480 }
481 cacheSaveDevice = 0;
482 cacheEnabled = false;
483}
484
485void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
486{
487 Q_Q(QNetworkReplyImpl);
488 bytesUploaded = bytesSent;
489 pauseNotificationHandling();
490 emit q->uploadProgress(bytesSent, bytesTotal);
491 resumeNotificationHandling();
492}
493
494
495qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
496{
497 enum { DesiredBufferSize = 32 * 1024 };
498 if (readBufferMaxSize == 0)
499 return DesiredBufferSize;
500
501 return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
502}
503
504void QNetworkReplyImplPrivate::initCacheSaveDevice()
505{
506 Q_Q(QNetworkReplyImpl);
507
508 // save the meta data
509 QNetworkCacheMetaData metaData;
510 metaData.setUrl(url);
511 metaData = backend->fetchCacheMetaData(metaData);
512
513 // save the redirect request also in the cache
514 QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
515 if (redirectionTarget.isValid()) {
516 QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
517 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
518 metaData.setAttributes(attributes);
519 }
520
521 cacheSaveDevice = networkCache()->prepare(metaData);
522
523 if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
524 if (cacheSaveDevice && !cacheSaveDevice->isOpen())
525 qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
526 "class %s probably needs to be fixed",
527 networkCache()->metaObject()->className());
528
529 networkCache()->remove(url);
530 cacheSaveDevice = 0;
531 cacheEnabled = false;
532 }
533}
534
535// we received downstream data and send this to the cache
536// and to our readBuffer (which in turn gets read by the user of QNetworkReply)
537void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
538{
539 Q_Q(QNetworkReplyImpl);
540 if (!q->isOpen())
541 return;
542
543 if (cacheEnabled && !cacheSaveDevice) {
544 initCacheSaveDevice();
545 }
546
547 qint64 bytesWritten = 0;
548 for (int i = 0; i < data.bufferCount(); i++) {
549 QByteArray const &item = data[i];
550
551 if (cacheSaveDevice)
552 cacheSaveDevice->write(item.constData(), item.size());
553 readBuffer.append(item);
554
555 bytesWritten += item.size();
556 }
557 data.clear();
558
559 bytesDownloaded += bytesWritten;
560 lastBytesDownloaded = bytesDownloaded;
561
562 appendDownstreamDataSignalEmissions();
563}
564
565void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
566{
567 Q_Q(QNetworkReplyImpl);
568
569 QPointer<QNetworkReplyImpl> qq = q;
570
571 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
572 if (preMigrationDownloaded != Q_INT64_C(-1))
573 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
574 pauseNotificationHandling();
575 emit q->downloadProgress(bytesDownloaded,
576 totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
577 // important: At the point of this readyRead(), the data parameter list must be empty,
578 // else implicit sharing will trigger memcpy when the user is reading data!
579 emit q->readyRead();
580
581 // hopefully we haven't been deleted here
582 if (!qq.isNull()) {
583 resumeNotificationHandling();
584 // do we still have room in the buffer?
585 if (nextDownstreamBlockSize() > 0)
586 backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
587 }
588}
589
590// this is used when it was fetched from the cache, right?
591void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
592{
593 Q_Q(QNetworkReplyImpl);
594 if (!q->isOpen())
595 return;
596
597 // read until EOF from data
598 if (copyDevice) {
599 qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
600 "backend probly needs to be fixed");
601 return;
602 }
603
604 copyDevice = data;
605 q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
606 q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
607
608 // start the copy:
609 _q_copyReadyRead();
610}
611
612void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
613{
614 Q_UNUSED(data)
615 // TODO implement
616
617 // TODO call
618
619 qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
620}
621
622void QNetworkReplyImplPrivate::finished()
623{
624 Q_Q(QNetworkReplyImpl);
625
626 if (state == Finished || state == Aborted || state == WaitingForSession)
627 return;
628
629 pauseNotificationHandling();
630 QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
631 if (preMigrationDownloaded != Q_INT64_C(-1))
632 totalSize = totalSize.toLongLong() + preMigrationDownloaded;
633
634 if (!manager.isNull()) {
635#ifndef QT_NO_BEARERMANAGEMENT
636 QNetworkSession *session = manager->d_func()->networkSession.data();
637 if (session && session->state() == QNetworkSession::Roaming &&
638 state == Working && errorCode != QNetworkReply::OperationCanceledError) {
639 // only content with a known size will fail with a temporary network failure error
640 if (!totalSize.isNull()) {
641 if (bytesDownloaded != totalSize) {
642 if (migrateBackend()) {
643 // either we are migrating or the request is finished/aborted
644 if (state == Reconnecting || state == WaitingForSession) {
645 resumeNotificationHandling();
646 return; // exit early if we are migrating.
647 }
648 } else {
649 error(QNetworkReply::TemporaryNetworkFailureError,
650 QNetworkReply::tr("Temporary network failure."));
651 }
652 }
653 }
654 }
655#endif
656 }
657 resumeNotificationHandling();
658
659 state = Finished;
660 pendingNotifications.clear();
661
662 pauseNotificationHandling();
663 if (totalSize.isNull() || totalSize == -1) {
664 emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
665 }
666
667 if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
668 emit q->uploadProgress(0, 0);
669 resumeNotificationHandling();
670
671 // if we don't know the total size of or we received everything save the cache
672 if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
673 completeCacheSave();
674
675 // note: might not be a good idea, since users could decide to delete us
676 // which would delete the backend too...
677 // maybe we should protect the backend
678 pauseNotificationHandling();
679 emit q->readChannelFinished();
680 emit q->finished();
681 resumeNotificationHandling();
682}
683
684void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
685{
686 Q_Q(QNetworkReplyImpl);
687
688 errorCode = code;
689 q->setErrorString(errorMessage);
690
691 // note: might not be a good idea, since users could decide to delete us
692 // which would delete the backend too...
693 // maybe we should protect the backend
694 emit q->error(code);
695}
696
697void QNetworkReplyImplPrivate::metaDataChanged()
698{
699 Q_Q(QNetworkReplyImpl);
700 // 1. do we have cookies?
701 // 2. are we allowed to set them?
702 if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
703 && (static_cast<QNetworkRequest::LoadControl>
704 (request.attribute(QNetworkRequest::CookieSaveControlAttribute,
705 QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
706 QList<QNetworkCookie> cookies =
707 qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
708 QNetworkCookieJar *jar = manager->cookieJar();
709 if (jar)
710 jar->setCookiesFromUrl(cookies, url);
711 }
712 emit q->metaDataChanged();
713}
714
715void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
716{
717 attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
718}
719
720void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
721{
722#ifndef QT_NO_OPENSSL
723 Q_Q(QNetworkReplyImpl);
724 emit q->sslErrors(errors);
725#else
726 Q_UNUSED(errors);
727#endif
728}
729
730bool QNetworkReplyImplPrivate::isFinished() const
731{
732 return (state == Finished || state == Aborted);
733}
734
735QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
736 : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
737{
738}
739
740QNetworkReplyImpl::~QNetworkReplyImpl()
741{
742 Q_D(QNetworkReplyImpl);
743
744 // This code removes the data from the cache if it was prematurely aborted.
745 // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
746 // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
747 if (d->isCachingEnabled())
748 d->networkCache()->remove(url());
749
750 if (d->outgoingDataBuffer)
751 delete d->outgoingDataBuffer;
752}
753
754void QNetworkReplyImpl::abort()
755{
756 Q_D(QNetworkReplyImpl);
757 if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
758 return;
759
760 // stop both upload and download
761 if (d->outgoingData)
762 disconnect(d->outgoingData, 0, this, 0);
763 if (d->copyDevice)
764 disconnect(d->copyDevice, 0, this, 0);
765
766 QNetworkReply::close();
767
768 if (d->state != QNetworkReplyImplPrivate::Finished) {
769 // emit signals
770 d->error(OperationCanceledError, tr("Operation canceled"));
771 d->finished();
772 }
773 d->state = QNetworkReplyImplPrivate::Aborted;
774
775 // finished may access the backend
776 if (d->backend) {
777 d->backend->deleteLater();
778 d->backend = 0;
779 }
780}
781
782void QNetworkReplyImpl::close()
783{
784 Q_D(QNetworkReplyImpl);
785 if (d->state == QNetworkReplyImplPrivate::Aborted ||
786 d->state == QNetworkReplyImplPrivate::Finished)
787 return;
788
789 // stop the download
790 if (d->backend)
791 d->backend->closeDownstreamChannel();
792 if (d->copyDevice)
793 disconnect(d->copyDevice, 0, this, 0);
794
795 QNetworkReply::close();
796
797 // emit signals
798 d->error(OperationCanceledError, tr("Operation canceled"));
799 d->finished();
800}
801
802bool QNetworkReplyImpl::canReadLine () const
803{
804 Q_D(const QNetworkReplyImpl);
805 return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
806}
807
808
809/*!
810 Returns the number of bytes available for reading with
811 QIODevice::read(). The number of bytes available may grow until
812 the finished() signal is emitted.
813*/
814qint64 QNetworkReplyImpl::bytesAvailable() const
815{
816 return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
817}
818
819void QNetworkReplyImpl::setReadBufferSize(qint64 size)
820{
821 Q_D(QNetworkReplyImpl);
822 if (size > d->readBufferMaxSize &&
823 size > d->readBuffer.byteAmount())
824 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
825
826 QNetworkReply::setReadBufferSize(size);
827
828 if (d->backend)
829 d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
830}
831
832#ifndef QT_NO_OPENSSL
833QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
834{
835 Q_D(const QNetworkReplyImpl);
836 QSslConfiguration config;
837 if (d->backend)
838 d->backend->fetchSslConfiguration(config);
839 return config;
840}
841
842void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
843{
844 Q_D(QNetworkReplyImpl);
845 if (d->backend && !config.isNull())
846 d->backend->setSslConfiguration(config);
847}
848
849void QNetworkReplyImpl::ignoreSslErrors()
850{
851 Q_D(QNetworkReplyImpl);
852 if (d->backend)
853 d->backend->ignoreSslErrors();
854}
855
856void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
857{
858 Q_D(QNetworkReplyImpl);
859 if (d->backend)
860 d->backend->ignoreSslErrors(errors);
861}
862#endif // QT_NO_OPENSSL
863
864/*!
865 \internal
866*/
867qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
868{
869 Q_D(QNetworkReplyImpl);
870 if (d->readBuffer.isEmpty())
871 return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
872
873 d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
874 if (maxlen == 1) {
875 // optimization for getChar()
876 *data = d->readBuffer.getChar();
877 return 1;
878 }
879
880 maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
881 return d->readBuffer.read(data, maxlen);
882}
883
884/*!
885 \internal Reimplemented for internal purposes
886*/
887bool QNetworkReplyImpl::event(QEvent *e)
888{
889 if (e->type() == QEvent::NetworkReplyUpdated) {
890 d_func()->handleNotifications();
891 return true;
892 }
893
894 return QObject::event(e);
895}
896
897/*
898 Migrates the backend of the QNetworkReply to a new network connection if required. Returns
899 true if the reply is migrated or it is not required; otherwise returns false.
900*/
901bool QNetworkReplyImplPrivate::migrateBackend()
902{
903 Q_Q(QNetworkReplyImpl);
904
905 // Network reply is already finished or aborted, don't need to migrate.
906 if (state == Finished || state == Aborted)
907 return true;
908
909 // Backend does not support resuming download.
910 if (!backend->canResume())
911 return false;
912
913 // Request has outgoing data, not migrating.
914 if (outgoingData)
915 return false;
916
917 // Request is serviced from the cache, don't need to migrate.
918 if (copyDevice)
919 return true;
920
921 state = QNetworkReplyImplPrivate::Reconnecting;
922
923 if (backend) {
924 delete backend;
925 backend = 0;
926 }
927
928 cookedHeaders.clear();
929 rawHeaders.clear();
930
931 preMigrationDownloaded = bytesDownloaded;
932
933 backend = manager->d_func()->findBackend(operation, request);
934
935 if (backend) {
936 backend->setParent(q);
937 backend->reply = this;
938 backend->setResumeOffset(bytesDownloaded);
939 }
940
941#ifndef QT_NO_HTTP
942 if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
943 _q_startOperation();
944 } else {
945 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
946 }
947#else
948 QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
949#endif // QT_NO_HTTP
950
951 return true;
952}
953
954#ifndef QT_NO_BEARERMANAGEMENT
955QDisabledNetworkReply::QDisabledNetworkReply(QObject *parent,
956 const QNetworkRequest &req,
957 QNetworkAccessManager::Operation op)
958: QNetworkReply(parent)
959{
960 setRequest(req);
961 setUrl(req.url());
962 setOperation(op);
963
964 qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
965
966 QString msg = QCoreApplication::translate("QNetworkAccessManager",
967 "Network access is disabled.");
968 setError(UnknownNetworkError, msg);
969
970 QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
971 Q_ARG(QNetworkReply::NetworkError, UnknownNetworkError));
972 QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
973}
974
975QDisabledNetworkReply::~QDisabledNetworkReply()
976{
977}
978#endif
979
980QT_END_NAMESPACE
981
982#include "moc_qnetworkreplyimpl_p.cpp"
983
Note: See TracBrowser for help on using the repository browser.