~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/network/access/qhttpnetworkreply.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Alessandro Ghersi
  • Date: 2009-11-02 18:30:08 UTC
  • mfrom: (1.2.2 upstream)
  • mto: (15.2.5 experimental)
  • mto: This revision was merged to the branch mainline in revision 88.
  • Revision ID: james.westby@ubuntu.com-20091102183008-b6a4gcs128mvfb3m
Tags: upstream-4.6.0~beta1
ImportĀ upstreamĀ versionĀ 4.6.0~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/****************************************************************************
2
2
**
3
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
 
4
** All rights reserved.
4
5
** Contact: Nokia Corporation (qt-info@nokia.com)
5
6
**
6
7
** This file is part of the QtNetwork module of the Qt Toolkit.
7
8
**
8
9
** $QT_BEGIN_LICENSE:LGPL$
9
 
** Commercial Usage
10
 
** Licensees holding valid Qt Commercial licenses may use this file in
11
 
** accordance with the Qt Commercial License Agreement provided with the
12
 
** Software or, alternatively, in accordance with the terms contained in
13
 
** a written agreement between you and Nokia.
 
10
** No Commercial Usage
 
11
** This file contains pre-release code and may not be distributed.
 
12
** You may use this file in accordance with the terms and conditions
 
13
** contained in the Technology Preview License Agreement accompanying
 
14
** this package.
14
15
**
15
16
** GNU Lesser General Public License Usage
16
17
** Alternatively, this file may be used under the terms of the GNU Lesser
20
21
** ensure the GNU Lesser General Public License version 2.1 requirements
21
22
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22
23
**
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.
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 are unsure which license is appropriate for your use, please
37
 
** contact the sales department at http://www.qtsoftware.com/contact.
 
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
** If you have questions regarding the use of this file, please contact
 
29
** Nokia at qt-info@nokia.com.
 
30
**
 
31
**
 
32
**
 
33
**
 
34
**
 
35
**
 
36
**
 
37
**
38
38
** $QT_END_LICENSE$
39
39
**
40
40
****************************************************************************/
162
162
{
163
163
    Q_D(const QHttpNetworkReply);
164
164
    if (d->connection)
165
 
        return d->connection->d_func()->bytesAvailable(*this);
166
 
    else
167
 
        return -1;
168
 
}
169
 
 
170
 
QByteArray QHttpNetworkReply::read(qint64 maxSize)
 
165
        return d->connection->d_func()->uncompressedBytesAvailable(*this);
 
166
    else
 
167
        return -1;
 
168
}
 
169
 
 
170
qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
 
171
{
 
172
    Q_D(const QHttpNetworkReply);
 
173
    if (d->connection)
 
174
        return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
 
175
    else
 
176
        return -1;
 
177
}
 
178
 
 
179
QByteArray QHttpNetworkReply::readAny()
171
180
{
172
181
    Q_D(QHttpNetworkReply);
173
 
    QByteArray data;
174
 
    if (d->connection)
175
 
        d->connection->d_func()->read(*this, data, maxSize, false);
176
 
    return data;
 
182
    return d->responseData.read();
177
183
}
178
184
 
179
185
bool QHttpNetworkReply::isFinished() const
181
187
    return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
182
188
}
183
189
 
 
190
bool QHttpNetworkReply::isPipeliningUsed() const
 
191
{
 
192
    return d_func()->pipeliningUsed;
 
193
}
184
194
 
185
195
 
186
196
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
187
197
    : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
188
198
      majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
 
199
      chunkedTransferEncoding(false),
 
200
      connectionCloseEnabled(true),
 
201
      forceConnectionCloseEnabled(false),
189
202
      currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
190
 
      autoDecompress(false), requestIsBuffering(false), requestIsPrepared(false)
 
203
      autoDecompress(false), responseData(), requestIsPrepared(false)
 
204
      ,pipeliningUsed(false)
191
205
{
192
206
}
193
207
 
204
218
    totalProgress = 0;
205
219
    currentChunkSize = 0;
206
220
    currentChunkRead = 0;
 
221
    connectionCloseEnabled = true;
207
222
    connection = 0;
208
223
#ifndef QT_NO_COMPRESS
209
224
    if (initInflate)
411
426
            }
412
427
            bool ok = parseStatus(fragment);
413
428
            state = ReadingHeaderState;
414
 
            fragment.clear(); // next fragment
415
 
 
416
 
            if (!ok)
 
429
            fragment.clear();
 
430
            if (!ok) {
417
431
                return -1;
 
432
            }
418
433
            break;
419
434
        } else {
420
435
            c = 0;
421
 
            bytes += socket->read(&c, 1);
 
436
            int haveRead = socket->read(&c, 1);
 
437
            if (haveRead == -1)
 
438
                return -1;
 
439
            bytes += haveRead;
422
440
            fragment.append(c);
423
441
        }
424
442
 
425
443
        // is this a valid reply?
426
444
        if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
 
445
        {
 
446
            fragment.clear();
427
447
            return -1;
 
448
        }
428
449
 
429
450
    }
430
451
 
470
491
qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
471
492
{
472
493
    qint64 bytes = 0;
473
 
    char crlfcrlf[5];
474
 
    crlfcrlf[4] = '\0';
475
494
    char c = 0;
476
495
    bool allHeaders = false;
477
496
    while (!allHeaders && socket->bytesAvailable()) {
491
510
        state = ReadingDataState;
492
511
        fragment.clear(); // next fragment
493
512
        bodyLength = contentLength(); // cache the length
 
513
 
 
514
        // cache isChunked() since it is called often
 
515
        chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
 
516
 
 
517
        // cache isConnectionCloseEnabled since it is called often
 
518
        QByteArray connectionHeaderField = headerField("connection");
 
519
        // check for explicit indication of close or the implicit connection close of HTTP/1.0
 
520
        connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
 
521
            headerField("proxy-connection").toLower().contains("close")) ||
 
522
            (majorVersion == 1 && minorVersion == 0 && connectionHeaderField.isEmpty());
494
523
    }
495
524
    return bytes;
496
525
}
531
560
 
532
561
bool QHttpNetworkReplyPrivate::isChunked()
533
562
{
534
 
    return headerField("transfer-encoding").toLower().contains("chunked");
535
 
}
536
 
 
537
 
bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
538
 
{
539
 
    return (headerField("connection").toLower().contains("close") ||
540
 
            headerField("proxy-connection").toLower().contains("close"));
541
 
}
542
 
 
543
 
qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out)
 
563
    return chunkedTransferEncoding;
 
564
}
 
565
 
 
566
bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
 
567
{
 
568
    return connectionCloseEnabled || forceConnectionCloseEnabled;
 
569
}
 
570
 
 
571
// note this function can only be used for non-chunked, non-compressed with
 
572
// known content length
 
573
qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
 
574
{
 
575
    qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
 
576
    QByteArray bd;
 
577
    bd.resize(toBeRead);
 
578
    qint64 haveRead = socket->read(bd.data(), bd.size());
 
579
    if (haveRead == -1) {
 
580
        bd.clear();
 
581
        return 0; // ### error checking here;
 
582
    }
 
583
    bd.resize(haveRead);
 
584
 
 
585
    rb->append(bd);
 
586
 
 
587
    if (contentRead + haveRead == bodyLength) {
 
588
        state = AllDoneState;
 
589
    }
 
590
 
 
591
    contentRead += haveRead;
 
592
    return haveRead;
 
593
}
 
594
 
 
595
 
 
596
qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
544
597
{
545
598
    qint64 bytes = 0;
546
599
    if (isChunked()) {
547
 
        bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
 
600
        bytes += readReplyBodyChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
548
601
    } else if (bodyLength > 0) { // we have a Content-Length
549
 
        bytes += transferRaw(socket, out, bodyLength - contentRead);
 
602
        bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
550
603
        if (contentRead + bytes == bodyLength)
551
604
            state = AllDoneState;
552
605
    } else {
553
 
        bytes += transferRaw(socket, out, socket->bytesAvailable());
 
606
        bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
554
607
    }
555
 
    if (state == AllDoneState)
556
 
        socket->readAll(); // Read the rest to clean (CRLF)
557
608
    contentRead += bytes;
558
609
    return bytes;
559
610
}
560
611
 
561
 
qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size)
 
612
qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size)
562
613
{
563
614
    qint64 bytes = 0;
564
615
    Q_ASSERT(in);
565
616
    Q_ASSERT(out);
566
617
 
567
618
    int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
568
 
    QByteArray raw(toBeRead, 0);
569
 
    while (size > 0) {
570
 
        qint64 read = in->read(raw.data(), raw.size());
571
 
        if (read == 0)
572
 
            return bytes;
573
 
        // ### error checking here
574
 
        qint64 written = out->write(raw.data(), read);
575
 
        if (written == 0)
576
 
            return bytes;
577
 
        if (read != written)
578
 
            qDebug() << "### read" << read << "written" << written;
579
 
        bytes += read;
580
 
        size -= read;
581
 
        out->waitForBytesWritten(-1); // throttle
 
619
    while (toBeRead > 0) {
 
620
        QByteArray byteData;
 
621
        byteData.resize(toBeRead);
 
622
        qint64 haveRead = in->read(byteData.data(), byteData.size());
 
623
        if (haveRead <= 0) {
 
624
            // ### error checking here
 
625
            byteData.clear();
 
626
            return bytes;
 
627
        }
 
628
 
 
629
        byteData.resize(haveRead);
 
630
        out->append(byteData);
 
631
        bytes += haveRead;
 
632
        size -= haveRead;
 
633
 
 
634
        toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
582
635
    }
583
636
    return bytes;
584
637
 
585
638
}
586
639
 
587
 
qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *out)
 
640
qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out)
588
641
{
589
642
    qint64 bytes = 0;
590
643
    while (in->bytesAvailable()) { // while we can read from input
605
658
            state = AllDoneState;
606
659
            break;
607
660
        }
608
 
        // otherwise, read data
609
 
        qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead);
610
 
        QByteArray buffer(readSize, 0);
611
 
        qint64 read = in->read(buffer.data(), readSize);
612
 
        bytes += read;
613
 
        currentChunkRead += read;
614
 
        qint64 written = out->write(buffer);
615
 
        Q_UNUSED(written); // Avoid compile warning when building release
616
 
        Q_ASSERT(read == written);
 
661
 
 
662
        // otherwise, try to read what is missing for this chunk
 
663
        qint64 haveRead = readReplyBodyRaw (in, out, currentChunkSize - currentChunkRead);
 
664
        currentChunkRead += haveRead;
 
665
        bytes += haveRead;
 
666
 
617
667
        // ### error checking here
618
 
        out->waitForBytesWritten(-1);
 
668
 
619
669
    }
620
670
    return bytes;
621
671
}
652
702
    return bytes;
653
703
}
654
704
 
 
705
void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba)
 
706
{
 
707
    responseData.append(qba);
 
708
 
 
709
    // clear the original! helps with implicit sharing and
 
710
    // avoiding memcpy when the user is reading the data
 
711
    qba.clear();
 
712
}
 
713
 
 
714
void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data)
 
715
{
 
716
    responseData.append(data);
 
717
 
 
718
    // clear the original! helps with implicit sharing and
 
719
    // avoiding memcpy when the user is reading the data
 
720
    data.clear();
 
721
}
 
722
 
 
723
void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data)
 
724
{
 
725
    // Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer
 
726
    // instead of one QByteArray.
 
727
    for(int i = 0; i < data.bufferCount(); i++) {
 
728
        QByteArray &byteData = data[i];
 
729
        compressedData.append(byteData.constData(), byteData.size());
 
730
    }
 
731
    data.clear();
 
732
}
 
733
 
 
734
 
 
735
bool QHttpNetworkReplyPrivate::shouldEmitSignals()
 
736
{
 
737
    // for 401 & 407 don't emit the data signals. Content along with these
 
738
    // responses are send only if the authentication fails.
 
739
    return (statusCode != 401 && statusCode != 407);
 
740
}
 
741
 
 
742
bool QHttpNetworkReplyPrivate::expectContent()
 
743
{
 
744
    // check whether we can expect content after the headers (rfc 2616, sec4.4)
 
745
    if ((statusCode >= 100 && statusCode < 200)
 
746
        || statusCode == 204 || statusCode == 304)
 
747
        return false;
 
748
    if (request.operation() == QHttpNetworkRequest::Head)
 
749
        return !shouldEmitSignals();
 
750
    if (contentLength() == 0)
 
751
        return false;
 
752
    return true;
 
753
}
 
754
 
 
755
void QHttpNetworkReplyPrivate::eraseData()
 
756
{
 
757
    compressedData.clear();
 
758
    responseData.clear();
 
759
}
 
760
 
 
761
 
655
762
// SSL support below
656
763
#ifndef QT_NO_OPENSSL
657
764
 
677
784
        d->connection->ignoreSslErrors();
678
785
}
679
786
 
 
787
void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
 
788
{
 
789
    Q_D(QHttpNetworkReply);
 
790
    if (d->connection)
 
791
        d->connection->ignoreSslErrors(errors);
 
792
}
 
793
 
680
794
 
681
795
#endif //QT_NO_OPENSSL
682
796