1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the QtNetwork module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | #include "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 "qnetworkaccesshttpbackend_p.h"
|
---|
50 |
|
---|
51 | #include <QtCore/QCoreApplication>
|
---|
52 |
|
---|
53 | QT_BEGIN_NAMESPACE
|
---|
54 |
|
---|
55 | inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
|
---|
56 | : backend(0), outgoingData(0), outgoingDataBuffer(0),
|
---|
57 | copyDevice(0),
|
---|
58 | cacheEnabled(false), cacheSaveDevice(0),
|
---|
59 | notificationHandlingPaused(false),
|
---|
60 | bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1),
|
---|
61 | httpStatusCode(0),
|
---|
62 | state(Idle)
|
---|
63 | {
|
---|
64 | }
|
---|
65 |
|
---|
66 | void QNetworkReplyImplPrivate::_q_startOperation()
|
---|
67 | {
|
---|
68 | // ensure this function is only being called once
|
---|
69 | if (state == Working) {
|
---|
70 | qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
|
---|
71 | return;
|
---|
72 | }
|
---|
73 | state = Working;
|
---|
74 |
|
---|
75 | // note: if that method is called directly, it cannot happen that the backend is 0,
|
---|
76 | // because we just checked via a qobject_cast that we got a http backend (see
|
---|
77 | // QNetworkReplyImplPrivate::setup())
|
---|
78 | if (!backend) {
|
---|
79 | error(QNetworkReplyImpl::ProtocolUnknownError,
|
---|
80 | QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
|
---|
81 | finished();
|
---|
82 | return;
|
---|
83 | }
|
---|
84 |
|
---|
85 | backend->open();
|
---|
86 | if (state != Finished) {
|
---|
87 | if (operation == QNetworkAccessManager::GetOperation)
|
---|
88 | pendingNotifications.append(NotifyDownstreamReadyWrite);
|
---|
89 |
|
---|
90 | handleNotifications();
|
---|
91 | }
|
---|
92 | }
|
---|
93 |
|
---|
94 | void QNetworkReplyImplPrivate::_q_copyReadyRead()
|
---|
95 | {
|
---|
96 | Q_Q(QNetworkReplyImpl);
|
---|
97 | if (state != Working)
|
---|
98 | return;
|
---|
99 | if (!copyDevice || !q->isOpen())
|
---|
100 | return;
|
---|
101 |
|
---|
102 | forever {
|
---|
103 | qint64 bytesToRead = nextDownstreamBlockSize();
|
---|
104 | if (bytesToRead == 0)
|
---|
105 | // we'll be called again, eventually
|
---|
106 | break;
|
---|
107 |
|
---|
108 | bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
|
---|
109 | QByteArray byteData;
|
---|
110 | byteData.resize(bytesToRead);
|
---|
111 | qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
|
---|
112 | if (bytesActuallyRead == -1) {
|
---|
113 | byteData.clear();
|
---|
114 | backendNotify(NotifyCopyFinished);
|
---|
115 | break;
|
---|
116 | }
|
---|
117 |
|
---|
118 | byteData.resize(bytesActuallyRead);
|
---|
119 | readBuffer.append(byteData);
|
---|
120 |
|
---|
121 | if (!copyDevice->isSequential() && copyDevice->atEnd()) {
|
---|
122 | backendNotify(NotifyCopyFinished);
|
---|
123 | bytesDownloaded += bytesActuallyRead;
|
---|
124 | break;
|
---|
125 | }
|
---|
126 |
|
---|
127 | bytesDownloaded += bytesActuallyRead;
|
---|
128 | }
|
---|
129 |
|
---|
130 | if (bytesDownloaded == lastBytesDownloaded) {
|
---|
131 | // we didn't read anything
|
---|
132 | return;
|
---|
133 | }
|
---|
134 |
|
---|
135 | lastBytesDownloaded = bytesDownloaded;
|
---|
136 | QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
---|
137 | pauseNotificationHandling();
|
---|
138 | emit q->downloadProgress(bytesDownloaded,
|
---|
139 | totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
|
---|
140 | emit q->readyRead();
|
---|
141 | resumeNotificationHandling();
|
---|
142 | }
|
---|
143 |
|
---|
144 | void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
|
---|
145 | {
|
---|
146 | _q_copyReadyRead();
|
---|
147 | }
|
---|
148 |
|
---|
149 | void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
|
---|
150 | {
|
---|
151 | Q_Q(QNetworkReplyImpl);
|
---|
152 |
|
---|
153 | // make sure this is only called once, ever.
|
---|
154 | //_q_bufferOutgoingData may call it or the readChannelFinished emission
|
---|
155 | if (state != Buffering)
|
---|
156 | return;
|
---|
157 |
|
---|
158 | // disconnect signals
|
---|
159 | QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
|
---|
160 | QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
|
---|
161 |
|
---|
162 | // finally, start the request
|
---|
163 | QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
---|
164 | }
|
---|
165 |
|
---|
166 | void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
|
---|
167 | {
|
---|
168 | Q_Q(QNetworkReplyImpl);
|
---|
169 |
|
---|
170 | if (!outgoingDataBuffer) {
|
---|
171 | // first call, create our buffer
|
---|
172 | outgoingDataBuffer = new QRingBuffer();
|
---|
173 |
|
---|
174 | QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
|
---|
175 | QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
|
---|
176 | }
|
---|
177 |
|
---|
178 | qint64 bytesBuffered = 0;
|
---|
179 | qint64 bytesToBuffer = 0;
|
---|
180 |
|
---|
181 | // read data into our buffer
|
---|
182 | forever {
|
---|
183 | bytesToBuffer = outgoingData->bytesAvailable();
|
---|
184 | // unknown? just try 2 kB, this also ensures we always try to read the EOF
|
---|
185 | if (bytesToBuffer <= 0)
|
---|
186 | bytesToBuffer = 2*1024;
|
---|
187 |
|
---|
188 | char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
|
---|
189 | bytesBuffered = outgoingData->read(dst, bytesToBuffer);
|
---|
190 |
|
---|
191 | if (bytesBuffered == -1) {
|
---|
192 | // EOF has been reached.
|
---|
193 | outgoingDataBuffer->chop(bytesToBuffer);
|
---|
194 |
|
---|
195 | _q_bufferOutgoingDataFinished();
|
---|
196 | break;
|
---|
197 | } else if (bytesBuffered == 0) {
|
---|
198 | // nothing read right now, just wait until we get called again
|
---|
199 | outgoingDataBuffer->chop(bytesToBuffer);
|
---|
200 |
|
---|
201 | break;
|
---|
202 | } else {
|
---|
203 | // don't break, try to read() again
|
---|
204 | outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
|
---|
205 | }
|
---|
206 | }
|
---|
207 | }
|
---|
208 |
|
---|
209 | void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
|
---|
210 | QIODevice *data)
|
---|
211 | {
|
---|
212 | Q_Q(QNetworkReplyImpl);
|
---|
213 |
|
---|
214 | outgoingData = data;
|
---|
215 | request = req;
|
---|
216 | url = request.url();
|
---|
217 | operation = op;
|
---|
218 |
|
---|
219 | if (outgoingData && backend) {
|
---|
220 | // there is data to be uploaded, e.g. HTTP POST.
|
---|
221 |
|
---|
222 | if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
|
---|
223 | // backend does not need upload buffering or
|
---|
224 | // fixed size non-sequential
|
---|
225 | // just start the operation
|
---|
226 | QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
---|
227 | } else {
|
---|
228 | bool bufferingDisallowed =
|
---|
229 | req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
|
---|
230 | false).toBool();
|
---|
231 |
|
---|
232 | if (bufferingDisallowed) {
|
---|
233 | // if a valid content-length header for the request was supplied, we can disable buffering
|
---|
234 | // if not, we will buffer anyway
|
---|
235 | if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
|
---|
236 | QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
---|
237 | } else {
|
---|
238 | state = Buffering;
|
---|
239 | QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
|
---|
240 | }
|
---|
241 | } else {
|
---|
242 | // _q_startOperation will be called when the buffering has finished.
|
---|
243 | state = Buffering;
|
---|
244 | QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
|
---|
245 | }
|
---|
246 | }
|
---|
247 | } else {
|
---|
248 | // No outgoing data (e.g. HTTP GET request)
|
---|
249 | // or no backend
|
---|
250 | // if no backend, _q_startOperation will handle the error of this
|
---|
251 |
|
---|
252 | // for HTTP, we want to send out the request as fast as possible to the network, without
|
---|
253 | // invoking methods in a QueuedConnection
|
---|
254 | if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
|
---|
255 | _q_startOperation();
|
---|
256 | } else {
|
---|
257 | QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
---|
258 | }
|
---|
259 | }
|
---|
260 |
|
---|
261 | q->QIODevice::open(QIODevice::ReadOnly);
|
---|
262 | }
|
---|
263 |
|
---|
264 | void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
|
---|
265 | {
|
---|
266 | Q_Q(QNetworkReplyImpl);
|
---|
267 | if (!pendingNotifications.contains(notification))
|
---|
268 | pendingNotifications.enqueue(notification);
|
---|
269 |
|
---|
270 | if (pendingNotifications.size() == 1)
|
---|
271 | QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
|
---|
272 | }
|
---|
273 |
|
---|
274 | void QNetworkReplyImplPrivate::handleNotifications()
|
---|
275 | {
|
---|
276 | if (notificationHandlingPaused)
|
---|
277 | return;
|
---|
278 |
|
---|
279 | NotificationQueue current = pendingNotifications;
|
---|
280 | pendingNotifications.clear();
|
---|
281 |
|
---|
282 | if (state != Working)
|
---|
283 | return;
|
---|
284 |
|
---|
285 | while (state == Working && !current.isEmpty()) {
|
---|
286 | InternalNotifications notification = current.dequeue();
|
---|
287 | switch (notification) {
|
---|
288 | case NotifyDownstreamReadyWrite:
|
---|
289 | if (copyDevice)
|
---|
290 | _q_copyReadyRead();
|
---|
291 | else
|
---|
292 | backend->downstreamReadyWrite();
|
---|
293 | break;
|
---|
294 |
|
---|
295 | case NotifyCloseDownstreamChannel:
|
---|
296 | backend->closeDownstreamChannel();
|
---|
297 | break;
|
---|
298 |
|
---|
299 | case NotifyCopyFinished: {
|
---|
300 | QIODevice *dev = copyDevice;
|
---|
301 | copyDevice = 0;
|
---|
302 | backend->copyFinished(dev);
|
---|
303 | break;
|
---|
304 | }
|
---|
305 | }
|
---|
306 | }
|
---|
307 | }
|
---|
308 |
|
---|
309 | // Do not handle the notifications while we are emitting downloadProgress
|
---|
310 | // or readyRead
|
---|
311 | void QNetworkReplyImplPrivate::pauseNotificationHandling()
|
---|
312 | {
|
---|
313 | notificationHandlingPaused = true;
|
---|
314 | }
|
---|
315 |
|
---|
316 | // Resume notification handling
|
---|
317 | void QNetworkReplyImplPrivate::resumeNotificationHandling()
|
---|
318 | {
|
---|
319 | Q_Q(QNetworkReplyImpl);
|
---|
320 | notificationHandlingPaused = false;
|
---|
321 | if (pendingNotifications.size() >= 1)
|
---|
322 | QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
|
---|
323 | }
|
---|
324 |
|
---|
325 | QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
|
---|
326 | {
|
---|
327 | if (!backend)
|
---|
328 | return 0;
|
---|
329 | return backend->networkCache();
|
---|
330 | }
|
---|
331 |
|
---|
332 | void QNetworkReplyImplPrivate::createCache()
|
---|
333 | {
|
---|
334 | // check if we can save and if we're allowed to
|
---|
335 | if (!networkCache()
|
---|
336 | || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
|
---|
337 | || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
|
---|
338 | QNetworkRequest::PreferNetwork).toInt()
|
---|
339 | == QNetworkRequest::AlwaysNetwork)
|
---|
340 | return;
|
---|
341 | cacheEnabled = true;
|
---|
342 | }
|
---|
343 |
|
---|
344 | bool QNetworkReplyImplPrivate::isCachingEnabled() const
|
---|
345 | {
|
---|
346 | return (cacheEnabled && networkCache() != 0);
|
---|
347 | }
|
---|
348 |
|
---|
349 | void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
|
---|
350 | {
|
---|
351 | if (!enable && !cacheEnabled)
|
---|
352 | return; // nothing to do
|
---|
353 | if (enable && cacheEnabled)
|
---|
354 | return; // nothing to do either!
|
---|
355 |
|
---|
356 | if (enable) {
|
---|
357 | if (bytesDownloaded) {
|
---|
358 | // refuse to enable in this case
|
---|
359 | qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
|
---|
360 | return;
|
---|
361 | }
|
---|
362 |
|
---|
363 | createCache();
|
---|
364 | } else {
|
---|
365 | // someone told us to turn on, then back off?
|
---|
366 | // ok... but you should make up your mind
|
---|
367 | qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
|
---|
368 | "backend %s probably needs to be fixed",
|
---|
369 | backend->metaObject()->className());
|
---|
370 | networkCache()->remove(url);
|
---|
371 | cacheSaveDevice = 0;
|
---|
372 | cacheEnabled = false;
|
---|
373 | }
|
---|
374 | }
|
---|
375 |
|
---|
376 | void QNetworkReplyImplPrivate::completeCacheSave()
|
---|
377 | {
|
---|
378 | if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
|
---|
379 | networkCache()->remove(url);
|
---|
380 | } else if (cacheEnabled && cacheSaveDevice) {
|
---|
381 | networkCache()->insert(cacheSaveDevice);
|
---|
382 | }
|
---|
383 | cacheSaveDevice = 0;
|
---|
384 | cacheEnabled = false;
|
---|
385 | }
|
---|
386 |
|
---|
387 | void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
|
---|
388 | {
|
---|
389 | Q_Q(QNetworkReplyImpl);
|
---|
390 | bytesUploaded = bytesSent;
|
---|
391 | pauseNotificationHandling();
|
---|
392 | emit q->uploadProgress(bytesSent, bytesTotal);
|
---|
393 | resumeNotificationHandling();
|
---|
394 | }
|
---|
395 |
|
---|
396 |
|
---|
397 | qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
|
---|
398 | {
|
---|
399 | enum { DesiredBufferSize = 32 * 1024 };
|
---|
400 | if (readBufferMaxSize == 0)
|
---|
401 | return DesiredBufferSize;
|
---|
402 |
|
---|
403 | return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
|
---|
404 | }
|
---|
405 |
|
---|
406 | void QNetworkReplyImplPrivate::initCacheSaveDevice()
|
---|
407 | {
|
---|
408 | Q_Q(QNetworkReplyImpl);
|
---|
409 |
|
---|
410 | // save the meta data
|
---|
411 | QNetworkCacheMetaData metaData;
|
---|
412 | metaData.setUrl(url);
|
---|
413 | metaData = backend->fetchCacheMetaData(metaData);
|
---|
414 |
|
---|
415 | // save the redirect request also in the cache
|
---|
416 | QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
---|
417 | if (redirectionTarget.isValid()) {
|
---|
418 | QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
|
---|
419 | attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
|
---|
420 | metaData.setAttributes(attributes);
|
---|
421 | }
|
---|
422 |
|
---|
423 | cacheSaveDevice = networkCache()->prepare(metaData);
|
---|
424 |
|
---|
425 | if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
|
---|
426 | if (cacheSaveDevice && !cacheSaveDevice->isOpen())
|
---|
427 | qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
|
---|
428 | "class %s probably needs to be fixed",
|
---|
429 | networkCache()->metaObject()->className());
|
---|
430 |
|
---|
431 | networkCache()->remove(url);
|
---|
432 | cacheSaveDevice = 0;
|
---|
433 | cacheEnabled = false;
|
---|
434 | }
|
---|
435 | }
|
---|
436 |
|
---|
437 | // we received downstream data and send this to the cache
|
---|
438 | // and to our readBuffer (which in turn gets read by the user of QNetworkReply)
|
---|
439 | void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
|
---|
440 | {
|
---|
441 | Q_Q(QNetworkReplyImpl);
|
---|
442 | if (!q->isOpen())
|
---|
443 | return;
|
---|
444 |
|
---|
445 | if (cacheEnabled && !cacheSaveDevice) {
|
---|
446 | initCacheSaveDevice();
|
---|
447 | }
|
---|
448 |
|
---|
449 | qint64 bytesWritten = 0;
|
---|
450 | for (int i = 0; i < data.bufferCount(); i++) {
|
---|
451 | QByteArray const &item = data[i];
|
---|
452 |
|
---|
453 | if (cacheSaveDevice)
|
---|
454 | cacheSaveDevice->write(item.constData(), item.size());
|
---|
455 | readBuffer.append(item);
|
---|
456 |
|
---|
457 | bytesWritten += item.size();
|
---|
458 | }
|
---|
459 | data.clear();
|
---|
460 |
|
---|
461 | bytesDownloaded += bytesWritten;
|
---|
462 | lastBytesDownloaded = bytesDownloaded;
|
---|
463 |
|
---|
464 | appendDownstreamDataSignalEmissions();
|
---|
465 | }
|
---|
466 |
|
---|
467 | void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
|
---|
468 | {
|
---|
469 | Q_Q(QNetworkReplyImpl);
|
---|
470 |
|
---|
471 | QPointer<QNetworkReplyImpl> qq = q;
|
---|
472 |
|
---|
473 | QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
---|
474 | pauseNotificationHandling();
|
---|
475 | emit q->downloadProgress(bytesDownloaded,
|
---|
476 | totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
|
---|
477 | // important: At the point of this readyRead(), the data parameter list must be empty,
|
---|
478 | // else implicit sharing will trigger memcpy when the user is reading data!
|
---|
479 | emit q->readyRead();
|
---|
480 |
|
---|
481 | // hopefully we haven't been deleted here
|
---|
482 | if (!qq.isNull()) {
|
---|
483 | resumeNotificationHandling();
|
---|
484 | // do we still have room in the buffer?
|
---|
485 | if (nextDownstreamBlockSize() > 0)
|
---|
486 | backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
---|
487 | }
|
---|
488 | }
|
---|
489 |
|
---|
490 | // this is used when it was fetched from the cache, right?
|
---|
491 | void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
|
---|
492 | {
|
---|
493 | Q_Q(QNetworkReplyImpl);
|
---|
494 | if (!q->isOpen())
|
---|
495 | return;
|
---|
496 |
|
---|
497 | // read until EOF from data
|
---|
498 | if (copyDevice) {
|
---|
499 | qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
|
---|
500 | "backend probly needs to be fixed");
|
---|
501 | return;
|
---|
502 | }
|
---|
503 |
|
---|
504 | copyDevice = data;
|
---|
505 | q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
|
---|
506 | q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
|
---|
507 |
|
---|
508 | // start the copy:
|
---|
509 | _q_copyReadyRead();
|
---|
510 | }
|
---|
511 |
|
---|
512 | void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
|
---|
513 | {
|
---|
514 | // TODO implement
|
---|
515 |
|
---|
516 | // TODO call
|
---|
517 |
|
---|
518 | qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
|
---|
519 | }
|
---|
520 |
|
---|
521 | void QNetworkReplyImplPrivate::finished()
|
---|
522 | {
|
---|
523 | Q_Q(QNetworkReplyImpl);
|
---|
524 | if (state == Finished || state == Aborted)
|
---|
525 | return;
|
---|
526 |
|
---|
527 | state = Finished;
|
---|
528 | pendingNotifications.clear();
|
---|
529 |
|
---|
530 | pauseNotificationHandling();
|
---|
531 | QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
---|
532 | if (totalSize.isNull() || totalSize == -1) {
|
---|
533 | emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
|
---|
534 | }
|
---|
535 |
|
---|
536 | if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
|
---|
537 | emit q->uploadProgress(0, 0);
|
---|
538 | resumeNotificationHandling();
|
---|
539 |
|
---|
540 | completeCacheSave();
|
---|
541 |
|
---|
542 | // note: might not be a good idea, since users could decide to delete us
|
---|
543 | // which would delete the backend too...
|
---|
544 | // maybe we should protect the backend
|
---|
545 | pauseNotificationHandling();
|
---|
546 | emit q->readChannelFinished();
|
---|
547 | emit q->finished();
|
---|
548 | resumeNotificationHandling();
|
---|
549 | }
|
---|
550 |
|
---|
551 | void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
|
---|
552 | {
|
---|
553 | Q_Q(QNetworkReplyImpl);
|
---|
554 |
|
---|
555 | errorCode = code;
|
---|
556 | q->setErrorString(errorMessage);
|
---|
557 |
|
---|
558 | // note: might not be a good idea, since users could decide to delete us
|
---|
559 | // which would delete the backend too...
|
---|
560 | // maybe we should protect the backend
|
---|
561 | emit q->error(code);
|
---|
562 | }
|
---|
563 |
|
---|
564 | void QNetworkReplyImplPrivate::metaDataChanged()
|
---|
565 | {
|
---|
566 | Q_Q(QNetworkReplyImpl);
|
---|
567 | // do we have cookies?
|
---|
568 | if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()) {
|
---|
569 | QList<QNetworkCookie> cookies =
|
---|
570 | qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
|
---|
571 | QNetworkCookieJar *jar = manager->cookieJar();
|
---|
572 | if (jar)
|
---|
573 | jar->setCookiesFromUrl(cookies, url);
|
---|
574 | }
|
---|
575 | emit q->metaDataChanged();
|
---|
576 | }
|
---|
577 |
|
---|
578 | void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
|
---|
579 | {
|
---|
580 | attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
|
---|
581 | }
|
---|
582 |
|
---|
583 | void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
|
---|
584 | {
|
---|
585 | #ifndef QT_NO_OPENSSL
|
---|
586 | Q_Q(QNetworkReplyImpl);
|
---|
587 | emit q->sslErrors(errors);
|
---|
588 | #else
|
---|
589 | Q_UNUSED(errors);
|
---|
590 | #endif
|
---|
591 | }
|
---|
592 |
|
---|
593 | bool QNetworkReplyImplPrivate::isFinished() const
|
---|
594 | {
|
---|
595 | return (state == Finished || state == Aborted);
|
---|
596 | }
|
---|
597 |
|
---|
598 | QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
|
---|
599 | : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
|
---|
600 | {
|
---|
601 | }
|
---|
602 |
|
---|
603 | QNetworkReplyImpl::~QNetworkReplyImpl()
|
---|
604 | {
|
---|
605 | Q_D(QNetworkReplyImpl);
|
---|
606 |
|
---|
607 | // This code removes the data from the cache if it was prematurely aborted.
|
---|
608 | // See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
|
---|
609 | // save had been properly finished. So if it is still enabled it means we got deleted/aborted.
|
---|
610 | if (d->isCachingEnabled())
|
---|
611 | d->networkCache()->remove(url());
|
---|
612 |
|
---|
613 | if (d->outgoingDataBuffer)
|
---|
614 | delete d->outgoingDataBuffer;
|
---|
615 | }
|
---|
616 |
|
---|
617 | void QNetworkReplyImpl::abort()
|
---|
618 | {
|
---|
619 | Q_D(QNetworkReplyImpl);
|
---|
620 | if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
|
---|
621 | return;
|
---|
622 |
|
---|
623 | // stop both upload and download
|
---|
624 | if (d->outgoingData)
|
---|
625 | disconnect(d->outgoingData, 0, this, 0);
|
---|
626 | if (d->copyDevice)
|
---|
627 | disconnect(d->copyDevice, 0, this, 0);
|
---|
628 |
|
---|
629 | QNetworkReply::close();
|
---|
630 |
|
---|
631 | if (d->state != QNetworkReplyImplPrivate::Finished) {
|
---|
632 | // emit signals
|
---|
633 | d->error(OperationCanceledError, tr("Operation canceled"));
|
---|
634 | d->finished();
|
---|
635 | }
|
---|
636 | d->state = QNetworkReplyImplPrivate::Aborted;
|
---|
637 |
|
---|
638 | // finished may access the backend
|
---|
639 | if (d->backend) {
|
---|
640 | d->backend->deleteLater();
|
---|
641 | d->backend = 0;
|
---|
642 | }
|
---|
643 | }
|
---|
644 |
|
---|
645 | void QNetworkReplyImpl::close()
|
---|
646 | {
|
---|
647 | Q_D(QNetworkReplyImpl);
|
---|
648 | if (d->state == QNetworkReplyImplPrivate::Aborted ||
|
---|
649 | d->state == QNetworkReplyImplPrivate::Finished)
|
---|
650 | return;
|
---|
651 |
|
---|
652 | // stop the download
|
---|
653 | if (d->backend)
|
---|
654 | d->backend->closeDownstreamChannel();
|
---|
655 | if (d->copyDevice)
|
---|
656 | disconnect(d->copyDevice, 0, this, 0);
|
---|
657 |
|
---|
658 | QNetworkReply::close();
|
---|
659 |
|
---|
660 | // emit signals
|
---|
661 | d->error(OperationCanceledError, tr("Operation canceled"));
|
---|
662 | d->finished();
|
---|
663 | }
|
---|
664 |
|
---|
665 | /*!
|
---|
666 | Returns the number of bytes available for reading with
|
---|
667 | QIODevice::read(). The number of bytes available may grow until
|
---|
668 | the finished() signal is emitted.
|
---|
669 | */
|
---|
670 | qint64 QNetworkReplyImpl::bytesAvailable() const
|
---|
671 | {
|
---|
672 | return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
|
---|
673 | }
|
---|
674 |
|
---|
675 | void QNetworkReplyImpl::setReadBufferSize(qint64 size)
|
---|
676 | {
|
---|
677 | Q_D(QNetworkReplyImpl);
|
---|
678 | if (size > d->readBufferMaxSize &&
|
---|
679 | size > d->readBuffer.byteAmount())
|
---|
680 | d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
---|
681 |
|
---|
682 | QNetworkReply::setReadBufferSize(size);
|
---|
683 |
|
---|
684 | if (d->backend)
|
---|
685 | d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
|
---|
686 | }
|
---|
687 |
|
---|
688 | #ifndef QT_NO_OPENSSL
|
---|
689 | QSslConfiguration QNetworkReplyImpl::sslConfigurationImplementation() const
|
---|
690 | {
|
---|
691 | Q_D(const QNetworkReplyImpl);
|
---|
692 | QSslConfiguration config;
|
---|
693 | if (d->backend)
|
---|
694 | d->backend->fetchSslConfiguration(config);
|
---|
695 | return config;
|
---|
696 | }
|
---|
697 |
|
---|
698 | void QNetworkReplyImpl::setSslConfigurationImplementation(const QSslConfiguration &config)
|
---|
699 | {
|
---|
700 | Q_D(QNetworkReplyImpl);
|
---|
701 | if (d->backend && !config.isNull())
|
---|
702 | d->backend->setSslConfiguration(config);
|
---|
703 | }
|
---|
704 |
|
---|
705 | void QNetworkReplyImpl::ignoreSslErrors()
|
---|
706 | {
|
---|
707 | Q_D(QNetworkReplyImpl);
|
---|
708 | if (d->backend)
|
---|
709 | d->backend->ignoreSslErrors();
|
---|
710 | }
|
---|
711 |
|
---|
712 | void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
|
---|
713 | {
|
---|
714 | Q_D(QNetworkReplyImpl);
|
---|
715 | if (d->backend)
|
---|
716 | d->backend->ignoreSslErrors(errors);
|
---|
717 | }
|
---|
718 | #endif // QT_NO_OPENSSL
|
---|
719 |
|
---|
720 | /*!
|
---|
721 | \internal
|
---|
722 | */
|
---|
723 | qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
|
---|
724 | {
|
---|
725 | Q_D(QNetworkReplyImpl);
|
---|
726 | if (d->readBuffer.isEmpty())
|
---|
727 | return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
|
---|
728 |
|
---|
729 | d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
---|
730 | if (maxlen == 1) {
|
---|
731 | // optimization for getChar()
|
---|
732 | *data = d->readBuffer.getChar();
|
---|
733 | return 1;
|
---|
734 | }
|
---|
735 |
|
---|
736 | maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
|
---|
737 | return d->readBuffer.read(data, maxlen);
|
---|
738 | }
|
---|
739 |
|
---|
740 | /*!
|
---|
741 | \internal Reimplemented for internal purposes
|
---|
742 | */
|
---|
743 | bool QNetworkReplyImpl::event(QEvent *e)
|
---|
744 | {
|
---|
745 | if (e->type() == QEvent::NetworkReplyUpdated) {
|
---|
746 | d_func()->handleNotifications();
|
---|
747 | return true;
|
---|
748 | }
|
---|
749 |
|
---|
750 | return QObject::event(e);
|
---|
751 | }
|
---|
752 |
|
---|
753 | QT_END_NAMESPACE
|
---|
754 |
|
---|
755 | #include "moc_qnetworkreplyimpl_p.cpp"
|
---|
756 |
|
---|