Ignore:
Timestamp:
Feb 11, 2010, 11:19:06 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/network/access/qnetworkreplyimpl.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information ([email protected])
     4** All rights reserved.
     5** Contact: Nokia Corporation ([email protected])
    56**
    67** This file is part of the QtNetwork module of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     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.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you
     37** @nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4747#include "QtCore/qdatetime.h"
    4848#include "QtNetwork/qsslconfiguration.h"
     49
    4950
    5051#include <QtCore/QCoreApplication>
     
    5354
    5455inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
    55     : copyDevice(0), networkCache(0),
     56    : backend(0), outgoingData(0), outgoingDataBuffer(0),
     57      copyDevice(0),
    5658      cacheEnabled(false), cacheSaveDevice(0),
     59
    5760      bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1),
     61
    5862      state(Idle)
    5963{
     
    6266void QNetworkReplyImplPrivate::_q_startOperation()
    6367{
    64     // This function is called exactly once
     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    }
    6573    state = Working;
     74
    6675    if (!backend) {
    6776        error(QNetworkReplyImpl::ProtocolUnknownError,
     
    7584        if (operation == QNetworkAccessManager::GetOperation)
    7685            pendingNotifications.append(NotifyDownstreamReadyWrite);
    77         if (outgoingData) {
    78             _q_sourceReadyRead();
    79 #if 0 // ### FIXME
    80             if (outgoingData->atEndOfStream() && writeBuffer.isEmpty())
    81                 // empty upload
    82                 emit q->uploadProgress(0, 0);
    83 #endif
    84         }
    8586
    8687        handleNotifications();
     
    8889}
    8990
    90 void QNetworkReplyImplPrivate::_q_sourceReadyRead()
    91 {
    92     // read data from the outgoingData QIODevice into our internal buffer
    93     enum { DesiredBufferSize = 32 * 1024 };
    94 
    95     if (writeBuffer.size() >= DesiredBufferSize)
    96         return;                 // don't grow the buffer too much
    97 
    98     // read as many bytes are available or up until we fill up the buffer
    99     // but always read at least one byte
    100     qint64 bytesToRead = qBound<qint64>(1, outgoingData->bytesAvailable(),
    101                                         DesiredBufferSize - writeBuffer.size());
    102     char *ptr = writeBuffer.reserve(bytesToRead);
    103     qint64 bytesActuallyRead = outgoingData->read(ptr, bytesToRead);
    104     if (bytesActuallyRead == -1) {
    105         // EOF
    106         writeBuffer.chop(bytesToRead);
    107         backendNotify(NotifyCloseUpstreamChannel);
    108         return;
    109     }
    110 
    111     if (bytesActuallyRead < bytesToRead)
    112         writeBuffer.chop(bytesToRead - bytesActuallyRead);
    113 
    114     // if we did read anything, let the backend know and handle it
    115     if (bytesActuallyRead)
    116         backendNotify(NotifyUpstreamReadyRead);
    117 
    118     // check for EOF again
    119     if (!outgoingData->isSequential() && outgoingData->atEnd())
    120         backendNotify(NotifyCloseUpstreamChannel);
    121 }
    122 
    123 void QNetworkReplyImplPrivate::_q_sourceReadChannelFinished()
    124 {
    125     _q_sourceReadyRead();
    126 }
    127 
    12891void QNetworkReplyImplPrivate::_q_copyReadyRead()
    12992{
    13093    Q_Q(QNetworkReplyImpl);
    131     if (!copyDevice && !q->isOpen())
    132         return;
    133 
    134     qint64 bytesToRead = nextDownstreamBlockSize();
    135     if (bytesToRead == 0)
    136         // we'll be called again, eventually
    137         return;
    138 
    139     bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
    140     char *ptr = readBuffer.reserve(bytesToRead);
    141     qint64 bytesActuallyRead = copyDevice->read(ptr, bytesToRead);
    142     if (bytesActuallyRead == -1) {
    143         readBuffer.chop(bytesToRead);
    144         backendNotify(NotifyCopyFinished);
    145         return;
    146     }
    147 
    148     if (bytesActuallyRead != bytesToRead)
    149         readBuffer.chop(bytesToRead - bytesActuallyRead);
    150 
    151     if (!copyDevice->isSequential() && copyDevice->atEnd())
    152         backendNotify(NotifyCopyFinished);
    153 
    154     bytesDownloaded += bytesActuallyRead;
     94    if (state != Working)
     95        return;
     96    if (!copyDevice || !q->isOpen())
     97        return;
     98
     99    forever {
     100        qint64 bytesToRead = nextDownstreamBlockSize();
     101        if (bytesToRead == 0)
     102            // we'll be called again, eventually
     103            break;
     104
     105        bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
     106        QByteArray byteData;
     107        byteData.resize(bytesToRead);
     108        qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
     109        if (bytesActuallyRead == -1) {
     110            byteData.clear();
     111            backendNotify(NotifyCopyFinished);
     112            break;
     113        }
     114
     115        byteData.resize(bytesActuallyRead);
     116        readBuffer.append(byteData);
     117
     118        if (!copyDevice->isSequential() && copyDevice->atEnd()) {
     119            backendNotify(NotifyCopyFinished);
     120            bytesDownloaded += bytesActuallyRead;
     121            break;
     122        }
     123
     124        bytesDownloaded += bytesActuallyRead;
     125    }
     126
     127    if (bytesDownloaded == lastBytesDownloaded) {
     128        // we didn't read anything
     129        return;
     130    }
     131
    155132    lastBytesDownloaded = bytesDownloaded;
    156133    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
     134
    157135    emit q->downloadProgress(bytesDownloaded,
    158136                             totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
    159137    emit q->readyRead();
     138
    160139}
    161140
     
    164143    _q_copyReadyRead();
    165144}
     145
     146
     147
     148
     149
     150
     151
     152
     153
     154
     155
     156
     157
     158
     159
     160
     161
     162
     163
     164
     165
     166
     167
     168
     169
     170
     171
     172
     173
     174
     175
     176
     177
     178
     179
     180
     181
     182
     183
     184
     185
     186
     187
     188
     189
     190
     191
     192
     193
     194
     195
     196
     197
     198
     199
     200
     201
     202
     203
     204
     205
    166206
    167207void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
     
    175215    operation = op;
    176216
    177     if (outgoingData) {
    178         q->connect(outgoingData, SIGNAL(readyRead()), SLOT(_q_sourceReadyRead()));
    179         q->connect(outgoingData, SIGNAL(readChannelFinished()), SLOT(_q_sourceReadChannelFinished()));
     217    if (outgoingData && backend) {
     218        // there is data to be uploaded, e.g. HTTP POST.
     219
     220        if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
     221            // backend does not need upload buffering or
     222            // fixed size non-sequential
     223            // just start the operation
     224            QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
     225        } else {
     226            bool bufferingDisallowed =
     227                    req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
     228                                             false).toBool();
     229
     230            if (bufferingDisallowed) {
     231                // if a valid content-length header for the request was supplied, we can disable buffering
     232                // if not, we will buffer anyway
     233                if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
     234                    QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
     235                } else {
     236                    state = Buffering;
     237                    QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
     238                }
     239            } else {
     240                // _q_startOperation will be called when the buffering has finished.
     241                state = Buffering;
     242                QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
     243            }
     244        }
     245    } else {
     246        // No outgoing data (e.g. HTTP GET request)
     247        // or no backend
     248        // if no backend, _q_startOperation will handle the error of this
     249        QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
    180250    }
    181251
    182252    q->QIODevice::open(QIODevice::ReadOnly);
    183     QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
    184 }
    185 
    186 void QNetworkReplyImplPrivate::setNetworkCache(QAbstractNetworkCache *nc)
    187 {
    188     networkCache = nc;
    189253}
    190254
     
    201265void QNetworkReplyImplPrivate::handleNotifications()
    202266{
     267
     268
     269
    203270    NotificationQueue current = pendingNotifications;
    204271    pendingNotifications.clear();
     
    207274        return;
    208275
    209     while (!current.isEmpty()) {
     276    while (!current.isEmpty()) {
    210277        InternalNotifications notification = current.dequeue();
    211278        switch (notification) {
     
    217284            break;
    218285
    219         case NotifyUpstreamReadyRead:
    220             backend->upstreamReadyRead();
    221             break;
    222 
    223286        case NotifyCloseDownstreamChannel:
    224287            backend->closeDownstreamChannel();
    225             break;
    226 
    227         case NotifyCloseUpstreamChannel:
    228             backend->closeUpstreamChannel();
    229288            break;
    230289
     
    239298}
    240299
     300
     301
     302
     303
     304
     305
     306
     307
     308
     309
     310
     311
     312
     313
     314
     315
     316
     317
     318
     319
     320
     321
     322
    241323void QNetworkReplyImplPrivate::createCache()
    242324{
    243325    // check if we can save and if we're allowed to
    244     if (!networkCache || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool())
     326    if (!networkCache()
     327        || !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
     328        || request.attribute(QNetworkRequest::CacheLoadControlAttribute,
     329                             QNetworkRequest::PreferNetwork).toInt()
     330            == QNetworkRequest::AlwaysNetwork)
    245331        return;
    246332    cacheEnabled = true;
     
    249335bool QNetworkReplyImplPrivate::isCachingEnabled() const
    250336{
    251     return (cacheEnabled && networkCache != 0);
     337    return (cacheEnabled && networkCache != 0);
    252338}
    253339
     
    273359               "backend %s probably needs to be fixed",
    274360               backend->metaObject()->className());
    275         networkCache->remove(url);
     361        networkCache->remove(url);
    276362        cacheSaveDevice = 0;
    277363        cacheEnabled = false;
     
    282368{
    283369    if (cacheEnabled && errorCode != QNetworkReplyImpl::NoError) {
    284         networkCache->remove(url);
     370        networkCache->remove(url);
    285371    } else if (cacheEnabled && cacheSaveDevice) {
    286         networkCache->insert(cacheSaveDevice);
     372        networkCache->insert(cacheSaveDevice);
    287373    }
    288374    cacheSaveDevice = 0;
     
    290376}
    291377
    292 void QNetworkReplyImplPrivate::consume(qint64 count)
    293 {
    294     Q_Q(QNetworkReplyImpl);
    295     if (count <= 0) {
    296         qWarning("QNetworkConnection: backend signalled that it consumed %ld bytes", long(count));
    297         return;
    298     }
    299 
    300     if (outgoingData)
    301         // schedule another read from the source
    302         QMetaObject::invokeMethod(q_func(), "_q_sourceReadyRead", Qt::QueuedConnection);
    303 
    304     writeBuffer.skip(count);
    305     if (bytesUploaded == -1)
    306         bytesUploaded = count;
    307     else
    308         bytesUploaded += count;
    309 
    310     QVariant totalSize = request.header(QNetworkRequest::ContentLengthHeader);
    311     emit q->uploadProgress(bytesUploaded,
    312                            totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
    313 }
     378void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
     379{
     380    Q_Q(QNetworkReplyImpl);
     381    bytesUploaded = bytesSent;
     382    pauseNotificationHandling();
     383    emit q->uploadProgress(bytesSent, bytesTotal);
     384    resumeNotificationHandling();
     385}
     386
    314387
    315388qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
     
    319392        return DesiredBufferSize;
    320393
    321     return qMax<qint64>(0, readBufferMaxSize - readBuffer.size());
    322 }
    323 
    324 void QNetworkReplyImplPrivate::feed(const QByteArray &data)
     394    return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
     395}
     396
     397// we received downstream data and send this to the cache
     398// and to our readBuffer (which in turn gets read by the user of QNetworkReply)
     399void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
    325400{
    326401    Q_Q(QNetworkReplyImpl);
    327402    if (!q->isOpen())
    328403        return;
    329 
    330     char *ptr = readBuffer.reserve(data.size());
    331     memcpy(ptr, data.constData(), data.size());
    332404
    333405    if (cacheEnabled && !cacheSaveDevice) {
     
    336408        metaData.setUrl(url);
    337409        metaData = backend->fetchCacheMetaData(metaData);
    338         cacheSaveDevice = networkCache->prepare(metaData);
     410
     411        // save the redirect request also in the cache
     412        QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
     413        if (redirectionTarget.isValid()) {
     414            QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
     415            attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
     416            metaData.setAttributes(attributes);
     417        }
     418
     419        cacheSaveDevice = networkCache()->prepare(metaData);
     420
    339421        if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
    340422            if (cacheSaveDevice && !cacheSaveDevice->isOpen())
    341423                qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
    342424                      "class %s probably needs to be fixed",
    343                       networkCache->metaObject()->className());
    344 
    345             networkCache->remove(url);
     425                      networkCache->metaObject()->className());
     426
     427            networkCache->remove(url);
    346428            cacheSaveDevice = 0;
    347429            cacheEnabled = false;
     
    349431    }
    350432
    351     if (cacheSaveDevice)
    352         cacheSaveDevice->write(data);
    353 
    354     bytesDownloaded += data.size();
     433    qint64 bytesWritten = 0;
     434    for (int i = 0; i < data.bufferCount(); i++) {
     435        QByteArray item = data[i];
     436
     437        if (cacheSaveDevice)
     438            cacheSaveDevice->write(item.constData(), item.size());
     439        readBuffer.append(item);
     440
     441        bytesWritten += item.size();
     442    }
     443    data.clear();
     444
     445    bytesDownloaded += bytesWritten;
    355446    lastBytesDownloaded = bytesDownloaded;
    356447
     
    358449
    359450    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
     451
    360452    emit q->downloadProgress(bytesDownloaded,
    361453                             totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
     454
     455
    362456    emit q->readyRead();
    363457
    364458    // hopefully we haven't been deleted here
    365459    if (!qq.isNull()) {
     460
    366461        // do we still have room in the buffer?
    367462        if (nextDownstreamBlockSize() > 0)
     
    370465}
    371466
    372 void QNetworkReplyImplPrivate::feed(QIODevice *data)
    373 {
    374     Q_Q(QNetworkReplyImpl);
    375     Q_ASSERT(q->isOpen());
     467// this is used when it was fetched from the cache, right?
     468void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
     469{
     470    Q_Q(QNetworkReplyImpl);
     471    if (!q->isOpen())
     472        return;
    376473
    377474    // read until EOF from data
     
    393490{
    394491    Q_Q(QNetworkReplyImpl);
    395     Q_ASSERT_X(state != Finished, "QNetworkReplyImpl",
    396                "Backend called finished/finishedWithError more than once");
     492   
     493        ;
    397494
    398495    state = Finished;
    399496    pendingNotifications.clear();
    400497
     498
    401499    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
    402     if (bytesDownloaded != lastBytesDownloaded || totalSize.isNull())
     500    if (
    403501        emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
    404     if (bytesUploaded == -1 && outgoingData)
     502    }
     503
     504    if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
    405505        emit q->uploadProgress(0, 0);
     506
    406507
    407508    completeCacheSave();
     
    410511    // which would delete the backend too...
    411512    // maybe we should protect the backend
     513
    412514    emit q->readChannelFinished();
    413515    emit q->finished();
     516
    414517}
    415518
     
    456559}
    457560
     561
     562
     563
     564
     565
    458566QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
    459567    : QNetworkReply(*new QNetworkReplyImplPrivate, parent)
     
    465573    Q_D(QNetworkReplyImpl);
    466574    if (d->isCachingEnabled())
    467         d->networkCache->remove(url());
     575        d->networkCache()->remove(url());
     576    if (d->outgoingDataBuffer)
     577        delete d->outgoingDataBuffer;
    468578}
    469579
     
    471581{
    472582    Q_D(QNetworkReplyImpl);
    473     if (d->state == QNetworkReplyImplPrivate::Aborted)
     583    if (d->state == QNetworkReplyImplPrivate::Aborted)
    474584        return;
    475585
    476586    // stop both upload and download
    477     if (d->backend) {
    478         d->backend->deleteLater();
    479         d->backend = 0;
    480     }
    481587    if (d->outgoingData)
    482588        disconnect(d->outgoingData, 0, this, 0);
     
    492598    }
    493599    d->state = QNetworkReplyImplPrivate::Aborted;
     600
     601
     602
     603
     604
     605
    494606}
    495607
     
    521633qint64 QNetworkReplyImpl::bytesAvailable() const
    522634{
    523     return QNetworkReply::bytesAvailable() + d_func()->readBuffer.size();
     635    return QNetworkReply::bytesAvailable() + d_func()->readBuffer.();
    524636}
    525637
     
    528640    Q_D(QNetworkReplyImpl);
    529641    if (size > d->readBufferMaxSize &&
    530         size == d->readBuffer.size())
     642        size ())
    531643        d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
    532644
     
    558670}
    559671
     672
     673
     674
     675
     676
     677
    560678#endif  // QT_NO_OPENSSL
    561679
     
    576694    }
    577695
    578     maxlen = qMin<qint64>(maxlen, d->readBuffer.size());
     696    maxlen = qMin<qint64>(maxlen, d->readBuffer.());
    579697    return d->readBuffer.read(data, maxlen);
    580698}
Note: See TracChangeset for help on using the changeset viewer.