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

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
44 edited
13 copied

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/network/access/access.pri

    r2 r561  
    33HEADERS += access/qftp.h \
    44           access/qhttp.h \
     5           access/qhttpnetworkheader_p.h \
     6           access/qhttpnetworkrequest_p.h \
     7           access/qhttpnetworkreply_p.h \
    58           access/qhttpnetworkconnection_p.h \
     9           access/qhttpnetworkconnectionchannel_p.h \
     10           access/qfilenetworkreply_p.h \
    611           access/qnetworkaccessmanager.h \
    712           access/qnetworkaccessmanager_p.h \
     
    1621           access/qnetworkcookie.h \
    1722           access/qnetworkcookie_p.h \
     23           access/qnetworkcookiejar.h \
     24           access/qnetworkcookiejar_p.h \
    1825           access/qnetworkrequest.h \
    1926           access/qnetworkrequest_p.h \
     
    2835SOURCES += access/qftp.cpp \
    2936           access/qhttp.cpp \
     37           access/qhttpnetworkheader.cpp \
     38           access/qhttpnetworkrequest.cpp \
     39           access/qhttpnetworkreply.cpp \
    3040           access/qhttpnetworkconnection.cpp \
     41           access/qhttpnetworkconnectionchannel.cpp \
     42           access/qfilenetworkreply.cpp \
    3143           access/qnetworkaccessmanager.cpp \
    3244           access/qnetworkaccesscache.cpp \
     
    3951           access/qnetworkaccesshttpbackend.cpp \
    4052           access/qnetworkcookie.cpp \
     53           access/qnetworkcookiejar.cpp \
    4154           access/qnetworkrequest.cpp \
    4255           access/qnetworkreply.cpp \
     
    4962   INCLUDEPATH += ../3rdparty/zlib
    5063} else:!contains(QT_CONFIG, no-zlib) {
    51    unix:LIBS += -lz
     64   unix:LIBS_PRIVATE += -lz
    5265#  win32:LIBS += libz.lib
    5366}
  • trunk/src/network/access/qabstractnetworkcache.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    284284
    285285/*!
     286    \since 4.6
     287
    286288    Returns all the attributes stored with this cache item.
    287289
     
    294296
    295297/*!
     298    \since 4.6
     299
    296300    Sets all attributes of this cache item to be the map \a attributes.
    297301
  • trunk/src/network/access/qabstractnetworkcache.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qabstractnetworkcache_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qftp.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    315315    bytesFromSocket.clear();
    316316
    317     if (socket)
     317    if (socket) {
    318318        delete socket;
     319        socket = 0;
     320    }
    319321    socket = new QTcpSocket(this);
    320322    socket->setObjectName(QLatin1String("QFtpDTP Passive state socket"));
     
    870872//    qDebug("QFtpPI state: %d [connected()]", state);
    871873#endif
     874    // try to improve performance by setting TCP_NODELAY
     875    commandSocket.setSocketOption(QAbstractSocket::LowDelayOption, 1);
     876
    872877    emit connectState(QFtp::Connected);
    873878}
     
    12921297    \brief The QFtp class provides an implementation of the client side of FTP protocol.
    12931298
    1294     \ingroup io
     1299    \ingroup network
    12951300    \inmodule QtNetwork
    1296     \mainclass
     1301
    12971302
    12981303    This class provides a direct interface to FTP that allows you to
     
    13411346    their results.
    13421347
    1343     Example: If you want to download the INSTALL file from Trolltech's
     1348    Example: If you want to download the INSTALL file from the Qt
    13441349    FTP server, you would write this:
    13451350
     
    13861391    non-Unix FTP servers.
    13871392
    1388     \sa QHttp, QNetworkAccessManager, QNetworkRequest, QNetworkReply,
     1393    \sa QNetworkAccessManager, QNetworkRequest, QNetworkReply,
    13891394        {FTP Example}
    13901395*/
     
    16591664int QFtp::connectToHost(const QString &host, quint16 port)
    16601665{
    1661     d_func()->pi.transferConnectionExtended = true;
    16621666    QStringList cmds;
    16631667    cmds << host;
    16641668    cmds << QString::number((uint)port);
    1665     return d_func()->addCommand(new QFtpCommand(ConnectToHost, cmds));
     1669    int id = d_func()->addCommand(new QFtpCommand(ConnectToHost, cmds));
     1670    d_func()->pi.transferConnectionExtended = true;
     1671    return id;
    16661672}
    16671673
     
    17221728int QFtp::setTransferMode(TransferMode mode)
    17231729{
     1730    int id = d_func()->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
    17241731    d_func()->pi.transferConnectionExtended = true;
    17251732    d_func()->transferMode = mode;
    1726     return d_func()->addCommand(new QFtpCommand(SetTransferMode, QStringList()));
     1733    return id;
    17271734}
    17281735
     
    17311738    port. Calling this function with \a host empty disables proxying.
    17321739
    1733     QFtp does not support FTP-over-HTTP proxy servers. Use QHttp for
    1734     this.
     1740    QFtp does not support FTP-over-HTTP proxy servers. Use
     1741    QNetworkAccessManager for this.
    17351742*/
    17361743int QFtp::setProxy(const QString &host, quint16 port)
  • trunk/src/network/access/qftp.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qhttp.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6565#endif
    6666
     67#ifndef QT_NO_HTTP
     68
    6769QT_BEGIN_NAMESPACE
    68 
    69 #ifndef QT_NO_HTTP
    7070
    7171class QHttpNormalRequest;
     
    122122    void _q_slotClosed();
    123123    void _q_slotBytesWritten(qint64 numBytes);
     124#ifndef QT_NO_OPENSSL
     125    void _q_slotEncryptedBytesWritten(qint64 numBytes);
     126#endif
    124127    void _q_slotDoFinished();
    125128    void _q_slotSendRequest();
     
    135138    void closeConn();
    136139    void setSock(QTcpSocket *sock);
     140
     141    void postMoreData();
    137142
    138143    QTcpSocket *socket;
     
    514519/*!
    515520    \class QHttpHeader
     521    \obsolete
    516522    \brief The QHttpHeader class contains header information for HTTP.
    517523
    518     \ingroup io
     524    \ingroup network
    519525    \inmodule QtNetwork
    520526
     
    625631QHttpHeader::~QHttpHeader()
    626632{
    627     delete d_ptr;
    628633}
    629634
     
    951956
    952957/*!
    953     Returns true if the header has an entry for the the special HTTP
     958    Returns true if the header has an entry for the special HTTP
    954959    header field \c content-type; otherwise returns false.
    955960
     
    10081013/*!
    10091014    \class QHttpResponseHeader
     1015    \obsolete
    10101016    \brief The QHttpResponseHeader class contains response header information for HTTP.
    10111017
    1012     \ingroup io
     1018    \ingroup network
    10131019    \inmodule QtNetwork
    10141020
     
    11531159}
    11541160
    1155 /*! \reimp
     1161/*! \internal
    11561162*/
    11571163bool QHttpResponseHeader::parseLine(const QString &line, int number)
     
    12121218/*!
    12131219    \class QHttpRequestHeader
     1220    \obsolete
    12141221    \brief The QHttpRequestHeader class contains request header information for HTTP.
    12151222
    1216     \ingroup io
     1223    \ingroup network
    12171224    \inmodule QtNetwork
    12181225
     
    13671374}
    13681375
    1369 /*! \reimp
     1376/*! \internal
    13701377*/
    13711378bool QHttpRequestHeader::parseLine(const QString &line, int number)
     
    14141421/*!
    14151422    \class QHttp
     1423    \obsolete
    14161424    \reentrant
    14171425
    14181426    \brief The QHttp class provides an implementation of the HTTP protocol.
    14191427
    1420     \ingroup io
     1428    \ingroup network
    14211429    \inmodule QtNetwork
    1422     \mainclass
     1430
    14231431
    14241432    This class provides a direct interface to HTTP that allows you to
    1425     have more control over the requests and that allows you to access
    1426     the response header fields. However, for new applications, it is
     1433    download and upload data with the HTTP protocol.
     1434    However, for new applications, it is
    14271435    recommended to use QNetworkAccessManager and QNetworkReply, as
    1428     those classes possess a simpler, yet more powerful API.
     1436    those classes possess a simpler, yet more powerful API
     1437    and a more modern protocol implementation.
    14291438
    14301439    The class works asynchronously, so there are no blocking
     
    14471456
    14481457    To make an HTTP request you must set up suitable HTTP headers. The
    1449     following example demonstrates, how to request the main HTML page
    1450     from the Trolltech home page (i.e., the URL
    1451     \c http://qtsoftware.com/index.html):
     1458    following example demonstrates how to request the main HTML page
     1459    from the Qt website (i.e., the URL \c http://qt.nokia.com/index.html):
    14521460
    14531461    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 2
     
    15931601    This enum is used to specify the mode of connection to use:
    15941602
    1595     \value ConnectionModeHttp The connection is a regular Http connection to the server
    1596     \value ConnectionModeHttps The Https protocol is used and the connection is encrypted using SSL.
    1597 
    1598     When using the Https mode, care should be taken to connect to the sslErrors signal, and
    1599     handle possible Ssl errors.
     1603    \value ConnectionModeHttp The connection is a regular HTTP connection to the server
     1604    \value ConnectionModeHttps The HTTPS protocol is used and the connection is encrypted using SSL.
     1605
     1606    When using the HTTPS mode, care should be taken to connect to the sslErrors signal, and
     1607    handle possible SSL errors.
    16001608
    16011609    \sa QSslSocket
     
    20352043
    20362044    If port is 0, it will use the default port for the \a mode used
    2037     (80 for Http and 443 fopr Https).
     2045    (80 for HTTP and 443 for HTTPS).
    20382046
    20392047    The function does not block; instead, it returns immediately. The request
     
    21622170
    21632171    \a path must be a absolute path like \c /index.html or an
    2164     absolute URI like \c http://qtsoftware.com/index.html and
     2172    absolute URI like \c http://example.com/index.html and
    21652173    must be encoded with either QUrl::toPercentEncoding() or
    21662174    QUrl::encodedPath().
     
    22012209
    22022210    \a path must be an absolute path like \c /index.html or an
    2203     absolute URI like \c http://qtsoftware.com/index.html and
     2211    absolute URI like \c http://example.com/index.html and
    22042212    must be encoded with either QUrl::toPercentEncoding() or
    22052213    QUrl::encodedPath().
     
    22522260
    22532261    \a path must be an absolute path like \c /index.html or an
    2254     absolute URI like \c http://qtsoftware.com/index.html.
     2262    absolute URI like \c http://example.com/index.html.
    22552263
    22562264    The function does not block; instead, it returns immediately. The request
     
    26572665}
    26582666
     2667#ifndef QT_NO_OPENSSL
     2668void QHttpPrivate::_q_slotEncryptedBytesWritten(qint64 written)
     2669{
     2670    Q_UNUSED(written);
     2671    postMoreData();
     2672}
     2673#endif
     2674
    26592675void QHttpPrivate::_q_slotBytesWritten(qint64 written)
    26602676{
     
    26622678    bytesDone += written;
    26632679    emit q->dataSendProgress(bytesDone, bytesTotal);
    2664 
     2680    postMoreData();
     2681}
     2682
     2683// Send the POST data
     2684void QHttpPrivate::postMoreData()
     2685{
    26652686    if (pendingPost)
    26662687        return;
     
    26692690        return;
    26702691
     2692    // the following is backported code from Qt 4.6 QNetworkAccessManager.
     2693    // We also have to check the encryptedBytesToWrite() if it is an SSL socket.
     2694#ifndef QT_NO_OPENSSL
     2695    QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
     2696    // if it is really an ssl socket, check more than just bytesToWrite()
     2697    if ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) == 0) {
     2698#else
    26712699    if (socket->bytesToWrite() == 0) {
     2700#endif
    26722701        int max = qMin<qint64>(4096, postDevice->size() - postDevice->pos());
    26732702        QByteArray arr;
     
    30873116                     q, SLOT(_q_slotBytesWritten(qint64)));
    30883117#ifndef QT_NO_NETWORKPROXY
    3089     QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
    3090                      q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
     3118    QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
     3119                     q, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
    30913120#endif
    30923121
    30933122#ifndef QT_NO_OPENSSL
    30943123    if (qobject_cast<QSslSocket *>(socket)) {
    3095         QObject::connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)),
    3096                          q, SIGNAL(sslErrors(const QList<QSslError> &)));
     3124        QObject::connect(socket, SIGNAL(sslErrors(QList<QSslError>)),
     3125                         q, SIGNAL(sslErrors(QList<QSslError>)));
     3126        QObject::connect(socket, SIGNAL(encryptedBytesWritten(qint64)),
     3127                         q, SLOT(_q_slotEncryptedBytesWritten(qint64)));
    30973128    }
    30983129#endif
  • trunk/src/network/access/qhttp.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4747#include <QtCore/qmap.h>
    4848#include <QtCore/qpair.h>
     49#include <QtCore/qscopedpointer.h>
    4950
    5051QT_BEGIN_HEADER
     
    109110    QHttpHeader(QHttpHeaderPrivate &dd, const QString &str = QString());
    110111    QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header);
    111     QHttpHeaderPrivate *d_ptr;
     112    QScopedPointer<QHttpHeaderPrivate> d_ptr;
    112113
    113114private:
     
    290291    Q_PRIVATE_SLOT(d_func(), void _q_slotClosed())
    291292    Q_PRIVATE_SLOT(d_func(), void _q_slotBytesWritten(qint64 numBytes))
     293#ifndef QT_NO_OPENSSL
     294    Q_PRIVATE_SLOT(d_func(), void _q_slotEncryptedBytesWritten(qint64 numBytes))
     295#endif
    292296    Q_PRIVATE_SLOT(d_func(), void _q_slotDoFinished())
    293297    Q_PRIVATE_SLOT(d_func(), void _q_slotSendRequest())
  • trunk/src/network/access/qhttpnetworkconnection.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4141
    4242#include "qhttpnetworkconnection_p.h"
     43#include "qhttpnetworkconnectionchannel_p.h"
     44#include "private/qnoncontiguousbytedevice_p.h"
    4345#include <private/qnetworkrequest_p.h>
    4446#include <private/qobject_p.h>
     
    4648#include <qnetworkproxy.h>
    4749#include <qauthenticator.h>
    48 #include <qbytearraymatcher.h>
     50
    4951#include <qbuffer.h>
    5052#include <qpair.h>
     
    6062#endif
    6163
    62 #ifndef QT_NO_COMPRESS
    63 #    include <zlib.h>
    64 static const unsigned char gz_magic[2] = {0x1f, 0x8b}; // gzip magic header
    65 // gzip flag byte
    66 #define HEAD_CRC     0x02 // bit 1 set: header CRC present
    67 #define EXTRA_FIELD  0x04 // bit 2 set: extra field present
    68 #define ORIG_NAME    0x08 // bit 3 set: original file name present
    69 #define COMMENT      0x10 // bit 4 set: file comment present
    70 #define RESERVED     0xE0 // bits 5..7: reserved
    71 #define CHUNK 16384
     64
     65
     66QT_BEGIN_NAMESPACE
     67
     68#ifdef Q_OS_SYMBIAN
     69const int QHttpNetworkConnectionPrivate::defaultChannelCount = 3;
     70#else
     71const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
    7272#endif
    7373
    74 QT_BEGIN_NAMESPACE
    75 
    76 class QHttpNetworkHeaderPrivate : public QSharedData
    77 {
    78 public:
    79     QUrl url;
    80     QList<QPair<QByteArray, QByteArray> > fields;
    81 
    82     QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
    83     QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
    84     inline qint64 contentLength() const;
    85     inline void setContentLength(qint64 length);
    86 
    87     inline QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
    88     inline QList<QByteArray> headerFieldValues(const QByteArray &name) const;
    89     inline void setHeaderField(const QByteArray &name, const QByteArray &data);
    90     bool operator==(const QHttpNetworkHeaderPrivate &other) const;
    91 
    92 };
    93 
    94 QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
    95     :url(newUrl)
    96 {
    97 }
    98 
    99 QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
    100     :QSharedData(other)
    101 {
    102     url = other.url;
    103     fields = other.fields;
    104 }
    105 
    106 qint64 QHttpNetworkHeaderPrivate::contentLength() const
    107 {
    108     bool ok = false;
    109     QByteArray value = headerField("content-length");
    110     qint64 length = value.toULongLong(&ok);
    111     if (ok)
    112         return length;
    113     return -1; // the header field is not set
    114 }
    115 
    116 void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
    117 {
    118     setHeaderField("Content-Length", QByteArray::number(length));
    119 }
    120 
    121 QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
    122 {
    123     QList<QByteArray> allValues = headerFieldValues(name);
    124     if (allValues.isEmpty())
    125         return defaultValue;
    126 
    127     QByteArray result;
    128     bool first = true;
    129     foreach (QByteArray value, allValues) {
    130         if (!first)
    131             result += ", ";
    132         first = false;
    133         result += value;
    134     }
    135     return result;
    136 }
    137 
    138 QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
    139 {
    140     QList<QByteArray> result;
    141     QByteArray lowerName = name.toLower();
    142     QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
    143                                                         end = fields.constEnd();
    144     for ( ; it != end; ++it)
    145         if (lowerName == it->first.toLower())
    146             result += it->second;
    147 
    148     return result;
    149 }
    150 
    151 void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data)
    152 {
    153     QByteArray lowerName = name.toLower();
    154     QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin();
    155     while (it != fields.end()) {
    156         if (lowerName == it->first.toLower())
    157             it = fields.erase(it);
    158         else
    159             ++it;
    160     }
    161     fields.append(qMakePair(name, data));
    162 }
    163 
    164 bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
    165 {
    166    return (url == other.url);
    167 }
    168 
    169 // QHttpNetworkRequestPrivate
    170 class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
    171 {
    172 public:
    173     QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
    174         QHttpNetworkRequest::Priority pri, const QUrl &newUrl = QUrl());
    175     QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
    176     ~QHttpNetworkRequestPrivate();
    177     bool operator==(const QHttpNetworkRequestPrivate &other) const;
    178     QByteArray methodName() const;
    179     QByteArray uri(bool throughProxy) const;
    180 
    181     static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
    182 
    183     QHttpNetworkRequest::Operation operation;
    184     QHttpNetworkRequest::Priority priority;
    185     mutable QIODevice *data;
    186     bool autoDecompress;
    187 };
    188 
    189 QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
    190         QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
    191     : QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), data(0),
    192       autoDecompress(false)
    193 {
    194 }
    195 
    196 QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other)
    197     : QHttpNetworkHeaderPrivate(other)
    198 {
    199     operation = other.operation;
    200     priority = other.priority;
    201     data = other.data;
    202     autoDecompress = other.autoDecompress;
    203 }
    204 
    205 QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
    206 {
    207 }
    208 
    209 bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const
    210 {
    211     return QHttpNetworkHeaderPrivate::operator==(other)
    212         && (operation == other.operation)
    213         && (data == other.data);
    214 }
    215 
    216 QByteArray QHttpNetworkRequestPrivate::methodName() const
    217 {
    218     QByteArray ba;
    219     switch (operation) {
    220     case QHttpNetworkRequest::Options:
    221         ba += "OPTIONS";
    222         break;
    223     case QHttpNetworkRequest::Get:
    224         ba += "GET";
    225         break;
    226     case QHttpNetworkRequest::Head:
    227         ba += "HEAD";
    228         break;
    229     case QHttpNetworkRequest::Post:
    230         ba += "POST";
    231         break;
    232     case QHttpNetworkRequest::Put:
    233         ba += "PUT";
    234         break;
    235     case QHttpNetworkRequest::Delete:
    236         ba += "DELETE";
    237         break;
    238     case QHttpNetworkRequest::Trace:
    239         ba += "TRACE";
    240         break;
    241     case QHttpNetworkRequest::Connect:
    242         ba += "CONNECT";
    243         break;
    244     default:
    245         break;
    246     }
    247     return ba;
    248 }
    249 
    250 QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
    251 {
    252     QUrl::FormattingOptions format(QUrl::RemoveFragment);
    253 
    254     // for POST, query data is send as content
    255     if (operation == QHttpNetworkRequest::Post && !data)
    256         format |= QUrl::RemoveQuery;
    257     // for requests through proxy, the Request-URI contains full url
    258     if (throughProxy)
    259         format |= QUrl::RemoveUserInfo;
    260     else
    261         format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
    262     QByteArray uri = url.toEncoded(format);
    263     if (uri.isEmpty() || (throughProxy && url.path().isEmpty()))
    264         uri += '/';
    265     return uri;
    266 }
    267 
    268 QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
    269 {
    270     QByteArray ba = request.d->methodName();
    271     QByteArray uri = request.d->uri(throughProxy);
    272     ba += " " + uri;
    273 
    274     QString majorVersion = QString::number(request.majorVersion());
    275     QString minorVersion = QString::number(request.minorVersion());
    276     ba += " HTTP/" + majorVersion.toLatin1() + "." + minorVersion.toLatin1() + "\r\n";
    277 
    278     QList<QPair<QByteArray, QByteArray> > fields = request.header();
    279     QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
    280     for (; it != fields.constEnd(); ++it)
    281         ba += it->first + ": " + it->second + "\r\n";
    282     if (request.d->operation == QHttpNetworkRequest::Post) {
    283         // add content type, if not set in the request
    284         if (request.headerField("content-type").isEmpty())
    285             ba += "Content-Type: application/x-www-form-urlencoded\r\n";
    286         if (!request.d->data && request.d->url.hasQuery()) {
    287             QByteArray query = request.d->url.encodedQuery();
    288             ba += "Content-Length: "+ QByteArray::number(query.size()) + "\r\n";
    289             ba += "\r\n";
    290             ba += query;
    291         } else {
    292             ba += "\r\n";
    293         }
    294     } else {
    295         ba += "\r\n";
    296     }
    297      return ba;
    298 }
    299 
    300 class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
    301 {
    302 public:
    303     QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
    304     ~QHttpNetworkReplyPrivate();
    305     qint64 readStatus(QAbstractSocket *socket);
    306     void parseStatus(const QByteArray &status);
    307     qint64 readHeader(QAbstractSocket *socket);
    308     void parseHeader(const QByteArray &header);
    309     qint64 readBody(QAbstractSocket *socket, QIODevice *out);
    310     bool findChallenge(bool forProxy, QByteArray &challenge) const;
    311     QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
    312     void clear();
    313 
    314     qint64 transferRaw(QIODevice *in, QIODevice *out, qint64 size);
    315     qint64 transferChunked(QIODevice *in, QIODevice *out);
    316     qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
    317 
    318     qint64 bytesAvailable() const;
    319     bool isChunked();
    320     bool connectionCloseEnabled();
    321     bool isGzipped();
    322 #ifndef QT_NO_COMPRESS
    323     bool gzipCheckHeader(QByteArray &content, int &pos);
    324     int gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated);
    325 #endif
    326     void removeAutoDecompressHeader();
    327 
    328     enum ReplyState {
    329         NothingDoneState,
    330         ReadingStatusState,
    331         ReadingHeaderState,
    332         ReadingDataState,
    333         AllDoneState
    334     } state;
    335 
    336     QHttpNetworkRequest request;
    337     int statusCode;
    338     int majorVersion;
    339     int minorVersion;
    340     QString errorString;
    341     QString reasonPhrase;
    342     qint64 bodyLength;
    343     qint64 contentRead;
    344     qint64 totalProgress;
    345     QByteArray fragment;
    346     qint64 currentChunkSize;
    347     qint64 currentChunkRead;
    348     QPointer<QHttpNetworkConnection> connection;
    349     bool initInflate;
    350     bool streamEnd;
    351 #ifndef QT_NO_COMPRESS
    352     z_stream inflateStrm;
    353 #endif
    354     bool autoDecompress;
    355 
    356     QByteArray responseData; // uncompressed body
    357     QByteArray compressedData; // compressed body (temporary)
    358     QBuffer requestDataBuffer;
    359     bool requestIsBuffering;
    360     bool requestIsPrepared;
    361 };
    362 
    363 QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
    364     : QHttpNetworkHeaderPrivate(newUrl), state(NothingDoneState), statusCode(100),
    365       majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
    366       currentChunkSize(0), currentChunkRead(0), connection(0), initInflate(false),
    367       autoDecompress(false), requestIsBuffering(false), requestIsPrepared(false)
    368 {
    369 }
    370 
    371 QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
    372 {
    373 }
    374 
    375 void QHttpNetworkReplyPrivate::clear()
    376 {
    377     state = NothingDoneState;
    378     statusCode = 100;
    379     bodyLength = 0;
    380     contentRead = 0;
    381     totalProgress = 0;
    382     currentChunkSize = 0;
    383     currentChunkRead = 0;
    384     connection = 0;
    385 #ifndef QT_NO_COMPRESS
    386     if (initInflate)
    387         inflateEnd(&inflateStrm);
    388 #endif
    389     initInflate = false;
    390     streamEnd = false;
    391     autoDecompress = false;
    392     fields.clear();
    393 }
    394 
    395 // QHttpNetworkReplyPrivate
    396 qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
    397 {
    398     return (state != ReadingDataState ? 0 : fragment.size());
    399 }
    400 
    401 bool QHttpNetworkReplyPrivate::isGzipped()
    402 {
    403     QByteArray encoding = headerField("content-encoding");
    404     return encoding.toLower() == "gzip";
    405 }
    406 
    407 void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
    408 {
    409     // The header "Content-Encoding  = gzip" is retained.
    410     // Content-Length is removed since the actual one send by the server is for compressed data
    411     QByteArray name("content-length");
    412     QByteArray lowerName = name.toLower();
    413     QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
    414                                                    end = fields.end();
    415     while (it != end) {
    416         if (name == it->first.toLower()) {
    417             fields.erase(it);
    418             break;
    419         }
    420         ++it;
    421     }
    422 
    423 }
    424 
    425 bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
    426 {
    427     challenge.clear();
    428     // find out the type of authentication protocol requested.
    429     QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
    430     // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
    431     QList<QByteArray> challenges = headerFieldValues(header);
    432     for (int i = 0; i<challenges.size(); i++) {
    433         QByteArray line = challenges.at(i);
    434         if (!line.toLower().startsWith("negotiate"))
    435             challenge = line;
    436     }
    437     return !challenge.isEmpty();
    438 }
    439 
    440 QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
    441 {
    442     // The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
    443     QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
    444     QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
    445     QList<QByteArray> challenges = headerFieldValues(header);
    446     for (int i = 0; i<challenges.size(); i++) {
    447         QByteArray line = challenges.at(i).trimmed().toLower();
    448         if (method < QAuthenticatorPrivate::Basic
    449             && line.startsWith("basic")) {
    450             method = QAuthenticatorPrivate::Basic;
    451         } else if (method < QAuthenticatorPrivate::Ntlm
    452             && line.startsWith("ntlm")) {
    453             method = QAuthenticatorPrivate::Ntlm;
    454         } else if (method < QAuthenticatorPrivate::DigestMd5
    455             && line.startsWith("digest")) {
    456             method = QAuthenticatorPrivate::DigestMd5;
    457         }
    458     }
    459     return method;
    460 }
    461 
    462 #ifndef QT_NO_COMPRESS
    463 bool QHttpNetworkReplyPrivate::gzipCheckHeader(QByteArray &content, int &pos)
    464 {
    465     int method = 0; // method byte
    466     int flags = 0;  // flags byte
    467     bool ret = false;
    468 
    469     // Assure two bytes in the buffer so we can peek ahead -- handle case
    470     // where first byte of header is at the end of the buffer after the last
    471     // gzip segment
    472     pos = -1;
    473     QByteArray &body = content;
    474     int maxPos = body.size()-1;
    475     if (maxPos < 1) {
    476         return ret;
    477     }
    478 
    479     // Peek ahead to check the gzip magic header
    480     if (body[0] != char(gz_magic[0]) ||
    481         body[1] != char(gz_magic[1])) {
    482         return ret;
    483     }
    484     pos += 2;
    485     // Check the rest of the gzip header
    486     if (++pos <= maxPos)
    487         method = body[pos];
    488     if (pos++ <= maxPos)
    489         flags = body[pos];
    490     if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
    491         return ret;
    492     }
    493 
    494     // Discard time, xflags and OS code:
    495     pos += 6;
    496     if (pos > maxPos)
    497         return ret;
    498     if ((flags & EXTRA_FIELD) && ((pos+2) <= maxPos)) { // skip the extra field
    499         unsigned len =  (unsigned)body[++pos];
    500         len += ((unsigned)body[++pos])<<8;
    501         pos += len;
    502         if (pos > maxPos)
    503             return ret;
    504     }
    505     if ((flags & ORIG_NAME) != 0) { // skip the original file name
    506         while(++pos <= maxPos && body[pos]) {}
    507     }
    508     if ((flags & COMMENT) != 0) {   // skip the .gz file comment
    509         while(++pos <= maxPos && body[pos]) {}
    510     }
    511     if ((flags & HEAD_CRC) != 0) {  // skip the header crc
    512         pos += 2;
    513         if (pos > maxPos)
    514             return ret;
    515     }
    516     ret = (pos < maxPos); // return failed, if no more bytes left
    517     return ret;
    518 }
    519 
    520 int QHttpNetworkReplyPrivate::gunzipBodyPartially(QByteArray &compressed, QByteArray &inflated)
    521 {
    522     int ret = Z_DATA_ERROR;
    523     unsigned have;
    524     unsigned char out[CHUNK];
    525     int pos = -1;
    526 
    527     if (!initInflate) {
    528         // check the header
    529         if (!gzipCheckHeader(compressed, pos))
    530             return ret;
    531         // allocate inflate state
    532         inflateStrm.zalloc = Z_NULL;
    533         inflateStrm.zfree = Z_NULL;
    534         inflateStrm.opaque = Z_NULL;
    535         inflateStrm.avail_in = 0;
    536         inflateStrm.next_in = Z_NULL;
    537         ret = inflateInit2(&inflateStrm, -MAX_WBITS);
    538         if (ret != Z_OK)
    539             return ret;
    540         initInflate = true;
    541         streamEnd = false;
    542     }
    543 
    544     //remove the header.
    545     compressed.remove(0, pos+1);
    546     // expand until deflate stream ends
    547     inflateStrm.next_in = (unsigned char *)compressed.data();
    548     inflateStrm.avail_in = compressed.size();
    549     do {
    550         inflateStrm.avail_out = sizeof(out);
    551         inflateStrm.next_out = out;
    552         ret = inflate(&inflateStrm, Z_NO_FLUSH);
    553         switch (ret) {
    554         case Z_NEED_DICT:
    555             ret = Z_DATA_ERROR;
    556             // and fall through
    557         case Z_DATA_ERROR:
    558         case Z_MEM_ERROR:
    559             inflateEnd(&inflateStrm);
    560             initInflate = false;
    561             return ret;
    562         }
    563         have = sizeof(out) - inflateStrm.avail_out;
    564         inflated.append(QByteArray((const char *)out, have));
    565      } while (inflateStrm.avail_out == 0);
    566     // clean up and return
    567     if (ret <= Z_ERRNO || ret == Z_STREAM_END) {
    568         inflateEnd(&inflateStrm);
    569         initInflate = false;
    570     }
    571     streamEnd = (ret == Z_STREAM_END);
    572     return ret;
    573 }
    574 #endif
    575 
    576 qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
    577 {
    578     qint64 bytes = 0;
    579     char c;
    580 
    581     while (socket->bytesAvailable()) {
    582         // allow both CRLF & LF (only) line endings
    583         if (socket->peek(&c, 1) == 1 && c == '\n') {
    584             bytes += socket->read(&c, 1); // read the "n"
    585             // remove the CR at the end
    586             if (fragment.endsWith('\r')) {
    587                 fragment.truncate(fragment.length()-1);
    588             }
    589             parseStatus(fragment);
    590             state = ReadingHeaderState;
    591             fragment.clear(); // next fragment
    592             break;
    593         } else {
    594             c = 0;
    595             bytes += socket->read(&c, 1);
    596             fragment.append(c);
    597         }
    598     }
    599     return bytes;
    600 }
    601 
    602 void QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
    603 {
    604     const QByteArrayMatcher sp(" ");
    605     int i = sp.indexIn(status);
    606     const QByteArray version = status.mid(0, i);
    607     int j = sp.indexIn(status, i + 1);
    608     const QByteArray code = status.mid(i + 1, j - i - 1);
    609     const QByteArray reason = status.mid(j + 1, status.count() - j);
    610 
    611     const QByteArrayMatcher slash("/");
    612     int k = slash.indexIn(version);
    613     const QByteArrayMatcher dot(".");
    614     int l = dot.indexIn(version, k);
    615     const QByteArray major = version.mid(k + 1, l - k - 1);
    616     const QByteArray minor = version.mid(l + 1, version.count() - l);
    617 
    618     majorVersion = QString::fromAscii(major.constData()).toInt();
    619     minorVersion = QString::fromAscii(minor.constData()).toInt();
    620     statusCode = QString::fromAscii(code.constData()).toInt();
    621     reasonPhrase = QString::fromAscii(reason.constData());
    622 }
    623 
    624 qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
    625 {
    626     qint64 bytes = 0;
    627     char crlfcrlf[5];
    628     crlfcrlf[4] = '\0';
    629     char c = 0;
    630     bool allHeaders = false;
    631     while (!allHeaders && socket->bytesAvailable()) {
    632         if (socket->peek(&c, 1) == 1 && c == '\n') {
    633             // check for possible header endings. As per HTTP rfc,
    634             // the header endings will be marked by CRLFCRLF. But
    635             // we will allow CRLFLF, LFLF & CRLFCRLF
    636             if (fragment.endsWith("\n\r") || fragment.endsWith('\n'))
    637                 allHeaders = true;
    638         }
    639         bytes += socket->read(&c, 1);
    640         fragment.append(c);
    641     }
    642     // we received all headers now parse them
    643     if (allHeaders) {
    644         parseHeader(fragment);
    645         state = ReadingDataState;
    646         fragment.clear(); // next fragment
    647         bodyLength = contentLength(); // cache the length
    648     }
    649     return bytes;
    650 }
    651 
    652 void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
    653 {
    654     // see rfc2616, sec 4 for information about HTTP/1.1 headers.
    655     // allows relaxed parsing here, accepts both CRLF & LF line endings
    656     const QByteArrayMatcher lf("\n");
    657     const QByteArrayMatcher colon(":");
    658     int i = 0;
    659     while (i < header.count()) {
    660         int j = colon.indexIn(header, i); // field-name
    661         if (j == -1)
    662             break;
    663         const QByteArray field = header.mid(i, j - i).trimmed();
    664         j++;
    665         // any number of LWS is allowed before and after the value
    666         QByteArray value;
    667         do {
    668             i = lf.indexIn(header, j);
    669             if (i == -1)
    670                 break;
    671             if (!value.isEmpty())
    672                 value += ' ';
    673             // check if we have CRLF or only LF
    674             bool hasCR = (i && header[i-1] == '\r');
    675             int length = i -(hasCR ? 1: 0) - j;
    676             value += header.mid(j, length).trimmed();
    677             j = ++i;
    678         } while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
    679         if (i == -1)
    680             break; // something is wrong
    681 
    682         fields.append(qMakePair(field, value));
    683     }
    684 }
    685 
    686 bool QHttpNetworkReplyPrivate::isChunked()
    687 {
    688     return headerField("transfer-encoding").toLower().contains("chunked");
    689 }
    690 
    691 bool QHttpNetworkReplyPrivate::connectionCloseEnabled()
    692 {
    693     return (headerField("connection").toLower().contains("close") ||
    694             headerField("proxy-connection").toLower().contains("close"));
    695 }
    696 
    697 qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QIODevice *out)
    698 {
    699     qint64 bytes = 0;
    700     if (isChunked()) {
    701         bytes += transferChunked(socket, out); // chunked transfer encoding (rfc 2616, sec 3.6)
    702     } else if (bodyLength > 0) { // we have a Content-Length
    703         bytes += transferRaw(socket, out, bodyLength - contentRead);
    704         if (contentRead + bytes == bodyLength)
    705             state = AllDoneState;
    706     } else {
    707         bytes += transferRaw(socket, out, socket->bytesAvailable());
    708     }
    709     if (state == AllDoneState)
    710         socket->readAll(); // Read the rest to clean (CRLF)
    711     contentRead += bytes;
    712     return bytes;
    713 }
    714 
    715 qint64 QHttpNetworkReplyPrivate::transferRaw(QIODevice *in, QIODevice *out, qint64 size)
    716 {
    717     qint64 bytes = 0;
    718     Q_ASSERT(in);
    719     Q_ASSERT(out);
    720 
    721     int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, in->bytesAvailable()));
    722     QByteArray raw(toBeRead, 0);
    723     while (size > 0) {
    724         qint64 read = in->read(raw.data(), raw.size());
    725         if (read == 0)
    726             return bytes;
    727         // ### error checking here
    728         qint64 written = out->write(raw.data(), read);
    729         if (written == 0)
    730             return bytes;
    731         if (read != written)
    732             qDebug() << "### read" << read << "written" << written;
    733         bytes += read;
    734         size -= read;
    735         out->waitForBytesWritten(-1); // throttle
    736     }
    737     return bytes;
    738 
    739 }
    740 
    741 qint64 QHttpNetworkReplyPrivate::transferChunked(QIODevice *in, QIODevice *out)
    742 {
    743     qint64 bytes = 0;
    744     while (in->bytesAvailable()) { // while we can read from input
    745         // if we are done with the current chunk, get the size of the new chunk
    746         if (currentChunkRead >= currentChunkSize) {
    747             currentChunkSize = 0;
    748             currentChunkRead = 0;
    749             if (bytes) {
    750                 char crlf[2];
    751                 bytes += in->read(crlf, 2); // read the "\r\n" after the chunk
    752             }
    753             bytes += getChunkSize(in, &currentChunkSize);
    754             if (currentChunkSize == -1)
    755                 break;
    756         }
    757         // if the chunk size is 0, end of the stream
    758         if (currentChunkSize == 0) {
    759             state = AllDoneState;
    760             break;
    761         }
    762         // otherwise, read data
    763         qint64 readSize = qMin(in->bytesAvailable(), currentChunkSize - currentChunkRead);
    764         QByteArray buffer(readSize, 0);
    765         qint64 read = in->read(buffer.data(), readSize);
    766         bytes += read;
    767         currentChunkRead += read;
    768         qint64 written = out->write(buffer);
    769         Q_UNUSED(written); // Avoid compile warning when building release
    770         Q_ASSERT(read == written);
    771         // ### error checking here
    772         out->waitForBytesWritten(-1);
    773     }
    774     return bytes;
    775 }
    776 
    777 qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *in, qint64 *chunkSize)
    778 {
    779     qint64 bytes = 0;
    780     char crlf[2];
    781     *chunkSize = -1;
    782     int bytesAvailable = in->bytesAvailable();
    783     while (bytesAvailable > bytes) {
    784         qint64 sniffedBytes =  in->peek(crlf, 2);
    785         int fragmentSize = fragment.size();
    786         // check the next two bytes for a "\r\n", skip blank lines
    787         if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
    788            ||(fragmentSize > 1 && fragment.endsWith('\r')  && crlf[0] == '\n'))
    789         {
    790             bytes += in->read(crlf, 1);     // read the \r or \n
    791             if (crlf[0] == '\r')
    792                 bytes += in->read(crlf, 1); // read the \n
    793             bool ok = false;
    794             // ignore the chunk-extension
    795             fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
    796             *chunkSize = fragment.toLong(&ok, 16);
    797             fragment.clear();
    798             break; // size done
    799         } else {
    800             // read the fragment to the buffer
    801             char c = 0;
    802             bytes += in->read(&c, 1);
    803             fragment.append(c);
    804         }
    805     }
    806     return bytes;
    807 }
    808 
    809 // QHttpNetworkConnectionPrivate
    810 
    811 typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
    812 
    813 class QHttpNetworkConnectionPrivate : public QObjectPrivate
    814 {
    815     Q_DECLARE_PUBLIC(QHttpNetworkConnection)
    816 public:
    817     QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
    818     ~QHttpNetworkConnectionPrivate();
    819     void init();
    820     void connectSignals(QAbstractSocket *socket);
    821 
    822     enum SocketState {
    823         IdleState = 0,          // ready to send request
    824         ConnectingState = 1,    // connecting to host
    825         WritingState = 2,       // writing the data
    826         WaitingState = 4,       // waiting for reply
    827         ReadingState = 8,       // reading the reply
    828         Wait4AuthState = 0x10,  // blocked for send till the current authentication slot is done
    829         BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|Wait4AuthState)
    830     };
    831 
    832     enum { ChunkSize = 4096 };
    833 
    834     int indexOf(QAbstractSocket *socket) const;
    835     bool isSocketBusy(QAbstractSocket *socket) const;
    836     bool isSocketWriting(QAbstractSocket *socket) const;
    837     bool isSocketWaiting(QAbstractSocket *socket) const;
    838     bool isSocketReading(QAbstractSocket *socket) const;
    839 
    840     QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
    841     void unqueueRequest(QAbstractSocket *socket);
    842     void prepareRequest(HttpMessagePair &request);
    843     bool sendRequest(QAbstractSocket *socket);
    844     void receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply);
    845     void resendCurrentRequest(QAbstractSocket *socket);
    846     void closeChannel(int channel);
    847     void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
    848 
    849     // private slots
    850     void _q_bytesWritten(qint64 bytes); // proceed sending
    851     void _q_readyRead(); // pending data to read
    852     void _q_disconnected(); // disconnected from host
    853     void _q_startNextRequest(); // send the next request from the queue
    854     void _q_restartPendingRequest(); // send the currently blocked request
    855     void _q_connected(); // start sending request
    856     void _q_error(QAbstractSocket::SocketError); // error from socket
    857 #ifndef QT_NO_NETWORKPROXY
    858     void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
    859 #endif
    860     void _q_dataReadyReadNoBuffer();
    861     void _q_dataReadyReadBuffer();
    862 
    863     void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
    864     bool ensureConnection(QAbstractSocket *socket);
    865     QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket);
    866     void eraseData(QHttpNetworkReply *reply);
    867 #ifndef QT_NO_COMPRESS
    868     bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
    869 #endif
    870     void bufferData(HttpMessagePair &request);
    871     void removeReply(QHttpNetworkReply *reply);
    872 
    873     QString hostName;
    874     quint16 port;
    875     bool encrypt;
    876 
    877     struct Channel {
    878         QAbstractSocket *socket;
    879         SocketState state;
    880         QHttpNetworkRequest request; // current request
    881         QHttpNetworkReply *reply; // current reply for this request
    882         qint64 written;
    883         qint64 bytesTotal;
    884         bool resendCurrent;
    885         int lastStatus; // last status received on this channel
    886         bool pendingEncrypt; // for https (send after encrypted)
    887         int reconnectAttempts; // maximum 2 reconnection attempts
    888         QAuthenticatorPrivate::Method authMehtod;
    889         QAuthenticatorPrivate::Method proxyAuthMehtod;
    890         QAuthenticator authenticator;
    891         QAuthenticator proxyAuthenticator;
    892 #ifndef QT_NO_OPENSSL
    893         bool ignoreSSLErrors;
    894 #endif
    895         Channel() :state(IdleState), reply(0), written(0), bytesTotal(0), resendCurrent(false), reconnectAttempts(2),
    896             authMehtod(QAuthenticatorPrivate::None), proxyAuthMehtod(QAuthenticatorPrivate::None)
    897 #ifndef QT_NO_OPENSSL
    898             , ignoreSSLErrors(false)
    899 #endif
    900         {}
    901     };
    902     static const int channelCount;
    903     Channel channels[2]; // maximum of 2 socket connections to the server
    904     bool pendingAuthSignal; // there is an incomplete authentication signal
    905     bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal
    906 
    907     void appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed);
    908     qint64 bytesAvailable(const QHttpNetworkReply &reply, bool compressed = false) const;
    909     qint64 read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed);
    910     void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
    911     bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
    912     void allDone(QAbstractSocket *socket, QHttpNetworkReply *reply);
    913     void handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply);
    914     inline bool emitSignals(QHttpNetworkReply *reply);
    915     inline bool expectContent(QHttpNetworkReply *reply);
    916 
    917 #ifndef QT_NO_OPENSSL
    918     void _q_encrypted(); // start sending request (https)
    919     void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
    920     QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const;
    921 #endif
    922 
    923 #ifndef QT_NO_NETWORKPROXY
    924     QNetworkProxy networkProxy;
    925 #endif
    926 
    927     //The request queues
    928     QList<HttpMessagePair> highPriorityQueue;
    929     QList<HttpMessagePair> lowPriorityQueue;
    930 };
    931 
    932 const int QHttpNetworkConnectionPrivate::channelCount = 2;
     74// the maximum amount of requests that might be pipelined into a socket
     75// from what was suggested, 3 seems to be OK
     76const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
     77
    93378
    93479QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
    93580: hostName(hostName), port(port), encrypt(encrypt),
     81  channelCount(defaultChannelCount),
    93682  pendingAuthSignal(false), pendingProxyAuthSignal(false)
    93783#ifndef QT_NO_NETWORKPROXY
     
    93985#endif
    94086{
    941 }
     87    channels = new QHttpNetworkConnectionChannel[channelCount];
     88}
     89
     90QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
     91: hostName(hostName), port(port), encrypt(encrypt),
     92  channelCount(channelCount),
     93  pendingAuthSignal(false), pendingProxyAuthSignal(false)
     94#ifndef QT_NO_NETWORKPROXY
     95  , networkProxy(QNetworkProxy::NoProxy)
     96#endif
     97{
     98    channels = new QHttpNetworkConnectionChannel[channelCount];
     99}
     100
     101
    942102
    943103QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
    944104{
    945105    for (int i = 0; i < channelCount; ++i) {
    946         channels[i].socket->close();
    947         delete channels[i].socket;
    948     }
    949 }
    950 
    951 void QHttpNetworkConnectionPrivate::connectSignals(QAbstractSocket *socket)
    952 {
    953     Q_Q(QHttpNetworkConnection);
    954 
    955     QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
    956                q, SLOT(_q_bytesWritten(qint64)),
    957                Qt::DirectConnection);
    958     QObject::connect(socket, SIGNAL(connected()),
    959                q, SLOT(_q_connected()),
    960                Qt::DirectConnection);
    961     QObject::connect(socket, SIGNAL(readyRead()),
    962                q, SLOT(_q_readyRead()),
    963                Qt::DirectConnection);
    964     QObject::connect(socket, SIGNAL(disconnected()),
    965                q, SLOT(_q_disconnected()),
    966                Qt::DirectConnection);
    967     QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
    968                q, SLOT(_q_error(QAbstractSocket::SocketError)),
    969                Qt::DirectConnection);
    970 #ifndef QT_NO_NETWORKPROXY
    971     QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
    972                q, SLOT(_q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*)),
    973                Qt::DirectConnection);
    974 #endif
    975 
    976 #ifndef QT_NO_OPENSSL
    977     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
    978     QObject::connect(sslSocket, SIGNAL(encrypted()),
    979                      q, SLOT(_q_encrypted()),
    980                      Qt::DirectConnection);
    981     QObject::connect(sslSocket, SIGNAL(sslErrors(const QList<QSslError>&)),
    982                q, SLOT(_q_sslErrors(const QList<QSslError>&)),
    983                Qt::DirectConnection);
    984 #endif
     106        if (channels[i].socket) {
     107            channels[i].socket->close();
     108            delete channels[i].socket;
     109        }
     110    }
     111    delete []channels;
    985112}
    986113
    987114void QHttpNetworkConnectionPrivate::init()
    988115{
    989     for (int i = 0; i < channelCount; ++i) {
    990 #ifndef QT_NO_OPENSSL
    991         channels[i].socket = new QSslSocket;
    992 #else
    993         channels[i].socket = new QTcpSocket;
    994 #endif
    995         connectSignals(channels[i].socket);
     116    for (int i = 0; i < channelCount; i++) {
     117        channels[i].setConnection(this->q_func());
     118        channels[i].init();
    996119    }
    997120}
     
    1002125        if (channels[i].socket == socket)
    1003126            return i;
    1004     return -1;
    1005 }
    1006 
    1007 bool QHttpNetworkConnectionPrivate::isSocketBusy(QAbstractSocket *socket) const
    1008 {
    1009     int i = indexOf(socket);
    1010     return (channels[i].state & BusyState);
    1011 }
    1012 
    1013 bool QHttpNetworkConnectionPrivate::isSocketWriting(QAbstractSocket *socket) const
    1014 {
    1015     int i = indexOf(socket);
    1016     return (i != -1 && (channels[i].state & WritingState));
    1017 }
    1018 
    1019 bool QHttpNetworkConnectionPrivate::isSocketWaiting(QAbstractSocket *socket) const
    1020 {
    1021     int i = indexOf(socket);
    1022     return (i != -1 && (channels[i].state & WaitingState));
    1023 }
    1024 
    1025 bool QHttpNetworkConnectionPrivate::isSocketReading(QAbstractSocket *socket) const
    1026 {
    1027     int i = indexOf(socket);
    1028     return (i != -1 && (channels[i].state & ReadingState));
    1029 }
    1030 
    1031 
    1032 void QHttpNetworkConnectionPrivate::appendData(QHttpNetworkReply &reply, const QByteArray &fragment, bool compressed)
    1033 {
    1034     QByteArray *ba = (compressed) ? &reply.d_func()->compressedData : &reply.d_func()->responseData;
    1035     ba->append(fragment);
    1036     return;
    1037 }
    1038 
    1039 qint64 QHttpNetworkConnectionPrivate::bytesAvailable(const QHttpNetworkReply &reply, bool compressed) const
    1040 {
    1041     const QByteArray *ba = (compressed) ? &reply.d_func()->compressedData : &reply.d_func()->responseData;
    1042     return ba->size();
    1043 }
    1044 
    1045 qint64 QHttpNetworkConnectionPrivate::read(QHttpNetworkReply &reply, QByteArray &data, qint64 maxSize, bool compressed)
    1046 {
    1047     QByteArray *ba = (compressed) ? &reply.d_func()->compressedData : &reply.d_func()->responseData;
    1048     if (maxSize == -1 || maxSize >= ba->size()) {
    1049         // read the whole data
    1050         data = *ba;
    1051         ba->clear();
    1052     } else {
    1053         // read only the requested length
    1054         data = ba->mid(0, maxSize);
    1055         ba->remove(0, maxSize);
    1056     }
    1057     return data.size();
    1058 }
    1059 
    1060 void QHttpNetworkConnectionPrivate::eraseData(QHttpNetworkReply *reply)
    1061 {
    1062     reply->d_func()->compressedData.clear();
    1063     reply->d_func()->responseData.clear();
     127
     128    qFatal("Called with unknown socket object.");
     129    return 0;
     130}
     131
     132qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
     133{
     134    return reply.d_func()->responseData.byteAmount();
     135}
     136
     137qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
     138{
     139    return reply.d_func()->responseData.sizeNextBlock();
    1064140}
    1065141
     
    1072148    QByteArray value;
    1073149    // check if Content-Length is provided
    1074     QIODevice *data = request.data();
    1075     if (data && request.contentLength() == -1) {
    1076         if (!data->isSequential())
    1077             request.setContentLength(data->size());
    1078         else
    1079             bufferData(messagePair); // ### or do chunked upload
     150    QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
     151    if (uploadByteDevice) {
     152        if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
     153            // both values known, take the smaller one.
     154            request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
     155        } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
     156            // content length not supplied by user, but the upload device knows it
     157            request.setContentLength(uploadByteDevice->size());
     158        } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
     159            // everything OK, the user supplied us the contentLength
     160        } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
     161            qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
     162        }
    1080163    }
    1081164    // set the Connection/Proxy-Connection: Keep-Alive headers
     
    1093176    }
    1094177#endif
    1095     // set the gzip header
     178
     179    // If the request had a accept-encoding set, we better not mess
     180    // with it. If it was not set, we announce that we understand gzip
     181    // and remember this fact in request.d->autoDecompress so that
     182    // we can later decompress the HTTP reply if it has such an
     183    // encoding.
    1096184    value = request.headerField("accept-encoding");
    1097185    if (value.isEmpty()) {
     
    1104192#endif
    1105193    }
     194
     195    // some websites mandate an accept-language header and fail
     196    // if it is not sent. This is a problem with the website and
     197    // not with us, but we work around this by setting
     198    // one always.
     199    value = request.headerField("accept-language");
     200    if (value.isEmpty()) {
     201        QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-'));
     202        QString acceptLanguage;
     203        if (systemLocale == QLatin1String("C"))
     204            acceptLanguage = QString::fromAscii("en,*");
     205        else if (systemLocale.startsWith(QLatin1String("en-")))
     206            acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale);
     207        else
     208            acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale);
     209        request.setHeaderField("Accept-Language", acceptLanguage.toAscii());
     210    }
     211
    1106212    // set the User Agent
    1107213    value = request.headerField("user-agent");
     
    1125231}
    1126232
    1127 bool QHttpNetworkConnectionPrivate::ensureConnection(QAbstractSocket *socket)
    1128 {
    1129     // make sure that this socket is in a connected state, if not initiate
    1130     // connection to the host.
    1131     if (socket->state() != QAbstractSocket::ConnectedState) {
    1132         // connect to the host if not already connected.
    1133         int index = indexOf(socket);
    1134         channels[index].state = ConnectingState;
    1135         channels[index].pendingEncrypt = encrypt;
    1136 
    1137         // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
    1138         // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
    1139         // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
    1140         // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
    1141         // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
    1142         // the phase is reset to Start.
    1143         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[index].authenticator);
    1144         if (priv && priv->phase == QAuthenticatorPrivate::Done)
    1145             priv->phase = QAuthenticatorPrivate::Start;
    1146         priv = QAuthenticatorPrivate::getPrivate(channels[index].proxyAuthenticator);
    1147         if (priv && priv->phase == QAuthenticatorPrivate::Done)
    1148             priv->phase = QAuthenticatorPrivate::Start;
    1149 
    1150         QString connectHost = hostName;
    1151         qint16 connectPort = port;
    1152 
    1153 #ifndef QT_NO_NETWORKPROXY
    1154         // HTTPS always use transparent proxy.
    1155         if (networkProxy.type() != QNetworkProxy::NoProxy && !encrypt) {
    1156             connectHost = networkProxy.hostName();
    1157             connectPort = networkProxy.port();
    1158         }
    1159 #endif
    1160         if (encrypt) {
    1161 #ifndef QT_NO_OPENSSL
    1162             QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
    1163             sslSocket->connectToHostEncrypted(connectHost, connectPort);
    1164             if (channels[index].ignoreSSLErrors)
    1165                 sslSocket->ignoreSslErrors();
    1166 #else
    1167             emitReplyError(socket, channels[index].reply, QNetworkReply::ProtocolUnknownError);
    1168 #endif
    1169         } else {
    1170             socket->connectToHost(connectHost, connectPort);
    1171         }
    1172         return false;
    1173     }
    1174     return true;
    1175 }
    1176 
    1177 
    1178 bool QHttpNetworkConnectionPrivate::sendRequest(QAbstractSocket *socket)
    1179 {
    1180     Q_Q(QHttpNetworkConnection);
    1181 
    1182     int i = indexOf(socket);
    1183     switch (channels[i].state) {
    1184     case IdleState: { // write the header
    1185         if (!ensureConnection(socket)) {
    1186             // wait for the connection (and encryption) to be done
    1187             // sendRequest will be called again from either
    1188             // _q_connected or _q_encrypted
    1189             return false;
    1190         }
    1191         channels[i].written = 0; // excluding the header
    1192         channels[i].bytesTotal = 0;
    1193         if (channels[i].reply) {
    1194             channels[i].reply->d_func()->clear();
    1195             channels[i].reply->d_func()->connection = q;
    1196             channels[i].reply->d_func()->autoDecompress = channels[i].request.d->autoDecompress;
    1197         }
    1198         channels[i].state = WritingState;
    1199         channels[i].pendingEncrypt = false;
    1200         // if the url contains authentication parameters, use the new ones
    1201         // both channels will use the new authentication parameters
    1202         if (!channels[i].request.url().userInfo().isEmpty()) {
    1203             QUrl url = channels[i].request.url();
    1204             QAuthenticator &auth = channels[i].authenticator;
    1205             if (url.userName() != auth.user()
    1206                 || (!url.password().isEmpty() && url.password() != auth.password())) {
    1207                 auth.setUser(url.userName());
    1208                 auth.setPassword(url.password());
    1209                 copyCredentials(i, &auth, false);
    1210             }
    1211             // clear the userinfo,  since we use the same request for resending
    1212             // userinfo in url can conflict with the one in the authenticator
    1213             url.setUserInfo(QString());
    1214             channels[i].request.setUrl(url);
    1215         }
    1216         createAuthorization(socket, channels[i].request);
    1217 #ifndef QT_NO_NETWORKPROXY
    1218         QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request,
    1219             (networkProxy.type() != QNetworkProxy::NoProxy));
    1220 #else
    1221         QByteArray header = QHttpNetworkRequestPrivate::header(channels[i].request,
    1222             false);
    1223 #endif
    1224         socket->write(header);
    1225         QIODevice *data = channels[i].request.d->data;
    1226         QHttpNetworkReply *reply = channels[i].reply;
    1227         if (reply && reply->d_func()->requestDataBuffer.size())
    1228             data = &channels[i].reply->d_func()->requestDataBuffer;
    1229         if (data && (data->isOpen() || data->open(QIODevice::ReadOnly))) {
    1230             if (data->isSequential()) {
    1231                 channels[i].bytesTotal = -1;
    1232                 QObject::connect(data, SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadNoBuffer()));
    1233                 QObject::connect(data, SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadNoBuffer()));
    1234             } else {
    1235                 channels[i].bytesTotal = data->size();
    1236             }
    1237         } else {
    1238             channels[i].state = WaitingState;
    1239             break;
    1240         }
    1241         // write the initial chunk together with the headers
    1242         // fall through
    1243     }
    1244     case WritingState: { // write the data
    1245         QIODevice *data = channels[i].request.d->data;
    1246         if (channels[i].reply->d_func()->requestDataBuffer.size())
    1247             data = &channels[i].reply->d_func()->requestDataBuffer;
    1248         if (!data || channels[i].bytesTotal == channels[i].written) {
    1249             channels[i].state = WaitingState; // now wait for response
    1250             break;
    1251         }
    1252 
    1253         QByteArray chunk;
    1254         chunk.resize(ChunkSize);
    1255         qint64 readSize = data->read(chunk.data(), ChunkSize);
    1256         if (readSize == -1) {
    1257             // source has reached EOF
    1258             channels[i].state = WaitingState; // now wait for response
    1259         } else if (readSize > 0) {
    1260             // source gave us something useful
    1261             channels[i].written += socket->write(chunk.data(), readSize);
    1262             if (channels[i].reply)
    1263                 emit channels[i].reply->dataSendProgress(channels[i].written, channels[i].bytesTotal);
    1264         }
    1265         break;
    1266     }
    1267     case WaitingState:
    1268     case ReadingState:
    1269     case Wait4AuthState:
    1270         // ignore _q_bytesWritten in these states
    1271         // fall through
    1272     default:
    1273         break;
    1274     }
    1275     return true;
    1276 }
    1277 
    1278 bool QHttpNetworkConnectionPrivate::emitSignals(QHttpNetworkReply *reply)
    1279 {
    1280     // for 401 & 407 don't emit the data signals. Content along with these
    1281     // responses are send only if the authentication fails.
    1282     return (reply && reply->d_func()->statusCode != 401 && reply->d_func()->statusCode != 407);
    1283 }
    1284 
    1285 bool QHttpNetworkConnectionPrivate::expectContent(QHttpNetworkReply *reply)
    1286 {
    1287     // check whether we can expect content after the headers (rfc 2616, sec4.4)
    1288     if (!reply)
    1289         return false;
    1290     if ((reply->d_func()->statusCode >= 100 && reply->d_func()->statusCode < 200)
    1291         || reply->d_func()->statusCode == 204 || reply->d_func()->statusCode == 304)
    1292         return false;
    1293     if (reply->d_func()->request.operation() == QHttpNetworkRequest::Head)
    1294         return !emitSignals(reply);
    1295     if (reply->d_func()->contentLength() == 0)
    1296         return false;
    1297     return true;
    1298 }
     233
     234
    1299235
    1300236void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
     
    1309245        int i = indexOf(socket);
    1310246        // remove the corrupt data if any
    1311         eraseData(channels[i].reply);
    1312         closeChannel(i);
     247        reply->d_func()->eraseData();
     248        channels[i].close();
    1313249        // send the next request
    1314         QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
    1315     }
    1316 }
    1317 
    1318 #ifndef QT_NO_COMPRESS
    1319 bool QHttpNetworkConnectionPrivate::expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete)
    1320 {
    1321     Q_ASSERT(socket);
    1322     Q_ASSERT(reply);
    1323 
    1324     qint64 total = bytesAvailable(*reply, true);
    1325     if (total >= CHUNK || dataComplete) {
    1326         int i = indexOf(socket);
    1327          // uncompress the data
    1328         QByteArray content, inflated;
    1329         read(*reply, content, -1, true);
    1330         int ret = Z_OK;
    1331         if (content.size())
    1332             ret = reply->d_func()->gunzipBodyPartially(content, inflated);
    1333         int retCheck = (dataComplete) ? Z_STREAM_END : Z_OK;
    1334         if (ret >= retCheck) {
    1335             if (inflated.size()) {
    1336                 reply->d_func()->totalProgress += inflated.size();
    1337                 appendData(*reply, inflated, false);
    1338                 if (emitSignals(reply)) {
    1339                     emit reply->readyRead();
    1340                     // make sure that the reply is valid
    1341                     if (channels[i].reply != reply)
    1342                         return true;
    1343                     emit reply->dataReadProgress(reply->d_func()->totalProgress, 0);
    1344                     // make sure that the reply is valid
    1345                     if (channels[i].reply != reply)
    1346                         return true;
    1347 
    1348                 }
    1349             }
    1350         } else {
    1351             emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
    1352             return false;
    1353         }
    1354     }
    1355     return true;
    1356 }
    1357 #endif
    1358 
    1359 void QHttpNetworkConnectionPrivate::receiveReply(QAbstractSocket *socket, QHttpNetworkReply *reply)
    1360 {
    1361     Q_ASSERT(socket);
    1362 
    1363     Q_Q(QHttpNetworkConnection);
    1364     qint64 bytes = 0;
    1365     QAbstractSocket::SocketState state = socket->state();
    1366     int i = indexOf(socket);
    1367 
    1368     // connection might be closed to signal the end of data
    1369     if (state == QAbstractSocket::UnconnectedState) {
    1370         if (!socket->bytesAvailable()) {
    1371             if (reply && reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
    1372                 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
    1373                 channels[i].state = IdleState;
    1374                 allDone(socket, reply);
    1375             } else {
    1376                 // try to reconnect/resend before sending an error.
    1377                 if (channels[i].reconnectAttempts-- > 0) {
    1378                     resendCurrentRequest(socket);
    1379                 } else {
    1380                     reply->d_func()->errorString = errorDetail(QNetworkReply::RemoteHostClosedError, socket);
    1381                     emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
    1382                     QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
    1383                 }
    1384             }
    1385         }
    1386     }
    1387 
    1388     // read loop for the response
    1389     while (socket->bytesAvailable()) {
    1390         QHttpNetworkReplyPrivate::ReplyState state = reply ? reply->d_func()->state : QHttpNetworkReplyPrivate::AllDoneState;
    1391         switch (state) {
    1392         case QHttpNetworkReplyPrivate::NothingDoneState:
    1393         case QHttpNetworkReplyPrivate::ReadingStatusState:
    1394             bytes += reply->d_func()->readStatus(socket);
    1395             channels[i].lastStatus = reply->d_func()->statusCode;
    1396             break;
    1397         case QHttpNetworkReplyPrivate::ReadingHeaderState:
    1398             bytes += reply->d_func()->readHeader(socket);
    1399             if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
    1400                 if (reply->d_func()->isGzipped() && reply->d_func()->autoDecompress) {
    1401                     // remove the Content-Length from header
    1402                     reply->d_func()->removeAutoDecompressHeader();
    1403                 } else {
    1404                     reply->d_func()->autoDecompress = false;
    1405                 }
    1406                 if (reply && reply->d_func()->statusCode == 100) {
    1407                     reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
    1408                     break; // ignore
    1409                 }
    1410                 if (emitSignals(reply))
    1411                     emit reply->headerChanged();
    1412                 if (!expectContent(reply)) {
    1413                     reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
    1414                     channels[i].state = IdleState;
    1415                     allDone(socket, reply);
    1416                     return;
    1417                 }
    1418             }
    1419             break;
    1420         case QHttpNetworkReplyPrivate::ReadingDataState: {
    1421             QBuffer fragment;
    1422             fragment.open(QIODevice::WriteOnly);
    1423             bytes = reply->d_func()->readBody(socket, &fragment);
    1424             if (bytes) {
    1425                 appendData(*reply, fragment.data(), reply->d_func()->autoDecompress);
    1426                 if (!reply->d_func()->autoDecompress) {
    1427                     reply->d_func()->totalProgress += fragment.size();
    1428                     if (emitSignals(reply)) {
    1429                         emit reply->readyRead();
    1430                         // make sure that the reply is valid
    1431                         if (channels[i].reply != reply)
    1432                             return;
    1433                         emit reply->dataReadProgress(reply->d_func()->totalProgress, reply->d_func()->bodyLength);
    1434                         // make sure that the reply is valid
    1435                         if (channels[i].reply != reply)
    1436                             return;
    1437                     }
    1438                 }
    1439 #ifndef QT_NO_COMPRESS
    1440                 else if (!expand(socket, reply, false)) { // expand a chunk if possible
    1441                     return; // ### expand failed
    1442                 }
    1443 #endif
    1444             }
    1445             if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState)
    1446                 break;
    1447             // everything done, fall through
    1448             }
    1449       case QHttpNetworkReplyPrivate::AllDoneState:
    1450             channels[i].state = IdleState;
    1451             allDone(socket, reply);
    1452             break;
    1453         default:
    1454             break;
    1455         }
    1456     }
    1457 }
    1458 
    1459 void QHttpNetworkConnectionPrivate::allDone(QAbstractSocket *socket, QHttpNetworkReply *reply)
    1460 {
    1461 #ifndef QT_NO_COMPRESS
    1462     // expand the whole data.
    1463     if (expectContent(reply) && reply->d_func()->autoDecompress && !reply->d_func()->streamEnd)
    1464         expand(socket, reply, true); // ### if expand returns false, its an error
    1465 #endif
    1466     // while handling 401 & 407, we might reset the status code, so save this.
    1467     bool emitFinished = emitSignals(reply);
    1468     handleStatus(socket, reply);
    1469     // ### at this point there should be no more data on the socket
    1470     // close if server requested
    1471     int i = indexOf(socket);
    1472     if (reply->d_func()->connectionCloseEnabled())
    1473         closeChannel(i);
    1474     // queue the finished signal, this is required since we might send new requests from
    1475     // slot connected to it. The socket will not fire readyRead signal, if we are already
    1476     // in the slot connected to readyRead
    1477     if (emitFinished)
    1478         QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
    1479     // reset the reconnection attempts after we receive a complete reply.
    1480     // in case of failures, each channel will attempt two reconnects before emitting error.
    1481     channels[i].reconnectAttempts = 2;
    1482 }
    1483 
    1484 void QHttpNetworkConnectionPrivate::handleStatus(QAbstractSocket *socket, QHttpNetworkReply *reply)
    1485 {
    1486     Q_ASSERT(socket);
    1487     Q_ASSERT(reply);
    1488 
    1489     Q_Q(QHttpNetworkConnection);
    1490 
    1491     int statusCode = reply->statusCode();
    1492     bool resend = false;
    1493 
    1494     switch (statusCode) {
    1495     case 401:
    1496     case 407:
    1497         handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend);
    1498         if (resend) {
    1499             eraseData(reply);
    1500             sendRequest(socket);
    1501         }
    1502         break;
    1503     default:
    1504250        QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
    1505251    }
     
    1528274
    1529275
     276// handles the authentication for one channel and eventually re-starts the other channels
    1530277bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
    1531278                                                                bool isProxy, bool &resend)
     
    1567314            if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) {
    1568315                // drop the request
    1569                 eraseData(channels[i].reply);
    1570                 closeChannel(i);
     316                reply->d_func()->eraseData();
     317                channels[i].close();
    1571318                channels[i].lastStatus = 0;
    1572                 channels[i].state =  Wait4AuthState;
     319                channels[i].state =  QHttpNetworkConnectionChannel::Wait4AuthState;
    1573320                return false;
    1574321            }
    1575322            // cannot use this socket until the slot returns
    1576             channels[i].state = WaitingState;
     323            channels[i].state = QHttpNetworkConnectionChannel::WaitingState;
    1577324            socket->blockSignals(true);
    1578325            if (!isProxy) {
     
    1589336            socket->blockSignals(false);
    1590337            // socket free to use
    1591             channels[i].state = IdleState;
     338            channels[i].state = QHttpNetworkConnectionChannel::IdleState;
    1592339            if (priv->phase != QAuthenticatorPrivate::Done) {
    1593340                // send any pending requests
    1594341                copyCredentials(i,  auth, isProxy);
    1595                 QMetaObject::invokeMethod(q, "_q_restartPendingRequest", Qt::QueuedConnection);
     342                QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection);
    1596343            }
    1597344        }
     
    1601348            emit channels[i].reply->headerChanged();
    1602349            emit channels[i].reply->readyRead();
    1603             emit channels[i].reply->finished();
    1604350            QNetworkReply::NetworkError errorCode =
    1605351                isProxy
     
    1613359            // remove pending request on the other channels
    1614360            for (int j = 0; j < channelCount; ++j) {
    1615                 if (j != i && channels[j].state ==  Wait4AuthState)
    1616                     channels[j].state = IdleState;
     361                if (j != i && channels[j].state ==  QHttpNetworkConnectionChannel::Wait4AuthState)
     362                    channels[j].state = QHttpNetworkConnectionChannel::IdleState;
    1617363            }
    1618364            return true;
     
    1630376
    1631377    int i = indexOf(socket);
     378
    1632379    if (channels[i].authMehtod != QAuthenticatorPrivate::None) {
    1633380        if (!(channels[i].authMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
     
    1635382            if (priv && priv->method != QAuthenticatorPrivate::None) {
    1636383                QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
    1637                 request.setHeaderField("authorization", response);
     384                request.setHeaderField("Authorization", response);
    1638385            }
    1639386        }
     
    1644391            if (priv && priv->method != QAuthenticatorPrivate::None) {
    1645392                QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
    1646                 request.setHeaderField("proxy-authorization", response);
     393                request.setHeaderField("Proxy-Authorization", response);
    1647394            }
    1648395        }
     
    1673420}
    1674421
    1675 void QHttpNetworkConnectionPrivate::unqueueRequest(QAbstractSocket *socket)
     422void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
     423{
     424    Q_Q(QHttpNetworkConnection);
     425
     426    QHttpNetworkRequest request = pair.first;
     427    switch (request.priority()) {
     428    case QHttpNetworkRequest::HighPriority:
     429        highPriorityQueue.prepend(pair);
     430        break;
     431    case QHttpNetworkRequest::NormalPriority:
     432    case QHttpNetworkRequest::LowPriority:
     433        lowPriorityQueue.prepend(pair);
     434        break;
     435    }
     436    QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
     437}
     438
     439void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket)
    1676440{
    1677441    Q_ASSERT(socket);
     
    1680444
    1681445    if (!highPriorityQueue.isEmpty()) {
    1682         for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
    1683             HttpMessagePair &messagePair = highPriorityQueue[j];
    1684             if (!messagePair.second->d_func()->requestIsPrepared)
    1685                 prepareRequest(messagePair);
    1686             if (!messagePair.second->d_func()->requestIsBuffering) {
    1687                 channels[i].request = messagePair.first;
    1688                 channels[i].reply = messagePair.second;
    1689                 sendRequest(socket);
    1690                 highPriorityQueue.removeAt(j);
    1691                 return;
    1692             }
    1693         }
     446        // remove from queue before sendRequest! else we might pipeline the same request again
     447        HttpMessagePair messagePair = highPriorityQueue.takeLast();
     448        if (!messagePair.second->d_func()->requestIsPrepared)
     449            prepareRequest(messagePair);
     450        channels[i].request = messagePair.first;
     451        channels[i].reply = messagePair.second;
     452        channels[i].sendRequest();
     453        return;
    1694454    }
    1695455
    1696456    if (!lowPriorityQueue.isEmpty()) {
    1697         for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
    1698             HttpMessagePair &messagePair = lowPriorityQueue[j];
    1699             if (!messagePair.second->d_func()->requestIsPrepared)
    1700                 prepareRequest(messagePair);
    1701             if (!messagePair.second->d_func()->requestIsBuffering) {
    1702                 channels[i].request = messagePair.first;
    1703                 channels[i].reply = messagePair.second;
    1704                 sendRequest(socket);
    1705                 lowPriorityQueue.removeAt(j);
    1706                 return;
    1707             }
    1708         }
    1709     }
    1710 }
    1711 
    1712 void QHttpNetworkConnectionPrivate::closeChannel(int channel)
    1713 {
    1714     QAbstractSocket *socket = channels[channel].socket;
    1715     socket->blockSignals(true);
    1716     socket->close();
    1717     socket->blockSignals(false);
    1718     channels[channel].state = IdleState;
    1719 }
    1720 
    1721 void QHttpNetworkConnectionPrivate::resendCurrentRequest(QAbstractSocket *socket)
    1722 {
    1723     Q_Q(QHttpNetworkConnection);
    1724     Q_ASSERT(socket);
     457        // remove from queue before sendRequest! else we might pipeline the same request again
     458        HttpMessagePair messagePair = lowPriorityQueue.takeLast();
     459        if (!messagePair.second->d_func()->requestIsPrepared)
     460            prepareRequest(messagePair);
     461        channels[i].request = messagePair.first;
     462        channels[i].reply = messagePair.second;
     463        channels[i].sendRequest();
     464        return;
     465    }
     466}
     467
     468// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
     469void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
     470{
     471    // return fast if there is nothing to pipeline
     472    if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
     473        return;
     474
    1725475    int i = indexOf(socket);
    1726     closeChannel(i);
    1727     channels[i].resendCurrent = true;
    1728     QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
    1729 }
     476
     477    bool highPriorityQueueProcessingDone = false;
     478    bool lowPriorityQueueProcessingDone = false;
     479
     480    while (!highPriorityQueueProcessingDone && !lowPriorityQueueProcessingDone) {
     481        // this loop runs once per request we intend to pipeline in.
     482
     483        if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
     484            return;
     485
     486        // the current request that is in must already support pipelining
     487        if (!channels[i].request.isPipeliningAllowed())
     488            return;
     489
     490        // the current request must be a idempotent (right now we only check GET)
     491        if (channels[i].request.operation() != QHttpNetworkRequest::Get)
     492            return;
     493
     494        // check if socket is connected
     495        if (socket->state() != QAbstractSocket::ConnectedState)
     496            return;
     497
     498        // check for resendCurrent
     499        if (channels[i].resendCurrent)
     500            return;
     501
     502        // we do not like authentication stuff
     503        // ### make sure to be OK with this in later releases
     504        if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty())
     505            return;
     506        if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty())
     507            return;
     508
     509        // check for pipeline length
     510        if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
     511            return;
     512
     513        // must be in ReadingState or WaitingState
     514        if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
     515               || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
     516            return;
     517
     518        highPriorityQueueProcessingDone = fillPipeline(highPriorityQueue, channels[i]);
     519        // not finished with highPriorityQueue? then loop again
     520        if (!highPriorityQueueProcessingDone)
     521            continue;
     522        // highPriorityQueue was processed, now deal with the lowPriorityQueue
     523        lowPriorityQueueProcessingDone = fillPipeline(lowPriorityQueue, channels[i]);
     524    }
     525}
     526
     527// returns true when the processing of a queue has been done
     528bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
     529{
     530    if (queue.isEmpty())
     531        return true;
     532
     533    for (int i = queue.count() - 1; i >= 0; --i) {
     534        HttpMessagePair messagePair = queue.at(i);
     535        const QHttpNetworkRequest &request = messagePair.first;
     536
     537        // we currently do not support pipelining if HTTP authentication is used
     538        if (!request.url().userInfo().isEmpty())
     539            continue;
     540
     541        // take only GET requests
     542        if (request.operation() != QHttpNetworkRequest::Get)
     543            continue;
     544
     545        if (!request.isPipeliningAllowed())
     546            continue;
     547
     548        // remove it from the queue
     549        queue.takeAt(i);
     550        // we modify the queue we iterate over here, but since we return from the function
     551        // afterwards this is fine.
     552
     553        // actually send it
     554        if (!messagePair.second->d_func()->requestIsPrepared)
     555            prepareRequest(messagePair);
     556        channel.pipelineInto(messagePair);
     557
     558        // return false because we processed something and need to process again
     559        return false;
     560    }
     561
     562    // return true, the queue has been processed and not changed
     563    return true;
     564}
     565
    1730566
    1731567QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket)
     
    1771607}
    1772608
     609// this is called from the destructor of QHttpNetworkReply. It is called when
     610// the reply was finished correctly or when it was aborted.
    1773611void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
    1774612{
    1775613    Q_Q(QHttpNetworkConnection);
    1776614
    1777     // remove the from active list.
     615    // check if the reply is currently being processed or it is pipelined in
    1778616    for (int i = 0; i < channelCount; ++i) {
     617        // is the reply associated the currently processing of this channel?
    1779618        if (channels[i].reply == reply) {
    1780619            channels[i].reply = 0;
    1781             closeChannel(i);
     620
     621            if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
     622                // the reply had to be prematurely removed, e.g. it was not finished
     623                // therefore we have to requeue the already pipelined requests.
     624                channels[i].requeueCurrentlyPipelinedRequests();
     625            }
     626
     627            // if HTTP mandates we should close
     628            // or the reply is not finished yet, e.g. it was aborted
     629            // we have to close that connection
     630            if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
     631                channels[i].close();
     632
    1782633            QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
    1783634            return;
     635        }
     636
     637        // is the reply inside the pipeline of this channel already?
     638        for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
     639            if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
     640               // Remove that HttpMessagePair
     641               channels[i].alreadyPipelinedRequests.removeAt(j);
     642
     643               channels[i].requeueCurrentlyPipelinedRequests();
     644
     645               // Since some requests had already been pipelined, but we removed
     646               // one and re-queued the others
     647               // we must force a connection close after the request that is
     648               // currently in processing has been finished.
     649               if (channels[i].reply)
     650                   channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
     651
     652               QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
     653               return;
     654            }
    1784655        }
    1785656    }
     
    1809680
    1810681
    1811 //private slots
    1812 void QHttpNetworkConnectionPrivate::_q_readyRead()
    1813 {
    1814     Q_Q(QHttpNetworkConnection);
    1815     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    1816     if (!socket)
    1817         return; // ### error
    1818     if (isSocketWaiting(socket) || isSocketReading(socket)) {
    1819         int i = indexOf(socket);
    1820         channels[i].state = ReadingState;
    1821         if (channels[i].reply)
    1822             receiveReply(socket, channels[i].reply);
    1823     }
    1824     // ### error
    1825 }
    1826 
    1827 void QHttpNetworkConnectionPrivate::_q_bytesWritten(qint64 bytes)
    1828 {
    1829     Q_UNUSED(bytes);
    1830     Q_Q(QHttpNetworkConnection);
    1831     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    1832     if (!socket)
    1833         return; // ### error
    1834     if (isSocketWriting(socket))
    1835         sendRequest(socket);
    1836     // otherwise we do nothing
    1837 }
    1838 
    1839 void QHttpNetworkConnectionPrivate::_q_disconnected()
    1840 {
    1841     Q_Q(QHttpNetworkConnection);
    1842     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    1843     if (!socket)
    1844         return; // ### error
    1845     // read the available data before closing
    1846     int i = indexOf(socket);
    1847     if (isSocketWaiting(socket) || isSocketReading(socket)) {
    1848         channels[i].state = ReadingState;
    1849         if (channels[i].reply)
    1850             receiveReply(socket, channels[i].reply);
    1851     }
    1852     channels[i].state = IdleState;
    1853 }
    1854682
    1855683void QHttpNetworkConnectionPrivate::_q_startNextRequest()
    1856684{
    1857     // send the current request again
    1858     if (channels[0].resendCurrent || channels[1].resendCurrent) {
    1859         int i = channels[0].resendCurrent ? 0:1;
    1860         QAbstractSocket *socket = channels[i].socket;
    1861         channels[i].resendCurrent = false;
    1862         channels[i].state = IdleState;
    1863         if (channels[i].reply)
    1864             sendRequest(socket);
     685    //resend the necessary ones.
     686    for (int i = 0; i < channelCount; ++i) {
     687        if (channels[i].resendCurrent) {
     688            channels[i].resendCurrent = false;
     689            channels[i].state = QHttpNetworkConnectionChannel::IdleState;
     690            if (channels[i].reply)
     691                channels[i].sendRequest();
     692        }
     693    }
     694    QAbstractSocket *socket = 0;
     695    for (int i = 0; i < channelCount; ++i) {
     696        QAbstractSocket *chSocket = channels[i].socket;
     697        // send the request using the idle socket
     698        if (!channels[i].isSocketBusy()) {
     699            socket = chSocket;
     700            break;
     701        }
     702    }
     703
     704    // this socket is free,
     705    if (socket)
     706        dequeueAndSendRequest(socket);
     707
     708    // try to push more into all sockets
     709    // ### FIXME we should move this to the beginning of the function
     710    // as soon as QtWebkit is properly using the pipelining
     711    // (e.g. not for XMLHttpRequest or the first page load)
     712    // ### FIXME we should also divide the requests more even
     713    // on the connected sockets
     714    //tryToFillPipeline(socket);
     715    // return fast if there is nothing to pipeline
     716    if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
    1865717        return;
    1866     }
    1867     // send the request using the idle socket
    1868     QAbstractSocket *socket = channels[0].socket;
    1869     if (isSocketBusy(socket)) {
    1870         socket = (isSocketBusy(channels[1].socket) ? 0 :channels[1].socket);
    1871     }
    1872 
    1873     if (!socket) {
    1874         return; // this will be called after finishing current request.
    1875     }
    1876     unqueueRequest(socket);
    1877 }
    1878 
    1879 void QHttpNetworkConnectionPrivate::_q_restartPendingRequest()
     718    for (int j = 0; j < channelCount; j++)
     719        fillPipeline(channels[j].socket);
     720}
     721
     722void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests()
    1880723{
    1881724    // send the request using the idle socket
    1882725    for (int i = 0 ; i < channelCount; ++i) {
    1883         QAbstractSocket *socket = channels[i].socket;
    1884         if (channels[i].state ==  Wait4AuthState) {
    1885             channels[i].state = IdleState;
     726        if (channels[i].state ==  QHttpNetworkConnectionChannel::Wait4AuthState) {
     727            channels[i].state = QHttpNetworkConnectionChannel::IdleState;
    1886728            if (channels[i].reply)
    1887                 sendRequest(socket);
    1888         }
    1889     }
    1890 }
    1891 
    1892 void QHttpNetworkConnectionPrivate::_q_connected()
    1893 {
    1894     Q_Q(QHttpNetworkConnection);
    1895     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    1896     if (!socket)
    1897         return; // ### error
    1898     int i = indexOf(socket);
    1899     // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
    1900     //channels[i].reconnectAttempts = 2;
    1901     if (!channels[i].pendingEncrypt) {
    1902         channels[i].state = IdleState;
    1903         if (channels[i].reply)
    1904             sendRequest(socket);
    1905         else
    1906             closeChannel(i);
    1907     }
    1908 }
    1909 
    1910 
    1911 void QHttpNetworkConnectionPrivate::_q_error(QAbstractSocket::SocketError socketError)
    1912 {
    1913     Q_Q(QHttpNetworkConnection);
    1914     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    1915     if (!socket)
    1916         return;
    1917     bool send2Reply = false;
    1918     int i = indexOf(socket);
    1919     QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
    1920 
    1921     switch (socketError) {
    1922     case QAbstractSocket::HostNotFoundError:
    1923         errorCode = QNetworkReply::HostNotFoundError;
    1924         break;
    1925     case QAbstractSocket::ConnectionRefusedError:
    1926         errorCode = QNetworkReply::ConnectionRefusedError;
    1927         break;
    1928     case QAbstractSocket::RemoteHostClosedError:
    1929         // try to reconnect/resend before sending an error.
    1930         // while "Reading" the _q_disconnected() will handle this.
    1931         if (channels[i].state != IdleState && channels[i].state != ReadingState) {
    1932             if (channels[i].reconnectAttempts-- > 0) {
    1933                 resendCurrentRequest(socket);
    1934                 return;
    1935             } else {
    1936                 send2Reply = true;
    1937                 errorCode = QNetworkReply::RemoteHostClosedError;
    1938             }
    1939         } else {
    1940             return;
    1941         }
    1942         break;
    1943     case QAbstractSocket::SocketTimeoutError:
    1944         // try to reconnect/resend before sending an error.
    1945         if (channels[i].state == WritingState &&  (channels[i].reconnectAttempts-- > 0)) {
    1946             resendCurrentRequest(socket);
    1947             return;
    1948         }
    1949         send2Reply = true;
    1950         errorCode = QNetworkReply::TimeoutError;
    1951         break;
    1952     case QAbstractSocket::ProxyAuthenticationRequiredError:
    1953         errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
    1954         break;
    1955     case QAbstractSocket::SslHandshakeFailedError:
    1956         errorCode = QNetworkReply::SslHandshakeFailedError;
    1957         break;
    1958     default:
    1959         // all other errors are treated as NetworkError
    1960         errorCode = QNetworkReply::UnknownNetworkError;
    1961         break;
    1962     }
    1963     QPointer<QObject> that = q;
    1964     QString errorString = errorDetail(errorCode, socket);
    1965     if (send2Reply) {
    1966         if (channels[i].reply) {
    1967             channels[i].reply->d_func()->errorString = errorString;
    1968             // this error matters only to this reply
    1969             emit channels[i].reply->finishedWithError(errorCode, errorString);
    1970         }
    1971         // send the next request
    1972         QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
    1973     } else {
    1974         // the failure affects all requests.
    1975         emit q->error(errorCode, errorString);
    1976     }
    1977     if (that) //signals make enter the event loop
    1978         closeChannel(i);
    1979 }
    1980 
    1981 #ifndef QT_NO_NETWORKPROXY
    1982 void QHttpNetworkConnectionPrivate::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
    1983 {
    1984     Q_Q(QHttpNetworkConnection);
    1985     emit q->proxyAuthenticationRequired(proxy, auth, q);
    1986 }
    1987 #endif
    1988 
    1989 void QHttpNetworkConnectionPrivate::_q_dataReadyReadNoBuffer()
    1990 {
    1991     Q_Q(QHttpNetworkConnection);
    1992     // data emitted either readyRead()
    1993     // find out which channel it is for
    1994     QIODevice *sender = qobject_cast<QIODevice *>(q->sender());
    1995 
    1996     // won't match anything if the qobject_cast above failed
    1997     for (int i = 0; i < channelCount; ++i) {
    1998         if (sender == channels[i].request.data()) {
    1999             sendRequest(channels[i].socket);
    2000             break;
    2001         }
    2002     }
    2003 }
    2004 
    2005 void QHttpNetworkConnectionPrivate::_q_dataReadyReadBuffer()
    2006 {
    2007     Q_Q(QHttpNetworkConnection);
    2008     QIODevice *sender = qobject_cast<QIODevice *>(q->sender());
    2009     HttpMessagePair *thePair = 0;
    2010     for (int i = 0; !thePair && i < lowPriorityQueue.size(); ++i)
    2011         if (lowPriorityQueue.at(i).first.data() == sender)
    2012             thePair = &lowPriorityQueue[i];
    2013 
    2014     for (int i = 0; !thePair && i < highPriorityQueue.size(); ++i)
    2015         if (highPriorityQueue.at(i).first.data() == sender)
    2016             thePair = &highPriorityQueue[i];
    2017 
    2018     if (thePair) {
    2019         bufferData(*thePair);
    2020 
    2021         // are we finished buffering?
    2022         if (!thePair->second->d_func()->requestIsBuffering)
    2023             _q_startNextRequest();
    2024     }
    2025 }
    2026 
    2027 void QHttpNetworkConnectionPrivate::bufferData(HttpMessagePair &messagePair)
    2028 {
    2029     Q_Q(QHttpNetworkConnection);
    2030     QHttpNetworkRequest &request = messagePair.first;
    2031     QHttpNetworkReply *reply = messagePair.second;
    2032     Q_ASSERT(request.data());
    2033     if (!reply->d_func()->requestIsBuffering) { // first time
    2034         QObject::connect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer()));
    2035         QObject::connect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer()));
    2036         reply->d_func()->requestIsBuffering = true;
    2037         reply->d_func()->requestDataBuffer.open(QIODevice::ReadWrite);
    2038     }
    2039 
    2040     // always try to read at least one byte
    2041     // ### FIXME! use a QRingBuffer
    2042     qint64 bytesToRead = qMax<qint64>(1, request.data()->bytesAvailable());
    2043     QByteArray newData;
    2044     newData.resize(bytesToRead);
    2045     qint64 bytesActuallyRead = request.data()->read(newData.data(), bytesToRead);
    2046 
    2047     if (bytesActuallyRead > 0) {
    2048         // we read something
    2049         newData.chop(bytesToRead - bytesActuallyRead);
    2050         reply->d_func()->requestDataBuffer.write(newData);
    2051     } else if (bytesActuallyRead == -1) { // last time
    2052         QObject::disconnect(request.data(), SIGNAL(readyRead()), q, SLOT(_q_dataReadyReadBuffer()));
    2053         QObject::disconnect(request.data(), SIGNAL(readChannelFinished()), q, SLOT(_q_dataReadyReadBuffer()));
    2054 
    2055         request.setContentLength(reply->d_func()->requestDataBuffer.size());
    2056         reply->d_func()->requestDataBuffer.seek(0);
    2057         reply->d_func()->requestIsBuffering = false;
    2058     }
    2059 }
    2060 
    2061 // QHttpNetworkConnection
     729                channels[i].sendRequest();
     730        }
     731    }
     732}
     733
    2062734
    2063735QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
    2064736    : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
     737{
     738    Q_D(QHttpNetworkConnection);
     739    d->init();
     740}
     741
     742QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
     743     : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
    2065744{
    2066745    Q_D(QHttpNetworkConnection);
     
    2151830#endif
    2152831
    2153 // QHttpNetworkRequest
    2154 
    2155 QHttpNetworkRequest::QHttpNetworkRequest(const QUrl &url, Operation operation, Priority priority)
    2156     : d(new QHttpNetworkRequestPrivate(operation, priority, url))
    2157 {
    2158 }
    2159 
    2160 QHttpNetworkRequest::QHttpNetworkRequest(const QHttpNetworkRequest &other)
    2161     : QHttpNetworkHeader(other), d(other.d)
    2162 {
    2163 }
    2164 
    2165 QHttpNetworkRequest::~QHttpNetworkRequest()
    2166 {
    2167 }
    2168 
    2169 QUrl QHttpNetworkRequest::url() const
    2170 {
    2171     return d->url;
    2172 }
    2173 void QHttpNetworkRequest::setUrl(const QUrl &url)
    2174 {
    2175     d->url = url;
    2176 }
    2177 
    2178 qint64 QHttpNetworkRequest::contentLength() const
    2179 {
    2180     return d->contentLength();
    2181 }
    2182 
    2183 void QHttpNetworkRequest::setContentLength(qint64 length)
    2184 {
    2185     d->setContentLength(length);
    2186 }
    2187 
    2188 QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
    2189 {
    2190     return d->fields;
    2191 }
    2192 
    2193 QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
    2194 {
    2195     return d->headerField(name, defaultValue);
    2196 }
    2197 
    2198 void QHttpNetworkRequest::setHeaderField(const QByteArray &name, const QByteArray &data)
    2199 {
    2200     d->setHeaderField(name, data);
    2201 }
    2202 
    2203 QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
    2204 {
    2205     d = other.d;
    2206     return *this;
    2207 }
    2208 
    2209 bool QHttpNetworkRequest::operator==(const QHttpNetworkRequest &other) const
    2210 {
    2211     return d->operator==(*other.d);
    2212 }
    2213 
    2214 QHttpNetworkRequest::Operation QHttpNetworkRequest::operation() const
    2215 {
    2216     return d->operation;
    2217 }
    2218 
    2219 void QHttpNetworkRequest::setOperation(Operation operation)
    2220 {
    2221     d->operation = operation;
    2222 }
    2223 
    2224 QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const
    2225 {
    2226     return d->priority;
    2227 }
    2228 
    2229 void QHttpNetworkRequest::setPriority(Priority priority)
    2230 {
    2231     d->priority = priority;
    2232 }
    2233 
    2234 QIODevice *QHttpNetworkRequest::data() const
    2235 {
    2236     return d->data;
    2237 }
    2238 
    2239 void QHttpNetworkRequest::setData(QIODevice *data)
    2240 {
    2241     d->data = data;
    2242 }
    2243 
    2244 int QHttpNetworkRequest::majorVersion() const
    2245 {
    2246     return 1;
    2247 }
    2248 
    2249 int QHttpNetworkRequest::minorVersion() const
    2250 {
    2251     return 1;
    2252 }
    2253 
    2254 // QHttpNetworkReply
    2255 
    2256 QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
    2257     : QObject(*new QHttpNetworkReplyPrivate(url), parent)
    2258 {
    2259 }
    2260 
    2261 QHttpNetworkReply::~QHttpNetworkReply()
    2262 {
    2263     Q_D(QHttpNetworkReply);
    2264     if (d->connection) {
    2265         d->connection->d_func()->removeReply(this);
    2266     }
    2267 }
    2268 
    2269 QUrl QHttpNetworkReply::url() const
    2270 {
    2271     return d_func()->url;
    2272 }
    2273 void QHttpNetworkReply::setUrl(const QUrl &url)
    2274 {
    2275     Q_D(QHttpNetworkReply);
    2276     d->url = url;
    2277 }
    2278 
    2279 qint64 QHttpNetworkReply::contentLength() const
    2280 {
    2281     return d_func()->contentLength();
    2282 }
    2283 
    2284 void QHttpNetworkReply::setContentLength(qint64 length)
    2285 {
    2286     Q_D(QHttpNetworkReply);
    2287     d->setContentLength(length);
    2288 }
    2289 
    2290 QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
    2291 {
    2292     return d_func()->fields;
    2293 }
    2294 
    2295 QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
    2296 {
    2297     return d_func()->headerField(name, defaultValue);
    2298 }
    2299 
    2300 void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
    2301 {
    2302     Q_D(QHttpNetworkReply);
    2303     d->setHeaderField(name, data);
    2304 }
    2305 
    2306 void QHttpNetworkReply::parseHeader(const QByteArray &header)
    2307 {
    2308     Q_D(QHttpNetworkReply);
    2309     d->parseHeader(header);
    2310 }
    2311 
    2312 QHttpNetworkRequest QHttpNetworkReply::request() const
    2313 {
    2314     return d_func()->request;
    2315 }
    2316 
    2317 void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
    2318 {
    2319     Q_D(QHttpNetworkReply);
    2320     d->request = request;
    2321 }
    2322 
    2323 int QHttpNetworkReply::statusCode() const
    2324 {
    2325     return d_func()->statusCode;
    2326 }
    2327 
    2328 void QHttpNetworkReply::setStatusCode(int code)
    2329 {
    2330     Q_D(QHttpNetworkReply);
    2331     d->statusCode = code;
    2332 }
    2333 
    2334 QString QHttpNetworkReply::errorString() const
    2335 {
    2336     return d_func()->errorString;
    2337 }
    2338 
    2339 QString QHttpNetworkReply::reasonPhrase() const
    2340 {
    2341     return d_func()->reasonPhrase;
    2342 }
    2343 
    2344 void QHttpNetworkReply::setErrorString(const QString &error)
    2345 {
    2346     Q_D(QHttpNetworkReply);
    2347     d->errorString = error;
    2348 }
    2349 
    2350 int QHttpNetworkReply::majorVersion() const
    2351 {
    2352     return d_func()->majorVersion;
    2353 }
    2354 
    2355 int QHttpNetworkReply::minorVersion() const
    2356 {
    2357     return d_func()->minorVersion;
    2358 }
    2359 
    2360 qint64 QHttpNetworkReply::bytesAvailable() const
    2361 {
    2362     Q_D(const QHttpNetworkReply);
    2363     if (d->connection)
    2364         return d->connection->d_func()->bytesAvailable(*this);
    2365     else
    2366         return -1;
    2367 }
    2368 
    2369 QByteArray QHttpNetworkReply::read(qint64 maxSize)
    2370 {
    2371     Q_D(QHttpNetworkReply);
    2372     QByteArray data;
    2373     if (d->connection)
    2374         d->connection->d_func()->read(*this, data, maxSize, false);
    2375     return data;
    2376 }
    2377 
    2378 bool QHttpNetworkReply::isFinished() const
    2379 {
    2380     return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
    2381 }
    2382832
    2383833// SSL support below
    2384834#ifndef QT_NO_OPENSSL
    2385 void QHttpNetworkConnectionPrivate::_q_encrypted()
    2386 {
    2387     Q_Q(QHttpNetworkConnection);
    2388     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    2389     if (!socket)
    2390         return; // ### error
    2391     channels[indexOf(socket)].state = IdleState;
    2392     sendRequest(socket);
    2393 }
    2394 
    2395 void QHttpNetworkConnectionPrivate::_q_sslErrors(const QList<QSslError> &errors)
    2396 {
    2397     Q_Q(QHttpNetworkConnection);
    2398     QAbstractSocket *socket = qobject_cast<QAbstractSocket*>(q->sender());
    2399     if (!socket)
    2400         return;
    2401     //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
    2402     emit q->sslErrors(errors);
    2403 }
    2404 
    2405835QSslConfiguration QHttpNetworkConnectionPrivate::sslConfiguration(const QHttpNetworkReply &reply) const
    2406836{
     837    if (!encrypt)
     838        return QSslConfiguration();
     839
    2407840    for (int i = 0; i < channelCount; ++i)
    2408841        if (channels[i].reply == &reply)
     
    2414847{
    2415848    Q_D(QHttpNetworkConnection);
     849    if (!d->encrypt)
     850        return;
     851
    2416852    // set the config on all channels
    2417853    for (int i = 0; i < d->channelCount; ++i)
     
    2422858{
    2423859    Q_D(QHttpNetworkConnection);
     860    if (!d->encrypt)
     861        return;
     862
    2424863    if (channel == -1) { // ignore for all channels
    2425864        for (int i = 0; i < d->channelCount; ++i) {
    2426865            static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
    2427             d->channels[i].ignoreSSLErrors = true;
     866            d->channels[i].ignoreAllSslErrors = true;
    2428867        }
    2429868
    2430869    } else {
    2431870        static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
    2432         d->channels[channel].ignoreSSLErrors = true;
    2433     }
    2434 }
    2435 
    2436 QSslConfiguration QHttpNetworkReply::sslConfiguration() const
    2437 {
    2438     Q_D(const QHttpNetworkReply);
    2439     if (d->connection)
    2440         return d->connection->d_func()->sslConfiguration(*this);
    2441     return QSslConfiguration();
    2442 }
    2443 
    2444 void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
    2445 {
    2446     Q_D(QHttpNetworkReply);
    2447     if (d->connection)
    2448         d->connection->setSslConfiguration(config);
    2449 }
    2450 
    2451 void QHttpNetworkReply::ignoreSslErrors()
    2452 {
    2453     Q_D(QHttpNetworkReply);
    2454     if (d->connection)
    2455         d->connection->ignoreSslErrors();
    2456 }
     871        d->channels[channel].ignoreAllSslErrors = true;
     872    }
     873}
     874
     875void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
     876{
     877    Q_D(QHttpNetworkConnection);
     878    if (!d->encrypt)
     879        return;
     880
     881    if (channel == -1) { // ignore for all channels
     882        for (int i = 0; i < d->channelCount; ++i) {
     883            static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
     884            d->channels[i].ignoreSslErrorsList = errors;
     885        }
     886
     887    } else {
     888        static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
     889        d->channels[channel].ignoreSslErrorsList = errors;
     890    }
     891}
     892
    2457893#endif //QT_NO_OPENSSL
    2458894
     895#ifndef QT_NO_NETWORKPROXY
     896// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
     897// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
     898// e.g. it is for SOCKS proxies which require authentication.
     899void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
     900{
     901    Q_Q(QHttpNetworkConnection);
     902    emit q->proxyAuthenticationRequired(proxy, auth, q);
     903    int i = indexOf(chan->socket);
     904    copyCredentials(i, auth, true);
     905}
     906#endif
     907
    2459908
    2460909QT_END_NAMESPACE
  • trunk/src/network/access/qhttpnetworkconnection_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5656#include <QtNetwork/qnetworkreply.h>
    5757#include <QtNetwork/qabstractsocket.h>
     58
     59#include <private/qobject_p.h>
     60#include <qauthenticator.h>
     61#include <qnetworkproxy.h>
     62#include <qbuffer.h>
     63
     64#include <private/qhttpnetworkheader_p.h>
     65#include <private/qhttpnetworkrequest_p.h>
     66#include <private/qhttpnetworkreply_p.h>
     67
     68#include <private/qhttpnetworkconnectionchannel_p.h>
    5869
    5970#ifndef QT_NO_HTTP
     
    7081class QHttpNetworkRequest;
    7182class QHttpNetworkReply;
     83class QByteArray;
    7284
    7385class QHttpNetworkConnectionPrivate;
     
    7890
    7991    QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
     92    QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = 0);
    8093    ~QHttpNetworkConnection();
    8194
     
    107120    void setSslConfiguration(const QSslConfiguration &config);
    108121    void ignoreSslErrors(int channel = -1);
     122    void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
    109123
    110124Q_SIGNALS:
     
    126140    Q_DISABLE_COPY(QHttpNetworkConnection)
    127141    friend class QHttpNetworkReply;
    128 
    129     Q_PRIVATE_SLOT(d_func(), void _q_bytesWritten(qint64))
    130     Q_PRIVATE_SLOT(d_func(), void _q_readyRead())
    131     Q_PRIVATE_SLOT(d_func(), void _q_disconnected())
     142    friend class QHttpNetworkConnectionChannel;
     143
    132144    Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
    133     Q_PRIVATE_SLOT(d_func(), void _q_restartPendingRequest())
    134     Q_PRIVATE_SLOT(d_func(), void _q_connected())
    135     Q_PRIVATE_SLOT(d_func(), void _q_error(QAbstractSocket::SocketError))
     145    Q_PRIVATE_SLOT(d_func(), void _q_restartAuthPendingRequests())
     146};
     147
     148
     149// private classes
     150typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
     151
     152
     153class QHttpNetworkConnectionPrivate : public QObjectPrivate
     154{
     155    Q_DECLARE_PUBLIC(QHttpNetworkConnection)
     156public:
     157    static const int defaultChannelCount;
     158    static const int defaultPipelineLength;
     159
     160    QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
     161    QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt);
     162    ~QHttpNetworkConnectionPrivate();
     163    void init();
     164
     165    enum { ChunkSize = 4096 };
     166
     167    int indexOf(QAbstractSocket *socket) const;
     168
     169    QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
     170    void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
     171    void dequeueAndSendRequest(QAbstractSocket *socket);
     172    void prepareRequest(HttpMessagePair &request);
     173
     174    void fillPipeline(QAbstractSocket *socket);
     175    bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
     176
     177    void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
     178
     179    // private slots
     180    void _q_startNextRequest(); // send the next request from the queue
     181    void _q_restartAuthPendingRequests(); // send the currently blocked request
     182
     183    void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
     184
     185    QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket);
     186
     187#ifndef QT_NO_COMPRESS
     188    bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
     189#endif
     190    void removeReply(QHttpNetworkReply *reply);
     191
     192    QString hostName;
     193    quint16 port;
     194    bool encrypt;
     195
     196    const int channelCount;
     197    QHttpNetworkConnectionChannel *channels; // parallel connections to the server
     198
     199    bool pendingAuthSignal; // there is an incomplete authentication signal
     200    bool pendingProxyAuthSignal; // there is an incomplete proxy authentication signal
     201
     202    qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
     203    qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
     204
     205
     206    void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
     207    bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
     208
     209
     210#ifndef QT_NO_OPENSSL
     211    QSslConfiguration sslConfiguration(const QHttpNetworkReply &reply) const;
     212#endif
     213
    136214#ifndef QT_NO_NETWORKPROXY
    137     Q_PRIVATE_SLOT(d_func(), void _q_proxyAuthenticationRequired(const QNetworkProxy&, QAuthenticator*))
    138 #endif
    139     Q_PRIVATE_SLOT(d_func(), void _q_dataReadyReadBuffer())
    140     Q_PRIVATE_SLOT(d_func(), void _q_dataReadyReadNoBuffer())
    141 
    142 #ifndef QT_NO_OPENSSL
    143     Q_PRIVATE_SLOT(d_func(), void _q_encrypted())
    144     Q_PRIVATE_SLOT(d_func(), void _q_sslErrors(const QList<QSslError>&))
    145 #endif
     215    QNetworkProxy networkProxy;
     216    void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth);
     217#endif
     218
     219    //The request queues
     220    QList<HttpMessagePair> highPriorityQueue;
     221    QList<HttpMessagePair> lowPriorityQueue;
     222
     223    friend class QHttpNetworkConnectionChannel;
    146224};
    147225
    148 class Q_AUTOTEST_EXPORT QHttpNetworkHeader
    149 {
    150 public:
    151     virtual ~QHttpNetworkHeader() {};
    152     virtual QUrl url() const = 0;
    153     virtual void setUrl(const QUrl &url) = 0;
    154 
    155     virtual int majorVersion() const = 0;
    156     virtual int minorVersion() const = 0;
    157 
    158     virtual qint64 contentLength() const = 0;
    159     virtual void setContentLength(qint64 length) = 0;
    160 
    161     virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
    162     virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
    163     virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
    164 };
    165 
    166 class QHttpNetworkRequestPrivate;
    167 class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader
    168 {
    169 public:
    170     enum Operation {
    171         Options,
    172         Get,
    173         Head,
    174         Post,
    175         Put,
    176         Delete,
    177         Trace,
    178         Connect
    179     };
    180 
    181     enum Priority {
    182         HighPriority,
    183         NormalPriority,
    184         LowPriority
    185     };
    186 
    187     QHttpNetworkRequest(const QUrl &url = QUrl(), Operation operation = Get, Priority priority = NormalPriority);
    188     QHttpNetworkRequest(const QHttpNetworkRequest &other);
    189     virtual ~QHttpNetworkRequest();
    190     QHttpNetworkRequest &operator=(const QHttpNetworkRequest &other);
    191     bool operator==(const QHttpNetworkRequest &other) const;
    192 
    193     QUrl url() const;
    194     void setUrl(const QUrl &url);
    195 
    196     int majorVersion() const;
    197     int minorVersion() const;
    198 
    199     qint64 contentLength() const;
    200     void setContentLength(qint64 length);
    201 
    202     QList<QPair<QByteArray, QByteArray> > header() const;
    203     QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
    204     void setHeaderField(const QByteArray &name, const QByteArray &data);
    205 
    206     Operation operation() const;
    207     void setOperation(Operation operation);
    208 
    209     Priority priority() const;
    210     void setPriority(Priority priority);
    211 
    212     QIODevice *data() const;
    213     void setData(QIODevice *data);
    214 
    215 private:
    216     QSharedDataPointer<QHttpNetworkRequestPrivate> d;
    217     friend class QHttpNetworkRequestPrivate;
    218     friend class QHttpNetworkConnectionPrivate;
    219 };
    220 
    221 class QHttpNetworkReplyPrivate;
    222 class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
    223 {
    224     Q_OBJECT
    225 public:
    226 
    227     explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = 0);
    228     virtual ~QHttpNetworkReply();
    229 
    230     QUrl url() const;
    231     void setUrl(const QUrl &url);
    232 
    233     int majorVersion() const;
    234     int minorVersion() const;
    235 
    236     qint64 contentLength() const;
    237     void setContentLength(qint64 length);
    238 
    239     QList<QPair<QByteArray, QByteArray> > header() const;
    240     QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
    241     void setHeaderField(const QByteArray &name, const QByteArray &data);
    242     void parseHeader(const QByteArray &header); // mainly for testing
    243 
    244     QHttpNetworkRequest request() const;
    245     void setRequest(const QHttpNetworkRequest &request);
    246 
    247     int statusCode() const;
    248     void setStatusCode(int code);
    249 
    250     QString errorString() const;
    251     void setErrorString(const QString &error);
    252 
    253     QString reasonPhrase() const;
    254 
    255     qint64 bytesAvailable() const;
    256     QByteArray read(qint64 maxSize = -1);
    257 
    258     bool isFinished() const;
    259 
    260 #ifndef QT_NO_OPENSSL
    261     QSslConfiguration sslConfiguration() const;
    262     void setSslConfiguration(const QSslConfiguration &config);
    263     void ignoreSslErrors();
    264 
    265 Q_SIGNALS:
    266     void sslErrors(const QList<QSslError> &errors);
    267 #endif
    268 
    269 Q_SIGNALS:
    270     void readyRead();
    271     void finished();
    272     void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
    273     void headerChanged();
    274     void dataReadProgress(int done, int total);
    275     void dataSendProgress(int done, int total);
    276 
    277 private:
    278     Q_DECLARE_PRIVATE(QHttpNetworkReply)
    279     friend class QHttpNetworkConnection;
    280     friend class QHttpNetworkConnectionPrivate;
    281 };
     226
    282227
    283228QT_END_NAMESPACE
    284229
    285 Q_DECLARE_METATYPE(QHttpNetworkRequest)
    286 //Q_DECLARE_METATYPE(QHttpNetworkReply)
    287 
    288230#endif // QT_NO_HTTP
    289231
  • trunk/src/network/access/qnetworkaccessbackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5151#include "qabstractnetworkcache.h"
    5252
     53#include "private/qnoncontiguousbytedevice_p.h"
     54
    5355QT_BEGIN_NAMESPACE
    5456
     
    9193    if (mode == QNetworkRequest::AlwaysCache
    9294        && (op == QNetworkAccessManager::GetOperation
    93         || op == QNetworkAccessManager::HeadOperation))
    94         return new QNetworkAccessCacheBackend;
     95        || op == QNetworkAccessManager::HeadOperation)) {
     96        QNetworkAccessBackend *backend = new QNetworkAccessCacheBackend;
     97        backend->manager = this;
     98        return backend;
     99    }
    95100
    96101    if (!factoryDataShutdown) {
     
    110115}
    111116
     117QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
     118{
     119    QNonContiguousByteDevice* device = 0;
     120
     121    if (reply->outgoingDataBuffer)
     122        device = QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer);
     123    else
     124        device = QNonContiguousByteDeviceFactory::create(reply->outgoingData);
     125
     126    bool bufferDisallowed =
     127            reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
     128                          QVariant(false)) == QVariant(true);
     129    if (bufferDisallowed)
     130        device->disableReset();
     131
     132    // make sure we delete this later
     133    device->setParent(this);
     134
     135    connect(device, SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
     136
     137    return device;
     138}
     139
     140// need to have this function since the reply is a private member variable
     141// and the special backends need to access this.
     142void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
     143{
     144    if (reply->isFinished())
     145        return;
     146    reply->emitUploadProgress(bytesSent, bytesTotal);
     147}
     148
    112149QNetworkAccessBackend::QNetworkAccessBackend()
     150    : manager(0)
     151    , reply(0)
    113152{
    114153}
     
    118157}
    119158
    120 void QNetworkAccessBackend::upstreamReadyRead()
    121 {
    122     // do nothing
    123 }
    124 
    125159void QNetworkAccessBackend::downstreamReadyWrite()
    126160{
     
    135169void QNetworkAccessBackend::ignoreSslErrors()
    136170{
     171    // do nothing
     172}
     173
     174void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
     175{
     176    Q_UNUSED(errors);
    137177    // do nothing
    138178}
     
    172212QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
    173213{
    174     return reply->networkCache; // should be the same as manager->networkCache
     214    if (!manager)
     215        return 0;
     216    return manager->networkCache;
    175217}
    176218
     
    185227}
    186228
    187 qint64 QNetworkAccessBackend::upstreamBytesAvailable() const
    188 {
    189     return reply->writeBuffer.size();
    190 }
    191 
    192 void QNetworkAccessBackend::upstreamBytesConsumed(qint64 count)
    193 {
    194     // remove count bytes from the write buffer
    195     reply->consume(count);
    196 }
    197 
    198 QByteArray QNetworkAccessBackend::readUpstream()
    199 {
    200     // ### this is expensive. Consider making QRingBuffer::peekAll keep the buffer it returns
    201     return reply->writeBuffer.peek(upstreamBytesAvailable());
    202 }
    203 
    204229qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
    205230{
     
    207232}
    208233
    209 qint64 QNetworkAccessBackend::downstreamBytesToConsume() const
    210 {
    211     return reply->writeBuffer.size();
    212 }
    213 
    214 void QNetworkAccessBackend::writeDownstreamData(const QByteArray &data)
    215 {
    216     reply->feed(data);
     234void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
     235{
     236    reply->appendDownstreamData(list);
    217237}
    218238
    219239void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
    220240{
    221     reply->feed(data);
     241    reply->appendDownstreamData(data);
    222242}
    223243
  • trunk/src/network/access/qnetworkaccessbackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    7171class QAbstractNetworkCache;
    7272class QNetworkCacheMetaData;
     73class QNetworkAccessBackendUploadIODevice;
     74class QNonContiguousByteDevice;
    7375
    7476// Should support direct file upload from disk or download to disk.
     
    8789    //
    8890    // - Upstream:
    89     //   Upstream is data that is being written into this connection,
    90     //   from the user. Upstream operates in a "pull" mechanism: the
    91     //   connection will be notified that there is more data available
    92     //   by a call to "upstreamReadyRead". The number of bytes
    93     //   available is given by upstreamBytesAvailable(). A call to
    94     //   readUpstream() always yields the entire upstream buffer. When
    95     //   the connection has processed a certain amount of bytes from
    96     //   that buffer, it should call upstreamBytesConsumed().
     91    //   The upstream uses a QNonContiguousByteDevice provided
     92    //   by the backend. This device emits the usual readyRead()
     93    //   signal when the backend has data available for the connection
     94    //   to write. The different backends can listen on this signal
     95    //   and then pull upload data from the QNonContiguousByteDevice and
     96    //   deal with it.
     97    //
    9798    //
    9899    // - Downstream:
     
    112113    virtual void open() = 0;
    113114    virtual void closeDownstreamChannel() = 0;
    114     virtual void closeUpstreamChannel() = 0;
    115115    virtual bool waitForDownstreamReadyRead(int msecs) = 0;
    116     virtual bool waitForUpstreamBytesWritten(int msecs) = 0;
    117116
    118117    // slot-like:
    119     virtual void upstreamReadyRead();
    120118    virtual void downstreamReadyWrite();
    121119    virtual void copyFinished(QIODevice *);
    122120    virtual void ignoreSslErrors();
     121    virtual void ignoreSslErrors(const QList<QSslError> &errors);
    123122
    124123    virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
     
    156155    void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
    157156
     157    // return true if the QNonContiguousByteDevice of the upload
     158    // data needs to support reset(). Currently needed for HTTP.
     159    // This will possibly enable buffering of the upload data.
     160    virtual bool needsResetableUploadData() { return false; }
     161
    158162protected:
    159     // these functions control the upstream mechanism
    160     // that is, data coming into the backend and out via the connection
    161     qint64 upstreamBytesAvailable() const;
    162     void upstreamBytesConsumed(qint64 count);
    163     QByteArray readUpstream();
     163    // Create the device used for reading the upload data
     164    QNonContiguousByteDevice* createUploadByteDevice();
     165
    164166
    165167    // these functions control the downstream mechanism
    166168    // that is, data that has come via the connection and is going out the backend
    167169    qint64 nextDownstreamBlockSize() const;
    168     qint64 downstreamBytesToConsume() const;
    169     void writeDownstreamData(const QByteArray &data);
     170    void writeDownstreamData(QByteDataBuffer &list);
     171
     172public slots:
     173    // for task 251801, needs to be a slot to be called asynchronously
    170174    void writeDownstreamData(QIODevice *data);
    171175
     
    180184    void redirectionRequested(const QUrl &destination);
    181185    void sslErrors(const QList<QSslError> &errors);
     186    void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
    182187
    183188private:
    184189    friend class QNetworkAccessManager;
    185190    friend class QNetworkAccessManagerPrivate;
     191    friend class QNetworkAccessBackendUploadIODevice;
    186192    QNetworkAccessManagerPrivate *manager;
    187193    QNetworkReplyImplPrivate *reply;
  • trunk/src/network/access/qnetworkaccesscache.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qnetworkaccesscache_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6565class QUrl;
    6666
     67// this class is not about caching files but about
     68// caching objects used by QNetworkAccessManager, e.g. existing TCP connections
     69// or credentials.
    6770class QNetworkAccessCache: public QObject
    6871{
  • trunk/src/network/access/qnetworkaccesscachebackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5353QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
    5454    : QNetworkAccessBackend()
     55    , device(0)
    5556{
    5657}
     
    8687    setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
    8788    setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
     89
     90    // set the raw headers
     91    QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
     92    QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
     93                                                       end = rawHeaders.constEnd();
     94    for ( ; it != end; ++it)
     95        setRawHeader(it->first, it->second);
     96
     97    // handle a possible redirect
     98    QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
     99    if (redirectionTarget.isValid()) {
     100        setAttribute(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
     101        redirectionRequested(redirectionTarget.toUrl());
     102    }
    88103
    89104    // signal we're open
  • trunk/src/network/access/qnetworkaccesscachebackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qnetworkaccessdatabackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    118118            emit metaDataChanged();
    119119
    120             writeDownstreamData(payload);
     120            QByteDataBuffer list;
     121            list.append(payload);
     122            payload.clear(); // important because of implicit sharing!
     123            writeDownstreamData(list);
     124
    121125            finished();
    122126            return;
  • trunk/src/network/access/qnetworkaccessdatabackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qnetworkaccessdebugpipebackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4242#include "qnetworkaccessdebugpipebackend_p.h"
    4343#include "QtCore/qdatastream.h"
     44#include <QCoreApplication>
     45#include "private/qnoncontiguousbytedevice_p.h"
    4446
    4547QT_BEGIN_NAMESPACE
     
    5052    ReadBufferSize = 16384,
    5153    WriteBufferSize = ReadBufferSize
    52 };
    53 
    54 struct QNetworkAccessDebugPipeBackend::DataPacket
    55 {
    56     QList<QPair<QByteArray, QByteArray> > headers;
    57     QByteArray data;
    5854};
    5955
     
    8076
    8177QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
    82     : incomingPacketSize(0), bareProtocol(false)
     78    : bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
     79    hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
    8380{
    8481}
     
    8683QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
    8784{
     85    // this is signals disconnect, not network!
    8886    socket.disconnect(this);    // we're not interested in the signals at this point
    8987}
     
    9391    socket.connectToHost(url().host(), url().port(12345));
    9492    socket.setReadBufferSize(ReadBufferSize);
     93
     94    // socket ready read -> we can push from socket to downstream
    9595    connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
    96     connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
    9796    connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError()));
    9897    connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
     98    connect(&socket, SIGNAL(connected()), SLOT(socketConnected()));
     99    // socket bytes written -> we can push more from upstream to socket
     100    connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
    99101
    100102    bareProtocol = url().queryItemValue(QLatin1String("bare")) == QLatin1String("1");
    101103
    102     if (!bareProtocol) {
    103         // "Handshake":
    104         // send outgoing metadata and the URL being requested
    105         DataPacket packet;
    106         //packet.metaData = request().metaData();
    107         packet.data = url().toEncoded();
    108         send(packet);
    109     }
     104    if (operation() == QNetworkAccessManager::PutOperation) {
     105        uploadByteDevice = createUploadByteDevice();
     106        QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
     107        QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
     108    }
     109}
     110
     111void QNetworkAccessDebugPipeBackend::socketReadyRead()
     112{
     113    pushFromSocketToDownstream();
     114}
     115
     116void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
     117{
     118    pushFromSocketToDownstream();
     119}
     120
     121void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
     122{
     123    pushFromUpstreamToSocket();
     124}
     125
     126void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
     127{
     128    pushFromUpstreamToSocket();
     129}
     130
     131void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
     132{
     133    QByteArray buffer;
     134
     135    if (socket.state() == QAbstractSocket::ConnectingState) {
     136        return;
     137    }
     138
     139    forever {
     140        if (hasDownloadFinished)
     141            return;
     142
     143        buffer.resize(ReadBufferSize);
     144        qint64 haveRead = socket.read(buffer.data(), ReadBufferSize);
     145       
     146        if (haveRead == -1) {
     147            hasDownloadFinished = true;
     148            // this ensures a good last downloadProgress is emitted
     149            setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
     150            possiblyFinish();
     151            break;
     152        } else if (haveRead == 0) {
     153            break;
     154        } else {
     155            // have read something
     156            buffer.resize(haveRead);
     157            bytesDownloaded += haveRead;
     158
     159            QByteDataBuffer list;
     160            list.append(buffer);
     161            buffer.clear(); // important because of implicit sharing!
     162            writeDownstreamData(list);
     163        }
     164    }
     165}
     166
     167void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
     168{
     169    // FIXME
     170    if (operation() == QNetworkAccessManager::PutOperation) {
     171        if (hasUploadFinished)
     172            return;
     173
     174        forever {
     175            if (socket.bytesToWrite() >= WriteBufferSize)
     176                return;
     177
     178            qint64 haveRead;
     179            const char *readPointer = uploadByteDevice->readPointer(WriteBufferSize, haveRead);
     180            if (haveRead == -1) {
     181                // EOF
     182                hasUploadFinished = true;
     183                emitReplyUploadProgress(bytesUploaded, bytesUploaded);
     184                possiblyFinish();
     185                break;
     186            } else if (haveRead == 0 || readPointer == 0) {
     187                // nothing to read right now, we will be called again later
     188                break;
     189            } else {
     190                qint64 haveWritten;
     191                haveWritten = socket.write(readPointer, haveRead);
     192
     193                if (haveWritten < 0) {
     194                    // write error!
     195                    QString msg = QCoreApplication::translate("QNetworkAccessDebugPipeBackend", "Write error writing to %1: %2")
     196                                  .arg(url().toString(), socket.errorString());
     197                    error(QNetworkReply::ProtocolFailure, msg);
     198                    finished();
     199                    return;
     200                } else {
     201                    uploadByteDevice->advanceReadPointer(haveWritten);
     202                    bytesUploaded += haveWritten;
     203                    emitReplyUploadProgress(bytesUploaded, -1);
     204                }
     205
     206                //QCoreApplication::processEvents();
     207
     208            }
     209        }
     210    }
     211}
     212
     213void QNetworkAccessDebugPipeBackend::possiblyFinish()
     214{
     215    if (hasEverythingFinished)
     216        return;
     217    hasEverythingFinished = true;
     218
     219    if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
     220        socket.close();
     221        finished();
     222    } else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
     223        socket.close();
     224        finished();
     225    }
     226
     227
    110228}
    111229
    112230void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
    113231{
    114     if (operation() == QNetworkAccessManager::GetOperation)
    115         socket.disconnectFromHost();
    116 }
    117 
    118 void QNetworkAccessDebugPipeBackend::closeUpstreamChannel()
    119 {
    120     if (operation() == QNetworkAccessManager::PutOperation)
    121         socket.disconnectFromHost();
    122     else if (operation() == QNetworkAccessManager::PostOperation) {
    123         send(DataPacket());
    124     }
    125 }
    126 
    127 bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms)
    128 {
    129     readyReadEmitted = false;
    130     if (socket.bytesAvailable()) {
    131         socketReadyRead();
    132         if (readyReadEmitted)
    133             return true;
    134     }
    135     socket.waitForReadyRead(ms);
    136     return readyReadEmitted;
    137 }
    138 
    139 bool QNetworkAccessDebugPipeBackend::waitForUpstreamBytesWritten(int ms)
    140 {
    141     bytesWrittenEmitted = false;
    142     upstreamReadyRead();
    143     if (bytesWrittenEmitted)
    144         return true;
    145 
    146     socket.waitForBytesWritten(ms);
    147     return bytesWrittenEmitted;
    148 }
    149 
    150 void QNetworkAccessDebugPipeBackend::upstreamReadyRead()
    151 {
    152     int maxWrite = WriteBufferSize - socket.bytesToWrite();
    153     if (maxWrite <= 0)
    154         return;                 // can't write yet, wait for the socket to write
    155 
    156     if (bareProtocol) {
    157         QByteArray data = readUpstream();
    158         if (data.isEmpty())
    159             return;
    160 
    161         socket.write(data);
    162         upstreamBytesConsumed(data.size());
    163         bytesWrittenEmitted = true;
    164         return;
    165     }
    166 
    167     DataPacket packet;
    168     packet.data = readUpstream();
    169     if (packet.data.isEmpty())
    170         return;                 // we'll be called again when there's data
    171     if (packet.data.size() > maxWrite)
    172         packet.data.truncate(maxWrite);
    173 
    174     if (!send(packet)) {
    175         QString msg = QObject::tr("Write error writing to %1: %2")
    176                       .arg(url().toString(), socket.errorString());
    177         error(QNetworkReply::ProtocolFailure, msg);
    178 
    179         finished();
    180         return;
    181     }
    182     upstreamBytesConsumed(packet.data.size());
    183     bytesWrittenEmitted = true;
    184 }
    185 
    186 void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
    187 {
    188     socketReadyRead();
    189 }
    190 
    191 void QNetworkAccessDebugPipeBackend::socketReadyRead()
    192 {
    193     if (bareProtocol) {
    194         qint64 bytesToRead = socket.bytesAvailable();
    195         if (bytesToRead) {
    196             QByteArray buffer;
    197             buffer.resize(bytesToRead);
    198             qint64 bytesRead = socket.read(buffer.data(), bytesToRead);
    199             if (bytesRead < bytesToRead)
    200                 buffer.truncate(bytesRead);
    201             writeDownstreamData(buffer);
    202             readyReadEmitted = true;
    203         }
    204         return;
    205     }
    206 
    207     while (canReceive() &&
    208            (socket.state() == QAbstractSocket::UnconnectedState || nextDownstreamBlockSize())) {
    209         DataPacket packet;
    210         if (receive(packet)) {
    211             if (!packet.headers.isEmpty()) {
    212                 QList<QPair<QByteArray, QByteArray> >::ConstIterator
    213                     it = packet.headers.constBegin(),
    214                     end = packet.headers.constEnd();
    215                 for ( ; it != end; ++it)
    216                     setRawHeader(it->first, it->second);
    217                 metaDataChanged();
    218             }
    219 
    220             if (!packet.data.isEmpty()) {
    221                 writeDownstreamData(packet.data);
    222                 readyReadEmitted = true;
    223             }
    224 
    225             if (packet.headers.isEmpty() && packet.data.isEmpty()) {
    226                 // it's an eof
    227                 socket.close();
    228                 readyReadEmitted = true;
    229             }
    230         } else {
    231             // got an error
    232             QString msg = QObject::tr("Read error reading from %1: %2")
    233                           .arg(url().toString(), socket.errorString());
    234             error(QNetworkReply::ProtocolFailure, msg);
    235 
    236             finished();
    237             return;
    238         }
    239     }
    240 }
    241 
    242 void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
    243 {
    244     upstreamReadyRead();
    245 }
     232    qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
     233    //if (operation() == QNetworkAccessManager::GetOperation)
     234    //    socket.disconnectFromHost();
     235}
     236
    246237
    247238void QNetworkAccessDebugPipeBackend::socketError()
    248239{
     240    qWarning("QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
    249241    QNetworkReply::NetworkError code;
    250242    switch (socket.error()) {
     
    270262void QNetworkAccessDebugPipeBackend::socketDisconnected()
    271263{
    272     socketReadyRead();
    273     if (incomingPacketSize == 0 && socket.bytesToWrite() == 0) {
     264    pushFromSocketToDownstream();
     265
     266    if (socket.bytesToWrite() == 0) {
    274267        // normal close
    275         finished();
    276268    } else {
    277269        // abnormal close
     
    279271                             .arg(url().toString());
    280272        error(QNetworkReply::RemoteHostClosedError, msg);
    281 
    282273        finished();
    283274    }
    284275}
    285276
    286 bool QNetworkAccessDebugPipeBackend::send(const DataPacket &packet)
    287 {
    288     QByteArray ba;
    289     {
    290         QDataStream stream(&ba, QIODevice::WriteOnly);
    291         stream.setVersion(QDataStream::Qt_4_4);
    292 
    293         stream << packet.headers << packet.data;
    294     }
    295 
    296     qint32 outgoingPacketSize = ba.size();
    297     qint64 written = socket.write((const char*)&outgoingPacketSize, sizeof outgoingPacketSize);
    298     written += socket.write(ba);
    299     return quint64(written) == (outgoingPacketSize + sizeof outgoingPacketSize);
    300 }
    301 
    302 bool QNetworkAccessDebugPipeBackend::receive(DataPacket &packet)
    303 {
    304     if (!canReceive())
    305         return false;
    306 
    307     // canReceive() does the setting up for us
    308     Q_ASSERT(socket.bytesAvailable() >= incomingPacketSize);
    309     QByteArray incomingPacket = socket.read(incomingPacketSize);
    310     QDataStream stream(&incomingPacket, QIODevice::ReadOnly);
    311     stream.setVersion(QDataStream::Qt_4_4);
    312     stream >> packet.headers >> packet.data;
    313 
    314     // reset for next packet:
    315     incomingPacketSize = 0;
    316     socket.setReadBufferSize(ReadBufferSize);
    317     return true;
    318 }
    319 
    320 bool QNetworkAccessDebugPipeBackend::canReceive()
    321 {
    322     if (incomingPacketSize == 0) {
    323         // read the packet size
    324         if (quint64(socket.bytesAvailable()) >= sizeof incomingPacketSize)
    325             socket.read((char*)&incomingPacketSize, sizeof incomingPacketSize);
    326         else
    327             return false;
    328     }
    329 
    330     if (incomingPacketSize == 0) {
    331         QString msg = QObject::tr("Protocol error: packet of size 0 received");
    332         error(QNetworkReply::ProtocolFailure, msg);
    333         finished();
    334 
    335         socket.blockSignals(true);
    336         socket.abort();
    337         socket.blockSignals(false);
    338         return false;
    339     }
    340 
    341     return socket.bytesAvailable() >= incomingPacketSize;
     277void QNetworkAccessDebugPipeBackend::socketConnected()
     278{
     279}
     280
     281bool QNetworkAccessDebugPipeBackend::waitForDownstreamReadyRead(int ms)
     282{
     283    Q_UNUSED(ms);
     284    qCritical("QNetworkAccess: Debug pipe backend does not support waitForReadyRead()");
     285    return false;
    342286}
    343287
  • trunk/src/network/access/qnetworkaccessdebugpipebackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6767    Q_OBJECT
    6868public:
    69     struct DataPacket;
    7069    QNetworkAccessDebugPipeBackend();
    7170    virtual ~QNetworkAccessDebugPipeBackend();
     
    7372    virtual void open();
    7473    virtual void closeDownstreamChannel();
    75     virtual void closeUpstreamChannel();
    7674    virtual bool waitForDownstreamReadyRead(int msecs);
    77     virtual bool waitForUpstreamBytesWritten(int msecs);
    7875
    79     virtual void upstreamReadyRead();
    8076    virtual void downstreamReadyWrite();
    8177
     78protected:
     79    void pushFromSocketToDownstream();
     80    void pushFromUpstreamToSocket();
     81    void possiblyFinish();
     82    QNonContiguousByteDevice *uploadByteDevice;
     83
    8284private slots:
     85    void uploadReadyReadSlot();
    8386    void socketReadyRead();
    8487    void socketBytesWritten(qint64 bytes);
    8588    void socketError();
    8689    void socketDisconnected();
     90    void socketConnected();
    8791
    8892private:
    8993    QTcpSocket socket;
    90     qint32 incomingPacketSize;
    91     bool readyReadEmitted;
    92     bool bytesWrittenEmitted;
    9394    bool bareProtocol;
     95    bool hasUploadFinished;
     96    bool hasDownloadFinished;
     97    bool hasEverythingFinished;
    9498
    95     bool send(const DataPacket &packet);
    96     bool canReceive();
    97     bool receive(DataPacket &packet);
     99    qint64 bytesDownloaded;
     100    qint64 bytesUploaded;
    98101};
    99102
  • trunk/src/network/access/qnetworkaccessfilebackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4444#include "qurlinfo.h"
    4545#include "qdir.h"
     46#include "private/qnoncontiguousbytedevice_p.h"
    4647
    4748#include <QtCore/QCoreApplication>
     
    7879
    7980QNetworkAccessFileBackend::QNetworkAccessFileBackend()
    80     : totalBytes(0)
     81    : uploadByteDevice(0), totalBytes(0), hasUploadFinished(false)
    8182{
    8283}
     
    109110    if (fileName.isEmpty()) {
    110111        if (url.scheme() == QLatin1String("qrc"))
    111             fileName = QLatin1String(":") + url.path();
     112            fileName = QLatin1Char(':') + url.path();
    112113        else
    113114            fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
     
    127128    case QNetworkAccessManager::PutOperation:
    128129        mode = QIODevice::WriteOnly | QIODevice::Truncate;
     130        uploadByteDevice = createUploadByteDevice();
     131        QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
     132        QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
    129133        break;
    130134    default:
     
    153157}
    154158
     159void QNetworkAccessFileBackend::uploadReadyReadSlot()
     160{
     161    if (hasUploadFinished)
     162        return;
     163
     164    forever {
     165        qint64 haveRead;
     166        const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
     167        if (haveRead == -1) {
     168            // EOF
     169            hasUploadFinished = true;
     170            file.flush();
     171            file.close();
     172            finished();
     173            break;
     174        } else if (haveRead == 0 || readPointer == 0) {
     175            // nothing to read right now, we will be called again later
     176            break;
     177        } else {
     178            qint64 haveWritten;
     179            haveWritten = file.write(readPointer, haveRead);
     180
     181            if (haveWritten < 0) {
     182                // write error!
     183                QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2")
     184                              .arg(url().toString(), file.errorString());
     185                error(QNetworkReply::ProtocolFailure, msg);
     186
     187                finished();
     188                return;
     189            } else {
     190                uploadByteDevice->advanceReadPointer(haveWritten);
     191            }
     192
     193
     194            file.flush();
     195        }
     196    }
     197}
     198
    155199void QNetworkAccessFileBackend::closeDownstreamChannel()
    156200{
    157201    if (operation() == QNetworkAccessManager::GetOperation) {
    158202        file.close();
    159         //downstreamChannelClosed();
    160     }
    161 }
    162 
    163 void QNetworkAccessFileBackend::closeUpstreamChannel()
    164 {
    165     if (operation() == QNetworkAccessManager::PutOperation) {
    166         file.close();
    167         finished();
    168203    }
    169204}
     
    173208    Q_ASSERT(operation() == QNetworkAccessManager::GetOperation);
    174209    return readMoreFromFile();
    175 }
    176 
    177 bool QNetworkAccessFileBackend::waitForUpstreamBytesWritten(int)
    178 {
    179     Q_ASSERT_X(false, "QNetworkAccessFileBackend::waitForUpstreamBytesWritten",
    180                "This function should never have been called, since there is never anything "
    181                "left to be written!");
    182     return false;
    183 }
    184 
    185 void QNetworkAccessFileBackend::upstreamReadyRead()
    186 {
    187     Q_ASSERT_X(operation() == QNetworkAccessManager::PutOperation, "QNetworkAccessFileBackend",
    188                "We're being told to upload data but operation isn't PUT!");
    189 
    190     // there's more data to be written to the file
    191     while (upstreamBytesAvailable()) {
    192         // write everything and let QFile handle it
    193         int written = file.write(readUpstream());
    194 
    195         if (written < 0) {
    196             // write error!
    197             QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2")
    198                                                     .arg(url().toString(), file.errorString());
    199             error(QNetworkReply::ProtocolFailure, msg);
    200 
    201             finished();
    202             return;
    203         }
    204 
    205         // successful write
    206         file.flush();
    207         upstreamBytesConsumed(written);
    208     }
    209210}
    210211
     
    263264        data.resize(actuallyRead);
    264265        totalBytes += actuallyRead;
    265         writeDownstreamData(data);
     266
     267        QByteDataBuffer list;
     268        list.append(data);
     269        data.clear(); // important because of implicit sharing!
     270        writeDownstreamData(list);
    266271    }
    267272    return true;
  • trunk/src/network/access/qnetworkaccessfilebackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6363class QNetworkAccessFileBackend: public QNetworkAccessBackend
    6464{
     65    Q_OBJECT
    6566public:
    6667    QNetworkAccessFileBackend();
     
    6970    virtual void open();
    7071    virtual void closeDownstreamChannel();
    71     virtual void closeUpstreamChannel();
    7272    virtual bool waitForDownstreamReadyRead(int msecs);
    73     virtual bool waitForUpstreamBytesWritten(int msecs);
    7473
    75     virtual void upstreamReadyRead();
    7674    virtual void downstreamReadyWrite();
    7775
     76public slots:
     77    void uploadReadyReadSlot();
     78protected:
     79    QNonContiguousByteDevice *uploadByteDevice;
    7880private:
    7981    QFile file;
    8082    qint64 totalBytes;
     83    bool hasUploadFinished;
    8184
    8285    bool loadFileInfo();
  • trunk/src/network/access/qnetworkaccessftpbackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4343#include "qnetworkaccessmanager_p.h"
    4444#include "QtNetwork/qauthenticator.h"
     45#include "private/qnoncontiguousbytedevice_p.h"
    4546
    4647#ifndef QT_NO_FTP
     
    8283}
    8384
    84 class QNetworkAccessFtpIODevice: public QIODevice
    85 {
    86     //Q_OBJECT
    87 public:
    88     QNetworkAccessFtpBackend *backend;
    89     bool eof;
    90 
    91     inline QNetworkAccessFtpIODevice(QNetworkAccessFtpBackend *parent)
    92         : QIODevice(parent), backend(parent), eof(false)
    93         { open(ReadOnly); }
    94 
    95     bool isSequential() const { return true; }
    96     bool atEnd() const { return backend->upstreamBytesAvailable() == 0; }
    97 
    98     qint64 bytesAvailable() const { return backend->upstreamBytesAvailable(); }
    99     qint64 bytesToWrite() const { return backend->downstreamBytesToConsume(); }
    100 protected:
    101     qint64 readData(char *data, qint64 maxlen)
    102     {
    103         const QByteArray toSend = backend->readUpstream();
    104         maxlen = qMin<qint64>(maxlen, toSend.size());
    105         if (!maxlen)
    106             return eof ? -1 : 0;
    107 
    108         backend->upstreamBytesConsumed(maxlen);
    109         memcpy(data, toSend.constData(), maxlen);
    110         return maxlen;
    111     }
    112 
    113     qint64 writeData(const char *, qint64)
    114     { return -1; }
    115 
    116     friend class QNetworkAccessFtpBackend;
    117 };
    118 
    119 class QNetworkAccessFtpFtp: public QFtp, public QNetworkAccessCache::CacheableObject
     85class QNetworkAccessCachedFtpConnection: public QFtp, public QNetworkAccessCache::CacheableObject
    12086{
    12187    // Q_OBJECT
    12288public:
    123     QNetworkAccessFtpFtp()
     89    QNetworkAccessCachedFtpConnection()
    12490    {
    12591        setExpires(true);
     
    183149    state = LoggingIn;
    184150
    185     QNetworkAccessCache* cache = QNetworkAccessManagerPrivate::getCache(this);
     151    QNetworkAccessCache* objectCache = QNetworkAccessManagerPrivate::getObjectCache(this);
    186152    QByteArray cacheKey = makeCacheKey(url);
    187     if (!cache->requestEntry(cacheKey, this,
     153    if (!objectCache->requestEntry(cacheKey, this,
    188154                             SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) {
    189         ftp = new QNetworkAccessFtpFtp;
     155        ftp = new QNetworkAccessCachedFtpConnection;
    190156#ifndef QT_NO_NETWORKPROXY
    191157        if (proxy.type() == QNetworkProxy::FtpCachingProxy)
     
    195161        ftp->login(url.userName(), url.password());
    196162
    197         cache->addEntry(cacheKey, ftp);
     163        objectCache->addEntry(cacheKey, ftp);
    198164        ftpConnectionReady(ftp);
    199165    }
    200166
    201     uploadDevice = new QNetworkAccessFtpIODevice(this);
     167    // Put operation
     168    if (operation() == QNetworkAccessManager::PutOperation) {
     169        uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice());
     170        uploadDevice->setParent(this);
     171    }
    202172}
    203173
     
    213183}
    214184
    215 void QNetworkAccessFtpBackend::closeUpstreamChannel()
    216 {
    217     if (operation() == QNetworkAccessManager::PutOperation) {
    218         Q_ASSERT(uploadDevice);
    219         uploadDevice->eof = true;
    220         if (!upstreamBytesAvailable())
    221             emit uploadDevice->readyRead();
    222     }
    223 }
    224 
    225185bool QNetworkAccessFtpBackend::waitForDownstreamReadyRead(int ms)
    226186{
     
    240200}
    241201
    242 bool QNetworkAccessFtpBackend::waitForUpstreamBytesWritten(int ms)
    243 {
    244     Q_UNUSED(ms);
    245     qCritical("QNetworkAccess: FTP backend does not support waitForBytesWritten()");
    246     return false;
    247 }
    248 
    249 void QNetworkAccessFtpBackend::upstreamReadyRead()
    250 {
    251     // uh... how does QFtp operate?
    252 }
    253 
    254202void QNetworkAccessFtpBackend::downstreamReadyWrite()
    255203{
     
    260208void QNetworkAccessFtpBackend::ftpConnectionReady(QNetworkAccessCache::CacheableObject *o)
    261209{
    262     ftp = static_cast<QNetworkAccessFtpFtp *>(o);
     210    ftp = static_cast<QNetworkAccessCachedFtpConnection *>(o);
    263211    connect(ftp, SIGNAL(done(bool)), SLOT(ftpDone()));
    264212    connect(ftp, SIGNAL(rawCommandReply(int,QString)), SLOT(ftpRawCommandReply(int,QString)));
     
    280228
    281229        QByteArray key = makeCacheKey(url());
    282         QNetworkAccessManagerPrivate::getCache(this)->releaseEntry(key);
     230        QNetworkAccessManagerPrivate::getObjectCache(this)->releaseEntry(key);
    283231
    284232        ftp = 0;
     
    331279        // we're not connected, so remove the cache entry:
    332280        QByteArray key = makeCacheKey(url());
    333         QNetworkAccessManagerPrivate::getCache(this)->removeEntry(key);
     281        QNetworkAccessManagerPrivate::getObjectCache(this)->removeEntry(key);
    334282
    335283        disconnect(ftp, 0, this, 0);
     
    408356void QNetworkAccessFtpBackend::ftpReadyRead()
    409357{
    410     writeDownstreamData(ftp->readAll());
     358    QByteArray data = ftp->readAll();
     359    QByteDataBuffer list;
     360    list.append(data);
     361    data.clear(); // important because of implicit sharing!
     362    writeDownstreamData(list);
    411363}
    412364
  • trunk/src/network/access/qnetworkaccessftpbackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6767
    6868class QNetworkAccessFtpIODevice;
    69 class QNetworkAccessFtpFtp;
     69class QNetworkAccessCachedFtpConnection;
    7070
    7171class QNetworkAccessFtpBackend: public QNetworkAccessBackend
     
    8888    virtual void open();
    8989    virtual void closeDownstreamChannel();
    90     virtual void closeUpstreamChannel();
    9190    virtual bool waitForDownstreamReadyRead(int msecs);
    92     virtual bool waitForUpstreamBytesWritten(int msecs);
    9391
    94     virtual void upstreamReadyRead();
    9592    virtual void downstreamReadyWrite();
    9693
     
    105102private:
    106103    friend class QNetworkAccessFtpIODevice;
    107     QPointer<QNetworkAccessFtpFtp> ftp;
    108     QNetworkAccessFtpIODevice *uploadDevice;
     104    QPointer<QNetworkAccessCachedFtpConnection> ftp;
     105    QIODevice *uploadDevice;
    109106    qint64 totalBytes;
    110107    int helpId, sizeId, mdtmId;
  • trunk/src/network/access/qnetworkaccesshttpbackend.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    213213    case QNetworkAccessManager::HeadOperation:
    214214    case QNetworkAccessManager::PutOperation:
     215    case QNetworkAccessManager::DeleteOperation:
    215216        break;
    216217
     
    243244    case 404:               // Not Found
    244245        code = QNetworkReply::ContentNotFoundError;
     246        break;
     247
     248    case 405:               // Method Not Allowed
     249        code = QNetworkReply::ContentOperationNotPermittedError;
    245250        break;
    246251
     
    266271}
    267272
    268 class QNetworkAccessHttpBackendCache: public QHttpNetworkConnection,
     273class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
    269274                                      public QNetworkAccessCache::CacheableObject
    270275{
    271276    // Q_OBJECT
    272277public:
    273     QNetworkAccessHttpBackendCache(const QString &hostName, quint16 port, bool encrypt)
     278    QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
    274279        : QHttpNetworkConnection(hostName, port, encrypt)
    275280    {
     
    287292};
    288293
    289 class QNetworkAccessHttpBackendIODevice: public QIODevice
    290 {
    291     // Q_OBJECT
    292 public:
    293     bool eof;
    294     QNetworkAccessHttpBackendIODevice(QNetworkAccessHttpBackend *parent)
    295         : QIODevice(parent), eof(false)
    296     {
    297         setOpenMode(ReadOnly);
    298     }
    299     bool isSequential() const { return true; }
    300     qint64 bytesAvailable() const
    301     { return static_cast<QNetworkAccessHttpBackend *>(parent())->upstreamBytesAvailable(); }
    302 
    303 protected:
    304     virtual qint64 readData(char *buffer, qint64 maxlen)
    305     {
    306         qint64 ret = static_cast<QNetworkAccessHttpBackend *>(parent())->deviceReadData(buffer, maxlen);
    307         if (!ret && eof)
    308             return -1;
    309         return ret;
    310     }
    311 
    312     virtual qint64 writeData(const char *, qint64)
    313     {
    314         return -1;              // cannot write
    315     }
    316 
    317     friend class QNetworkAccessHttpBackend;
    318 };
    319 
    320294QNetworkAccessHttpBackend::QNetworkAccessHttpBackend()
    321295    : QNetworkAccessBackend(), httpReply(0), http(0), uploadDevice(0)
    322296#ifndef QT_NO_OPENSSL
    323     , pendingSslConfiguration(0), pendingIgnoreSslErrors(false)
     297    , pendingSslConfiguration(0), pendingIgnoreAllSslErrors(false)
    324298#endif
    325299{
     
    338312{
    339313    if (http) {
     314        // This is abut disconnecting signals, not about disconnecting TCP connections
    340315        disconnect(http, 0, this, 0);
    341         QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getCache(this);
     316
     317        // Get the object cache that stores our QHttpNetworkConnection objects
     318        QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this);
    342319        cache->releaseEntry(cacheKey);
    343320    }
    344321
     322    // This is abut disconnecting signals, not about disconnecting TCP connections
    345323    if (httpReply)
    346324        disconnect(httpReply, 0, this, 0);
     
    508486        invalidateCache();
    509487        httpRequest.setOperation(QHttpNetworkRequest::Post);
    510         uploadDevice = new QNetworkAccessHttpBackendIODevice(this);
     488        httpRequest.setUploadByteDevice(createUploadByteDevice());
    511489        break;
    512490
     
    514492        invalidateCache();
    515493        httpRequest.setOperation(QHttpNetworkRequest::Put);
    516         uploadDevice = new QNetworkAccessHttpBackendIODevice(this);
     494        httpRequest.setUploadByteDevice(createUploadByteDevice());
     495        break;
     496
     497    case QNetworkAccessManager::DeleteOperation:
     498        invalidateCache();
     499        httpRequest.setOperation(QHttpNetworkRequest::Delete);
    517500        break;
    518501
     
    521504    }
    522505
    523     httpRequest.setData(uploadDevice);
    524506    httpRequest.setUrl(url());
    525507
     
    529511
    530512    if (loadedFromCache) {
    531         QNetworkAccessBackend::finished();
     513        // commented this out since it will be called later anyway
     514        // by copyFinished()
     515        //QNetworkAccessBackend::finished();
    532516        return;    // no need to send the request! :)
    533517    }
     518
     519    if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
     520        httpRequest.setPipeliningAllowed(true);
    534521
    535522    httpReply = http->sendRequest(httpRequest);
     
    538525    if (pendingSslConfiguration)
    539526        httpReply->setSslConfiguration(*pendingSslConfiguration);
    540     if (pendingIgnoreSslErrors)
     527    if (pendingIgnoreAllSslErrors)
    541528        httpReply->ignoreSslErrors();
     529    httpReply->ignoreSslErrors(pendingIgnoreSslErrorsList);
    542530#endif
    543531
     
    603591    // check if we have an open connection to this host
    604592    cacheKey = makeCacheKey(this, theProxy);
    605     QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getCache(this);
    606     if ((http = static_cast<QNetworkAccessHttpBackendCache *>(cache->requestEntryNow(cacheKey))) == 0) {
     593    QNetworkAccessCache *cache = QNetworkAccessManagerPrivate::getObjectCache(this);
     594    // the http object is actually a QHttpNetworkConnection
     595    http = static_cast<QNetworkAccessCachedHttpConnection *>(cache->requestEntryNow(cacheKey));
     596    if (http == 0) {
    607597        // no entry in cache; create an object
    608         http = new QNetworkAccessHttpBackendCache(url.host(), url.port(), encrypt);
     598        // the http object is actually a QHttpNetworkConnection
     599        http = new QNetworkAccessCachedHttpConnection(url.host(), url.port(), encrypt);
    609600
    610601#ifndef QT_NO_NETWORKPROXY
     
    613604#endif
    614605
     606        // cache the QHttpNetworkConnection corresponding to this cache key
    615607        cache->addEntry(cacheKey, http);
    616608    }
     
    623615{
    624616    // this indicates that the user closed the stream while the reply isn't finished yet
    625 }
    626 
    627 void QNetworkAccessHttpBackend::closeUpstreamChannel()
    628 {
    629     // this indicates that the user finished uploading the data for POST
    630     Q_ASSERT(uploadDevice);
    631     uploadDevice->eof = true;
    632     emit uploadDevice->readChannelFinished();
    633617}
    634618
     
    652636}
    653637
    654 bool QNetworkAccessHttpBackend::waitForUpstreamBytesWritten(int msecs)
    655 {
    656 
    657     // ### FIXME: not implemented in QHttpNetworkAccess
    658     Q_UNUSED(msecs);
    659     qCritical("QNetworkAccess: HTTP backend does not support waitForBytesWritten()");
    660     return false;
    661 }
    662 
    663 void QNetworkAccessHttpBackend::upstreamReadyRead()
    664 {
    665     // There is more data available from the user to be uploaded
    666     // QHttpNetworkAccess implements the upload rate control:
    667     //  we simply tell QHttpNetworkAccess that there is more data available
    668     //  it'll pull from us when it can (through uploadDevice)
    669 
    670     Q_ASSERT(uploadDevice);
    671     emit uploadDevice->readyRead();
    672 }
    673 
    674 qint64 QNetworkAccessHttpBackend::deviceReadData(char *buffer, qint64 maxlen)
    675 {
    676     QByteArray toBeUploaded = readUpstream();
    677     if (toBeUploaded.isEmpty())
    678         return 0;               // nothing to be uploaded
    679 
    680     maxlen = qMin<qint64>(maxlen, toBeUploaded.length());
    681 
    682     memcpy(buffer, toBeUploaded.constData(), maxlen);
    683     upstreamBytesConsumed(maxlen);
    684     return maxlen;
    685 }
    686638
    687639void QNetworkAccessHttpBackend::downstreamReadyWrite()
     
    702654        return;
    703655
    704     // We implement the download rate control
    705     // Don't read from QHttpNetworkAccess more than QNetworkAccessBackend wants
    706     // One of the two functions above will be called when we can read again
    707 
    708     qint64 bytesToRead = qBound<qint64>(0, httpReply->bytesAvailable(), nextDownstreamBlockSize());
    709     if (!bytesToRead)
    710         return;
    711 
    712     QByteArray data = httpReply->read(bytesToRead);
    713     writeDownstreamData(data);
     656    // We read possibly more than nextDownstreamBlockSize(), but
     657    // this is not a critical thing since it is already in the
     658    // memory anyway
     659
     660    QByteDataBuffer list;
     661
     662    while (httpReply->bytesAvailable() != 0 && nextDownstreamBlockSize() != 0 && nextDownstreamBlockSize() > list.byteAmount()) {
     663        QByteArray data = httpReply->readAny();
     664        list.append(data);
     665    }
     666
     667    if (!list.isEmpty())
     668      writeDownstreamData(list);
    714669}
    715670
     
    733688    // once we call finished(), we won't have access to httpReply anymore
    734689    QSslConfiguration sslConfig = httpReply->sslConfiguration();
    735     if (pendingSslConfiguration)
     690    if (pendingSslConfiguration) {
    736691        *pendingSslConfiguration = sslConfig;
    737     else if (!sslConfig.isNull())
    738         pendingSslConfiguration = new QSslConfiguration(sslConfig);
     692    } else if (!sslConfig.isNull()) {
     693        QT_TRY {
     694            pendingSslConfiguration = new QSslConfiguration(sslConfig);
     695        } QT_CATCH(...) {
     696            qWarning("QNetworkAccess: could not allocate a QSslConfiguration object for a SSL connection.");
     697        }
     698    }
    739699#endif
    740700
     
    762722void QNetworkAccessHttpBackend::replyHeaderChanged()
    763723{
     724    setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, httpReply->isPipeliningUsed());
     725
    764726    // reconstruct the HTTP header
    765727    QList<QPair<QByteArray, QByteArray> > headerMap = httpReply->header();
     
    771733        QByteArray value = rawHeader(it->first);
    772734        if (!value.isEmpty()) {
    773             if (it->first.toLower() == "set-cookie")
     735            if (qstricmp(it->first.constData(), "set-cookie") == 0)
    774736                value += "\n";
    775737            else
     
    905867    checkForRedirect(status);
    906868
    907     writeDownstreamData(contents);
     869    emit metaDataChanged();
     870
     871    // invoke this asynchronously, else Arora/QtDemoBrowser don't like cached downloads
     872    // see task 250221 / 251801
     873    qRegisterMetaType<QIODevice*>("QIODevice*");
     874    QMetaObject::invokeMethod(this, "writeDownstreamData", Qt::QueuedConnection, Q_ARG(QIODevice*, contents));
     875
     876
    908877#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
    909878    qDebug() << "Successfully sent cache:" << url() << contents->size() << "bytes";
     
    926895        httpReply->ignoreSslErrors();
    927896    else
    928         pendingIgnoreSslErrors = true;
     897        pendingIgnoreAllSslErrors = true;
     898}
     899
     900void QNetworkAccessHttpBackend::ignoreSslErrors(const QList<QSslError> &errors)
     901{
     902    if (httpReply) {
     903        httpReply->ignoreSslErrors(errors);
     904    } else {
     905        // the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
     906        // is called before QNetworkAccessManager::get() (or post(), etc.)
     907        pendingIgnoreSslErrorsList = errors;
     908    }
    929909}
    930910
     
    972952            continue;
    973953
     954        // for 4.6.0, we were planning to not store the date header in the
     955        // cached resource; through that we planned to reduce the number
     956        // of writes to disk when using a QNetworkDiskCache (i.e. don't
     957        // write to disk when only the date changes).
     958        // However, without the date we cannot calculate the age of the page
     959        // anymore. Consider a proper fix of that problem for 4.6.1.
     960        //if (header == "date")
     961            //continue;
     962
    974963        // Don't store Warning 1xx headers
    975964        if (header == "warning") {
     
    10371026        metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second));
    10381027
    1039     bool canDiskCache = true; // Everything defaults to being cacheable on disk
    1040 
    1041     // 14.32
    1042     // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client
    1043     // had sent "Cache-Control: no-cache".
    1044     it = cacheHeaders.findRawHeader("pragma");
    1045     if (it != cacheHeaders.rawHeaders.constEnd()
    1046         && it->second == "no-cache")
     1028    bool canDiskCache;
     1029    // only cache GET replies by default, all other replies (POST, PUT, DELETE)
     1030    //  are not cacheable by default (according to RFC 2616 section 9)
     1031    if (httpReply->request().operation() == QHttpNetworkRequest::Get) {
     1032
     1033        canDiskCache = true;
     1034        // 14.32
     1035        // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the client
     1036        // had sent "Cache-Control: no-cache".
     1037        it = cacheHeaders.findRawHeader("pragma");
     1038        if (it != cacheHeaders.rawHeaders.constEnd()
     1039            && it->second == "no-cache")
     1040            canDiskCache = false;
     1041
     1042        // HTTP/1.1. Check the Cache-Control header
     1043        if (cacheControl.contains("no-cache"))
     1044            canDiskCache = false;
     1045        else if (cacheControl.contains("no-store"))
     1046            canDiskCache = false;
     1047
     1048    // responses to POST might be cacheable
     1049    } else if (httpReply->request().operation() == QHttpNetworkRequest::Post) {
     1050
    10471051        canDiskCache = false;
    1048 
    1049     // HTTP/1.1. Check the Cache-Control header
    1050     if (cacheControl.contains("no-cache"))
     1052        // some pages contain "expires:" and "cache-control: no-cache" field,
     1053        // so we only might cache POST requests if we get "cache-control: max-age ..."
     1054        if (cacheControl.contains("max-age"))
     1055            canDiskCache = true;
     1056
     1057    // responses to PUT and DELETE are not cacheable
     1058    } else {
    10511059        canDiskCache = false;
    1052     else if (cacheControl.contains("no-store"))
    1053         canDiskCache = false;
     1060    }
    10541061
    10551062    metaData.setSaveToDisk(canDiskCache);
  • trunk/src/network/access/qnetworkaccesshttpbackend_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6767QT_BEGIN_NAMESPACE
    6868
    69 class QNetworkAccessHttpBackendCache;
     69class QNetworkAccessCachedHttpConnection;
    7070
    7171class QNetworkAccessHttpBackendIODevice;
     
    8080    virtual void open();
    8181    virtual void closeDownstreamChannel();
    82     virtual void closeUpstreamChannel();
    8382    virtual bool waitForDownstreamReadyRead(int msecs);
    84     virtual bool waitForUpstreamBytesWritten(int msecs);
    8583
    86     virtual void upstreamReadyRead();
    8784    virtual void downstreamReadyWrite();
    8885    virtual void copyFinished(QIODevice *);
    8986#ifndef QT_NO_OPENSSL
    9087    virtual void ignoreSslErrors();
     88    virtual void ignoreSslErrors(const QList<QSslError> &errors);
    9189
    9290    virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
     
    9694
    9795    qint64 deviceReadData(char *buffer, qint64 maxlen);
     96
     97    // we return true since HTTP needs to send PUT/POST data again after having authenticated
     98    bool needsResetableUploadData() { return true; }
    9899
    99100private slots:
     
    107108private:
    108109    QHttpNetworkReply *httpReply;
    109     QPointer<QNetworkAccessHttpBackendCache> http;
     110    QPointer<QNetworkAccessCachedHttpConnection> http;
    110111    QByteArray cacheKey;
    111     QNetworkAccessHttpBackendIODevice *uploadDevice;
     112    QNetworkAccessBackendUploadIODevice *uploadDevice;
     113
    112114#ifndef QT_NO_OPENSSL
    113115    QSslConfiguration *pendingSslConfiguration;
    114     bool pendingIgnoreSslErrors;
     116    bool pendingIgnoreAllSslErrors;
     117    QList<QSslError> pendingIgnoreSslErrorsList;
    115118#endif
    116119
     
    123126    void readFromHttp();
    124127    void checkForRedirect(const int statusCode);
    125 
    126     friend class QNetworkAccessHttpBackendIODevice;
    127128};
    128129
  • trunk/src/network/access/qnetworkaccessmanager.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5353#include "qnetworkaccessdatabackend_p.h"
    5454#include "qnetworkaccessdebugpipebackend_p.h"
     55#include "qfilenetworkreply_p.h"
    5556
    5657#include "QtCore/qbuffer.h"
     
    9697    \class QNetworkAccessManager
    9798    \brief The QNetworkAccessManager class allows the application to
    98     post network requests and receive replies
     99    send network requests and receive replies
    99100    \since 4.4
    100101
     102    \ingroup network
    101103    \inmodule QtNetwork
    102104    \reentrant
     
    113115    QNetworkReply object. The returned object is used to obtain any data
    114116    returned in response to the corresponding request.
    115     the reply to is where most of the signals as well
    116     as the downloaded data are posted.
    117117
    118118    A simple download off the network could be accomplished with:
     
    123123    as well as meta-data (headers, etc.).
    124124
    125     \note The slot is responsible for deleting the object at that point.
     125    \note After the request has finished, it is the responsibility of the user
     126    to delete the QNetworkReply object at an appropriate time. Do not directly
     127    delete it inside the slot connected to finished(). You can use the
     128    deleteLater() function.
    126129
    127130    A more involved example, assuming the manager is already existent,
     
    148151    \value PostOperation        send the contents of an HTML form for
    149152    processing via HTTP POST (created with post())
     153
     154    \value DeleteOperation      delete contents operation (created with
     155    deleteResource())
    150156
    151157    \omitvalue UnknownOperation
     
    200206    See QNetworkReply::finished() for information on the status that
    201207    the object will be in.
     208
     209    \note Do not delete the \a reply object in the slot connected to this
     210    signal. Use deleteLater().
    202211
    203212    \sa QNetworkReply::finished(), QNetworkReply::error()
     
    300309    case QNetworkProxy::FtpCachingProxy:
    301310        key.setScheme(QLatin1String("proxy-ftp"));
     311        break;
    302312
    303313    case QNetworkProxy::DefaultProxy:
     
    478488        delete d->networkCache;
    479489        d->networkCache = cache;
    480         d->networkCache->setParent(this);
     490        if (d->networkCache)
     491            d->networkCache->setParent(this);
    481492    }
    482493}
     
    538549
    539550/*!
    540     This function is used to post a request to obtain the network
    541     headers for \a request. It takes its name after the HTTP request
    542     associated (HEAD). It returns a new QNetworkReply object which
    543     will contain such headers.
     551    Posts a request to obtain the network headers for \a request
     552    and returns a new QNetworkReply object which will contain such headers
     553
     554    The function is named after the HTTP request associated (HEAD).
    544555*/
    545556QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
     
    549560
    550561/*!
    551     This function is used to post a request to obtain the contents of
    552     the target \a request. It will cause the contents to be
    553     downloaded, along with the headers associated with it. It returns
    554     a new QNetworkReply object opened for reading which emits its
    555     QIODevice::readyRead() signal whenever new data arrives.
    556 
    557     \sa post(), put()
     562    Posts a request to obtain the contents of the target \a request
     563    and returns a new QNetworkReply object opened for reading which emits the
     564    \l{QIODevice::readyRead()}{readyRead()} signal whenever new data
     565    arrives.
     566
     567    The contents as well as associated headers will be downloaded.
     568
     569    \sa post(), put(), deleteResource()
    558570*/
    559571QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
     
    563575
    564576/*!
    565     This function is used to send an HTTP POST request to the
    566     destination specified by \a request. The contents of the \a data
     577    Sends an HTTP POST request to the destination specified by \a request
     578    and returns a new QNetworkReply object opened for reading that will
     579    contain the reply sent by the server. The contents of  the \a data
    567580    device will be uploaded to the server.
     581
     582    \a data must be open for reading and must remain valid until the
     583    finished() signal is emitted for this reply.
     584
     585    \note Sending a POST request on protocols other than HTTP and
     586    HTTPS is undefined and will probably fail.
     587
     588    \sa get(), put(), deleteResource()
     589*/
     590QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
     591{
     592    return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data));
     593}
     594
     595/*!
     596    \overload
     597
     598    Sends the contents of the \a data byte array to the destination
     599    specified by \a request.
     600*/
     601QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
     602{
     603    QBuffer *buffer = new QBuffer;
     604    buffer->setData(data);
     605    buffer->open(QIODevice::ReadOnly);
     606
     607    QNetworkReply *reply = post(request, buffer);
     608    buffer->setParent(reply);
     609    return reply;
     610}
     611
     612/*!
     613    Uploads the contents of \a data to the destination \a request and
     614    returnes a new QNetworkReply object that will be open for reply.
    568615
    569616    \a data must be opened for reading when this function is called
     
    571618    this reply.
    572619
    573     The returned QNetworkReply object will be open for reading and
    574     will contain the reply sent by the server to the POST request.
    575 
    576     Note: sending a POST request on protocols other than HTTP and
    577     HTTPS is undefined and will probably fail.
    578 
    579     \sa get(), put()
    580 */
    581 QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
    582 {
    583     return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data));
     620    Whether anything will be available for reading from the returned
     621    object is protocol dependent. For HTTP, the server may send a
     622    small HTML page indicating the upload was successful (or not).
     623    Other protocols will probably have content in their replies.
     624
     625    \note For HTTP, this request will send a PUT request, which most servers
     626    do not allow. Form upload mechanisms, including that of uploading
     627    files through HTML forms, use the POST mechanism.
     628
     629    \sa get(), post()
     630*/
     631QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
     632{
     633    return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data));
    584634}
    585635
    586636/*!
    587637    \overload
    588     This function sends the contents of the \a data byte array to the
    589     destination specified by \a request.
    590 */
    591 QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
     638    Sends the contents of the \a data byte array to the destination
     639    specified by \a request.
     640*/
     641QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
    592642{
    593643    QBuffer *buffer = new QBuffer;
     
    595645    buffer->open(QIODevice::ReadOnly);
    596646
    597     QNetworkReply *reply = post(request, buffer);
    598     buffer->setParent(reply);
    599     return reply;
    600 }
    601 
    602 /*!
    603     This function is used to upload the contents of \a data to the
    604     destination \a request.
    605 
    606     \a data must be opened for reading when this function is called
    607     and must remain valid until the finished() signal is emitted for
    608     this reply.
    609 
    610     The returned QNetworkReply object will be open for reply, but
    611     whether anything will be available for reading is protocol
    612     dependent. For HTTP, the server may send a small HTML page
    613     indicating the upload was successful (or not). Other protocols
    614     will probably have content in their replies.
    615 
    616     For HTTP, this request will send a PUT request, which most servers
    617     do not allow. Form upload mechanisms, including that of uploading
    618     files through HTML forms, use the POST mechanism.
    619 
    620     \sa get(), post()
    621 */
    622 QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
    623 {
    624     return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data));
    625 }
    626 
    627 /*!
    628     \overload
    629     This function sends the contents of the \a data byte array to the
    630     destination specified by \a request.
    631 */
    632 QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
    633 {
    634     QBuffer *buffer = new QBuffer;
    635     buffer->setData(data);
    636     buffer->open(QIODevice::ReadOnly);
    637 
    638647    QNetworkReply *reply = put(request, buffer);
    639648    buffer->setParent(reply);
    640649    return reply;
     650}
     651
     652/*!
     653    \since 4.6
     654
     655    Sends a request to delete the resource identified by the URL of \a request.
     656
     657    \note This feature is currently available for HTTP only, performing an
     658    HTTP DELETE request.
     659
     660    \sa get(), post(), put()
     661*/
     662QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
     663{
     664    return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request));
    641665}
    642666
     
    659683{
    660684    Q_D(QNetworkAccessManager);
     685
     686    // fast path for GET on file:// URLs
     687    // Also if the scheme is empty we consider it a file.
     688    // The QNetworkAccessFileBackend will right now only be used
     689    // for PUT or qrc://
     690    if (op == QNetworkAccessManager::GetOperation
     691         && (req.url().scheme() == QLatin1String("file")
     692             || req.url().scheme().isEmpty())) {
     693        return new QFileNetworkReply(this, req);
     694    }
     695
    661696    QNetworkRequest request = req;
    662697    if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
     
    686721    }
    687722
    688     // third step: setup the reply
     723    // third step: find a backend
     724    priv->backend = d->findBackend(op, request);
     725
     726    // fourth step: setup the reply
    689727    priv->setup(op, request, outgoingData);
    690     if (request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt() !=
    691         QNetworkRequest::AlwaysNetwork)
    692         priv->setNetworkCache(d->networkCache);
    693728#ifndef QT_NO_NETWORKPROXY
    694729    QList<QNetworkProxy> proxyList = d->queryProxy(QNetworkProxyQuery(request.url()));
    695730    priv->proxyList = proxyList;
    696731#endif
    697 
    698     // fourth step: find a backend
    699     priv->backend = d->findBackend(op, request);
    700732    if (priv->backend) {
    701733        priv->backend->setParent(reply);
     
    748780        // keep the ugly hack in here
    749781        QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this);
     782        that->cookieJar = new QNetworkCookieJar(that->q_func());
    750783        that->cookieJarCreated = true;
    751         that->cookieJar = new QNetworkCookieJar(that->q_func());
    752784    }
    753785}
     
    784816{
    785817    Q_Q(QNetworkAccessManager);
    786 
     818    // ### FIXME Tracking of successful authentications
     819    // This code is a bit broken right now for SOCKS authentication
     820    // first request: proxyAuthenticationRequired gets emitted, credentials gets saved
     821    // second request: (proxy != backend->reply->lastProxyAuthentication) does not evaluate to true,
     822    //      proxyAuthenticationRequired gets emitted again
     823    // possible solution: some tracking inside the authenticator
     824    //      or a new function proxyAuthenticationSucceeded(true|false)
    787825    if (proxy != backend->reply->lastProxyAuthentication) {
    788826        QNetworkAuthenticationCredential *cred = fetchCachedCredentials(proxy);
     
    819857            QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
    820858            auth->insert(QString(), authenticator->user(), authenticator->password());
    821             cache.addEntry(cacheKey, auth); // replace the existing one, if there's any
     859            objectCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
    822860
    823861            if (realm.isEmpty()) {
     
    853891    if (cacheKey.isEmpty())
    854892        return 0;
    855     if (!cache.hasEntry(cacheKey))
     893    if (!objectCache.hasEntry(cacheKey))
    856894        return 0;
    857895
    858896    QNetworkAuthenticationCache *auth =
    859         static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey));
     897        static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
    860898    QNetworkAuthenticationCredential *cred = auth->findClosestMatch(QString());
    861     cache.releaseEntry(cacheKey);
     899    objectCache.releaseEntry(cacheKey);
    862900
    863901    // proxy cache credentials always have exactly one item
     
    900938    do {
    901939        QByteArray cacheKey = authenticationKey(copy, realm);
    902         if (cache.hasEntry(cacheKey)) {
     940        if (objectCache.hasEntry(cacheKey)) {
    903941            QNetworkAuthenticationCache *auth =
    904                 static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey));
     942                static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
    905943            auth->insert(domain, authenticator->user(), authenticator->password());
    906             cache.releaseEntry(cacheKey);
     944            objectCache.releaseEntry(cacheKey);
    907945        } else {
    908946            QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
    909947            auth->insert(domain, authenticator->user(), authenticator->password());
    910             cache.addEntry(cacheKey, auth);
     948            objectCache.addEntry(cacheKey, auth);
    911949        }
    912950
     
    942980
    943981    QByteArray cacheKey = authenticationKey(url, realm);
    944     if (!cache.hasEntry(cacheKey))
     982    if (!objectCache.hasEntry(cacheKey))
    945983        return 0;
    946984
    947985    QNetworkAuthenticationCache *auth =
    948         static_cast<QNetworkAuthenticationCache *>(cache.requestEntryNow(cacheKey));
     986        static_cast<QNetworkAuthenticationCache *>(objectCache.requestEntryNow(cacheKey));
    949987    QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
    950     cache.releaseEntry(cacheKey);
     988    objectCache.releaseEntry(cacheKey);
    951989    return cred;
    952990}
     
    954992void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
    955993{
    956     manager->d_func()->cache.clear();
     994    manager->d_func()->objectCache.clear();
     995}
     996
     997QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
     998{
    957999}
    9581000
  • trunk/src/network/access/qnetworkaccessmanager.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    7575        PutOperation,
    7676        PostOperation,
     77        DeleteOperation,
    7778
    7879        UnknownOperation = 0
     
    101102    QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
    102103    QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
     104    QNetworkReply *deleteResource(const QNetworkRequest &request);
    103105
    104106Q_SIGNALS:
  • trunk/src/network/access/qnetworkaccessmanager_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    7777          cookieJarCreated(false)
    7878    { }
     79    ~QNetworkAccessManagerPrivate();
    7980
    8081    void _q_replyFinished();
     
    99100    QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request);
    100101
     102    // this is the cache for storing downloaded files
    101103    QAbstractNetworkCache *networkCache;
     104
    102105    QNetworkCookieJar *cookieJar;
    103106
    104     QNetworkAccessCache cache;
     107
    105108#ifndef QT_NO_NETWORKPROXY
    106109    QNetworkProxy proxy;
     
    110113    bool cookieJarCreated;
    111114
    112     static inline QNetworkAccessCache *getCache(QNetworkAccessBackend *backend)
    113     { return &backend->manager->cache; }
     115
     116    // this cache can be used by individual backends to cache e.g. their TCP connections to a server
     117    // and use the connections for multiple requests.
     118    QNetworkAccessCache objectCache;
     119    static inline QNetworkAccessCache *getObjectCache(QNetworkAccessBackend *backend)
     120    { return &backend->manager->objectCache; }
    114121    Q_AUTOTEST_EXPORT static void clearCache(QNetworkAccessManager *manager);
    115122
  • trunk/src/network/access/qnetworkcookie.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    505505        if (!d->domain.isEmpty()) {
    506506            result += "; domain=";
    507             result += QUrl::toAce(d->domain);
     507            QString domainNoDot = d->domain;
     508            if (domainNoDot.startsWith(QLatin1Char('.'))) {
     509                result += '.';
     510                domainNoDot = domainNoDot.mid(1);
     511            }
     512            result += QUrl::toAce(domainNoDot);
    508513        }
    509514        if (!d->path.isEmpty()) {
     
    914919QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
    915920{
     921    // cookieString can be a number of set-cookie header strings joined together
     922    // by \n, parse each line separately.
     923    QList<QNetworkCookie> cookies;
     924    QList<QByteArray> list = cookieString.split('\n');
     925    for (int a = 0; a < list.size(); a++)
     926        cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
     927    return cookies;
     928}
     929
     930QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
     931{
    916932    // According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
    917933    // the Set-Cookie response header is of the format:
     
    931947        QNetworkCookie cookie;
    932948
    933         // When there are multiple SetCookie headers they are join with a new line
    934         // \n will always be the start of a new cookie
    935         int endOfSetCookie = cookieString.indexOf('\n', position);
    936         if (endOfSetCookie == -1)
    937             endOfSetCookie = length;
    938 
    939949        // The first part is always the "NAME=VALUE" part
    940950        QPair<QByteArray,QByteArray> field = nextField(cookieString, position);
     
    947957        position = nextNonWhitespace(cookieString, position);
    948958        bool endOfCookie = false;
    949         while (!endOfCookie && position < endOfSetCookie)
     959        while (!endOfCookie && position < length) {
    950960            switch (cookieString.at(position++)) {
    951961            case ',':
     
    970980                    QDateTime dt = parseDateString(dateString.toLower());
    971981                    if (!dt.isValid()) {
    972                         cookie = QNetworkCookie();
    973                         endOfCookie = true;
    974                         continue;
     982                        return result;
    975983                    }
    976984                    cookie.setExpirationDate(dt);
     
    989997                    int secs = field.second.toInt(&ok);
    990998                    if (!ok)
    991                         // invalid cookie string
    992                         return QList<QNetworkCookie>();
     999                        return result;
    9931000                    cookie.setExpirationDate(now.addSecs(secs));
    9941001                } else if (field.first == "path") {
     
    10041011                    if (field.second != "1") {
    10051012                        // oops, we don't know how to handle this cookie
    1006                         cookie = QNetworkCookie();
    1007                         endOfCookie = true;
    1008                         continue;
     1013                        return result;
    10091014                    }
    10101015                } else {
     
    10141019
    10151020                position = nextNonWhitespace(cookieString, position);
    1016                 if (position > endOfSetCookie)
    1017                     endOfCookie = true;
    10181021            }
     1022        }
    10191023
    10201024        if (!cookie.name().isEmpty())
     
    10281032QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
    10291033{
    1030     s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ")";
     1034    s.nospace() << "QNetworkCookie(" << cookie.toRawForm(QNetworkCookie::Full) << ')';
    10311035    return s.space();
    10321036}
    10331037#endif
    10341038
    1035 
    1036 
    1037 class QNetworkCookieJarPrivate: public QObjectPrivate
    1038 {
    1039 public:
    1040     QList<QNetworkCookie> allCookies;
    1041 
    1042     Q_DECLARE_PUBLIC(QNetworkCookieJar)
    1043 };
    1044 
    1045 /*!
    1046     \class QNetworkCookieJar
    1047     \brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
    1048     \since 4.4
    1049 
    1050     Cookies are small bits of information that stateless protocols
    1051     like HTTP use to maintain some persistent information across
    1052     requests.
    1053 
    1054     A cookie is set by a remote server when it replies to a request
    1055     and it expects the same cookie to be sent back when further
    1056     requests are sent.
    1057 
    1058     The cookie jar is the object that holds all cookies set in
    1059     previous requests. Web browsers save their cookie jars to disk in
    1060     order to conserve permanent cookies across invocations of the
    1061     application.
    1062 
    1063     QNetworkCookieJar does not implement permanent storage: it only
    1064     keeps the cookies in memory. Once the QNetworkCookieJar object is
    1065     deleted, all cookies it held will be discarded as well. If you
    1066     want to save the cookies, you should derive from this class and
    1067     implement the saving to disk to your own storage format.
    1068 
    1069     This class implements only the basic security recommended by the
    1070     cookie specifications and does not implement any cookie acceptance
    1071     policy (it accepts all cookies set by any requests). In order to
    1072     override those rules, you should reimplement the
    1073     cookiesForUrl() and setCookiesFromUrl() virtual
    1074     functions. They are called by QNetworkReply and
    1075     QNetworkAccessManager when they detect new cookies and when they
    1076     require cookies.
    1077 
    1078     \sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
    1079     QNetworkRequest, QNetworkAccessManager::setCookieJar()
    1080 */
    1081 
    1082 /*!
    1083     Creates a QNetworkCookieJar object and sets the parent object to
    1084     be \a parent.
    1085 
    1086     The cookie jar is initialized to empty.
    1087 */
    1088 QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
    1089     : QObject(*new QNetworkCookieJarPrivate, parent)
    1090 {
    1091 }
    1092 
    1093 /*!
    1094     Destroys this cookie jar object and discards all cookies stored in
    1095     it. Cookies are not saved to disk in the QNetworkCookieJar default
    1096     implementation.
    1097 
    1098     If you need to save the cookies to disk, you have to derive from
    1099     QNetworkCookieJar and save the cookies to disk yourself.
    1100 */
    1101 QNetworkCookieJar::~QNetworkCookieJar()
    1102 {
    1103 }
    1104 
    1105 /*!
    1106     Returns all cookies stored in this cookie jar. This function is
    1107     suitable for derived classes to save cookies to disk, as well as
    1108     to implement cookie expiration and other policies.
    1109 
    1110     \sa setAllCookies(), cookiesForUrl()
    1111 */
    1112 QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
    1113 {
    1114     return d_func()->allCookies;
    1115 }
    1116 
    1117 /*!
    1118     Sets the internal list of cookies held by this cookie jar to be \a
    1119     cookieList. This function is suitable for derived classes to
    1120     implement loading cookies from permanent storage, or their own
    1121     cookie acceptance policies by reimplementing
    1122     setCookiesFromUrl().
    1123 
    1124     \sa allCookies(), setCookiesFromUrl()
    1125 */
    1126 void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
    1127 {
    1128     Q_D(QNetworkCookieJar);
    1129     d->allCookies = cookieList;
    1130 }
    1131 
    1132 static inline bool isParentPath(QString path, QString reference)
    1133 {
    1134     if (!path.endsWith(QLatin1Char('/')))
    1135         path += QLatin1Char('/');
    1136     if (!reference.endsWith(QLatin1Char('/')))
    1137         reference += QLatin1Char('/');
    1138     return path.startsWith(reference);
    1139 }
    1140 
    1141 static inline bool isParentDomain(QString domain, QString reference)
    1142 {
    1143     if (!reference.startsWith(QLatin1Char('.')))
    1144         return domain == reference;
    1145 
    1146     return domain.endsWith(reference) || domain == reference.mid(1);
    1147 }
    1148 
    1149 /*!
    1150     Adds the cookies in the list \a cookieList to this cookie
    1151     jar. Default values for path and domain are taken from the \a
    1152     url object.
    1153 
    1154     Returns true if one or more cookes are set for url otherwise false.
    1155 
    1156     If a cookie already exists in the cookie jar, it will be
    1157     overridden by those in \a cookieList.
    1158 
    1159     The default QNetworkCookieJar class implements only a very basic
    1160     security policy (it makes sure that the cookies' domain and path
    1161     match the reply's). To enhance the security policy with your own
    1162     algorithms, override setCookiesFromUrl().
    1163 
    1164     Also, QNetworkCookieJar does not have a maximum cookie jar
    1165     size. Reimplement this function to discard older cookies to create
    1166     room for new ones.
    1167 
    1168     \sa cookiesForUrl(), QNetworkAccessManager::setCookieJar()
    1169 */
    1170 bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
    1171                                           const QUrl &url)
    1172 {
    1173     Q_D(QNetworkCookieJar);
    1174     QString defaultDomain = url.host();
    1175     QString pathAndFileName = url.path();
    1176     QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
    1177     if (defaultPath.isEmpty())
    1178         defaultPath = QLatin1Char('/');
    1179 
    1180     int added = 0;
    1181     QDateTime now = QDateTime::currentDateTime();
    1182     foreach (QNetworkCookie cookie, cookieList) {
    1183         bool isDeletion = !cookie.isSessionCookie() &&
    1184                           cookie.expirationDate() < now;
    1185 
    1186         // validate the cookie & set the defaults if unset
    1187         // (RFC 2965: "The request-URI MUST path-match the Path attribute of the cookie.")
    1188         if (cookie.path().isEmpty())
    1189             cookie.setPath(defaultPath);
    1190         else if (!isParentPath(pathAndFileName, cookie.path()))
    1191             continue;           // not accepted
    1192 
    1193         if (cookie.domain().isEmpty()) {
    1194             cookie.setDomain(defaultDomain);
    1195         } else {
    1196             QString domain = cookie.domain();
    1197             if (!(isParentDomain(domain, defaultDomain)
    1198                 || isParentDomain(defaultDomain, domain))) {
    1199                     continue;           // not accepted
    1200             }
    1201         }
    1202 
    1203         QList<QNetworkCookie>::Iterator it = d->allCookies.begin(),
    1204                                        end = d->allCookies.end();
    1205         for ( ; it != end; ++it)
    1206             // does this cookie already exist?
    1207             if (cookie.name() == it->name() &&
    1208                 cookie.domain() == it->domain() &&
    1209                 cookie.path() == it->path()) {
    1210                 // found a match
    1211                 d->allCookies.erase(it);
    1212                 break;
    1213             }
    1214 
    1215         // did not find a match
    1216         if (!isDeletion) {
    1217             d->allCookies += cookie;
    1218             ++added;
    1219         }
    1220     }
    1221     return (added > 0);
    1222 }
    1223 
    1224 /*!
    1225     Returns the cookies to be added to when a request is sent to
    1226     \a url. This function is called by the default
    1227     QNetworkAccessManager::createRequest(), which adds the
    1228     cookies returned by this function to the request being sent.
    1229 
    1230     If more than one cookie with the same name is found, but with
    1231     differing paths, the one with longer path is returned before the
    1232     one with shorter path. In other words, this function returns
    1233     cookies sorted by path length.
    1234 
    1235     The default QNetworkCookieJar class implements only a very basic
    1236     security policy (it makes sure that the cookies' domain and path
    1237     match the reply's). To enhance the security policy with your own
    1238     algorithms, override cookiesForUrl().
    1239 
    1240     \sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
    1241 */
    1242 QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
    1243 {
    1244 //     \b Warning! This is only a dumb implementation!
    1245 //     It does NOT follow all of the recommendations from
    1246 //     http://wp.netscape.com/newsref/std/cookie_spec.html
    1247 //     It does not implement a very good cross-domain verification yet.
    1248 
    1249     Q_D(const QNetworkCookieJar);
    1250     QDateTime now = QDateTime::currentDateTime();
    1251     QList<QNetworkCookie> result;
    1252 
    1253     // scan our cookies for something that matches
    1254     QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
    1255                                         end = d->allCookies.constEnd();
    1256     for ( ; it != end; ++it) {
    1257         if (!isParentDomain(url.host(), it->domain()))
    1258             continue;
    1259         if (!isParentPath(url.path(), it->path()))
    1260             continue;
    1261         if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
    1262             continue;
    1263 
    1264         // insert this cookie into result, sorted by path
    1265         QList<QNetworkCookie>::Iterator insertIt = result.begin();
    1266         while (insertIt != result.end()) {
    1267             if (insertIt->path().length() < it->path().length()) {
    1268                 // insert here
    1269                 insertIt = result.insert(insertIt, *it);
    1270                 break;
    1271             } else {
    1272                 ++insertIt;
    1273             }
    1274         }
    1275 
    1276         // this is the shortest path yet, just append
    1277         if (insertIt == result.end())
    1278             result += *it;
    1279     }
    1280 
    1281     return result;
    1282 }
    1283 
    12841039QT_END_NAMESPACE
  • trunk/src/network/access/qnetworkcookie.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    107107Q_DECLARE_TYPEINFO(QNetworkCookie, Q_MOVABLE_TYPE);
    108108
    109 class QNetworkCookieJarPrivate;
    110 class Q_NETWORK_EXPORT QNetworkCookieJar: public QObject
    111 {
    112     Q_OBJECT
    113 public:
    114     QNetworkCookieJar(QObject *parent = 0);
    115     virtual ~QNetworkCookieJar();
    116 
    117     virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
    118     virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
    119 
    120 protected:
    121     QList<QNetworkCookie> allCookies() const;
    122     void setAllCookies(const QList<QNetworkCookie> &cookieList);
    123 
    124 private:
    125     Q_DECLARE_PRIVATE(QNetworkCookieJar)
    126     Q_DISABLE_COPY(QNetworkCookieJar)
    127 };
    128 
    129109#ifndef QT_NO_DEBUG_STREAM
    130110class QDebug;
     
    134114QT_END_NAMESPACE
    135115
     116// ### Qt5 remove this include
     117#include "qnetworkcookiejar.h"
     118
    136119Q_DECLARE_METATYPE(QNetworkCookie)
    137120Q_DECLARE_METATYPE(QList<QNetworkCookie>)
  • trunk/src/network/access/qnetworkcookie_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6262public:
    6363    inline QNetworkCookiePrivate() : secure(false), httpOnly(false) { }
     64    static QList<QNetworkCookie> parseSetCookieHeaderLine(const QByteArray &cookieString);
    6465
    6566    QDateTime expirationDate;
  • trunk/src/network/access/qnetworkdiskcache.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4141
    4242//#define QNETWORKDISKCACHE_DEBUG
     43
     44#ifndef QT_NO_NETWORKDISKCACHE
    4345
    4446#include "qnetworkdiskcache.h"
    4547#include "qnetworkdiskcache_p.h"
     48#include "QtCore/qscopedpointer.h"
    4649
    4750#include <qfile.h>
     
    8083
    8184    Note you have to set the cache directory before it will work.
     85
     86    A network disk cache can be enabled by:
     87
     88    \snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 0
     89
     90    When sending requests, to control the preference of when to use the cache
     91    and when to use the network, consider the following:
     92
     93    \snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 1
     94
     95    To check whether the response came from the cache or from the network, the
     96    following can be applied:
     97
     98    \snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 2
    8299*/
    83100
     
    181198        }
    182199    }
    183 
    184     QCacheItem *cacheItem = new QCacheItem;
     200    QScopedPointer<QCacheItem> cacheItem(new QCacheItem);
    185201    cacheItem->metaData = metaData;
    186202
     
    191207    } else {
    192208        QString templateName = d->tmpCacheFileName();
    193         cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
    194         cacheItem->file->open();
     209        QT_TRY {
     210            cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
     211        } QT_CATCH(...) {
     212            cacheItem->file = 0;
     213        }
     214        if (!cacheItem->file || !cacheItem->file->open()) {
     215            qWarning() << "QNetworkDiskCache::prepare() unable to open temporary file";
     216            cacheItem.reset();
     217            return 0;
     218        }
    195219        cacheItem->writeHeader(cacheItem->file);
    196220        device = cacheItem->file;
    197221    }
    198     d->inserting[device] = cacheItem;
     222    d->inserting[device] = cacheItem.take();
    199223    return device;
    200224}
     
    230254    if (QFile::exists(fileName)) {
    231255        if (!QFile::remove(fileName)) {
    232             qWarning() << "QNetworkDiskCache: could't remove the cache file " << fileName;
     256            qWarning() << "QNetworkDiskCache: couldn't remove the cache file " << fileName;
    233257            return;
    234258        }
     
    355379#endif
    356380    Q_D(QNetworkDiskCache);
    357     QBuffer *buffer = 0;
     381    QScopedPointer<QBuffer> buffer;
    358382    if (!url.isValid())
    359         return buffer;
     383        return 0;
    360384    if (d->lastItem.metaData.url() == url && d->lastItem.data.isOpen()) {
    361         buffer = new QBuffer;
     385        buffer.reset(new QBuffer);
    362386        buffer->setData(d->lastItem.data.data());
    363387    } else {
    364         QFile *file = new QFile(d->cacheFileName(url));
    365         if (!file->open(QFile::ReadOnly | QIODevice::Unbuffered)) {
    366             delete file;
     388        QScopedPointer<QFile> file(new QFile(d->cacheFileName(url)));
     389        if (!file->open(QFile::ReadOnly | QIODevice::Unbuffered))
    367390            return 0;
    368         }
    369         if (!d->lastItem.read(file, true)) {
     391
     392        if (!d->lastItem.read(file.data(), true)) {
    370393            file->close();
    371394            remove(url);
    372             delete file;
    373395            return 0;
    374396        }
    375397        if (d->lastItem.data.isOpen()) {
    376398            // compressed
    377             buffer = new QBuffer;
     399            buffer.reset(new QBuffer);
    378400            buffer->setData(d->lastItem.data.data());
    379             delete file;
    380401        } else {
    381             buffer = new QBuffer;
     402            buffer.reset(new QBuffer);
    382403            // ### verify that QFile uses the fd size and not the file name
    383404            qint64 size = file->size() - file->pos();
     
    387408#endif
    388409            if (p) {
    389                 file->setParent(buffer);
    390410                buffer->setData((const char *)p, size);
     411                file.take()->setParent(buffer.data());
    391412            } else {
    392413                buffer->setData(file->readAll());
    393                 delete file;
    394414            }
    395415        }
    396416    }
    397417    buffer->open(QBuffer::ReadOnly);
    398     return buffer;
     418    return buffer.take();
    399419}
    400420
     
    463483    Returns the current size of the cache.
    464484
    465     When the current size of the cache is greater then the maximumCacheSize()
     485    When the current size of the cache is greater than the maximumCacheSize()
    466486    older cache files are removed until the total size is less then 90% of
    467487    maximumCacheSize() starting with the oldest ones first using the file
     
    491511    QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
    492512
    493     QMap<QDateTime, QString> cacheItems;
     513    QMultiMap<QDateTime, QString> cacheItems;
    494514    qint64 totalSize = 0;
    495515    while (it.hasNext()) {
     
    498518        QString fileName = info.fileName();
    499519        if (fileName.endsWith(CACHE_POSTFIX) && fileName.startsWith(CACHE_PREFIX)) {
    500             cacheItems[info.created()] = path;
     520            cacheItems.insert(info.created(), path);
    501521            totalSize += info.size();
    502522        }
     
    505525    int removedFiles = 0;
    506526    qint64 goal = (maximumCacheSize() * 9) / 10;
    507     QMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
     527    QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
    508528    while (i != cacheItems.constEnd()) {
    509529        if (totalSize < goal)
     
    666686
    667687QT_END_NAMESPACE
     688
     689#endif // QT_NO_NETWORKDISKCACHE
  • trunk/src/network/access/qnetworkdiskcache.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5050
    5151QT_MODULE(Network)
     52
     53#ifndef QT_NO_NETWORKDISKCACHE
    5254
    5355class QNetworkDiskCachePrivate;
     
    8789};
    8890
     91#endif // QT_NO_NETWORKDISKCACHE
     92
    8993QT_END_NAMESPACE
    9094
     
    9296
    9397#endif // QNETWORKDISKCACHE_H
    94 
  • trunk/src/network/access/qnetworkdiskcache_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
  • trunk/src/network/access/qnetworkreply.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6060    \since 4.4
    6161    \brief The QNetworkReply class contains the data and headers for a request
    62     posted with QNetworkAccessManager
     62    sent with QNetworkAccessManager
    6363
    6464    \reentrant
     65    \ingroup network
    6566    \inmodule QtNetwork
    6667
     
    8889    content.
    8990
     91    \note Do not delete the object in the slot connected to the
     92    error() or finished() signal. Use deleteLater().
     93
    9094    \sa QNetworkRequest, QNetworkAccessManager
    9195*/
     
    152156    were not accepted (if any)
    153157
     158    \value ContentReSendError          the request needed to be sent
     159    again, but this failed for example because the upload data
     160    could not be read a second time.
     161
    154162    \value ProtocolUnknownError         the Network Access API cannot
    155163    honor the request because the protocol is not known
     
    164172    was detected
    165173
    166     \value UnknownContentError          an unknonwn error related to
     174    \value UnknownContentError          an unknown error related to
    167175    the remote content was detected
    168176
     
    229237    parameter is this object.
    230238
     239    \note Do not delete the object in the slot connected to this
     240    signal. Use deleteLater().
     241
    231242    \sa QNetworkAccessManager::finished()
    232243*/
     
    242253    detected. Call errorString() to obtain a textual representation of
    243254    the error condition.
     255
     256    \note Do not delete the object in the slot connected to this
     257    signal. Use deleteLater().
    244258
    245259    \sa error(), errorString()
     
    436450{
    437451    return d_func()->errorCode;
     452}
     453
     454/*!
     455    \since 4.6
     456
     457    Returns true when the reply has finished or was aborted.
     458
     459    \sa isRunning()
     460*/
     461bool QNetworkReply::isFinished() const
     462{
     463    return d_func()->isFinished();
     464}
     465
     466/*!
     467    \since 4.6
     468
     469    Returns true when the request is still processing and the
     470    reply has not finished or was aborted yet.
     471
     472    \sa isFinished()
     473*/
     474bool QNetworkReply::isRunning() const
     475{
     476    return !isFinished();
    438477}
    439478
     
    556595    }
    557596}
     597
     598/*!
     599    \overload
     600    \since 4.6
     601
     602    If this function is called, the SSL errors given in \a errors
     603    will be ignored.
     604
     605    Note that you can set the expected certificate in the SSL error:
     606    If, for instance, you want to issue a request to a server that uses
     607    a self-signed certificate, consider the following snippet:
     608
     609    \snippet doc/src/snippets/code/src_network_access_qnetworkreply.cpp 0
     610
     611    Multiple calls to this function will replace the list of errors that
     612    were passed in previous calls.
     613    You can clear the list of errors you want to ignore by calling this
     614    function with an empty list.
     615
     616    \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
     617*/
     618void QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
     619{
     620    // do this cryptic trick, because we could not add a virtual method to this class later on
     621    // since that breaks binary compatibility
     622    int id = metaObject()->indexOfMethod("ignoreSslErrorsImplementation(QList<QSslError>)");
     623    if (id != -1) {
     624        QList<QSslError> copy(errors);
     625        void *arr[] = { 0, &copy };
     626        qt_metacall(QMetaObject::InvokeMetaMethod, id, arr);
     627    }
     628}
    558629#endif
    559630
     
    570641    found.
    571642
    572     \sa sslConfiguration(), sslErrors()
     643    \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
    573644*/
    574645void QNetworkReply::ignoreSslErrors()
  • trunk/src/network/access/qnetworkreply.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    9393        ContentNotFoundError,
    9494        AuthenticationRequiredError,
     95        ContentReSendError,
    9596        UnknownContentError = 299,
    9697
     
    116117    QNetworkRequest request() const;
    117118    NetworkError error() const;
     119    bool isFinished() const;
     120    bool isRunning() const;
    118121    QUrl url() const;
    119122
     
    132135    QSslConfiguration sslConfiguration() const;
    133136    void setSslConfiguration(const QSslConfiguration &configuration);
     137    void ignoreSslErrors(const QList<QSslError> &errors);
    134138#endif
    135139
  • trunk/src/network/access/qnetworkreply_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    7676    { reply->d_func()->manager = manager; }
    7777
     78    virtual bool isFinished() const { return false; }
     79
    7880    Q_DECLARE_PUBLIC(QNetworkReply)
    7981};
  • 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 (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4747#include "QtCore/qdatetime.h"
    4848#include "QtNetwork/qsslconfiguration.h"
     49#include "qnetworkaccesshttpbackend_p.h"
    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      notificationHandlingPaused(false),
    5760      bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1),
     61      httpStatusCode(0),
    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    pauseNotificationHandling();
    157135    emit q->downloadProgress(bytesDownloaded,
    158136                             totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
    159137    emit q->readyRead();
     138    resumeNotificationHandling();
    160139}
    161140
     
    164143    _q_copyReadyRead();
    165144}
     145
     146void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
     147{
     148    Q_Q(QNetworkReplyImpl);
     149
     150    // make sure this is only called once, ever.
     151    //_q_bufferOutgoingData may call it or the readChannelFinished emission
     152    if (state != Buffering)
     153        return;
     154
     155    // disconnect signals
     156    QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
     157    QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
     158
     159    // finally, start the request
     160    QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
     161}
     162
     163void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
     164{
     165    Q_Q(QNetworkReplyImpl);
     166
     167    if (!outgoingDataBuffer) {
     168        // first call, create our buffer
     169        outgoingDataBuffer = new QRingBuffer();
     170
     171        QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
     172        QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
     173    }
     174
     175    qint64 bytesBuffered = 0;
     176    qint64 bytesToBuffer = 0;
     177
     178    // read data into our buffer
     179    forever {
     180        bytesToBuffer = outgoingData->bytesAvailable();
     181        // unknown? just try 2 kB, this also ensures we always try to read the EOF
     182        if (bytesToBuffer <= 0)
     183            bytesToBuffer = 2*1024;
     184
     185        char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
     186        bytesBuffered = outgoingData->read(dst, bytesToBuffer);
     187
     188        if (bytesBuffered == -1) {
     189            // EOF has been reached.
     190            outgoingDataBuffer->chop(bytesToBuffer);
     191
     192            _q_bufferOutgoingDataFinished();
     193            break;
     194        } else if (bytesBuffered == 0) {
     195            // nothing read right now, just wait until we get called again
     196            outgoingDataBuffer->chop(bytesToBuffer);
     197
     198            break;
     199        } else {
     200            // don't break, try to read() again
     201            outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
     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    if (notificationHandlingPaused)
     268        return;
     269
    203270    NotificationQueue current = pendingNotifications;
    204271    pendingNotifications.clear();
     
    207274        return;
    208275
    209     while (!current.isEmpty()) {
     276    while (state == Working && !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// Do not handle the notifications while we are emitting downloadProgress
     301// or readyRead
     302void QNetworkReplyImplPrivate::pauseNotificationHandling()
     303{
     304    notificationHandlingPaused = true;
     305}
     306
     307// Resume notification handling
     308void QNetworkReplyImplPrivate::resumeNotificationHandling()
     309{
     310    Q_Q(QNetworkReplyImpl);
     311    notificationHandlingPaused = false;
     312    if (pendingNotifications.size() >= 1)
     313        QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
     314}
     315
     316QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
     317{
     318    if (!backend)
     319        return 0;
     320    return backend->networkCache();
     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    pauseNotificationHandling();
    360452    emit q->downloadProgress(bytesDownloaded,
    361453                             totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
     454    // important: At the point of this readyRead(), the data parameter list must be empty,
     455    // else implicit sharing will trigger memcpy when the user is reading data!
    362456    emit q->readyRead();
    363457
    364458    // hopefully we haven't been deleted here
    365459    if (!qq.isNull()) {
     460        resumeNotificationHandling();
    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    if (state == Finished || state == Aborted)
     493        return;
    397494
    398495    state = Finished;
    399496    pendingNotifications.clear();
    400497
     498    pauseNotificationHandling();
    401499    QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
    402     if (bytesDownloaded != lastBytesDownloaded || totalSize.isNull())
     500    if (totalSize.isNull() || totalSize == -1) {
    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    resumeNotificationHandling();
    406507
    407508    completeCacheSave();
     
    410511    // which would delete the backend too...
    411512    // maybe we should protect the backend
     513    pauseNotificationHandling();
    412514    emit q->readChannelFinished();
    413515    emit q->finished();
     516    resumeNotificationHandling();
    414517}
    415518
     
    456559}
    457560
     561bool QNetworkReplyImplPrivate::isFinished() const
     562{
     563    return (state == Finished || state == Aborted);
     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::Finished || 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    // finished may access the backend
     602    if (d->backend) {
     603        d->backend->deleteLater();
     604        d->backend = 0;
     605    }
    494606}
    495607
     
    521633qint64 QNetworkReplyImpl::bytesAvailable() const
    522634{
    523     return QNetworkReply::bytesAvailable() + d_func()->readBuffer.size();
     635    return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
    524636}
    525637
     
    528640    Q_D(QNetworkReplyImpl);
    529641    if (size > d->readBufferMaxSize &&
    530         size == d->readBuffer.size())
     642        size > d->readBuffer.byteAmount())
    531643        d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
    532644
     
    558670}
    559671
     672void QNetworkReplyImpl::ignoreSslErrorsImplementation(const QList<QSslError> &errors)
     673{
     674    Q_D(QNetworkReplyImpl);
     675    if (d->backend)
     676        d->backend->ignoreSslErrors(errors);
     677}
    560678#endif  // QT_NO_OPENSSL
    561679
     
    576694    }
    577695
    578     maxlen = qMin<qint64>(maxlen, d->readBuffer.size());
     696    maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
    579697    return d->readBuffer.read(data, maxlen);
    580698}
  • trunk/src/network/access/qnetworkreplyimpl_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6060#include "QtCore/qmap.h"
    6161#include "QtCore/qqueue.h"
     62#include "QtCore/qbuffer.h"
    6263#include "private/qringbuffer_p.h"
     64#include "private/qbytedata_p.h"
    6365
    6466QT_BEGIN_NAMESPACE
     
    8890    Q_INVOKABLE void setSslConfigurationImplementation(const QSslConfiguration &configuration);
    8991    virtual void ignoreSslErrors();
     92    Q_INVOKABLE virtual void ignoreSslErrorsImplementation(const QList<QSslError> &errors);
    9093#endif
    9194
    9295    Q_DECLARE_PRIVATE(QNetworkReplyImpl)
    9396    Q_PRIVATE_SLOT(d_func(), void _q_startOperation())
    94     Q_PRIVATE_SLOT(d_func(), void _q_sourceReadyRead())
    95     Q_PRIVATE_SLOT(d_func(), void _q_sourceReadChannelFinished())
    9697    Q_PRIVATE_SLOT(d_func(), void _q_copyReadyRead())
    9798    Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished())
     99    Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
     100    Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
    98101};
    99102
     
    103106    enum InternalNotifications {
    104107        NotifyDownstreamReadyWrite,
    105         NotifyUpstreamReadyRead,
    106108        NotifyCloseDownstreamChannel,
    107         NotifyCloseUpstreamChannel,
    108109        NotifyCopyFinished
    109110    };
     
    111112    enum State {
    112113        Idle,
    113         Opening,
     114        Buffering,
    114115        Working,
    115116        Finished,
     
    126127    void _q_copyReadyRead();
    127128    void _q_copyReadChannelFinished();
     129    void _q_bufferOutgoingData();
     130    void _q_bufferOutgoingDataFinished();
    128131
    129132    void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
    130133               QIODevice *outgoingData);
    131     void setNetworkCache(QAbstractNetworkCache *networkCache);
     134
     135    void pauseNotificationHandling();
     136    void resumeNotificationHandling();
    132137    void backendNotify(InternalNotifications notification);
    133138    void handleNotifications();
     
    139144    bool isCachingEnabled() const;
    140145    void consume(qint64 count);
     146    void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal);
    141147    qint64 nextDownstreamBlockSize() const;
    142     void feed(const QByteArray &data);
    143     void feed(QIODevice *data);
     148    void appendDownstreamData(QByteDataBuffer &data);
     149    void appendDownstreamData(QIODevice *data);
    144150    void finished();
    145151    void error(QNetworkReply::NetworkError code, const QString &errorString);
     
    148154    void sslErrors(const QList<QSslError> &errors);
    149155
     156    bool isFinished() const;
     157
    150158    QNetworkAccessBackend *backend;
    151159    QIODevice *outgoingData;
     160    QRingBuffer *outgoingDataBuffer;
    152161    QIODevice *copyDevice;
    153     QAbstractNetworkCache *networkCache;
     162    QAbstractNetworkCache *networkCache() const;
    154163
    155164    bool cacheEnabled;
     
    157166
    158167    NotificationQueue pendingNotifications;
     168    bool notificationHandlingPaused;
     169
    159170    QUrl urlForLastAuthentication;
    160171#ifndef QT_NO_NETWORKPROXY
     
    163174#endif
    164175
    165     QRingBuffer readBuffer;
    166     QRingBuffer writeBuffer;
     176    QByteDataBuffer readBuffer;
    167177    qint64 bytesDownloaded;
    168178    qint64 lastBytesDownloaded;
  • trunk/src/network/access/qnetworkrequest.cpp

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    4949#include "QtCore/qdatetime.h"
    5050
     51#include <ctype.h>
     52#ifndef QT_NO_DATESTRING
     53# include <stdio.h>
     54#endif
     55
    5156QT_BEGIN_NAMESPACE
    5257
    5358/*!
    5459    \class QNetworkRequest
    55     \brief The QNetworkRequest class holds one request to be sent with the Network Access API.
     60    \brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
    5661    \since 4.4
    5762
    58     \ingroup io
     63    \ingroup network
    5964    \inmodule QtNetwork
    6065
     
    154159        be automatically cached. If true, data may be cached, provided
    155160        it is cacheable (what is cacheable depends on the protocol
    156         being used). Note that the default QNetworkAccessManager
    157         implementation does not support caching, so it will ignore
    158         this attribute.
     161        being used).
    159162
    160163    \value SourceIsFromCacheAttribute
     
    162165        Indicates whether the data was obtained from cache
    163166        or not.
     167
     168    \value DoNotBufferUploadDataAttribute
     169        Requests only, type: QVariant::Bool (default: false)
     170        Indicates whether the QNetworkAccessManager code is
     171        allowed to buffer the upload data, e.g. when doing a HTTP POST.
     172        When using this flag with sequential upload data, the ContentLengthHeader
     173        header must be set.
     174
     175     \value HttpPipeliningAllowedAttribute
     176        Requests only, type: QVariant::Bool (default: false)
     177        Indicates whether the QNetworkAccessManager code is
     178        allowed to use HTTP pipelining with this request.
     179
     180     \value HttpPipeliningWasUsedAttribute
     181        Replies only, type: QVariant::Bool
     182        Indicates whether the HTTP pipelining was used for receiving
     183        this reply.
    164184
    165185    \value User
     
    217237
    218238#ifndef QT_NO_OPENSSL
     239        sslConfiguration = 0;
    219240        if (other.sslConfiguration)
    220241            sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
    221         else
    222             sslConfiguration = 0;
    223242#endif
    224243    }
     
    467486}
    468487#endif
     488
     489/*!
     490    \since 4.6
     491
     492    Allows setting a reference to the \a object initiating
     493    the request.
     494
     495    For example QtWebKit sets the originating object to the
     496    QWebFrame that initiated the request.
     497
     498    \sa originatingObject()
     499*/
     500void QNetworkRequest::setOriginatingObject(QObject *object)
     501{
     502    d->originatingObject = object;
     503}
     504
     505/*!
     506    \since 4.6
     507
     508    Returns a reference to the object that initiated this
     509    network request; returns 0 if not set or the object has
     510    been destroyed.
     511
     512    \sa setOriginatingObject()
     513*/
     514QObject *QNetworkRequest::originatingObject() const
     515{
     516    return d->originatingObject.data();
     517}
    469518
    470519static QByteArray headerName(QNetworkRequest::KnownHeaders header)
     
    563612    // headerName is not empty here
    564613
    565     QByteArray lower = headerName.toLower();
    566     switch (lower.at(0)) {
     614    switch (tolower(headerName.at(0))) {
    567615    case 'c':
    568         if (lower == "content-type")
     616        if (qstricmp(headerName.constData(), "content-type") == 0)
    569617            return QNetworkRequest::ContentTypeHeader;
    570         else if (lower == "content-length")
     618        else if (qstricmp(headerName.constData(), "content-length") == 0)
    571619            return QNetworkRequest::ContentLengthHeader;
    572         else if (lower == "cookie")
     620        else if (qstricmp(headerName.constData(), "cookie") == 0)
    573621            return QNetworkRequest::CookieHeader;
    574622        break;
    575623
    576624    case 'l':
    577         if (lower == "location")
     625        if (qstricmp(headerName.constData(), "location") == 0)
    578626            return QNetworkRequest::LocationHeader;
    579         else if (lower == "last-modified")
     627        else if (qstricmp(headerName.constData(), "last-modified") == 0)
    580628            return QNetworkRequest::LastModifiedHeader;
    581629        break;
    582630
    583631    case 's':
    584         if (lower == "set-cookie")
     632        if (qstricmp(headerName.constData(), "set-cookie") == 0)
    585633            return QNetworkRequest::SetCookieHeader;
    586634        break;
     
    654702QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
    655703{
    656     QByteArray lowerKey = key.toLower();
    657704    RawHeadersList::ConstIterator it = rawHeaders.constBegin();
    658705    RawHeadersList::ConstIterator end = rawHeaders.constEnd();
    659706    for ( ; it != end; ++it)
    660         if (it->first.toLower() == lowerKey)
     707        if (qstricmp(it->first.constData(), key.constData()) == 0)
    661708            return it;
    662709
     
    732779void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
    733780{
    734     QByteArray lowerKey = key.toLower();
    735781    RawHeadersList::Iterator it = rawHeaders.begin();
    736782    while (it != rawHeaders.end()) {
    737         if (it->first.toLower() == lowerKey)
     783        if (qstricmp(it->first.constData(), key.constData()) == 0)
    738784            it = rawHeaders.erase(it);
    739785        else
     
    762808}
    763809
     810// Fast month string to int conversion. This code
     811// assumes that the Month name is correct and that
     812// the string is at least three chars long.
     813static int name_to_month(const char* month_str)
     814{
     815    switch (month_str[0]) {
     816    case 'J':
     817        switch (month_str[1]) {
     818        case 'a':
     819            return 1;
     820            break;
     821        case 'u':
     822            switch (month_str[2] ) {
     823            case 'n':
     824                return 6;
     825                break;
     826            case 'l':
     827                return 7;
     828                break;
     829            }
     830        }
     831        break;
     832    case 'F':
     833        return 2;
     834        break;
     835    case 'M':
     836        switch (month_str[2] ) {
     837        case 'r':
     838            return 3;
     839            break;
     840        case 'y':
     841            return 5;
     842            break;
     843        }
     844        break;
     845    case 'A':
     846        switch (month_str[1]) {
     847        case 'p':
     848            return 4;
     849            break;
     850        case 'u':
     851            return 8;
     852            break;
     853        }
     854        break;
     855    case 'O':
     856        return 10;
     857        break;
     858    case 'S':
     859        return 9;
     860        break;
     861    case 'N':
     862        return 11;
     863        break;
     864    case 'D':
     865        return 12;
     866        break;
     867    }
     868
     869    return 0;
     870}
     871
    764872QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
    765873{
     
    777885        dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
    778886    } else {
    779         // eat the weekday, the comma and the space following it
    780         QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
    781 
    782         QLocale c = QLocale::c();
    783         if (pos == 3)
    784             // must be RFC 1123 date
    785             dt = c.toDateTime(sansWeekday, QLatin1String("dd MMM yyyy hh:mm:ss 'GMT"));
    786         else
     887        // Use sscanf over QLocal/QDateTimeParser for speed reasons. See the
     888        // QtWebKit performance benchmarks to get an idea.
     889        if (pos == 3) {
     890            char month_name[4];
     891            int day, year, hour, minute, second;
     892            if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6)
     893                dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second));
     894        } else {
     895            QLocale c = QLocale::c();
     896            // eat the weekday, the comma and the space following it
     897            QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
    787898            // must be RFC 850 date
    788899            dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
     900        }
    789901    }
    790902#endif // QT_NO_DATESTRING
  • trunk/src/network/access/qnetworkrequest.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    7676        CacheSaveControlAttribute,
    7777        SourceIsFromCacheAttribute,
     78        DoNotBufferUploadDataAttribute,
     79        HttpPipeliningAllowedAttribute,
     80        HttpPipeliningWasUsedAttribute,
    7881
    7982        User = 1000,
     
    118121#endif
    119122
     123    void setOriginatingObject(QObject *object);
     124    QObject *originatingObject() const;
     125
    120126private:
    121127    QSharedDataPointer<QNetworkRequestPrivate> d;
  • trunk/src/network/access/qnetworkrequest_p.h

    r2 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    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 have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    5959#include "QtCore/qhash.h"
    6060#include "QtCore/qshareddata.h"
     61#include "QtCore/qsharedpointer.h"
    6162
    6263QT_BEGIN_NAMESPACE
     
    7475    CookedHeadersMap cookedHeaders;
    7576    AttributesMap attributes;
     77    QWeakPointer<QObject> originatingObject;
    7678
    7779    RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
Note: See TracChangeset for help on using the changeset viewer.