src/network/access/qhttp.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/network/access/qhttp.cpp	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,3155 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//#define QHTTP_DEBUG
+
+#include <qplatformdefs.h>
+#include "qhttp.h"
+
+#ifndef QT_NO_HTTP
+# include "private/qobject_p.h"
+# include "qtcpsocket.h"
+# include "qsslsocket.h"
+# include "qtextstream.h"
+# include "qmap.h"
+# include "qlist.h"
+# include "qstring.h"
+# include "qstringlist.h"
+# include "qbuffer.h"
+# include "private/qringbuffer_p.h"
+# include "qcoreevent.h"
+# include "qurl.h"
+# include "qnetworkproxy.h"
+# include "qauthenticator.h"
+# include "qauthenticator_p.h"
+# include "qdebug.h"
+# include "qtimer.h"
+#endif
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNormalRequest;
+class QHttpRequest
+{
+public:
+    QHttpRequest() : finished(false)
+    { id = idCounter.fetchAndAddRelaxed(1); }
+    virtual ~QHttpRequest()
+    { }
+
+    virtual void start(QHttp *) = 0;
+    virtual bool hasRequestHeader();
+    virtual QHttpRequestHeader requestHeader();
+
+    virtual QIODevice *sourceDevice() = 0;
+    virtual QIODevice *destinationDevice() = 0;
+
+    int id;
+    bool finished;
+
+private:
+    static QBasicAtomicInt idCounter;
+};
+
+class QHttpPrivate : public QObjectPrivate
+{
+public:
+    Q_DECLARE_PUBLIC(QHttp)
+
+    inline QHttpPrivate()
+        : socket(0), reconnectAttempts(2),
+          deleteSocket(0), state(QHttp::Unconnected),
+          error(QHttp::NoError), port(0), mode(QHttp::ConnectionModeHttp),
+          toDevice(0), postDevice(0), bytesDone(0), chunkedSize(-1),
+          repost(false), pendingPost(false)
+    {
+    }
+
+    inline ~QHttpPrivate()
+    {
+        while (!pending.isEmpty())
+            delete pending.takeFirst();
+
+        if (deleteSocket)
+            delete socket;
+    }
+
+    // private slots
+    void _q_startNextRequest();
+    void _q_slotReadyRead();
+    void _q_slotConnected();
+    void _q_slotError(QAbstractSocket::SocketError);
+    void _q_slotClosed();
+    void _q_slotBytesWritten(qint64 numBytes);
+#ifndef QT_NO_OPENSSL
+    void _q_slotEncryptedBytesWritten(qint64 numBytes);
+#endif
+    void _q_slotDoFinished();
+    void _q_slotSendRequest();
+    void _q_continuePost();
+
+    int addRequest(QHttpNormalRequest *);
+    int addRequest(QHttpRequest *);
+    void finishedWithSuccess();
+    void finishedWithError(const QString &detail, int errorCode);
+
+    void init();
+    void setState(int);
+    void closeConn();
+    void setSock(QTcpSocket *sock);
+
+    void postMoreData();
+
+    QTcpSocket *socket;
+    int reconnectAttempts;
+    bool deleteSocket;
+    QList<QHttpRequest *> pending;
+
+    QHttp::State state;
+    QHttp::Error error;
+    QString errorString;
+
+    QString hostName;
+    quint16 port;
+    QHttp::ConnectionMode mode;
+
+    QByteArray buffer;
+    QIODevice *toDevice;
+    QIODevice *postDevice;
+
+    qint64 bytesDone;
+    qint64 bytesTotal;
+    qint64 chunkedSize;
+
+    QHttpRequestHeader header;
+
+    bool readHeader;
+    QString headerStr;
+    QHttpResponseHeader response;
+
+    QRingBuffer rba;
+
+#ifndef QT_NO_NETWORKPROXY
+    QNetworkProxy proxy;
+    QAuthenticator proxyAuthenticator;
+#endif
+    QAuthenticator authenticator;
+    bool repost;
+    bool hasFinishedWithError;
+    bool pendingPost;
+    QTimer post100ContinueTimer;
+};
+
+QBasicAtomicInt QHttpRequest::idCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
+
+bool QHttpRequest::hasRequestHeader()
+{
+    return false;
+}
+
+QHttpRequestHeader QHttpRequest::requestHeader()
+{
+    return QHttpRequestHeader();
+}
+
+/****************************************************
+ *
+ * QHttpNormalRequest
+ *
+ ****************************************************/
+
+class QHttpNormalRequest : public QHttpRequest
+{
+public:
+    QHttpNormalRequest(const QHttpRequestHeader &h, QIODevice *d, QIODevice *t) :
+        header(h), to(t)
+    {
+        is_ba = false;
+        data.dev = d;
+    }
+
+    QHttpNormalRequest(const QHttpRequestHeader &h, QByteArray *d, QIODevice *t) :
+        header(h), to(t)
+    {
+        is_ba = true;
+        data.ba = d;
+    }
+
+    ~QHttpNormalRequest()
+    {
+        if (is_ba)
+            delete data.ba;
+    }
+
+    void start(QHttp *);
+    bool hasRequestHeader();
+    QHttpRequestHeader requestHeader();
+    inline void setRequestHeader(const QHttpRequestHeader &h) { header = h; }
+
+    QIODevice *sourceDevice();
+    QIODevice *destinationDevice();
+
+protected:
+    QHttpRequestHeader header;
+
+private:
+    union {
+        QByteArray *ba;
+        QIODevice *dev;
+    } data;
+    bool is_ba;
+    QIODevice *to;
+};
+
+void QHttpNormalRequest::start(QHttp *http)
+{
+    if (!http->d_func()->socket)
+        http->d_func()->setSock(0);
+    http->d_func()->header = header;
+
+    if (is_ba) {
+        http->d_func()->buffer = *data.ba;
+        if (http->d_func()->buffer.size() >= 0)
+            http->d_func()->header.setContentLength(http->d_func()->buffer.size());
+
+        http->d_func()->postDevice = 0;
+    } else {
+        http->d_func()->buffer = QByteArray();
+
+        if (data.dev && (data.dev->isOpen() || data.dev->open(QIODevice::ReadOnly))) {
+            http->d_func()->postDevice = data.dev;
+            if (http->d_func()->postDevice->size() >= 0)
+                http->d_func()->header.setContentLength(http->d_func()->postDevice->size());
+        } else {
+            http->d_func()->postDevice = 0;
+        }
+    }
+
+    if (to && (to->isOpen() || to->open(QIODevice::WriteOnly)))
+        http->d_func()->toDevice = to;
+    else
+        http->d_func()->toDevice = 0;
+
+    http->d_func()->reconnectAttempts = 2;
+    http->d_func()->_q_slotSendRequest();
+}
+
+bool QHttpNormalRequest::hasRequestHeader()
+{
+    return true;
+}
+
+QHttpRequestHeader QHttpNormalRequest::requestHeader()
+{
+    return header;
+}
+
+QIODevice *QHttpNormalRequest::sourceDevice()
+{
+    if (is_ba)
+        return 0;
+    return data.dev;
+}
+
+QIODevice *QHttpNormalRequest::destinationDevice()
+{
+    return to;
+}
+
+/****************************************************
+ *
+ * QHttpPGHRequest
+ * (like a QHttpNormalRequest, but for the convenience
+ * functions put(), get() and head() -- i.e. set the
+ * host header field correctly before sending the
+ * request)
+ *
+ ****************************************************/
+
+class QHttpPGHRequest : public QHttpNormalRequest
+{
+public:
+    QHttpPGHRequest(const QHttpRequestHeader &h, QIODevice *d, QIODevice *t) :
+        QHttpNormalRequest(h, d, t)
+    { }
+
+    QHttpPGHRequest(const QHttpRequestHeader &h, QByteArray *d, QIODevice *t) :
+        QHttpNormalRequest(h, d, t)
+    { }
+
+    ~QHttpPGHRequest()
+    { }
+
+    void start(QHttp *);
+};
+
+void QHttpPGHRequest::start(QHttp *http)
+{
+    if (http->d_func()->port && http->d_func()->port != 80)
+	header.setValue(QLatin1String("Host"), http->d_func()->hostName + QLatin1Char(':') + QString::number(http->d_func()->port));
+    else
+	header.setValue(QLatin1String("Host"), http->d_func()->hostName);
+    QHttpNormalRequest::start(http);
+}
+
+/****************************************************
+ *
+ * QHttpSetHostRequest
+ *
+ ****************************************************/
+
+class QHttpSetHostRequest : public QHttpRequest
+{
+public:
+    QHttpSetHostRequest(const QString &h, quint16 p, QHttp::ConnectionMode m)
+        : hostName(h), port(p), mode(m)
+    { }
+
+    void start(QHttp *);
+
+    QIODevice *sourceDevice()
+    { return 0; }
+    QIODevice *destinationDevice()
+    { return 0; }
+
+private:
+    QString hostName;
+    quint16 port;
+    QHttp::ConnectionMode mode;
+};
+
+void QHttpSetHostRequest::start(QHttp *http)
+{
+    http->d_func()->hostName = hostName;
+    http->d_func()->port = port;
+    http->d_func()->mode = mode;
+
+#ifdef QT_NO_OPENSSL
+    if (mode == QHttp::ConnectionModeHttps) {
+        // SSL requested but no SSL support compiled in
+        http->d_func()->finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTPS connection requested but SSL support not compiled in")),
+                          QHttp::UnknownError);
+        return;
+    }
+#endif
+
+    http->d_func()->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * QHttpSetUserRequest
+ *
+ ****************************************************/
+
+class QHttpSetUserRequest : public QHttpRequest
+{
+public:
+    QHttpSetUserRequest(const QString &userName, const QString &password) :
+        user(userName), pass(password)
+    { }
+
+    void start(QHttp *);
+
+    QIODevice *sourceDevice()
+    { return 0; }
+    QIODevice *destinationDevice()
+    { return 0; }
+
+private:
+    QString user;
+    QString pass;
+};
+
+void QHttpSetUserRequest::start(QHttp *http)
+{
+    http->d_func()->authenticator.setUser(user);
+    http->d_func()->authenticator.setPassword(pass);
+    http->d_func()->finishedWithSuccess();
+}
+
+#ifndef QT_NO_NETWORKPROXY
+
+/****************************************************
+ *
+ * QHttpSetProxyRequest
+ *
+ ****************************************************/
+
+class QHttpSetProxyRequest : public QHttpRequest
+{
+public:
+    inline QHttpSetProxyRequest(const QNetworkProxy &proxy)
+    {
+        this->proxy = proxy;
+    }
+
+    inline void start(QHttp *http)
+    {
+        http->d_func()->proxy = proxy;
+        QString user = proxy.user();
+        if (!user.isEmpty())
+            http->d_func()->proxyAuthenticator.setUser(user);
+        QString password = proxy.password();
+        if (!password.isEmpty())
+            http->d_func()->proxyAuthenticator.setPassword(password);
+        http->d_func()->finishedWithSuccess();
+    }
+
+    inline QIODevice *sourceDevice()
+    { return 0; }
+    inline QIODevice *destinationDevice()
+    { return 0; }
+private:
+    QNetworkProxy proxy;
+};
+
+#endif // QT_NO_NETWORKPROXY
+
+/****************************************************
+ *
+ * QHttpSetSocketRequest
+ *
+ ****************************************************/
+
+class QHttpSetSocketRequest : public QHttpRequest
+{
+public:
+    QHttpSetSocketRequest(QTcpSocket *s) : socket(s)
+    { }
+
+    void start(QHttp *);
+
+    QIODevice *sourceDevice()
+    { return 0; }
+    QIODevice *destinationDevice()
+    { return 0; }
+
+private:
+    QTcpSocket *socket;
+};
+
+void QHttpSetSocketRequest::start(QHttp *http)
+{
+    http->d_func()->setSock(socket);
+    http->d_func()->finishedWithSuccess();
+}
+
+/****************************************************
+ *
+ * QHttpCloseRequest
+ *
+ ****************************************************/
+
+class QHttpCloseRequest : public QHttpRequest
+{
+public:
+    QHttpCloseRequest()
+    { }
+    void start(QHttp *);
+
+    QIODevice *sourceDevice()
+    { return 0; }
+    QIODevice *destinationDevice()
+    { return 0; }
+};
+
+void QHttpCloseRequest::start(QHttp *http)
+{
+    http->d_func()->closeConn();
+}
+
+class QHttpHeaderPrivate
+{
+    Q_DECLARE_PUBLIC(QHttpHeader)
+public:
+    inline virtual ~QHttpHeaderPrivate() {}
+
+    QList<QPair<QString, QString> > values;
+    bool valid;
+    QHttpHeader *q_ptr;
+};
+
+/****************************************************
+ *
+ * QHttpHeader
+ *
+ ****************************************************/
+
+/*!
+    \class QHttpHeader
+    \obsolete
+    \brief The QHttpHeader class contains header information for HTTP.
+
+    \ingroup network
+    \inmodule QtNetwork
+
+    In most cases you should use the more specialized derivatives of
+    this class, QHttpResponseHeader and QHttpRequestHeader, rather
+    than directly using QHttpHeader.
+
+    QHttpHeader provides the HTTP header fields. A HTTP header field
+    consists of a name followed by a colon, a single space, and the
+    field value. (See RFC 1945.) Field names are case-insensitive. A
+    typical header field looks like this:
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 0
+
+    In the API the header field name is called the "key" and the
+    content is called the "value". You can get and set a header
+    field's value by using its key with value() and setValue(), e.g.
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 1
+
+    Some fields are so common that getters and setters are provided
+    for them as a convenient alternative to using \l value() and
+    \l setValue(), e.g. contentLength() and contentType(),
+    setContentLength() and setContentType().
+
+    Each header key has a \e single value associated with it. If you
+    set the value for a key which already exists the previous value
+    will be discarded.
+
+    \sa QHttpRequestHeader QHttpResponseHeader
+*/
+
+/*!
+    \fn int QHttpHeader::majorVersion() const
+
+    Returns the major protocol-version of the HTTP header.
+*/
+
+/*!
+    \fn int QHttpHeader::minorVersion() const
+
+    Returns the minor protocol-version of the HTTP header.
+*/
+
+/*!
+        Constructs an empty HTTP header.
+*/
+QHttpHeader::QHttpHeader()
+    : d_ptr(new QHttpHeaderPrivate)
+{
+    Q_D(QHttpHeader);
+    d->q_ptr = this;
+    d->valid = true;
+}
+
+/*!
+        Constructs a copy of \a header.
+*/
+QHttpHeader::QHttpHeader(const QHttpHeader &header)
+    : d_ptr(new QHttpHeaderPrivate)
+{
+    Q_D(QHttpHeader);
+    d->q_ptr = this;
+    d->valid = header.d_func()->valid;
+    d->values = header.d_func()->values;
+}
+
+/*!
+    Constructs a HTTP header for \a str.
+
+    This constructor parses the string \a str for header fields and
+    adds this information. The \a str should consist of one or more
+    "\r\n" delimited lines; each of these lines should have the format
+    key, colon, space, value.
+*/
+QHttpHeader::QHttpHeader(const QString &str)
+    : d_ptr(new QHttpHeaderPrivate)
+{
+    Q_D(QHttpHeader);
+    d->q_ptr = this;
+    d->valid = true;
+    parse(str);
+}
+
+/*! \internal
+ */
+QHttpHeader::QHttpHeader(QHttpHeaderPrivate &dd, const QString &str)
+    : d_ptr(&dd)
+{
+    Q_D(QHttpHeader);
+    d->q_ptr = this;
+    d->valid = true;
+    if (!str.isEmpty())
+        parse(str);
+}
+
+/*! \internal
+ */
+QHttpHeader::QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header)
+    : d_ptr(&dd)
+{
+    Q_D(QHttpHeader);
+    d->q_ptr = this;
+    d->valid = header.d_func()->valid;
+    d->values = header.d_func()->values;
+}
+/*!
+    Destructor.
+*/
+QHttpHeader::~QHttpHeader()
+{
+}
+
+/*!
+    Assigns \a h and returns a reference to this http header.
+*/
+QHttpHeader &QHttpHeader::operator=(const QHttpHeader &h)
+{
+    Q_D(QHttpHeader);
+    d->values = h.d_func()->values;
+    d->valid = h.d_func()->valid;
+    return *this;
+}
+
+/*!
+    Returns true if the HTTP header is valid; otherwise returns false.
+
+    A QHttpHeader is invalid if it was created by parsing a malformed string.
+*/
+bool QHttpHeader::isValid() const
+{
+    Q_D(const QHttpHeader);
+    return d->valid;
+}
+
+/*! \internal
+    Parses the HTTP header string \a str for header fields and adds
+    the keys/values it finds. If the string is not parsed successfully
+    the QHttpHeader becomes \link isValid() invalid\endlink.
+
+    Returns true if \a str was successfully parsed; otherwise returns false.
+
+    \sa toString()
+*/
+bool QHttpHeader::parse(const QString &str)
+{
+    Q_D(QHttpHeader);
+    QStringList lst;
+    int pos = str.indexOf(QLatin1Char('\n'));
+    if (pos > 0 && str.at(pos - 1) == QLatin1Char('\r'))
+        lst = str.trimmed().split(QLatin1String("\r\n"));
+    else
+        lst = str.trimmed().split(QLatin1String("\n"));
+    lst.removeAll(QString()); // No empties
+
+    if (lst.isEmpty())
+        return true;
+
+    QStringList lines;
+    QStringList::Iterator it = lst.begin();
+    for (; it != lst.end(); ++it) {
+        if (!(*it).isEmpty()) {
+            if ((*it)[0].isSpace()) {
+                if (!lines.isEmpty()) {
+                    lines.last() += QLatin1Char(' ');
+                    lines.last() += (*it).trimmed();
+                }
+            } else {
+                lines.append((*it));
+            }
+        }
+    }
+
+    int number = 0;
+    it = lines.begin();
+    for (; it != lines.end(); ++it) {
+        if (!parseLine(*it, number++)) {
+            d->valid = false;
+            return false;
+        }
+    }
+    return true;
+}
+
+/*! \internal
+*/
+void QHttpHeader::setValid(bool v)
+{
+    Q_D(QHttpHeader);
+    d->valid = v;
+}
+
+/*!
+    Returns the first value for the entry with the given \a key. If no entry
+    has this \a key, an empty string is returned.
+
+    \sa setValue() removeValue() hasKey() keys()
+*/
+QString QHttpHeader::value(const QString &key) const
+{
+    Q_D(const QHttpHeader);
+    QString lowercaseKey = key.toLower();
+    QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+    while (it != d->values.constEnd()) {
+        if ((*it).first.toLower() == lowercaseKey)
+            return (*it).second;
+        ++it;
+    }
+    return QString();
+}
+
+/*!
+    Returns all the entries with the given \a key. If no entry
+    has this \a key, an empty string list is returned.
+*/
+QStringList QHttpHeader::allValues(const QString &key) const
+{
+    Q_D(const QHttpHeader);
+    QString lowercaseKey = key.toLower();
+    QStringList valueList;
+    QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+    while (it != d->values.constEnd()) {
+        if ((*it).first.toLower() == lowercaseKey)
+            valueList.append((*it).second);
+        ++it;
+    }
+    return valueList;
+}
+
+/*!
+    Returns a list of the keys in the HTTP header.
+
+    \sa hasKey()
+*/
+QStringList QHttpHeader::keys() const
+{
+    Q_D(const QHttpHeader);
+    QStringList keyList;
+    QSet<QString> seenKeys;
+    QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+    while (it != d->values.constEnd()) {
+        const QString &key = (*it).first;
+        QString lowercaseKey = key.toLower();
+        if (!seenKeys.contains(lowercaseKey)) {
+            keyList.append(key);
+            seenKeys.insert(lowercaseKey);
+        }
+        ++it;
+    }
+    return keyList;
+}
+
+/*!
+    Returns true if the HTTP header has an entry with the given \a
+    key; otherwise returns false.
+
+    \sa value() setValue() keys()
+*/
+bool QHttpHeader::hasKey(const QString &key) const
+{
+    Q_D(const QHttpHeader);
+    QString lowercaseKey = key.toLower();
+    QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+    while (it != d->values.constEnd()) {
+        if ((*it).first.toLower() == lowercaseKey)
+            return true;
+        ++it;
+    }
+    return false;
+}
+
+/*!
+    Sets the value of the entry with the \a key to \a value.
+
+    If no entry with \a key exists, a new entry with the given \a key
+    and \a value is created. If an entry with the \a key already
+    exists, the first value is discarded and replaced with the given
+    \a value.
+
+    \sa value() hasKey() removeValue()
+*/
+void QHttpHeader::setValue(const QString &key, const QString &value)
+{
+    Q_D(QHttpHeader);
+    QString lowercaseKey = key.toLower();
+    QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+    while (it != d->values.end()) {
+        if ((*it).first.toLower() == lowercaseKey) {
+            (*it).second = value;
+            return;
+        }
+        ++it;
+    }
+    // not found so add
+    addValue(key, value);
+}
+
+/*!
+    Sets the header entries to be the list of key value pairs in \a values.
+*/
+void QHttpHeader::setValues(const QList<QPair<QString, QString> > &values)
+{
+    Q_D(QHttpHeader);
+    d->values = values;
+}
+
+/*!
+    Adds a new entry with the \a key and \a value.
+*/
+void QHttpHeader::addValue(const QString &key, const QString &value)
+{
+    Q_D(QHttpHeader);
+    d->values.append(qMakePair(key, value));
+}
+
+/*!
+    Returns all the entries in the header.
+*/
+QList<QPair<QString, QString> > QHttpHeader::values() const
+{
+    Q_D(const QHttpHeader);
+    return d->values;
+}
+
+/*!
+    Removes the entry with the key \a key from the HTTP header.
+
+    \sa value() setValue()
+*/
+void QHttpHeader::removeValue(const QString &key)
+{
+    Q_D(QHttpHeader);
+    QString lowercaseKey = key.toLower();
+    QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+    while (it != d->values.end()) {
+        if ((*it).first.toLower() == lowercaseKey) {
+            d->values.erase(it);
+            return;
+        }
+        ++it;
+    }
+}
+
+/*!
+    Removes all the entries with the key \a key from the HTTP header.
+*/
+void QHttpHeader::removeAllValues(const QString &key)
+{
+    Q_D(QHttpHeader);
+    QString lowercaseKey = key.toLower();
+    QList<QPair<QString, QString> >::Iterator it = d->values.begin();
+    while (it != d->values.end()) {
+        if ((*it).first.toLower() == lowercaseKey) {
+            it = d->values.erase(it);
+            continue;
+        }
+        ++it;
+    }
+}
+
+/*! \internal
+    Parses the single HTTP header line \a line which has the format
+    key, colon, space, value, and adds key/value to the headers. The
+    linenumber is \a number. Returns true if the line was successfully
+    parsed and the key/value added; otherwise returns false.
+
+    \sa parse()
+*/
+bool QHttpHeader::parseLine(const QString &line, int)
+{
+    int i = line.indexOf(QLatin1Char(':'));
+    if (i == -1)
+        return false;
+
+    addValue(line.left(i).trimmed(), line.mid(i + 1).trimmed());
+
+    return true;
+}
+
+/*!
+    Returns a string representation of the HTTP header.
+
+    The string is suitable for use by the constructor that takes a
+    QString. It consists of lines with the format: key, colon, space,
+    value, "\r\n".
+*/
+QString QHttpHeader::toString() const
+{
+    Q_D(const QHttpHeader);
+    if (!isValid())
+        return QLatin1String("");
+
+    QString ret = QLatin1String("");
+
+    QList<QPair<QString, QString> >::ConstIterator it = d->values.constBegin();
+    while (it != d->values.constEnd()) {
+        ret += (*it).first + QLatin1String(": ") + (*it).second + QLatin1String("\r\n");
+        ++it;
+    }
+    return ret;
+}
+
+/*!
+    Returns true if the header has an entry for the special HTTP
+    header field \c content-length; otherwise returns false.
+
+    \sa contentLength() setContentLength()
+*/
+bool QHttpHeader::hasContentLength() const
+{
+    return hasKey(QLatin1String("content-length"));
+}
+
+/*!
+    Returns the value of the special HTTP header field \c
+    content-length.
+
+    \sa setContentLength() hasContentLength()
+*/
+uint QHttpHeader::contentLength() const
+{
+    return value(QLatin1String("content-length")).toUInt();
+}
+
+/*!
+    Sets the value of the special HTTP header field \c content-length
+    to \a len.
+
+    \sa contentLength() hasContentLength()
+*/
+void QHttpHeader::setContentLength(int len)
+{
+    setValue(QLatin1String("content-length"), QString::number(len));
+}
+
+/*!
+    Returns true if the header has an entry for the special HTTP
+    header field \c content-type; otherwise returns false.
+
+    \sa contentType() setContentType()
+*/
+bool QHttpHeader::hasContentType() const
+{
+    return hasKey(QLatin1String("content-type"));
+}
+
+/*!
+    Returns the value of the special HTTP header field \c content-type.
+
+    \sa setContentType() hasContentType()
+*/
+QString QHttpHeader::contentType() const
+{
+    QString type = value(QLatin1String("content-type"));
+    if (type.isEmpty())
+        return QString();
+
+    int pos = type.indexOf(QLatin1Char(';'));
+    if (pos == -1)
+        return type;
+
+    return type.left(pos).trimmed();
+}
+
+/*!
+    Sets the value of the special HTTP header field \c content-type to
+    \a type.
+
+    \sa contentType() hasContentType()
+*/
+void QHttpHeader::setContentType(const QString &type)
+{
+    setValue(QLatin1String("content-type"), type);
+}
+
+class QHttpResponseHeaderPrivate : public QHttpHeaderPrivate
+{
+    Q_DECLARE_PUBLIC(QHttpResponseHeader)
+public:
+    int statCode;
+    QString reasonPhr;
+    int majVer;
+    int minVer;
+};
+
+/****************************************************
+ *
+ * QHttpResponseHeader
+ *
+ ****************************************************/
+
+/*!
+    \class QHttpResponseHeader
+    \obsolete
+    \brief The QHttpResponseHeader class contains response header information for HTTP.
+
+    \ingroup network
+    \inmodule QtNetwork
+
+    This class is used by the QHttp class to report the header
+    information that the client received from the server.
+
+    HTTP responses have a status code that indicates the status of the
+    response. This code is a 3-digit integer result code (for details
+    see to RFC 1945). In addition to the status code, you can also
+    specify a human-readable text that describes the reason for the
+    code ("reason phrase"). This class allows you to get the status
+    code and the reason phrase.
+
+    \sa QHttpRequestHeader, QHttp, {HTTP Example}
+*/
+
+/*!
+    Constructs an empty HTTP response header.
+*/
+QHttpResponseHeader::QHttpResponseHeader()
+    : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+    setValid(false);
+}
+
+/*!
+    Constructs a copy of \a header.
+*/
+QHttpResponseHeader::QHttpResponseHeader(const QHttpResponseHeader &header)
+    : QHttpHeader(*new QHttpResponseHeaderPrivate, header)
+{
+    Q_D(QHttpResponseHeader);
+    d->statCode = header.d_func()->statCode;
+    d->reasonPhr = header.d_func()->reasonPhr;
+    d->majVer = header.d_func()->majVer;
+    d->minVer = header.d_func()->minVer;
+}
+
+/*!
+    Copies the contents of \a header into this QHttpResponseHeader.
+*/
+QHttpResponseHeader &QHttpResponseHeader::operator=(const QHttpResponseHeader &header)
+{
+    Q_D(QHttpResponseHeader);
+    QHttpHeader::operator=(header);
+    d->statCode = header.d_func()->statCode;
+    d->reasonPhr = header.d_func()->reasonPhr;
+    d->majVer = header.d_func()->majVer;
+    d->minVer = header.d_func()->minVer;
+    return *this;
+}
+
+/*!
+    Constructs a HTTP response header from the string \a str. The
+    string is parsed and the information is set. The \a str should
+    consist of one or more "\r\n" delimited lines; the first line should be the
+    status-line (format: HTTP-version, space, status-code, space,
+    reason-phrase); each of remaining lines should have the format key, colon,
+    space, value.
+*/
+QHttpResponseHeader::QHttpResponseHeader(const QString &str)
+    : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+    parse(str);
+}
+
+/*!
+    \since 4.1
+
+    Constructs a QHttpResponseHeader, setting the status code to \a code, the
+    reason phrase to \a text and the protocol-version to \a majorVer and \a
+    minorVer.
+
+    \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+QHttpResponseHeader::QHttpResponseHeader(int code, const QString &text, int majorVer, int minorVer)
+    : QHttpHeader(*new QHttpResponseHeaderPrivate)
+{
+    setStatusLine(code, text, majorVer, minorVer);
+}
+
+/*!
+    \since 4.1
+
+    Sets the status code to \a code, the reason phrase to \a text and
+    the protocol-version to \a majorVer and \a minorVer.
+
+    \sa statusCode() reasonPhrase() majorVersion() minorVersion()
+*/
+void QHttpResponseHeader::setStatusLine(int code, const QString &text, int majorVer, int minorVer)
+{
+    Q_D(QHttpResponseHeader);
+    setValid(true);
+    d->statCode = code;
+    d->reasonPhr = text;
+    d->majVer = majorVer;
+    d->minVer = minorVer;
+}
+
+/*!
+    Returns the status code of the HTTP response header.
+
+    \sa reasonPhrase() majorVersion() minorVersion()
+*/
+int QHttpResponseHeader::statusCode() const
+{
+    Q_D(const QHttpResponseHeader);
+    return d->statCode;
+}
+
+/*!
+    Returns the reason phrase of the HTTP response header.
+
+    \sa statusCode() majorVersion() minorVersion()
+*/
+QString QHttpResponseHeader::reasonPhrase() const
+{
+    Q_D(const QHttpResponseHeader);
+    return d->reasonPhr;
+}
+
+/*!
+    Returns the major protocol-version of the HTTP response header.
+
+    \sa minorVersion() statusCode() reasonPhrase()
+*/
+int QHttpResponseHeader::majorVersion() const
+{
+    Q_D(const QHttpResponseHeader);
+    return d->majVer;
+}
+
+/*!
+    Returns the minor protocol-version of the HTTP response header.
+
+    \sa majorVersion() statusCode() reasonPhrase()
+*/
+int QHttpResponseHeader::minorVersion() const
+{
+    Q_D(const QHttpResponseHeader);
+    return d->minVer;
+}
+
+/*! \internal
+*/
+bool QHttpResponseHeader::parseLine(const QString &line, int number)
+{
+    Q_D(QHttpResponseHeader);
+    if (number != 0)
+        return QHttpHeader::parseLine(line, number);
+
+    QString l = line.simplified();
+    if (l.length() < 10)
+        return false;
+
+    if (l.left(5) == QLatin1String("HTTP/") && l[5].isDigit() && l[6] == QLatin1Char('.') &&
+        l[7].isDigit() && l[8] == QLatin1Char(' ') && l[9].isDigit()) {
+        d->majVer = l[5].toLatin1() - '0';
+        d->minVer = l[7].toLatin1() - '0';
+
+        int pos = l.indexOf(QLatin1Char(' '), 9);
+        if (pos != -1) {
+            d->reasonPhr = l.mid(pos + 1);
+            d->statCode = l.mid(9, pos - 9).toInt();
+        } else {
+            d->statCode = l.mid(9).toInt();
+            d->reasonPhr.clear();
+        }
+    } else {
+        return false;
+    }
+
+    return true;
+}
+
+/*! \reimp
+*/
+QString QHttpResponseHeader::toString() const
+{
+    Q_D(const QHttpResponseHeader);
+    QString ret(QLatin1String("HTTP/%1.%2 %3 %4\r\n%5\r\n"));
+    return ret.arg(d->majVer).arg(d->minVer).arg(d->statCode).arg(d->reasonPhr).arg(QHttpHeader::toString());
+}
+
+class QHttpRequestHeaderPrivate : public QHttpHeaderPrivate
+{
+    Q_DECLARE_PUBLIC(QHttpRequestHeader)
+public:
+    QString m;
+    QString p;
+    int majVer;
+    int minVer;
+};
+
+/****************************************************
+ *
+ * QHttpRequestHeader
+ *
+ ****************************************************/
+
+/*!
+    \class QHttpRequestHeader
+    \obsolete
+    \brief The QHttpRequestHeader class contains request header information for HTTP.
+
+    \ingroup network
+    \inmodule QtNetwork
+
+    This class is used in the QHttp class to report the header
+    information if the client requests something from the server.
+
+    HTTP requests have a method which describes the request's action.
+    The most common requests are "GET" and "POST". In addition to the
+    request method the header also includes a request-URI to specify
+    the location for the method to use.
+
+    The method, request-URI and protocol-version can be set using a
+    constructor or later using setRequest(). The values can be
+    obtained using method(), path(), majorVersion() and
+    minorVersion().
+
+    Note that the request-URI must be in the format expected by the
+    HTTP server. That is, all reserved characters must be encoded in
+    %HH (where HH are two hexadecimal digits). See
+    QUrl::toPercentEncoding() for more information.
+
+    Important inherited functions: setValue() and value().
+
+    \sa QHttpResponseHeader QHttp
+*/
+
+/*!
+    Constructs an empty HTTP request header.
+*/
+QHttpRequestHeader::QHttpRequestHeader()
+    : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+    setValid(false);
+}
+
+/*!
+    Constructs a HTTP request header for the method \a method, the
+    request-URI \a path and the protocol-version \a majorVer and \a
+    minorVer. The \a path argument must be properly encoded for an
+    HTTP request.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QString &method, const QString &path, int majorVer, int minorVer)
+    : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+    Q_D(QHttpRequestHeader);
+    d->m = method;
+    d->p = path;
+    d->majVer = majorVer;
+    d->minVer = minorVer;
+}
+
+/*!
+    Constructs a copy of \a header.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QHttpRequestHeader &header)
+    : QHttpHeader(*new QHttpRequestHeaderPrivate, header)
+{
+    Q_D(QHttpRequestHeader);
+    d->m = header.d_func()->m;
+    d->p = header.d_func()->p;
+    d->majVer = header.d_func()->majVer;
+    d->minVer = header.d_func()->minVer;
+}
+
+/*!
+    Copies the content of \a header into this QHttpRequestHeader
+*/
+QHttpRequestHeader &QHttpRequestHeader::operator=(const QHttpRequestHeader &header)
+{
+    Q_D(QHttpRequestHeader);
+    QHttpHeader::operator=(header);
+    d->m = header.d_func()->m;
+    d->p = header.d_func()->p;
+    d->majVer = header.d_func()->majVer;
+    d->minVer = header.d_func()->minVer;
+    return *this;
+}
+
+/*!
+    Constructs a HTTP request header from the string \a str. The \a
+    str should consist of one or more "\r\n" delimited lines; the first line
+    should be the request-line (format: method, space, request-URI, space
+    HTTP-version); each of the remaining lines should have the format key,
+    colon, space, value.
+*/
+QHttpRequestHeader::QHttpRequestHeader(const QString &str)
+    : QHttpHeader(*new QHttpRequestHeaderPrivate)
+{
+    parse(str);
+}
+
+/*!
+    This function sets the request method to \a method, the
+    request-URI to \a path and the protocol-version to \a majorVer and
+    \a minorVer. The \a path argument must be properly encoded for an
+    HTTP request.
+
+    \sa method() path() majorVersion() minorVersion()
+*/
+void QHttpRequestHeader::setRequest(const QString &method, const QString &path, int majorVer, int minorVer)
+{
+    Q_D(QHttpRequestHeader);
+    setValid(true);
+    d->m = method;
+    d->p = path;
+    d->majVer = majorVer;
+    d->minVer = minorVer;
+}
+
+/*!
+    Returns the method of the HTTP request header.
+
+    \sa path() majorVersion() minorVersion() setRequest()
+*/
+QString QHttpRequestHeader::method() const
+{
+    Q_D(const QHttpRequestHeader);
+    return d->m;
+}
+
+/*!
+    Returns the request-URI of the HTTP request header.
+
+    \sa method() majorVersion() minorVersion() setRequest()
+*/
+QString QHttpRequestHeader::path() const
+{
+    Q_D(const QHttpRequestHeader);
+    return d->p;
+}
+
+/*!
+    Returns the major protocol-version of the HTTP request header.
+
+    \sa minorVersion() method() path() setRequest()
+*/
+int QHttpRequestHeader::majorVersion() const
+{
+    Q_D(const QHttpRequestHeader);
+    return d->majVer;
+}
+
+/*!
+    Returns the minor protocol-version of the HTTP request header.
+
+    \sa majorVersion() method() path() setRequest()
+*/
+int QHttpRequestHeader::minorVersion() const
+{
+    Q_D(const QHttpRequestHeader);
+    return d->minVer;
+}
+
+/*! \internal
+*/
+bool QHttpRequestHeader::parseLine(const QString &line, int number)
+{
+    Q_D(QHttpRequestHeader);
+    if (number != 0)
+        return QHttpHeader::parseLine(line, number);
+
+    QStringList lst = line.simplified().split(QLatin1String(" "));
+    if (lst.count() > 0) {
+        d->m = lst[0];
+        if (lst.count() > 1) {
+            d->p = lst[1];
+            if (lst.count() > 2) {
+                QString v = lst[2];
+                if (v.length() >= 8 && v.left(5) == QLatin1String("HTTP/") &&
+                    v[5].isDigit() && v[6] == QLatin1Char('.') && v[7].isDigit()) {
+                    d->majVer = v[5].toLatin1() - '0';
+                    d->minVer = v[7].toLatin1() - '0';
+                    return true;
+                }
+            }
+        }
+    }
+
+    return false;
+}
+
+/*! \reimp
+*/
+QString QHttpRequestHeader::toString() const
+{
+    Q_D(const QHttpRequestHeader);
+    QString first(QLatin1String("%1 %2"));
+    QString last(QLatin1String(" HTTP/%3.%4\r\n%5\r\n"));
+    return first.arg(d->m).arg(d->p) +
+        last.arg(d->majVer).arg(d->minVer).arg(QHttpHeader::toString());
+}
+
+
+/****************************************************
+ *
+ * QHttp
+ *
+ ****************************************************/
+/*!
+    \class QHttp
+    \obsolete
+    \reentrant
+
+    \brief The QHttp class provides an implementation of the HTTP protocol.
+
+    \ingroup network
+    \inmodule QtNetwork
+
+
+    This class provides a direct interface to HTTP that allows you to
+    download and upload data with the HTTP protocol.
+    However, for new applications, it is
+    recommended to use QNetworkAccessManager and QNetworkReply, as
+    those classes possess a simpler, yet more powerful API
+    and a more modern protocol implementation.
+
+    The class works asynchronously, so there are no blocking
+    functions. If an operation cannot be executed immediately, the
+    function will still return straight away and the operation will be
+    scheduled for later execution. The results of scheduled operations
+    are reported via signals. This approach depends on the event loop
+    being in operation.
+
+    The operations that can be scheduled (they are called "requests"
+    in the rest of the documentation) are the following: setHost(),
+    get(), post(), head() and request().
+
+    All of these requests return a unique identifier that allows you
+    to keep track of the request that is currently executed. When the
+    execution of a request starts, the requestStarted() signal with
+    the identifier is emitted and when the request is finished, the
+    requestFinished() signal is emitted with the identifier and a bool
+    that indicates if the request finished with an error.
+
+    To make an HTTP request you must set up suitable HTTP headers. The
+    following example demonstrates how to request the main HTML page
+    from the Qt website (i.e., the URL \c http://qt.nokia.com/index.html):
+
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 2
+
+    For the common HTTP requests \c GET, \c POST and \c HEAD, QHttp
+    provides the convenience functions get(), post() and head(). They
+    already use a reasonable header and if you don't have to set
+    special header fields, they are easier to use. The above example
+    can also be written as:
+
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 3
+
+    For this example the following sequence of signals is emitted
+    (with small variations, depending on network traffic, etc.):
+
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 4
+
+    The dataSendProgress() and dataReadProgress() signals in the above
+    example are useful if you want to show a \link QProgressBar
+    progress bar\endlink to inform the user about the progress of the
+    download. The second argument is the total size of data. In
+    certain cases it is not possible to know the total amount in
+    advance, in which case the second argument is 0. (If you connect
+    to a QProgressBar a total of 0 results in a busy indicator.)
+
+    When the response header is read, it is reported with the
+    responseHeaderReceived() signal.
+
+    The readyRead() signal tells you that there is data ready to be
+    read. The amount of data can then be queried with the
+    bytesAvailable() function and it can be read with the read()
+    or readAll() functions.
+
+    If an error occurs during the execution of one of the commands in
+    a sequence of commands, all the pending commands (i.e. scheduled,
+    but not yet executed commands) are cleared and no signals are
+    emitted for them.
+
+    For example, if you have the following sequence of requests
+
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 5
+
+    and the get() request fails because the host lookup fails, then
+    the post() request is never executed and the signals would look
+    like this:
+
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 6
+
+    You can then get details about the error with the error() and
+    errorString() functions. Note that only unexpected behavior, like
+    network failure is considered as an error. If the server response
+    contains an error status, like a 404 response, this is reported as
+    a normal response case. So you should always check the \link
+    QHttpResponseHeader::statusCode() status code \endlink of the
+    response header.
+
+    The functions currentId() and currentRequest() provide more
+    information about the currently executing request.
+
+    The functions hasPendingRequests() and clearPendingRequests()
+    allow you to query and clear the list of pending requests.
+
+    \sa QFtp, QNetworkAccessManager, QNetworkRequest, QNetworkReply,
+        {HTTP Example}, {Torrent Example}
+*/
+
+/*!
+    Constructs a QHttp object. The \a parent parameter is passed on
+    to the QObject constructor.
+*/
+QHttp::QHttp(QObject *parent)
+    : QObject(*new QHttpPrivate, parent)
+{
+    Q_D(QHttp);
+    d->init();
+}
+
+/*!
+    Constructs a QHttp object. Subsequent requests are done by
+    connecting to the server \a hostName on port \a port.
+
+    The \a parent parameter is passed on to the QObject constructor.
+
+    \sa setHost()
+*/
+QHttp::QHttp(const QString &hostName, quint16 port, QObject *parent)
+    : QObject(*new QHttpPrivate, parent)
+{
+    Q_D(QHttp);
+    d->init();
+
+    d->hostName = hostName;
+    d->port = port;
+}
+
+/*!
+    Constructs a QHttp object. Subsequent requests are done by
+    connecting to the server \a hostName on port \a port using the
+    connection mode \a mode.
+
+    If port is 0, it will use the default port for the \a mode used
+    (80 for Http and 443 for Https).
+
+    The \a parent parameter is passed on to the QObject constructor.
+
+    \sa setHost()
+*/
+QHttp::QHttp(const QString &hostName, ConnectionMode mode, quint16 port, QObject *parent)
+    : QObject(*new QHttpPrivate, parent)
+{
+    Q_D(QHttp);
+    d->init();
+
+    d->hostName = hostName;
+    if (port == 0)
+        port = (mode == ConnectionModeHttp) ? 80 : 443;
+    d->port = port;
+    d->mode = mode;
+}
+
+void QHttpPrivate::init()
+{
+    Q_Q(QHttp);
+    errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown error"));
+    QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+    post100ContinueTimer.setSingleShot(true);
+    QObject::connect(&post100ContinueTimer, SIGNAL(timeout()), q, SLOT(_q_continuePost()));
+}
+
+/*!
+    Destroys the QHttp object. If there is an open connection, it is
+    closed.
+*/
+QHttp::~QHttp()
+{
+    abort();
+}
+
+/*!
+    \enum QHttp::ConnectionMode
+    \since 4.3
+
+    This enum is used to specify the mode of connection to use:
+
+    \value ConnectionModeHttp The connection is a regular HTTP connection to the server
+    \value ConnectionModeHttps The HTTPS protocol is used and the connection is encrypted using SSL.
+
+    When using the HTTPS mode, care should be taken to connect to the sslErrors signal, and
+    handle possible SSL errors.
+
+    \sa QSslSocket
+*/
+
+/*!
+    \enum QHttp::State
+
+    This enum is used to specify the state the client is in:
+
+    \value Unconnected There is no connection to the host.
+    \value HostLookup A host name lookup is in progress.
+    \value Connecting An attempt to connect to the host is in progress.
+    \value Sending The client is sending its request to the server.
+    \value Reading The client's request has been sent and the client
+    is reading the server's response.
+    \value Connected The connection to the host is open, but the client is
+    neither sending a request, nor waiting for a response.
+    \value Closing The connection is closing down, but is not yet
+    closed. (The state will be \c Unconnected when the connection is
+    closed.)
+
+    \sa stateChanged() state()
+*/
+
+/*!  \enum QHttp::Error
+
+    This enum identifies the error that occurred.
+
+    \value NoError No error occurred.
+    \value HostNotFound The host name lookup failed.
+    \value ConnectionRefused The server refused the connection.
+    \value UnexpectedClose The server closed the connection unexpectedly.
+    \value InvalidResponseHeader The server sent an invalid response header.
+    \value WrongContentLength The client could not read the content correctly
+    because an error with respect to the content length occurred.
+    \value Aborted The request was aborted with abort().
+    \value ProxyAuthenticationRequiredError QHttp is using a proxy, and the
+    proxy server requires authentication to establish a connection.
+    \value AuthenticationRequiredError The web server requires authentication
+    to complete the request.
+    \value UnknownError An error other than those specified above
+    occurred.
+
+    \sa error()
+*/
+
+/*!
+    \fn void QHttp::stateChanged(int state)
+
+    This signal is emitted when the state of the QHttp object changes.
+    The argument \a state is the new state of the connection; it is
+    one of the \l State values.
+
+    This usually happens when a request is started, but it can also
+    happen when the server closes the connection or when a call to
+    close() succeeded.
+
+    \sa get() post() head() request() close() state() State
+*/
+
+/*!
+    \fn void QHttp::responseHeaderReceived(const QHttpResponseHeader &resp);
+
+    This signal is emitted when the HTTP header of a server response
+    is available. The header is passed in \a resp.
+
+    \sa get() post() head() request() readyRead()
+*/
+
+/*!
+    \fn void QHttp::readyRead(const QHttpResponseHeader &resp)
+
+    This signal is emitted when there is new response data to read.
+
+    If you specified a device in the request where the data should be
+    written to, then this signal is \e not emitted; instead the data
+    is written directly to the device.
+
+    The response header is passed in \a resp.
+
+    You can read the data with the readAll() or read() functions
+
+    This signal is useful if you want to process the data in chunks as
+    soon as it becomes available. If you are only interested in the
+    complete data, just connect to the requestFinished() signal and
+    read the data then instead.
+
+    \sa get() post() request() readAll() read() bytesAvailable()
+*/
+
+/*!
+    \fn void QHttp::dataSendProgress(int done, int total)
+
+    This signal is emitted when this object sends data to a HTTP
+    server to inform it about the progress of the upload.
+
+    \a done is the amount of data that has already arrived and \a
+    total is the total amount of data. It is possible that the total
+    amount of data that should be transferred cannot be determined, in
+    which case \a total is 0.(If you connect to a QProgressBar, the
+    progress bar shows a busy indicator if the total is 0).
+
+    \warning \a done and \a total are not necessarily the size in
+    bytes, since for large files these values might need to be
+    "scaled" to avoid overflow.
+
+    \sa dataReadProgress(), post(), request(), QProgressBar
+*/
+
+/*!
+    \fn void QHttp::dataReadProgress(int done, int total)
+
+    This signal is emitted when this object reads data from a HTTP
+    server to indicate the current progress of the download.
+
+    \a done is the amount of data that has already arrived and \a
+    total is the total amount of data. It is possible that the total
+    amount of data that should be transferred cannot be determined, in
+    which case \a total is 0.(If you connect to a QProgressBar, the
+    progress bar shows a busy indicator if the total is 0).
+
+    \warning \a done and \a total are not necessarily the size in
+    bytes, since for large files these values might need to be
+    "scaled" to avoid overflow.
+
+    \sa dataSendProgress() get() post() request() QProgressBar
+*/
+
+/*!
+    \fn void QHttp::requestStarted(int id)
+
+    This signal is emitted when processing the request identified by
+    \a id starts.
+
+    \sa requestFinished() done()
+*/
+
+/*!
+    \fn void QHttp::requestFinished(int id, bool error)
+
+    This signal is emitted when processing the request identified by
+    \a id has finished. \a error is true if an error occurred during
+    the processing; otherwise \a error is false.
+
+    \sa requestStarted() done() error() errorString()
+*/
+
+/*!
+    \fn void QHttp::done(bool error)
+
+    This signal is emitted when the last pending request has finished;
+    (it is emitted after the last request's requestFinished() signal).
+    \a error is true if an error occurred during the processing;
+    otherwise \a error is false.
+
+    \sa requestFinished() error() errorString()
+*/
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*!
+    \fn void QHttp::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
+    \since 4.3
+
+    This signal can be emitted when a \a proxy that requires
+    authentication is used. The \a authenticator object can then be
+    filled in with the required details to allow authentication and
+    continue the connection.
+
+    \note It is not possible to use a QueuedConnection to connect to
+    this signal, as the connection will fail if the authenticator has
+    not been filled in with new information when the signal returns.
+
+    \sa QAuthenticator, QNetworkProxy
+*/
+
+#endif
+
+/*!
+    \fn void QHttp::authenticationRequired(const QString &hostname, quint16 port, QAuthenticator *authenticator)
+    \since 4.3
+
+    This signal can be emitted when a web server on a given \a hostname and \a
+    port requires authentication. The \a authenticator object can then be
+    filled in with the required details to allow authentication and continue
+    the connection.
+
+    \note It is not possible to use a QueuedConnection to connect to
+    this signal, as the connection will fail if the authenticator has
+    not been filled in with new information when the signal returns.
+
+    \sa QAuthenticator, QNetworkProxy
+*/
+
+/*!
+    \fn void QHttp::sslErrors(const QList<QSslError> &errors)
+    \since 4.3
+
+    Forwards the sslErrors signal from the QSslSocket used in QHttp. \a errors
+    is the list of errors that occurred during the SSL handshake. Unless you
+    call ignoreSslErrors() from within a slot connected to this signal when an
+    error occurs, QHttp will tear down the connection immediately after
+    emitting the signal.
+
+    \sa QSslSocket QSslSocket::ignoreSslErrors()
+*/
+
+/*!
+    Aborts the current request and deletes all scheduled requests.
+
+    For the current request, the requestFinished() signal with the \c
+    error argument \c true is emitted. For all other requests that are
+    affected by the abort(), no signals are emitted.
+
+    Since this slot also deletes the scheduled requests, there are no
+    requests left and the done() signal is emitted (with the \c error
+    argument \c true).
+
+    \sa clearPendingRequests()
+*/
+void QHttp::abort()
+{
+    Q_D(QHttp);
+    if (d->pending.isEmpty())
+        return;
+
+    d->finishedWithError(tr("Request aborted"), Aborted);
+    clearPendingRequests();
+    if (d->socket)
+        d->socket->abort();
+    d->closeConn();
+}
+
+/*!
+    Returns the number of bytes that can be read from the response
+    content at the moment.
+
+    \sa get() post() request() readyRead() read() readAll()
+*/
+qint64 QHttp::bytesAvailable() const
+{
+    Q_D(const QHttp);
+#if defined(QHTTP_DEBUG)
+    qDebug("QHttp::bytesAvailable(): %d bytes", (int)d->rba.size());
+#endif
+    return qint64(d->rba.size());
+}
+
+/*! \fn qint64 QHttp::readBlock(char *data, quint64 maxlen)
+
+    Use read() instead.
+*/
+
+/*!
+    Reads \a maxlen bytes from the response content into \a data and
+    returns the number of bytes read. Returns -1 if an error occurred.
+
+    \sa get() post() request() readyRead() bytesAvailable() readAll()
+*/
+qint64 QHttp::read(char *data, qint64 maxlen)
+{
+    Q_D(QHttp);
+    if (data == 0 && maxlen != 0) {
+        qWarning("QHttp::read: Null pointer error");
+        return -1;
+    }
+    if (maxlen >= d->rba.size())
+        maxlen = d->rba.size();
+    int readSoFar = 0;
+    while (!d->rba.isEmpty() && readSoFar < maxlen) {
+        int nextBlockSize = d->rba.nextDataBlockSize();
+        int bytesToRead = qMin<qint64>(maxlen - readSoFar, nextBlockSize);
+        memcpy(data + readSoFar, d->rba.readPointer(), bytesToRead);
+        d->rba.free(bytesToRead);
+        readSoFar += bytesToRead;
+    }
+
+    d->bytesDone += maxlen;
+#if defined(QHTTP_DEBUG)
+    qDebug("QHttp::read(): read %lld bytes (%lld bytes done)", maxlen, d->bytesDone);
+#endif
+    return maxlen;
+}
+
+/*!
+    Reads all the bytes from the response content and returns them.
+
+    \sa get() post() request() readyRead() bytesAvailable() read()
+*/
+QByteArray QHttp::readAll()
+{
+    qint64 avail = bytesAvailable();
+    QByteArray tmp;
+    tmp.resize(int(avail));
+    qint64 got = read(tmp.data(), int(avail));
+    tmp.resize(got);
+    return tmp;
+}
+
+/*!
+    Returns the identifier of the HTTP request being executed or 0 if
+    there is no request being executed (i.e. they've all finished).
+
+    \sa currentRequest()
+*/
+int QHttp::currentId() const
+{
+    Q_D(const QHttp);
+    if (d->pending.isEmpty())
+        return 0;
+    return d->pending.first()->id;
+}
+
+/*!
+    Returns the request header of the HTTP request being executed. If
+    the request is one issued by setHost() or close(), it
+    returns an invalid request header, i.e.
+    QHttpRequestHeader::isValid() returns false.
+
+    \sa currentId()
+*/
+QHttpRequestHeader QHttp::currentRequest() const
+{
+    Q_D(const QHttp);
+    if (!d->pending.isEmpty()) {
+        QHttpRequest *r = d->pending.first();
+        if (r->hasRequestHeader())
+            return r->requestHeader();
+    }
+    return QHttpRequestHeader();
+}
+
+/*!
+    Returns the received response header of the most recently finished HTTP
+    request. If no response has yet been received
+    QHttpResponseHeader::isValid() will return false.
+
+    \sa currentRequest()
+*/
+QHttpResponseHeader QHttp::lastResponse() const
+{
+    Q_D(const QHttp);
+    return d->response;
+}
+
+/*!
+    Returns the QIODevice pointer that is used as the data source of the HTTP
+    request being executed. If there is no current request or if the request
+    does not use an IO device as the data source, this function returns 0.
+
+    This function can be used to delete the QIODevice in the slot connected to
+    the requestFinished() signal.
+
+    \sa currentDestinationDevice() post() request()
+*/
+QIODevice *QHttp::currentSourceDevice() const
+{
+    Q_D(const QHttp);
+    if (d->pending.isEmpty())
+        return 0;
+    return d->pending.first()->sourceDevice();
+}
+
+/*!
+    Returns the QIODevice pointer that is used as to store the data of the HTTP
+    request being executed. If there is no current request or if the request
+    does not store the data to an IO device, this function returns 0.
+
+    This function can be used to delete the QIODevice in the slot connected to
+    the requestFinished() signal.
+
+    \sa currentSourceDevice() get() post() request()
+*/
+QIODevice *QHttp::currentDestinationDevice() const
+{
+    Q_D(const QHttp);
+    if (d->pending.isEmpty())
+        return 0;
+    return d->pending.first()->destinationDevice();
+}
+
+/*!
+    Returns true if there are any requests scheduled that have not yet
+    been executed; otherwise returns false.
+
+    The request that is being executed is \e not considered as a
+    scheduled request.
+
+    \sa clearPendingRequests() currentId() currentRequest()
+*/
+bool QHttp::hasPendingRequests() const
+{
+    Q_D(const QHttp);
+    return d->pending.count() > 1;
+}
+
+/*!
+    Deletes all pending requests from the list of scheduled requests.
+    This does not affect the request that is being executed. If
+    you want to stop this as well, use abort().
+
+    \sa hasPendingRequests() abort()
+*/
+void QHttp::clearPendingRequests()
+{
+    Q_D(QHttp);
+    // delete all entires except the first one
+    while (d->pending.count() > 1)
+        delete d->pending.takeLast();
+}
+
+/*!
+    Sets the HTTP server that is used for requests to \a hostName on
+    port \a port.
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::setHost(const QString &hostName, quint16 port)
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpSetHostRequest(hostName, port, ConnectionModeHttp));
+}
+
+/*!
+    Sets the HTTP server that is used for requests to \a hostName on
+    port \a port using the connection mode \a mode.
+
+    If port is 0, it will use the default port for the \a mode used
+    (80 for HTTP and 443 for HTTPS).
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    \sa get() post() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::setHost(const QString &hostName, ConnectionMode mode, quint16 port)
+{
+#ifdef QT_NO_OPENSSL
+    if (mode == ConnectionModeHttps)
+        qWarning("QHttp::setHost: HTTPS connection requested but SSL support not compiled in");
+#endif
+    Q_D(QHttp);
+    if (port == 0)
+        port = (mode == ConnectionModeHttp) ? 80 : 443;
+    return d->addRequest(new QHttpSetHostRequest(hostName, port, mode));
+}
+
+/*!
+    Replaces the internal QTcpSocket that QHttp uses with \a
+    socket. This is useful if you want to use your own custom QTcpSocket
+    subclass instead of the plain QTcpSocket that QHttp uses by default.
+    QHttp does not take ownership of the socket, and will not delete \a
+    socket when destroyed.
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    Note: If QHttp is used in a non-GUI thread that runs its own event
+    loop, you must move \a socket to that thread before calling setSocket().
+
+    \sa QObject::moveToThread(), {Thread Support in Qt}
+*/
+int QHttp::setSocket(QTcpSocket *socket)
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpSetSocketRequest(socket));
+}
+
+/*!
+    This function sets the user name \a userName and password \a
+    password for web pages that require authentication.
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+*/
+int QHttp::setUser(const QString &userName, const QString &password)
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpSetUserRequest(userName, password));
+}
+
+#ifndef QT_NO_NETWORKPROXY
+
+/*!
+    Enables HTTP proxy support, using the proxy server \a host on port \a
+    port. \a username and \a password can be provided if the proxy server
+    requires authentication.
+
+    Example:
+
+    \snippet doc/src/snippets/code/src_network_access_qhttp.cpp 7
+
+    QHttp supports non-transparent web proxy servers only, such as the Squid
+    Web proxy cache server (from \l http://www.squid.org/). For transparent
+    proxying, such as SOCKS5, use QNetworkProxy instead.
+
+    \note setProxy() has to be called before setHost() for it to take effect.
+    If setProxy() is called after setHost(), then it will not apply until after 
+    setHost() is called again.
+
+    \sa QFtp::setProxy()
+*/
+int QHttp::setProxy(const QString &host, int port,
+                    const QString &username, const QString &password)
+{
+    Q_D(QHttp);
+    QNetworkProxy proxy(QNetworkProxy::HttpProxy, host, port, username, password);
+    return d->addRequest(new QHttpSetProxyRequest(proxy));
+}
+
+/*!
+    \overload
+
+    Enables HTTP proxy support using the proxy settings from \a
+    proxy. If \a proxy is a transparent proxy, QHttp will call
+    QAbstractSocket::setProxy() on the underlying socket. If the type
+    is QNetworkProxy::HttpCachingProxy, QHttp will behave like the
+    previous function.
+
+    \note for compatibility with Qt 4.3, if the proxy type is
+    QNetworkProxy::HttpProxy and the request type is unencrypted (that
+    is, ConnectionModeHttp), QHttp will treat the proxy as a caching
+    proxy.
+*/
+int QHttp::setProxy(const QNetworkProxy &proxy)
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpSetProxyRequest(proxy));
+}
+
+#endif
+
+/*!
+    Sends a get request for \a path to the server set by setHost() or
+    as specified in the constructor.
+
+    \a path must be a absolute path like \c /index.html or an
+    absolute URI like \c http://example.com/index.html and
+    must be encoded with either QUrl::toPercentEncoding() or
+    QUrl::encodedPath().
+
+    If the IO device \a to is 0 the readyRead() signal is emitted
+    every time new content data is available to read.
+
+    If the IO device \a to is not 0, the content data of the response
+    is written directly to the device. Make sure that the \a to
+    pointer is valid for the duration of the operation (it is safe to
+    delete it when the requestFinished() signal is emitted).
+
+    \section1 Request Processing
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    \sa setHost(), post(), head(), request(), requestStarted(),
+    requestFinished(), done()
+*/
+int QHttp::get(const QString &path, QIODevice *to)
+{
+    Q_D(QHttp);
+    QHttpRequestHeader header(QLatin1String("GET"), path);
+    header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+    return d->addRequest(new QHttpPGHRequest(header, (QIODevice *) 0, to));
+}
+
+/*!
+    Sends a post request for \a path to the server set by setHost() or
+    as specified in the constructor.
+
+    \a path must be an absolute path like \c /index.html or an
+    absolute URI like \c http://example.com/index.html and
+    must be encoded with either QUrl::toPercentEncoding() or
+    QUrl::encodedPath().
+
+    The incoming data comes via the \a data IO device.
+
+    If the IO device \a to is 0 the readyRead() signal is emitted
+    every time new content data is available to read.
+
+    If the IO device \a to is not 0, the content data of the response
+    is written directly to the device. Make sure that the \a to
+    pointer is valid for the duration of the operation (it is safe to
+    delete it when the requestFinished() signal is emitted).
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    \sa setHost() get() head() request() requestStarted() requestFinished() done()
+*/
+int QHttp::post(const QString &path, QIODevice *data, QIODevice *to )
+{
+    Q_D(QHttp);
+    QHttpRequestHeader header(QLatin1String("POST"), path);
+    header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+    return d->addRequest(new QHttpPGHRequest(header, data, to));
+}
+
+/*!
+    \overload
+
+    \a data is used as the content data of the HTTP request.
+*/
+int QHttp::post(const QString &path, const QByteArray &data, QIODevice *to)
+{
+    Q_D(QHttp);
+    QHttpRequestHeader header(QLatin1String("POST"), path);
+    header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+    return d->addRequest(new QHttpPGHRequest(header, new QByteArray(data), to));
+}
+
+/*!
+    Sends a header request for \a path to the server set by setHost()
+    or as specified in the constructor.
+
+    \a path must be an absolute path like \c /index.html or an
+    absolute URI like \c http://example.com/index.html.
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    \sa setHost() get() post() request() requestStarted() requestFinished() done()
+*/
+int QHttp::head(const QString &path)
+{
+    Q_D(QHttp);
+    QHttpRequestHeader header(QLatin1String("HEAD"), path);
+    header.setValue(QLatin1String("Connection"), QLatin1String("Keep-Alive"));
+    return d->addRequest(new QHttpPGHRequest(header, (QIODevice*)0, 0));
+}
+
+/*!
+    Sends a request to the server set by setHost() or as specified in
+    the constructor. Uses the \a header as the HTTP request header.
+    You are responsible for setting up a header that is appropriate
+    for your request.
+
+    The incoming data comes via the \a data IO device.
+
+    If the IO device \a to is 0 the readyRead() signal is emitted
+    every time new content data is available to read.
+
+    If the IO device \a to is not 0, the content data of the response
+    is written directly to the device. Make sure that the \a to
+    pointer is valid for the duration of the operation (it is safe to
+    delete it when the requestFinished() signal is emitted).
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    \sa setHost() get() post() head() requestStarted() requestFinished() done()
+*/
+int QHttp::request(const QHttpRequestHeader &header, QIODevice *data, QIODevice *to)
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpNormalRequest(header, data, to));
+}
+
+/*!
+    \overload
+
+    \a data is used as the content data of the HTTP request.
+*/
+int QHttp::request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to )
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpNormalRequest(header, new QByteArray(data), to));
+}
+
+/*!
+    Closes the connection; this is useful if you have a keep-alive
+    connection and want to close it.
+
+    For the requests issued with get(), post() and head(), QHttp sets
+    the connection to be keep-alive. You can also do this using the
+    header you pass to the request() function. QHttp only closes the
+    connection to the HTTP server if the response header requires it
+    to do so.
+
+    The function does not block; instead, it returns immediately. The request
+    is scheduled, and its execution is performed asynchronously. The
+    function returns a unique identifier which is passed by
+    requestStarted() and requestFinished().
+
+    When the request is started the requestStarted() signal is
+    emitted. When it is finished the requestFinished() signal is
+    emitted.
+
+    If you want to close the connection immediately, you have to use
+    abort() instead.
+
+    \sa stateChanged() abort() requestStarted() requestFinished() done()
+*/
+int QHttp::close()
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpCloseRequest());
+}
+
+/*!
+    \obsolete
+
+    Behaves the same as close().
+*/
+int QHttp::closeConnection()
+{
+    Q_D(QHttp);
+    return d->addRequest(new QHttpCloseRequest());
+}
+
+int QHttpPrivate::addRequest(QHttpNormalRequest *req)
+{
+    QHttpRequestHeader h = req->requestHeader();
+    if (h.path().isEmpty()) {
+        // note: the following qWarning is autotested. If you change it, change the test too.
+        qWarning("QHttp: empty path requested is invalid -- using '/'");
+        h.setRequest(h.method(), QLatin1String("/"), h.majorVersion(), h.minorVersion());
+        req->setRequestHeader(h);
+    }
+
+    // contine below
+    return addRequest(static_cast<QHttpRequest *>(req));
+}
+
+int QHttpPrivate::addRequest(QHttpRequest *req)
+{
+    Q_Q(QHttp);
+    pending.append(req);
+
+    if (pending.count() == 1) {
+        // don't emit the requestStarted() signal before the id is returned
+        QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+    }
+    return req->id;
+}
+
+void QHttpPrivate::_q_startNextRequest()
+{
+    Q_Q(QHttp);
+    if (pending.isEmpty())
+        return;
+    QHttpRequest *r = pending.first();
+
+    error = QHttp::NoError;
+    errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown error"));
+
+    if (q->bytesAvailable() != 0)
+        q->readAll(); // clear the data
+    emit q->requestStarted(r->id);
+    r->start(q);
+}
+
+void QHttpPrivate::_q_slotSendRequest()
+{
+    if (hostName.isNull()) {
+        finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "No server set to connect to")),
+                          QHttp::UnknownError);
+        return;
+    }
+
+    QString connectionHost = hostName;
+    int connectionPort = port;
+    bool sslInUse = false;
+
+#ifndef QT_NO_OPENSSL
+    QSslSocket *sslSocket = qobject_cast<QSslSocket *>(socket);
+    if (mode == QHttp::ConnectionModeHttps || (sslSocket && sslSocket->isEncrypted()))
+        sslInUse = true;
+#endif
+
+#ifndef QT_NO_NETWORKPROXY
+    bool cachingProxyInUse = false;
+    bool transparentProxyInUse = false;
+    if (proxy.type() == QNetworkProxy::DefaultProxy)
+        proxy = QNetworkProxy::applicationProxy();
+
+    if (proxy.type() == QNetworkProxy::HttpCachingProxy) {
+        if (proxy.hostName().isEmpty())
+            proxy.setType(QNetworkProxy::NoProxy);
+        else
+            cachingProxyInUse = true;
+    } else if (proxy.type() == QNetworkProxy::HttpProxy) {
+        // Compatibility behaviour: HttpProxy can be used to mean both
+        // transparent and caching proxy
+        if (proxy.hostName().isEmpty()) {
+            proxy.setType(QNetworkProxy::NoProxy);
+        } else if (sslInUse) {
+            // Disallow use of cacheing proxy with HTTPS; instead fall back to
+            // transparent HTTP CONNECT proxying.
+            transparentProxyInUse = true;
+        } else {
+            proxy.setType(QNetworkProxy::HttpCachingProxy);
+            cachingProxyInUse = true;
+        }
+    }
+
+    // Proxy support. Insert the Proxy-Authorization item into the
+    // header before it's sent off to the proxy.
+    if (cachingProxyInUse) {
+        QUrl proxyUrl;
+        proxyUrl.setScheme(QLatin1String("http"));
+        proxyUrl.setHost(hostName);
+        if (port && port != 80)
+            proxyUrl.setPort(port);
+        QString request = QString::fromAscii(proxyUrl.resolved(QUrl::fromEncoded(header.path().toLatin1())).toEncoded());
+
+        header.setRequest(header.method(), request, header.majorVersion(), header.minorVersion());
+        header.setValue(QLatin1String("Proxy-Connection"), QLatin1String("keep-alive"));
+
+        QAuthenticatorPrivate *auth = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
+        if (auth && auth->method != QAuthenticatorPrivate::None) {
+            QByteArray response = auth->calculateResponse(header.method().toLatin1(), header.path().toLatin1());
+            header.setValue(QLatin1String("Proxy-Authorization"), QString::fromLatin1(response));
+        }
+
+        connectionHost = proxy.hostName();
+        connectionPort = proxy.port();
+    }
+
+    if (transparentProxyInUse || sslInUse) {
+        socket->setProxy(proxy);
+    }
+#endif
+
+    // Username support. Insert the user and password into the query
+    // string.
+    QAuthenticatorPrivate *auth = QAuthenticatorPrivate::getPrivate(authenticator);
+    if (auth && auth->method != QAuthenticatorPrivate::None) {
+        QByteArray response = auth->calculateResponse(header.method().toLatin1(), header.path().toLatin1());
+        header.setValue(QLatin1String("Authorization"), QString::fromLatin1(response));
+    }
+
+    // Do we need to setup a new connection or can we reuse an
+    // existing one?
+    if (socket->peerName() != connectionHost || socket->peerPort() != connectionPort
+        || socket->state() != QTcpSocket::ConnectedState
+#ifndef QT_NO_OPENSSL
+        || (sslSocket && sslSocket->isEncrypted() != (mode == QHttp::ConnectionModeHttps))
+#endif
+        ) {
+        socket->blockSignals(true);
+        socket->abort();
+        socket->blockSignals(false);
+
+        setState(QHttp::Connecting);
+#ifndef QT_NO_OPENSSL
+        if (sslSocket && mode == QHttp::ConnectionModeHttps) {
+            sslSocket->connectToHostEncrypted(hostName, port);
+        } else
+#endif
+        {
+            socket->connectToHost(connectionHost, connectionPort);
+        }
+    } else {
+        _q_slotConnected();
+    }
+
+}
+
+void QHttpPrivate::finishedWithSuccess()
+{
+    Q_Q(QHttp);
+    if (pending.isEmpty())
+        return;
+    QHttpRequest *r = pending.first();
+
+    // did we recurse?
+    if (r->finished)
+        return;
+    r->finished = true;
+    hasFinishedWithError = false;
+
+    emit q->requestFinished(r->id, false);
+    if (hasFinishedWithError) {
+        // we recursed and changed into an error. The finishedWithError function
+        // below has emitted the done(bool) signal and cleared the queue by now.
+        return;
+    }
+
+    pending.removeFirst();
+    delete r;
+
+    if (pending.isEmpty()) {
+        emit q->done(false);
+    } else {
+        _q_startNextRequest();
+    }
+}
+
+void QHttpPrivate::finishedWithError(const QString &detail, int errorCode)
+{
+    Q_Q(QHttp);
+    if (pending.isEmpty())
+        return;
+    QHttpRequest *r = pending.first();
+    hasFinishedWithError = true;
+
+    error = QHttp::Error(errorCode);
+    errorString = detail;
+
+    // did we recurse?
+    if (!r->finished) {
+        r->finished = true;
+        emit q->requestFinished(r->id, true);
+    }
+
+    while (!pending.isEmpty())
+        delete pending.takeFirst();
+    emit q->done(hasFinishedWithError);
+}
+
+void QHttpPrivate::_q_slotClosed()
+{
+    Q_Q(QHttp);
+
+    if (state == QHttp::Reading) {
+        if (response.hasKey(QLatin1String("content-length"))) {
+            // We got Content-Length, so did we get all bytes?
+            if (bytesDone + q->bytesAvailable() != response.contentLength()) {
+                finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Wrong content length")), QHttp::WrongContentLength);
+            }
+        }
+    } else if (state == QHttp::Connecting || state == QHttp::Sending) {
+        finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Server closed connection unexpectedly")), QHttp::UnexpectedClose);
+    }
+
+    postDevice = 0;
+    if (state != QHttp::Closing)
+        setState(QHttp::Closing);
+    QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+}
+
+void QHttpPrivate::_q_continuePost()
+{
+    if (pendingPost) {
+        pendingPost = false;
+        setState(QHttp::Sending);
+        _q_slotBytesWritten(0);
+    }
+}
+
+void QHttpPrivate::_q_slotConnected()
+{
+    if (state != QHttp::Sending) {
+        bytesDone = 0;
+        setState(QHttp::Sending);
+    }
+
+    QString str = header.toString();
+    bytesTotal = str.length();
+    socket->write(str.toLatin1(), bytesTotal);
+#if defined(QHTTP_DEBUG)
+    qDebug("QHttp: write request header %p:\n---{\n%s}---", &header, str.toLatin1().constData());
+#endif
+
+    if (postDevice) {
+        postDevice->seek(0);    // reposition the device
+        bytesTotal += postDevice->size();
+        //check for 100-continue
+        if (header.value(QLatin1String("expect")).contains(QLatin1String("100-continue"), Qt::CaseInsensitive)) {
+            //create a time out for 2 secs.
+            pendingPost = true;
+            post100ContinueTimer.start(2000);
+        }
+    } else {
+        bytesTotal += buffer.size();
+        socket->write(buffer, buffer.size());
+    }
+}
+
+void QHttpPrivate::_q_slotError(QAbstractSocket::SocketError err)
+{
+    Q_Q(QHttp);
+    postDevice = 0;
+
+    if (state == QHttp::Connecting || state == QHttp::Reading || state == QHttp::Sending) {
+        switch (err) {
+        case QTcpSocket::ConnectionRefusedError:
+            finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused (or timed out)")), QHttp::ConnectionRefused);
+            break;
+        case QTcpSocket::HostNotFoundError:
+            finishedWithError(QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
+                              .arg(socket->peerName()), QHttp::HostNotFound);
+            break;
+        case QTcpSocket::RemoteHostClosedError:
+            if (state == QHttp::Sending && reconnectAttempts--) {
+                setState(QHttp::Closing);
+                setState(QHttp::Unconnected);
+                socket->blockSignals(true);
+                socket->abort();
+                socket->blockSignals(false);
+                QMetaObject::invokeMethod(q, "_q_slotSendRequest", Qt::QueuedConnection);
+                return;
+            }
+            break;
+#ifndef QT_NO_NETWORKPROXY
+        case QTcpSocket::ProxyAuthenticationRequiredError:
+            finishedWithError(socket->errorString(), QHttp::ProxyAuthenticationRequiredError);
+            break;
+#endif
+        default:
+            finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTP request failed")), QHttp::UnknownError);
+            break;
+        }
+    }
+
+    closeConn();
+}
+
+#ifndef QT_NO_OPENSSL
+void QHttpPrivate::_q_slotEncryptedBytesWritten(qint64 written)
+{
+    Q_UNUSED(written);
+    postMoreData();
+}
+#endif
+
+void QHttpPrivate::_q_slotBytesWritten(qint64 written)
+{
+    Q_Q(QHttp);
+    bytesDone += written;
+    emit q->dataSendProgress(bytesDone, bytesTotal);
+    postMoreData();
+}
+
+// Send the POST data
+void QHttpPrivate::postMoreData()
+{
+    if (pendingPost)
+        return;
+
+    if (!postDevice)
+        return;
+
+    // the following is backported code from Qt 4.6 QNetworkAccessManager.
+    // We also have to check the encryptedBytesToWrite() if it is an SSL socket.
+#ifndef QT_NO_OPENSSL
+    QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
+    // if it is really an ssl socket, check more than just bytesToWrite()
+    if ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0)) == 0) {
+#else
+    if (socket->bytesToWrite() == 0) {
+#endif
+        int max = qMin<qint64>(4096, postDevice->size() - postDevice->pos());
+        QByteArray arr;
+        arr.resize(max);
+
+        int n = postDevice->read(arr.data(), max);
+        if (n < 0) {
+            qWarning("Could not read enough bytes from the device");
+            closeConn();
+            return;
+        }
+        if (postDevice->atEnd()) {
+            postDevice = 0;
+        }
+
+        socket->write(arr, n);
+    }
+}
+
+void QHttpPrivate::_q_slotReadyRead()
+{
+    Q_Q(QHttp);
+    QHttp::State oldState = state;
+    if (state != QHttp::Reading) {
+        setState(QHttp::Reading);
+        readHeader = true;
+        headerStr = QLatin1String("");
+        bytesDone = 0;
+        chunkedSize = -1;
+        repost = false;
+    }
+
+    while (readHeader) {
+        bool end = false;
+        QString tmp;
+        while (!end && socket->canReadLine()) {
+            tmp = QString::fromAscii(socket->readLine());
+            if (tmp == QLatin1String("\r\n") || tmp == QLatin1String("\n") || tmp.isEmpty())
+                end = true;
+            else
+                headerStr += tmp;
+        }
+
+        if (!end)
+            return;
+
+        response = QHttpResponseHeader(headerStr);
+        headerStr = QLatin1String("");
+#if defined(QHTTP_DEBUG)
+        qDebug("QHttp: read response header:\n---{\n%s}---", response.toString().toLatin1().constData());
+#endif
+        // Check header
+        if (!response.isValid()) {
+            finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP response header")),
+                              QHttp::InvalidResponseHeader);
+            closeConn();
+            return;
+        }
+
+        int statusCode = response.statusCode();
+        if (statusCode == 401 || statusCode == 407) { // (Proxy) Authentication required
+            QAuthenticator *auth =
+#ifndef QT_NO_NETWORKPROXY
+                statusCode == 407
+                ? &proxyAuthenticator :
+#endif
+                &authenticator;
+            if (auth->isNull())
+                auth->detach();
+            QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
+            priv->parseHttpResponse(response, (statusCode == 407));
+            if (priv->phase == QAuthenticatorPrivate::Done) {
+                socket->blockSignals(true);
+#ifndef QT_NO_NETWORKPROXY
+                if (statusCode == 407)
+                    emit q->proxyAuthenticationRequired(proxy, auth);
+                else
+#endif
+                    emit q->authenticationRequired(hostName, port, auth);
+                socket->blockSignals(false);
+            } else if (priv->phase == QAuthenticatorPrivate::Invalid) {
+                finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown authentication method")),
+                        QHttp::AuthenticationRequiredError);
+                closeConn();
+                return;
+            }
+
+            // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
+            if (priv->phase == QAuthenticatorPrivate::Done) {
+#ifndef QT_NO_NETWORKPROXY
+                if (statusCode == 407)
+                    finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy authentication required")),
+                                      QHttp::ProxyAuthenticationRequiredError);
+                else
+#endif
+                    finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Authentication required")),
+                                      QHttp::AuthenticationRequiredError);
+                closeConn();
+                return;
+            } else {
+                // close the connection if it isn't already and reconnect using the chosen authentication method
+                bool willClose = (response.value(QLatin1String("proxy-connection")).toLower() == QLatin1String("close"))
+                                 || (response.value(QLatin1String("connection")).toLower() == QLatin1String("close"));
+                if (willClose) {
+                    if (socket) {
+                        setState(QHttp::Closing);
+                        socket->blockSignals(true);
+                        socket->close();
+                        socket->blockSignals(false);
+                        socket->readAll();
+                    }
+                    _q_slotSendRequest();
+                    return;
+                } else {
+                    repost = true;
+                }
+            }
+        } else {
+            buffer.clear();
+        }
+
+        if (response.statusCode() == 100 && pendingPost) {
+            // if we have pending POST, start sending data otherwise ignore
+            post100ContinueTimer.stop();
+            QMetaObject::invokeMethod(q, "_q_continuePost", Qt::QueuedConnection); 
+            return;
+        }
+
+        // The 100-continue header is ignored (in case of no 'expect:100-continue' header),
+        // because when using the POST method, we send both the request header and data in
+        // one chunk.
+        if (response.statusCode() != 100) {
+            post100ContinueTimer.stop();
+            pendingPost = false;
+            readHeader = false;
+            if (response.hasKey(QLatin1String("transfer-encoding")) &&
+                response.value(QLatin1String("transfer-encoding")).toLower().contains(QLatin1String("chunked")))
+                chunkedSize = 0;
+
+            if (!repost)
+                emit q->responseHeaderReceived(response);
+            if (state == QHttp::Unconnected || state == QHttp::Closing)
+                return;
+        } else {
+            // Restore the state, the next incoming data will be treated as if
+            // we never say the 100 response.
+            state = oldState;
+        }
+    }
+
+    bool everythingRead = false;
+
+    if (q->currentRequest().method() == QLatin1String("HEAD") ||
+        response.statusCode() == 304 || response.statusCode() == 204 ||
+        response.statusCode() == 205) {
+        // HEAD requests have only headers as replies
+        // These status codes never have a body:
+        //  304 Not Modified
+        //  204 No Content
+        //  205 Reset Content
+        everythingRead = true;
+    } else {
+        qint64 n = socket->bytesAvailable();
+        QByteArray *arr = 0;
+        if (chunkedSize != -1) {
+            // transfer-encoding is chunked
+            for (;;) {
+                // get chunk size
+                if (chunkedSize == 0) {
+                    if (!socket->canReadLine())
+                        break;
+                    QString sizeString = QString::fromAscii(socket->readLine());
+                    int tPos = sizeString.indexOf(QLatin1Char(';'));
+                    if (tPos != -1)
+                        sizeString.truncate(tPos);
+                    bool ok;
+                    chunkedSize = sizeString.toInt(&ok, 16);
+                    if (!ok) {
+                        finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP chunked body")),
+                                          QHttp::WrongContentLength);
+                        closeConn();
+                        delete arr;
+                        return;
+                    }
+                    if (chunkedSize == 0) // last-chunk
+                        chunkedSize = -2;
+                }
+
+                // read trailer
+                while (chunkedSize == -2 && socket->canReadLine()) {
+                    QString read = QString::fromAscii(socket->readLine());
+                    if (read == QLatin1String("\r\n") || read == QLatin1String("\n"))
+                        chunkedSize = -1;
+                }
+                if (chunkedSize == -1) {
+                    everythingRead = true;
+                    break;
+                }
+
+                // make sure that you can read the terminating CRLF,
+                // otherwise wait until next time...
+                n = socket->bytesAvailable();
+                if (n == 0)
+                    break;
+                if (n == chunkedSize || n == chunkedSize+1) {
+                    n = chunkedSize - 1;
+                    if (n == 0)
+                        break;
+                }
+
+                // read data
+                qint64 toRead = chunkedSize < 0 ? n : qMin(n, chunkedSize);
+                if (!arr)
+                    arr = new QByteArray;
+                uint oldArrSize = arr->size();
+                arr->resize(oldArrSize + toRead);
+                qint64 read = socket->read(arr->data()+oldArrSize, toRead);
+                arr->resize(oldArrSize + read);
+
+                chunkedSize -= read;
+
+                if (chunkedSize == 0 && n - read >= 2) {
+                    // read terminating CRLF
+                    char tmp[2];
+                    socket->read(tmp, 2);
+                    if (tmp[0] != '\r' || tmp[1] != '\n') {
+                        finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Invalid HTTP chunked body")),
+                                          QHttp::WrongContentLength);
+                        closeConn();
+                        delete arr;
+                        return;
+                    }
+                }
+            }
+        } else if (response.hasContentLength()) {
+            if (repost && (n < response.contentLength())) {
+                // wait for the content to be available fully
+                // if repost is required, the content is ignored
+                return;
+            }
+            n = qMin(qint64(response.contentLength() - bytesDone), n);
+            if (n > 0) {
+                arr = new QByteArray;
+                arr->resize(n);
+                qint64 read = socket->read(arr->data(), n);
+                arr->resize(read);
+            }
+            if (bytesDone + q->bytesAvailable() + n == response.contentLength())
+                everythingRead = true;
+        } else if (n > 0) {
+            // workaround for VC++ bug
+            QByteArray temp = socket->readAll();
+            arr = new QByteArray(temp);
+        }
+
+        if (arr && !repost) {
+            n = arr->size();
+            if (toDevice) {
+                qint64 bytesWritten;
+                bytesWritten = toDevice->write(*arr, n);
+                delete arr;
+                arr = 0;
+                // if writing to the device does not succeed, quit with error
+                if (bytesWritten == -1 || bytesWritten < n) {
+                    finishedWithError(QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Error writing response to device")), QHttp::UnknownError);
+                } else {
+                    bytesDone += bytesWritten;
+#if defined(QHTTP_DEBUG)
+                qDebug("QHttp::_q_slotReadyRead(): read %lld bytes (%lld bytes done)", n, bytesDone);
+#endif
+                }
+                if (response.hasContentLength())
+                    emit q->dataReadProgress(bytesDone, response.contentLength());
+                else
+                    emit q->dataReadProgress(bytesDone, 0);
+            } else {
+                char *ptr = rba.reserve(arr->size());
+                memcpy(ptr, arr->data(), arr->size());
+                delete arr;
+                arr = 0;
+#if defined(QHTTP_DEBUG)
+                qDebug("QHttp::_q_slotReadyRead(): read %lld bytes (%lld bytes done)", n, bytesDone + q->bytesAvailable());
+#endif
+                if (response.hasContentLength())
+                    emit q->dataReadProgress(bytesDone + q->bytesAvailable(), response.contentLength());
+                else
+                    emit q->dataReadProgress(bytesDone + q->bytesAvailable(), 0);
+                emit q->readyRead(response);
+            }
+        }
+
+        delete arr;
+    }
+
+    if (everythingRead) {
+        if (repost) {
+            _q_slotSendRequest();
+            return;
+        }
+        // Handle "Connection: close"
+        if (response.value(QLatin1String("connection")).toLower() == QLatin1String("close")) {
+            closeConn();
+        } else {
+            setState(QHttp::Connected);
+            // Start a timer, so that we emit the keep alive signal
+            // "after" this method returned.
+            QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+        }
+    }
+}
+
+void QHttpPrivate::_q_slotDoFinished()
+{
+    if (state == QHttp::Connected) {
+        finishedWithSuccess();
+    } else if (state != QHttp::Unconnected) {
+        setState(QHttp::Unconnected);
+        finishedWithSuccess();
+    }
+}
+
+
+/*!
+    Returns the current state of the object. When the state changes,
+    the stateChanged() signal is emitted.
+
+    \sa State stateChanged()
+*/
+QHttp::State QHttp::state() const
+{
+    Q_D(const QHttp);
+    return d->state;
+}
+
+/*!
+    Returns the last error that occurred. This is useful to find out
+    what happened when receiving a requestFinished() or a done()
+    signal with the \c error argument \c true.
+
+    If you start a new request, the error status is reset to \c NoError.
+*/
+QHttp::Error QHttp::error() const
+{
+    Q_D(const QHttp);
+    return d->error;
+}
+
+/*!
+    Returns a human-readable description of the last error that
+    occurred. This is useful to present a error message to the user
+    when receiving a requestFinished() or a done() signal with the \c
+    error argument \c true.
+*/
+QString QHttp::errorString() const
+{
+    Q_D(const QHttp);
+    return d->errorString;
+}
+
+void QHttpPrivate::setState(int s)
+{
+    Q_Q(QHttp);
+#if defined(QHTTP_DEBUG)
+    qDebug("QHttp state changed %d -> %d", state, s);
+#endif
+    state = QHttp::State(s);
+    emit q->stateChanged(s);
+}
+
+void QHttpPrivate::closeConn()
+{
+    Q_Q(QHttp);
+    // If no connection is open -> ignore
+    if (state == QHttp::Closing || state == QHttp::Unconnected)
+        return;
+
+    postDevice = 0;
+    setState(QHttp::Closing);
+
+    // Already closed ?
+    if (!socket || !socket->isOpen()) {
+        QMetaObject::invokeMethod(q, "_q_slotDoFinished", Qt::QueuedConnection);
+    } else {
+        // Close now.
+        socket->close();
+    }
+}
+
+void QHttpPrivate::setSock(QTcpSocket *sock)
+{
+    Q_Q(const QHttp);
+
+    // disconnect all existing signals
+    if (socket)
+        socket->disconnect();
+    if (deleteSocket)
+        delete socket;
+
+    // use the new QTcpSocket socket, or create one if socket is 0.
+    deleteSocket = (sock == 0);
+    socket = sock;
+    if (!socket) {
+#ifndef QT_NO_OPENSSL
+        if (QSslSocket::supportsSsl())
+            socket = new QSslSocket();
+        else
+#endif
+            socket = new QTcpSocket();
+    }
+
+    // connect all signals
+    QObject::connect(socket, SIGNAL(connected()), q, SLOT(_q_slotConnected()));
+    QObject::connect(socket, SIGNAL(disconnected()), q, SLOT(_q_slotClosed()));
+    QObject::connect(socket, SIGNAL(readyRead()), q, SLOT(_q_slotReadyRead()));
+    QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), q, SLOT(_q_slotError(QAbstractSocket::SocketError)));
+    QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
+                     q, SLOT(_q_slotBytesWritten(qint64)));
+#ifndef QT_NO_NETWORKPROXY
+    QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)),
+                     q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
+#endif
+
+#ifndef QT_NO_OPENSSL
+    if (qobject_cast<QSslSocket *>(socket)) {
+        QObject::connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)),
+                         q, SIGNAL(sslErrors(const QList<QSslError> &)));
+        QObject::connect(socket, SIGNAL(encryptedBytesWritten(qint64)),
+                         q, SLOT(_q_slotEncryptedBytesWritten(qint64)));
+    }
+#endif
+}
+
+/*!
+    Tells the QSslSocket used for the Http connection to ignore the errors
+    reported in the sslErrors() signal.
+
+    Note that this function must be called from within a slot connected to the
+    sslErrors() signal to have any effect.
+
+    \sa QSslSocket QSslSocket::sslErrors()
+*/
+#ifndef QT_NO_OPENSSL
+void QHttp::ignoreSslErrors()
+{
+    Q_D(QHttp);
+    QSslSocket *sslSocket = qobject_cast<QSslSocket *>(d->socket);
+    if (sslSocket)
+        sslSocket->ignoreSslErrors();
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qhttp.cpp"
+
+#endif