src/network/socket/qhttpsocketengine.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtNetwork module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qhttpsocketengine_p.h"
       
    43 #include "qtcpsocket.h"
       
    44 #include "qhostaddress.h"
       
    45 #include "qdatetime.h"
       
    46 #include "qurl.h"
       
    47 #include "qhttp.h"
       
    48 
       
    49 #if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP)
       
    50 #include <qdebug.h>
       
    51 
       
    52 QT_BEGIN_NAMESPACE
       
    53 
       
    54 #define DEBUG
       
    55 
       
    56 QHttpSocketEngine::QHttpSocketEngine(QObject *parent)
       
    57     : QAbstractSocketEngine(*new QHttpSocketEnginePrivate, parent)
       
    58 {
       
    59 }
       
    60 
       
    61 QHttpSocketEngine::~QHttpSocketEngine()
       
    62 {
       
    63 }
       
    64 
       
    65 bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
       
    66 {
       
    67     Q_D(QHttpSocketEngine);
       
    68     if (type != QAbstractSocket::TcpSocket)
       
    69         return false;
       
    70 
       
    71     setProtocol(protocol);
       
    72     setSocketType(type);
       
    73     d->socket = new QTcpSocket(this);
       
    74 
       
    75     // Explicitly disable proxying on the proxy socket itself to avoid
       
    76     // unwanted recursion.
       
    77     d->socket->setProxy(QNetworkProxy::NoProxy);
       
    78 
       
    79     // Intercept all the signals.
       
    80     connect(d->socket, SIGNAL(connected()),
       
    81             this, SLOT(slotSocketConnected()),
       
    82             Qt::DirectConnection);
       
    83     connect(d->socket, SIGNAL(disconnected()),
       
    84             this, SLOT(slotSocketDisconnected()),
       
    85             Qt::DirectConnection);
       
    86     connect(d->socket, SIGNAL(readyRead()),
       
    87             this, SLOT(slotSocketReadNotification()),
       
    88             Qt::DirectConnection);
       
    89     connect(d->socket, SIGNAL(bytesWritten(qint64)),
       
    90             this, SLOT(slotSocketBytesWritten()),
       
    91             Qt::DirectConnection);
       
    92     connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)),
       
    93             this, SLOT(slotSocketError(QAbstractSocket::SocketError)),
       
    94             Qt::DirectConnection);
       
    95     connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
       
    96             this, SLOT(slotSocketStateChanged(QAbstractSocket::SocketState)),
       
    97             Qt::DirectConnection);
       
    98 
       
    99     return true;
       
   100 }
       
   101 
       
   102 bool QHttpSocketEngine::initialize(int, QAbstractSocket::SocketState)
       
   103 {
       
   104     return false;
       
   105 }
       
   106 
       
   107 void QHttpSocketEngine::setProxy(const QNetworkProxy &proxy)
       
   108 {
       
   109     Q_D(QHttpSocketEngine);
       
   110     d->proxy = proxy;
       
   111     QString user = proxy.user();
       
   112     if (!user.isEmpty())
       
   113         d->authenticator.setUser(user);
       
   114     QString password = proxy.password();
       
   115     if (!password.isEmpty())
       
   116         d->authenticator.setPassword(password);
       
   117 }
       
   118 
       
   119 int QHttpSocketEngine::socketDescriptor() const
       
   120 {
       
   121     Q_D(const QHttpSocketEngine);
       
   122     return d->socket ? d->socket->socketDescriptor() : 0;
       
   123 }
       
   124 
       
   125 bool QHttpSocketEngine::isValid() const
       
   126 {
       
   127     Q_D(const QHttpSocketEngine);
       
   128     return d->socket;
       
   129 }
       
   130 
       
   131 bool QHttpSocketEngine::connectInternal()
       
   132 {
       
   133     Q_D(QHttpSocketEngine);
       
   134 
       
   135     // If the handshake is done, enter ConnectedState state and return true.
       
   136     if (d->state == Connected) {
       
   137         qWarning("QHttpSocketEngine::connectToHost: called when already connected");
       
   138         setState(QAbstractSocket::ConnectedState);
       
   139         return true;
       
   140     }
       
   141 
       
   142     if (d->state == ConnectSent && d->socketState != QAbstractSocket::ConnectedState)
       
   143         setState(QAbstractSocket::UnconnectedState);
       
   144 
       
   145     // Handshake isn't done. If unconnected, start connecting.
       
   146     if (d->state == None && d->socket->state() == QAbstractSocket::UnconnectedState) {
       
   147         setState(QAbstractSocket::ConnectingState);
       
   148         d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
       
   149     }
       
   150 
       
   151     // If connected (might happen right away, at least for localhost services
       
   152     // on some BSD systems), there might already be bytes available.
       
   153     if (bytesAvailable())
       
   154         slotSocketReadNotification();
       
   155 
       
   156     return d->socketState == QAbstractSocket::ConnectedState;
       
   157 }
       
   158 
       
   159 bool QHttpSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
       
   160 {
       
   161     Q_D(QHttpSocketEngine);
       
   162 
       
   163     setPeerAddress(address);
       
   164     setPeerPort(port);
       
   165     d->peerName.clear();
       
   166 
       
   167     return connectInternal();
       
   168 }
       
   169 
       
   170 bool QHttpSocketEngine::connectToHostByName(const QString &hostname, quint16 port)
       
   171 {
       
   172     Q_D(QHttpSocketEngine);
       
   173 
       
   174     setPeerAddress(QHostAddress());
       
   175     setPeerPort(port);
       
   176     d->peerName = hostname;
       
   177 
       
   178     return connectInternal();
       
   179 }
       
   180 
       
   181 bool QHttpSocketEngine::bind(const QHostAddress &, quint16)
       
   182 {
       
   183     return false;
       
   184 }
       
   185 
       
   186 bool QHttpSocketEngine::listen()
       
   187 {
       
   188     return false;
       
   189 }
       
   190 
       
   191 int QHttpSocketEngine::accept()
       
   192 {
       
   193     return 0;
       
   194 }
       
   195 
       
   196 void QHttpSocketEngine::close()
       
   197 {
       
   198     Q_D(QHttpSocketEngine);
       
   199     if (d->socket) {
       
   200         d->socket->close();
       
   201         delete d->socket;
       
   202         d->socket = 0;
       
   203     }
       
   204 }
       
   205 
       
   206 qint64 QHttpSocketEngine::bytesAvailable() const
       
   207 {
       
   208     Q_D(const QHttpSocketEngine);
       
   209     return d->readBuffer.size() + (d->socket ? d->socket->bytesAvailable() : 0);
       
   210 }
       
   211 
       
   212 qint64 QHttpSocketEngine::read(char *data, qint64 maxlen)
       
   213 {
       
   214     Q_D(QHttpSocketEngine);
       
   215     qint64 bytesRead = 0;
       
   216 
       
   217     if (!d->readBuffer.isEmpty()) {
       
   218         // Read as much from the buffer as we can.
       
   219         bytesRead = qMin((qint64)d->readBuffer.size(), maxlen);
       
   220         memcpy(data, d->readBuffer.constData(), bytesRead);
       
   221         data += bytesRead;
       
   222         maxlen -= bytesRead;
       
   223         d->readBuffer = d->readBuffer.mid(bytesRead);
       
   224     }
       
   225 
       
   226     qint64 bytesReadFromSocket = d->socket->read(data, maxlen);
       
   227 
       
   228     if (d->socket->state() == QAbstractSocket::UnconnectedState
       
   229         && d->socket->bytesAvailable() == 0) {
       
   230         emitReadNotification();
       
   231     }
       
   232 
       
   233     if (bytesReadFromSocket > 0) {
       
   234         // Add to what we read so far.
       
   235         bytesRead += bytesReadFromSocket;
       
   236     } else if (bytesRead == 0 && bytesReadFromSocket == -1) {
       
   237         // If nothing has been read so far, and the direct socket read
       
   238         // failed, return the socket's error. Otherwise, fall through and
       
   239         // return as much as we read so far.
       
   240         close();
       
   241         setError(QAbstractSocket::RemoteHostClosedError,
       
   242                  QLatin1String("Remote host closed"));
       
   243         setState(QAbstractSocket::UnconnectedState);
       
   244         return -1;
       
   245     }
       
   246     return bytesRead;
       
   247 }
       
   248 
       
   249 qint64 QHttpSocketEngine::write(const char *data, qint64 len)
       
   250 {
       
   251     Q_D(QHttpSocketEngine);
       
   252     return d->socket->write(data, len);
       
   253 }
       
   254 
       
   255 #ifndef QT_NO_UDPSOCKET
       
   256 qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *,
       
   257                                        quint16 *)
       
   258 {
       
   259     return 0;
       
   260 }
       
   261 
       
   262 qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QHostAddress &,
       
   263                                         quint16)
       
   264 {
       
   265     return 0;
       
   266 }
       
   267 
       
   268 bool QHttpSocketEngine::hasPendingDatagrams() const
       
   269 {
       
   270     return false;
       
   271 }
       
   272 
       
   273 qint64 QHttpSocketEngine::pendingDatagramSize() const
       
   274 {
       
   275     return 0;
       
   276 }
       
   277 #endif // QT_NO_UDPSOCKET
       
   278 
       
   279 int QHttpSocketEngine::option(SocketOption option) const
       
   280 {
       
   281     Q_D(const QHttpSocketEngine);
       
   282     if (d->socket) {
       
   283         // convert the enum and call the real socket
       
   284         if (option == QAbstractSocketEngine::LowDelayOption)
       
   285             return d->socket->socketOption(QAbstractSocket::LowDelayOption).toInt();
       
   286         if (option == QAbstractSocketEngine::KeepAliveOption)
       
   287             return d->socket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
       
   288     }
       
   289     return -1;
       
   290 }
       
   291 
       
   292 bool QHttpSocketEngine::setOption(SocketOption option, int value)
       
   293 {
       
   294     Q_D(QHttpSocketEngine);
       
   295     if (d->socket) {
       
   296         // convert the enum and call the real socket
       
   297         if (option == QAbstractSocketEngine::LowDelayOption)
       
   298             d->socket->setSocketOption(QAbstractSocket::LowDelayOption, value);
       
   299         if (option == QAbstractSocketEngine::KeepAliveOption)
       
   300             d->socket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
       
   301         return true;
       
   302     }
       
   303     return false;
       
   304 }
       
   305 
       
   306 /*
       
   307    Returns the difference between msecs and elapsed. If msecs is -1,
       
   308    however, -1 is returned.
       
   309 */
       
   310 static int qt_timeout_value(int msecs, int elapsed)
       
   311 {
       
   312     if (msecs == -1)
       
   313         return -1;
       
   314 
       
   315     int timeout = msecs - elapsed;
       
   316     return timeout < 0 ? 0 : timeout;
       
   317 }
       
   318 
       
   319 bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
       
   320 {
       
   321     Q_D(const QHttpSocketEngine);
       
   322 
       
   323     if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
       
   324         return false;
       
   325 
       
   326     QTime stopWatch;
       
   327     stopWatch.start();
       
   328 
       
   329     // Wait for more data if nothing is available.
       
   330     if (!d->socket->bytesAvailable()) {
       
   331         if (!d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
       
   332             if (d->socket->state() == QAbstractSocket::UnconnectedState)
       
   333                 return true;
       
   334             setError(d->socket->error(), d->socket->errorString());
       
   335             if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
       
   336                 *timedOut = true;
       
   337             return false;
       
   338         }
       
   339     }
       
   340 
       
   341     // If we're not connected yet, wait until we are, or until an error
       
   342     // occurs.
       
   343     while (d->state != Connected && d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
       
   344         // Loop while the protocol handshake is taking place.
       
   345     }
       
   346 
       
   347     // Report any error that may occur.
       
   348     if (d->state != Connected) {
       
   349         setError(d->socket->error(), d->socket->errorString());
       
   350         if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
       
   351             *timedOut = true;
       
   352         return false;
       
   353     }
       
   354     return true;
       
   355 }
       
   356 
       
   357 bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
       
   358 {
       
   359     Q_D(const QHttpSocketEngine);
       
   360 
       
   361     // If we're connected, just forward the call.
       
   362     if (d->state == Connected) {
       
   363         if (d->socket->bytesToWrite()) {
       
   364             if (!d->socket->waitForBytesWritten(msecs)) {
       
   365                 if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
       
   366                     *timedOut = true;
       
   367                 return false;
       
   368             }
       
   369         }
       
   370         return true;
       
   371     }
       
   372 
       
   373     QTime stopWatch;
       
   374     stopWatch.start();
       
   375 
       
   376     // If we're not connected yet, wait until we are, and until bytes have
       
   377     // been received (i.e., the socket has connected, we have sent the
       
   378     // greeting, and then received the response).
       
   379     while (d->state != Connected && d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
       
   380         // Loop while the protocol handshake is taking place.
       
   381     }
       
   382 
       
   383     // Report any error that may occur.
       
   384     if (d->state != Connected) {
       
   385 //        setError(d->socket->error(), d->socket->errorString());
       
   386         if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
       
   387             *timedOut = true;
       
   388     }
       
   389 
       
   390     return true;
       
   391 }
       
   392 
       
   393 bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
       
   394                                            bool checkRead, bool checkWrite,
       
   395                                            int msecs, bool *timedOut)
       
   396 {
       
   397     Q_UNUSED(checkRead);
       
   398 
       
   399     if (!checkWrite) {
       
   400         // Not interested in writing? Then we wait for read notifications.
       
   401         bool canRead = waitForRead(msecs, timedOut);
       
   402         if (readyToRead)
       
   403             *readyToRead = canRead;
       
   404         return canRead;
       
   405     }
       
   406 
       
   407     // Interested in writing? Then we wait for write notifications.
       
   408     bool canWrite = waitForWrite(msecs, timedOut);
       
   409     if (readyToWrite)
       
   410         *readyToWrite = canWrite;
       
   411     return canWrite;
       
   412 }
       
   413 
       
   414 bool QHttpSocketEngine::isReadNotificationEnabled() const
       
   415 {
       
   416     Q_D(const QHttpSocketEngine);
       
   417     return d->readNotificationEnabled;
       
   418 }
       
   419 
       
   420 void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
       
   421 {
       
   422     Q_D(QHttpSocketEngine);
       
   423     if (d->readNotificationEnabled == enable)
       
   424         return;
       
   425 
       
   426     d->readNotificationEnabled = enable;
       
   427     if (enable) {
       
   428         // Enabling read notification can trigger a notification.
       
   429         if (bytesAvailable())
       
   430             slotSocketReadNotification();
       
   431     }
       
   432 }
       
   433 
       
   434 bool QHttpSocketEngine::isWriteNotificationEnabled() const
       
   435 {
       
   436     Q_D(const QHttpSocketEngine);
       
   437     return d->writeNotificationEnabled;
       
   438 }
       
   439 
       
   440 void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
       
   441 {
       
   442     Q_D(QHttpSocketEngine);
       
   443     d->writeNotificationEnabled = enable;
       
   444     if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
       
   445         QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection);
       
   446 }
       
   447 
       
   448 bool QHttpSocketEngine::isExceptionNotificationEnabled() const
       
   449 {
       
   450     Q_D(const QHttpSocketEngine);
       
   451     return d->exceptNotificationEnabled;
       
   452 }
       
   453 
       
   454 void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
       
   455 {
       
   456     Q_D(QHttpSocketEngine);
       
   457     d->exceptNotificationEnabled = enable;
       
   458 }
       
   459 
       
   460 void QHttpSocketEngine::slotSocketConnected()
       
   461 {
       
   462     Q_D(QHttpSocketEngine);
       
   463 
       
   464     // Send the greeting.
       
   465     const char method[] = "CONNECT ";
       
   466     QByteArray peerAddress = d->peerName.isEmpty() ?
       
   467                              d->peerAddress.toString().toLatin1() :
       
   468                              QUrl::toAce(d->peerName);
       
   469     QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
       
   470     QByteArray data = method;
       
   471     data += path;
       
   472     data += " HTTP/1.1\r\n";
       
   473     data += "Proxy-Connection: keep-alive\r\n"
       
   474             "User-Agent: Mozilla/5.0\r\n"
       
   475             "Host: " + peerAddress + "\r\n";
       
   476     QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
       
   477     //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
       
   478     if (priv && priv->method != QAuthenticatorPrivate::None) {
       
   479         data += "Proxy-Authorization: " + priv->calculateResponse(method, path);
       
   480         data += "\r\n";
       
   481     }
       
   482     data += "\r\n";
       
   483 //     qDebug() << ">>>>>>>> sending request" << this;
       
   484 //     qDebug() << data;
       
   485 //     qDebug() << ">>>>>>>";
       
   486     d->socket->write(data);
       
   487     d->state = ConnectSent;
       
   488 }
       
   489 
       
   490 void QHttpSocketEngine::slotSocketDisconnected()
       
   491 {
       
   492 }
       
   493 
       
   494 void QHttpSocketEngine::slotSocketReadNotification()
       
   495 {
       
   496     Q_D(QHttpSocketEngine);
       
   497     if (d->state != Connected && d->socket->bytesAvailable() == 0)
       
   498         return;
       
   499 
       
   500     if (d->state == Connected) {
       
   501         // Forward as a read notification.
       
   502         if (d->readNotificationEnabled)
       
   503             emitReadNotification();
       
   504         return;
       
   505     }
       
   506 
       
   507   readResponseContent:
       
   508     if (d->state == ReadResponseContent) {
       
   509         char dummybuffer[4096];
       
   510         while (d->pendingResponseData) {
       
   511             int read = d->socket->read(dummybuffer, qMin(sizeof(dummybuffer), (size_t)d->pendingResponseData));
       
   512             if (read >= 0)
       
   513                 dummybuffer[read] = 0;
       
   514 
       
   515             if (read == 0)
       
   516                 return;
       
   517             if (read == -1) {
       
   518                 d->socket->disconnectFromHost();
       
   519                 emitWriteNotification();
       
   520                 return;
       
   521             }
       
   522             d->pendingResponseData -= read;
       
   523         }
       
   524         if (d->pendingResponseData > 0)
       
   525             return;
       
   526         d->state = SendAuthentication;
       
   527         slotSocketConnected();
       
   528         return;
       
   529     }
       
   530 
       
   531     // Still in handshake mode. Wait until we've got a full response.
       
   532     bool done = false;
       
   533     do {
       
   534         d->readBuffer += d->socket->readLine();
       
   535     } while (!(done = d->readBuffer.endsWith("\r\n\r\n")) && d->socket->canReadLine());
       
   536 
       
   537     if (!done) {
       
   538         // Wait for more.
       
   539         return;
       
   540     }
       
   541 
       
   542     if (!d->readBuffer.startsWith("HTTP/1.")) {
       
   543         // protocol error, this isn't HTTP
       
   544         d->readBuffer.clear();
       
   545         d->socket->close();
       
   546         setState(QAbstractSocket::UnconnectedState);
       
   547         setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy"));
       
   548         emitConnectionNotification();
       
   549         return;
       
   550     }
       
   551 
       
   552     QHttpResponseHeader responseHeader(QString::fromLatin1(d->readBuffer));
       
   553     d->readBuffer.clear();
       
   554 
       
   555     int statusCode = responseHeader.statusCode();
       
   556     if (statusCode == 200) {
       
   557         d->state = Connected;
       
   558         setLocalAddress(d->socket->localAddress());
       
   559         setLocalPort(d->socket->localPort());
       
   560         setState(QAbstractSocket::ConnectedState);
       
   561     } else if (statusCode == 407) {
       
   562         if (d->authenticator.isNull())
       
   563             d->authenticator.detach();
       
   564         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
       
   565 
       
   566         priv->parseHttpResponse(responseHeader, true);
       
   567 
       
   568         if (priv->phase == QAuthenticatorPrivate::Invalid) {
       
   569             // problem parsing the reply
       
   570             d->socket->close();
       
   571             setState(QAbstractSocket::UnconnectedState);
       
   572             setError(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy"));
       
   573             emitConnectionNotification();
       
   574             return;
       
   575         }
       
   576 
       
   577         bool willClose;
       
   578         QString proxyConnectionHeader = responseHeader.value(QLatin1String("Proxy-Connection"));
       
   579         proxyConnectionHeader = proxyConnectionHeader.toLower();
       
   580         if (proxyConnectionHeader == QLatin1String("close")) {
       
   581             willClose = true;
       
   582         } else if (proxyConnectionHeader == QLatin1String("keep-alive")) {
       
   583             willClose = false;
       
   584         } else {
       
   585             // no Proxy-Connection header, so use the default
       
   586             // HTTP 1.1's default behaviour is to keep persistent connections
       
   587             // HTTP 1.0 or earlier, so we expect the server to close
       
   588             willClose = (responseHeader.majorVersion() * 0x100 + responseHeader.minorVersion()) <= 0x0100;
       
   589         }
       
   590 
       
   591         if (willClose) {
       
   592             // the server will disconnect, so let's avoid receiving an error
       
   593             // especially since the signal below may trigger a new event loop
       
   594             d->socket->disconnectFromHost();
       
   595             d->socket->readAll();
       
   596         }
       
   597 
       
   598         if (priv->phase == QAuthenticatorPrivate::Done)
       
   599             emit proxyAuthenticationRequired(d->proxy, &d->authenticator);
       
   600 
       
   601         // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
       
   602         if (priv->phase == QAuthenticatorPrivate::Done) {
       
   603             setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
       
   604             d->socket->disconnectFromHost();
       
   605         } else {
       
   606             // close the connection if it isn't already and reconnect using the chosen authentication method
       
   607             d->state = SendAuthentication;
       
   608             if (willClose) {
       
   609                 d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
       
   610             } else {
       
   611                 bool ok;
       
   612                 int contentLength = responseHeader.value(QLatin1String("Content-Length")).toInt(&ok);
       
   613                 if (ok && contentLength > 0) {
       
   614                     d->state = ReadResponseContent;
       
   615                     d->pendingResponseData = contentLength;
       
   616                     goto readResponseContent;
       
   617                 } else {
       
   618                     d->state = SendAuthentication;
       
   619                     slotSocketConnected();
       
   620                 }
       
   621             }
       
   622             return;
       
   623         }
       
   624     } else {
       
   625         d->socket->close();
       
   626         setState(QAbstractSocket::UnconnectedState);
       
   627         if (statusCode == 403 || statusCode == 405) {
       
   628             // 403 Forbidden
       
   629             // 405 Method Not Allowed
       
   630             setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection"));
       
   631         } else if (statusCode == 404) {
       
   632             // 404 Not Found: host lookup error
       
   633             setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
       
   634         } else if (statusCode == 503) {
       
   635             // 503 Service Unavailable: Connection Refused
       
   636             setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused"));
       
   637         } else {
       
   638             // Some other reply
       
   639             //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
       
   640             setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy"));
       
   641         }
       
   642     }
       
   643 
       
   644     // The handshake is done; notify that we're connected (or failed to connect)
       
   645     emitConnectionNotification();
       
   646 }
       
   647 
       
   648 void QHttpSocketEngine::slotSocketBytesWritten()
       
   649 {
       
   650     Q_D(QHttpSocketEngine);
       
   651     if (d->state == Connected && d->writeNotificationEnabled)
       
   652         emitWriteNotification();
       
   653 }
       
   654 
       
   655 void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
       
   656 {
       
   657     Q_D(QHttpSocketEngine);
       
   658     d->readBuffer.clear();
       
   659 
       
   660     if (d->state != Connected) {
       
   661         // we are in proxy handshaking stages
       
   662         if (error == QAbstractSocket::HostNotFoundError)
       
   663             setError(QAbstractSocket::ProxyNotFoundError, tr("Proxy server not found"));
       
   664         else if (error == QAbstractSocket::ConnectionRefusedError)
       
   665             setError(QAbstractSocket::ProxyConnectionRefusedError, tr("Proxy connection refused"));
       
   666         else if (error == QAbstractSocket::SocketTimeoutError)
       
   667             setError(QAbstractSocket::ProxyConnectionTimeoutError, tr("Proxy server connection timed out"));
       
   668         else if (error == QAbstractSocket::RemoteHostClosedError)
       
   669             setError(QAbstractSocket::ProxyConnectionClosedError, tr("Proxy connection closed prematurely"));
       
   670         else
       
   671             setError(error, d->socket->errorString());
       
   672         emitConnectionNotification();
       
   673         return;
       
   674     }
       
   675 
       
   676     // We're connected
       
   677     if (error == QAbstractSocket::SocketTimeoutError)
       
   678         return;                 // ignore this error
       
   679 
       
   680     d->state = None;
       
   681     setError(error, d->socket->errorString());
       
   682     if (error == QAbstractSocket::RemoteHostClosedError) {
       
   683         emitReadNotification();
       
   684     } else {
       
   685         qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
       
   686     }
       
   687 }
       
   688 
       
   689 void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
       
   690 {
       
   691     Q_UNUSED(state);
       
   692 }
       
   693 
       
   694 void QHttpSocketEngine::emitPendingReadNotification()
       
   695 {
       
   696     Q_D(QHttpSocketEngine);
       
   697     d->readNotificationPending = false;
       
   698     if (d->readNotificationEnabled)
       
   699         emit readNotification();
       
   700 }
       
   701 
       
   702 void QHttpSocketEngine::emitPendingWriteNotification()
       
   703 {
       
   704     Q_D(QHttpSocketEngine);
       
   705     d->writeNotificationPending = false;
       
   706     if (d->writeNotificationEnabled)
       
   707         emit writeNotification();
       
   708 }
       
   709 
       
   710 void QHttpSocketEngine::emitPendingConnectionNotification()
       
   711 {
       
   712     Q_D(QHttpSocketEngine);
       
   713     d->connectionNotificationPending = false;
       
   714     emit connectionNotification();
       
   715 }
       
   716 
       
   717 void QHttpSocketEngine::emitReadNotification()
       
   718 {
       
   719     Q_D(QHttpSocketEngine);
       
   720     d->readNotificationActivated = true;
       
   721     if (d->readNotificationEnabled && !d->readNotificationPending) {
       
   722         d->readNotificationPending = true;
       
   723         QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection);
       
   724     }
       
   725 }
       
   726 
       
   727 void QHttpSocketEngine::emitWriteNotification()
       
   728 {
       
   729     Q_D(QHttpSocketEngine);
       
   730     d->writeNotificationActivated = true;
       
   731     if (d->writeNotificationEnabled && !d->writeNotificationPending) {
       
   732         d->writeNotificationPending = true;
       
   733         QMetaObject::invokeMethod(this, "emitPendingWriteNotification", Qt::QueuedConnection);
       
   734     }
       
   735 }
       
   736 
       
   737 void QHttpSocketEngine::emitConnectionNotification()
       
   738 {
       
   739     Q_D(QHttpSocketEngine);
       
   740     if (!d->connectionNotificationPending) {
       
   741         d->connectionNotificationPending = true;
       
   742         QMetaObject::invokeMethod(this, "emitPendingConnectionNotification", Qt::QueuedConnection);
       
   743     }
       
   744 }
       
   745 
       
   746 QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
       
   747     : readNotificationEnabled(false)
       
   748     , writeNotificationEnabled(false)
       
   749     , exceptNotificationEnabled(false)
       
   750     , readNotificationActivated(false)
       
   751     , writeNotificationActivated(false)
       
   752     , readNotificationPending(false)
       
   753     , writeNotificationPending(false)
       
   754     , connectionNotificationPending(false)
       
   755     , pendingResponseData(0)
       
   756 {
       
   757     socket = 0;
       
   758     state = QHttpSocketEngine::None;
       
   759 }
       
   760 
       
   761 QHttpSocketEnginePrivate::~QHttpSocketEnginePrivate()
       
   762 {
       
   763 }
       
   764 
       
   765 QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
       
   766                                                                     const QNetworkProxy &proxy,
       
   767                                                                     QObject *parent)
       
   768 {
       
   769     if (socketType != QAbstractSocket::TcpSocket)
       
   770         return 0;
       
   771 
       
   772     // proxy type must have been resolved by now
       
   773     if (proxy.type() != QNetworkProxy::HttpProxy)
       
   774         return 0;
       
   775 
       
   776     // we only accept active sockets
       
   777     if (!qobject_cast<QAbstractSocket *>(parent))
       
   778         return 0;
       
   779 
       
   780     QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
       
   781     engine->setProxy(proxy);
       
   782     return engine;
       
   783 }
       
   784 
       
   785 QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(int, QObject *)
       
   786 {
       
   787     return 0;
       
   788 }
       
   789 
       
   790 QT_END_NAMESPACE
       
   791 
       
   792 #endif