util/src/network/socket/qhttpsocketengine.cpp
changeset 7 f7bc934e204c
equal deleted inserted replaced
3:41300fa6a67c 7:f7bc934e204c
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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 = d->socket->read(data, maxlen);
       
   216 
       
   217     if (d->socket->state() == QAbstractSocket::UnconnectedState
       
   218         && d->socket->bytesAvailable() == 0) {
       
   219         emitReadNotification();
       
   220     }
       
   221 
       
   222     if (bytesRead == -1) {
       
   223         // If nothing has been read so far, and the direct socket read
       
   224         // failed, return the socket's error. Otherwise, fall through and
       
   225         // return as much as we read so far.
       
   226         close();
       
   227         setError(QAbstractSocket::RemoteHostClosedError,
       
   228                  QLatin1String("Remote host closed"));
       
   229         setState(QAbstractSocket::UnconnectedState);
       
   230         return -1;
       
   231     }
       
   232     return bytesRead;
       
   233 }
       
   234 
       
   235 qint64 QHttpSocketEngine::write(const char *data, qint64 len)
       
   236 {
       
   237     Q_D(QHttpSocketEngine);
       
   238     return d->socket->write(data, len);
       
   239 }
       
   240 
       
   241 #ifndef QT_NO_UDPSOCKET
       
   242 qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *,
       
   243                                        quint16 *)
       
   244 {
       
   245     return 0;
       
   246 }
       
   247 
       
   248 qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QHostAddress &,
       
   249                                         quint16)
       
   250 {
       
   251     return 0;
       
   252 }
       
   253 
       
   254 bool QHttpSocketEngine::hasPendingDatagrams() const
       
   255 {
       
   256     return false;
       
   257 }
       
   258 
       
   259 qint64 QHttpSocketEngine::pendingDatagramSize() const
       
   260 {
       
   261     return 0;
       
   262 }
       
   263 #endif // QT_NO_UDPSOCKET
       
   264 
       
   265 qint64 QHttpSocketEngine::bytesToWrite() const
       
   266 {
       
   267     Q_D(const QHttpSocketEngine);
       
   268     if (d->socket) {
       
   269         return d->socket->bytesToWrite();
       
   270     } else {
       
   271         return 0;
       
   272     }
       
   273 }
       
   274 
       
   275 int QHttpSocketEngine::option(SocketOption option) const
       
   276 {
       
   277     Q_D(const QHttpSocketEngine);
       
   278     if (d->socket) {
       
   279         // convert the enum and call the real socket
       
   280         if (option == QAbstractSocketEngine::LowDelayOption)
       
   281             return d->socket->socketOption(QAbstractSocket::LowDelayOption).toInt();
       
   282         if (option == QAbstractSocketEngine::KeepAliveOption)
       
   283             return d->socket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
       
   284     }
       
   285     return -1;
       
   286 }
       
   287 
       
   288 bool QHttpSocketEngine::setOption(SocketOption option, int value)
       
   289 {
       
   290     Q_D(QHttpSocketEngine);
       
   291     if (d->socket) {
       
   292         // convert the enum and call the real socket
       
   293         if (option == QAbstractSocketEngine::LowDelayOption)
       
   294             d->socket->setSocketOption(QAbstractSocket::LowDelayOption, value);
       
   295         if (option == QAbstractSocketEngine::KeepAliveOption)
       
   296             d->socket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
       
   297         return true;
       
   298     }
       
   299     return false;
       
   300 }
       
   301 
       
   302 /*
       
   303    Returns the difference between msecs and elapsed. If msecs is -1,
       
   304    however, -1 is returned.
       
   305 */
       
   306 static int qt_timeout_value(int msecs, int elapsed)
       
   307 {
       
   308     if (msecs == -1)
       
   309         return -1;
       
   310 
       
   311     int timeout = msecs - elapsed;
       
   312     return timeout < 0 ? 0 : timeout;
       
   313 }
       
   314 
       
   315 bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
       
   316 {
       
   317     Q_D(const QHttpSocketEngine);
       
   318 
       
   319     if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
       
   320         return false;
       
   321 
       
   322     QTime stopWatch;
       
   323     stopWatch.start();
       
   324 
       
   325     // Wait for more data if nothing is available.
       
   326     if (!d->socket->bytesAvailable()) {
       
   327         if (!d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
       
   328             if (d->socket->state() == QAbstractSocket::UnconnectedState)
       
   329                 return true;
       
   330             setError(d->socket->error(), d->socket->errorString());
       
   331             if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
       
   332                 *timedOut = true;
       
   333             return false;
       
   334         }
       
   335     }
       
   336 
       
   337     // If we're not connected yet, wait until we are, or until an error
       
   338     // occurs.
       
   339     while (d->state != Connected && d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
       
   340         // Loop while the protocol handshake is taking place.
       
   341     }
       
   342 
       
   343     // Report any error that may occur.
       
   344     if (d->state != Connected) {
       
   345         setError(d->socket->error(), d->socket->errorString());
       
   346         if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
       
   347             *timedOut = true;
       
   348         return false;
       
   349     }
       
   350     return true;
       
   351 }
       
   352 
       
   353 bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
       
   354 {
       
   355     Q_D(const QHttpSocketEngine);
       
   356 
       
   357     // If we're connected, just forward the call.
       
   358     if (d->state == Connected) {
       
   359         if (d->socket->bytesToWrite()) {
       
   360             if (!d->socket->waitForBytesWritten(msecs)) {
       
   361                 if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
       
   362                     *timedOut = true;
       
   363                 return false;
       
   364             }
       
   365         }
       
   366         return true;
       
   367     }
       
   368 
       
   369     QTime stopWatch;
       
   370     stopWatch.start();
       
   371 
       
   372     // If we're not connected yet, wait until we are, and until bytes have
       
   373     // been received (i.e., the socket has connected, we have sent the
       
   374     // greeting, and then received the response).
       
   375     while (d->state != Connected && d->socket->waitForReadyRead(qt_timeout_value(msecs, stopWatch.elapsed()))) {
       
   376         // Loop while the protocol handshake is taking place.
       
   377     }
       
   378 
       
   379     // Report any error that may occur.
       
   380     if (d->state != Connected) {
       
   381 //        setError(d->socket->error(), d->socket->errorString());
       
   382         if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
       
   383             *timedOut = true;
       
   384     }
       
   385 
       
   386     return true;
       
   387 }
       
   388 
       
   389 bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
       
   390                                            bool checkRead, bool checkWrite,
       
   391                                            int msecs, bool *timedOut)
       
   392 {
       
   393     Q_UNUSED(checkRead);
       
   394 
       
   395     if (!checkWrite) {
       
   396         // Not interested in writing? Then we wait for read notifications.
       
   397         bool canRead = waitForRead(msecs, timedOut);
       
   398         if (readyToRead)
       
   399             *readyToRead = canRead;
       
   400         return canRead;
       
   401     }
       
   402 
       
   403     // Interested in writing? Then we wait for write notifications.
       
   404     bool canWrite = waitForWrite(msecs, timedOut);
       
   405     if (readyToWrite)
       
   406         *readyToWrite = canWrite;
       
   407     return canWrite;
       
   408 }
       
   409 
       
   410 bool QHttpSocketEngine::isReadNotificationEnabled() const
       
   411 {
       
   412     Q_D(const QHttpSocketEngine);
       
   413     return d->readNotificationEnabled;
       
   414 }
       
   415 
       
   416 void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
       
   417 {
       
   418     Q_D(QHttpSocketEngine);
       
   419     if (d->readNotificationEnabled == enable)
       
   420         return;
       
   421 
       
   422     d->readNotificationEnabled = enable;
       
   423     if (enable) {
       
   424         // Enabling read notification can trigger a notification.
       
   425         if (bytesAvailable())
       
   426             slotSocketReadNotification();
       
   427     }
       
   428 }
       
   429 
       
   430 bool QHttpSocketEngine::isWriteNotificationEnabled() const
       
   431 {
       
   432     Q_D(const QHttpSocketEngine);
       
   433     return d->writeNotificationEnabled;
       
   434 }
       
   435 
       
   436 void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
       
   437 {
       
   438     Q_D(QHttpSocketEngine);
       
   439     d->writeNotificationEnabled = enable;
       
   440     if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
       
   441         QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection);
       
   442 }
       
   443 
       
   444 bool QHttpSocketEngine::isExceptionNotificationEnabled() const
       
   445 {
       
   446     Q_D(const QHttpSocketEngine);
       
   447     return d->exceptNotificationEnabled;
       
   448 }
       
   449 
       
   450 void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
       
   451 {
       
   452     Q_D(QHttpSocketEngine);
       
   453     d->exceptNotificationEnabled = enable;
       
   454 }
       
   455 
       
   456 void QHttpSocketEngine::slotSocketConnected()
       
   457 {
       
   458     Q_D(QHttpSocketEngine);
       
   459 
       
   460     // Send the greeting.
       
   461     const char method[] = "CONNECT ";
       
   462     QByteArray peerAddress = d->peerName.isEmpty() ?
       
   463                              d->peerAddress.toString().toLatin1() :
       
   464                              QUrl::toAce(d->peerName);
       
   465     QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
       
   466     QByteArray data = method;
       
   467     data += path;
       
   468     data += " HTTP/1.1\r\n";
       
   469     data += "Proxy-Connection: keep-alive\r\n"
       
   470             "User-Agent: Mozilla/5.0\r\n"
       
   471             "Host: " + peerAddress + "\r\n";
       
   472     QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
       
   473     //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
       
   474     if (priv && priv->method != QAuthenticatorPrivate::None) {
       
   475         data += "Proxy-Authorization: " + priv->calculateResponse(method, path);
       
   476         data += "\r\n";
       
   477     }
       
   478     data += "\r\n";
       
   479 //     qDebug() << ">>>>>>>> sending request" << this;
       
   480 //     qDebug() << data;
       
   481 //     qDebug() << ">>>>>>>";
       
   482     d->socket->write(data);
       
   483     d->state = ConnectSent;
       
   484 }
       
   485 
       
   486 void QHttpSocketEngine::slotSocketDisconnected()
       
   487 {
       
   488 }
       
   489 
       
   490 void QHttpSocketEngine::slotSocketReadNotification()
       
   491 {
       
   492     Q_D(QHttpSocketEngine);
       
   493     if (d->state != Connected && d->socket->bytesAvailable() == 0)
       
   494         return;
       
   495 
       
   496     if (d->state == Connected) {
       
   497         // Forward as a read notification.
       
   498         if (d->readNotificationEnabled)
       
   499             emitReadNotification();
       
   500         return;
       
   501     }
       
   502 
       
   503   readResponseContent:
       
   504     if (d->state == ReadResponseContent) {
       
   505         char dummybuffer[4096];
       
   506         while (d->pendingResponseData) {
       
   507             int read = d->socket->read(dummybuffer, qMin(sizeof(dummybuffer), (size_t)d->pendingResponseData));
       
   508             if (read >= 0)
       
   509                 dummybuffer[read] = 0;
       
   510 
       
   511             if (read == 0)
       
   512                 return;
       
   513             if (read == -1) {
       
   514                 d->socket->disconnectFromHost();
       
   515                 emitWriteNotification();
       
   516                 return;
       
   517             }
       
   518             d->pendingResponseData -= read;
       
   519         }
       
   520         if (d->pendingResponseData > 0)
       
   521             return;
       
   522         d->state = SendAuthentication;
       
   523         slotSocketConnected();
       
   524         return;
       
   525     }
       
   526 
       
   527     // Still in handshake mode. Wait until we've got a full response.
       
   528     bool done = false;
       
   529     do {
       
   530         d->readBuffer += d->socket->readLine();
       
   531     } while (!(done = d->readBuffer.endsWith("\r\n\r\n")) && d->socket->canReadLine());
       
   532 
       
   533     if (!done) {
       
   534         // Wait for more.
       
   535         return;
       
   536     }
       
   537 
       
   538     if (!d->readBuffer.startsWith("HTTP/1.")) {
       
   539         // protocol error, this isn't HTTP
       
   540         d->readBuffer.clear();
       
   541         d->socket->close();
       
   542         setState(QAbstractSocket::UnconnectedState);
       
   543         setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy"));
       
   544         emitConnectionNotification();
       
   545         return;
       
   546     }
       
   547 
       
   548     QHttpResponseHeader responseHeader(QString::fromLatin1(d->readBuffer));
       
   549     d->readBuffer.clear(); // we parsed the proxy protocol response. from now on direct socket reading will be done
       
   550 
       
   551     int statusCode = responseHeader.statusCode();
       
   552     if (statusCode == 200) {
       
   553         d->state = Connected;
       
   554         setLocalAddress(d->socket->localAddress());
       
   555         setLocalPort(d->socket->localPort());
       
   556         setState(QAbstractSocket::ConnectedState);
       
   557     } else if (statusCode == 407) {
       
   558         if (d->authenticator.isNull())
       
   559             d->authenticator.detach();
       
   560         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
       
   561 
       
   562         priv->parseHttpResponse(responseHeader, true);
       
   563 
       
   564         if (priv->phase == QAuthenticatorPrivate::Invalid) {
       
   565             // problem parsing the reply
       
   566             d->socket->close();
       
   567             setState(QAbstractSocket::UnconnectedState);
       
   568             setError(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy"));
       
   569             emitConnectionNotification();
       
   570             return;
       
   571         }
       
   572 
       
   573         bool willClose;
       
   574         QString proxyConnectionHeader = responseHeader.value(QLatin1String("Proxy-Connection"));
       
   575         proxyConnectionHeader = proxyConnectionHeader.toLower();
       
   576         if (proxyConnectionHeader == QLatin1String("close")) {
       
   577             willClose = true;
       
   578         } else if (proxyConnectionHeader == QLatin1String("keep-alive")) {
       
   579             willClose = false;
       
   580         } else {
       
   581             // no Proxy-Connection header, so use the default
       
   582             // HTTP 1.1's default behaviour is to keep persistent connections
       
   583             // HTTP 1.0 or earlier, so we expect the server to close
       
   584             willClose = (responseHeader.majorVersion() * 0x100 + responseHeader.minorVersion()) <= 0x0100;
       
   585         }
       
   586 
       
   587         if (willClose) {
       
   588             // the server will disconnect, so let's avoid receiving an error
       
   589             // especially since the signal below may trigger a new event loop
       
   590             d->socket->disconnectFromHost();
       
   591             d->socket->readAll();
       
   592         }
       
   593 
       
   594         if (priv->phase == QAuthenticatorPrivate::Done)
       
   595             emit proxyAuthenticationRequired(d->proxy, &d->authenticator);
       
   596 
       
   597         // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
       
   598         if (priv->phase == QAuthenticatorPrivate::Done) {
       
   599             setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
       
   600             d->socket->disconnectFromHost();
       
   601         } else {
       
   602             // close the connection if it isn't already and reconnect using the chosen authentication method
       
   603             d->state = SendAuthentication;
       
   604             if (willClose) {
       
   605                 d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
       
   606             } else {
       
   607                 bool ok;
       
   608                 int contentLength = responseHeader.value(QLatin1String("Content-Length")).toInt(&ok);
       
   609                 if (ok && contentLength > 0) {
       
   610                     d->state = ReadResponseContent;
       
   611                     d->pendingResponseData = contentLength;
       
   612                     goto readResponseContent;
       
   613                 } else {
       
   614                     d->state = SendAuthentication;
       
   615                     slotSocketConnected();
       
   616                 }
       
   617             }
       
   618             return;
       
   619         }
       
   620     } else {
       
   621         d->socket->close();
       
   622         setState(QAbstractSocket::UnconnectedState);
       
   623         if (statusCode == 403 || statusCode == 405) {
       
   624             // 403 Forbidden
       
   625             // 405 Method Not Allowed
       
   626             setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection"));
       
   627         } else if (statusCode == 404) {
       
   628             // 404 Not Found: host lookup error
       
   629             setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
       
   630         } else if (statusCode == 503) {
       
   631             // 503 Service Unavailable: Connection Refused
       
   632             setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused"));
       
   633         } else {
       
   634             // Some other reply
       
   635             //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
       
   636             setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy"));
       
   637         }
       
   638     }
       
   639 
       
   640     // The handshake is done; notify that we're connected (or failed to connect)
       
   641     emitConnectionNotification();
       
   642 }
       
   643 
       
   644 void QHttpSocketEngine::slotSocketBytesWritten()
       
   645 {
       
   646     Q_D(QHttpSocketEngine);
       
   647     if (d->state == Connected && d->writeNotificationEnabled)
       
   648         emitWriteNotification();
       
   649 }
       
   650 
       
   651 void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
       
   652 {
       
   653     Q_D(QHttpSocketEngine);
       
   654     d->readBuffer.clear();
       
   655 
       
   656     if (d->state != Connected) {
       
   657         // we are in proxy handshaking stages
       
   658         if (error == QAbstractSocket::HostNotFoundError)
       
   659             setError(QAbstractSocket::ProxyNotFoundError, tr("Proxy server not found"));
       
   660         else if (error == QAbstractSocket::ConnectionRefusedError)
       
   661             setError(QAbstractSocket::ProxyConnectionRefusedError, tr("Proxy connection refused"));
       
   662         else if (error == QAbstractSocket::SocketTimeoutError)
       
   663             setError(QAbstractSocket::ProxyConnectionTimeoutError, tr("Proxy server connection timed out"));
       
   664         else if (error == QAbstractSocket::RemoteHostClosedError)
       
   665             setError(QAbstractSocket::ProxyConnectionClosedError, tr("Proxy connection closed prematurely"));
       
   666         else
       
   667             setError(error, d->socket->errorString());
       
   668         emitConnectionNotification();
       
   669         return;
       
   670     }
       
   671 
       
   672     // We're connected
       
   673     if (error == QAbstractSocket::SocketTimeoutError)
       
   674         return;                 // ignore this error
       
   675 
       
   676     d->state = None;
       
   677     setError(error, d->socket->errorString());
       
   678     if (error == QAbstractSocket::RemoteHostClosedError) {
       
   679         emitReadNotification();
       
   680     } else {
       
   681         qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
       
   682     }
       
   683 }
       
   684 
       
   685 void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
       
   686 {
       
   687     Q_UNUSED(state);
       
   688 }
       
   689 
       
   690 void QHttpSocketEngine::emitPendingReadNotification()
       
   691 {
       
   692     Q_D(QHttpSocketEngine);
       
   693     d->readNotificationPending = false;
       
   694     if (d->readNotificationEnabled)
       
   695         emit readNotification();
       
   696 }
       
   697 
       
   698 void QHttpSocketEngine::emitPendingWriteNotification()
       
   699 {
       
   700     Q_D(QHttpSocketEngine);
       
   701     d->writeNotificationPending = false;
       
   702     if (d->writeNotificationEnabled)
       
   703         emit writeNotification();
       
   704 }
       
   705 
       
   706 void QHttpSocketEngine::emitPendingConnectionNotification()
       
   707 {
       
   708     Q_D(QHttpSocketEngine);
       
   709     d->connectionNotificationPending = false;
       
   710     emit connectionNotification();
       
   711 }
       
   712 
       
   713 void QHttpSocketEngine::emitReadNotification()
       
   714 {
       
   715     Q_D(QHttpSocketEngine);
       
   716     d->readNotificationActivated = true;
       
   717     if (d->readNotificationEnabled && !d->readNotificationPending) {
       
   718         d->readNotificationPending = true;
       
   719         QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection);
       
   720     }
       
   721 }
       
   722 
       
   723 void QHttpSocketEngine::emitWriteNotification()
       
   724 {
       
   725     Q_D(QHttpSocketEngine);
       
   726     d->writeNotificationActivated = true;
       
   727     if (d->writeNotificationEnabled && !d->writeNotificationPending) {
       
   728         d->writeNotificationPending = true;
       
   729         QMetaObject::invokeMethod(this, "emitPendingWriteNotification", Qt::QueuedConnection);
       
   730     }
       
   731 }
       
   732 
       
   733 void QHttpSocketEngine::emitConnectionNotification()
       
   734 {
       
   735     Q_D(QHttpSocketEngine);
       
   736     if (!d->connectionNotificationPending) {
       
   737         d->connectionNotificationPending = true;
       
   738         QMetaObject::invokeMethod(this, "emitPendingConnectionNotification", Qt::QueuedConnection);
       
   739     }
       
   740 }
       
   741 
       
   742 QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
       
   743     : readNotificationEnabled(false)
       
   744     , writeNotificationEnabled(false)
       
   745     , exceptNotificationEnabled(false)
       
   746     , readNotificationActivated(false)
       
   747     , writeNotificationActivated(false)
       
   748     , readNotificationPending(false)
       
   749     , writeNotificationPending(false)
       
   750     , connectionNotificationPending(false)
       
   751     , pendingResponseData(0)
       
   752 {
       
   753     socket = 0;
       
   754     state = QHttpSocketEngine::None;
       
   755 }
       
   756 
       
   757 QHttpSocketEnginePrivate::~QHttpSocketEnginePrivate()
       
   758 {
       
   759 }
       
   760 
       
   761 QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
       
   762                                                                     const QNetworkProxy &proxy,
       
   763                                                                     QObject *parent)
       
   764 {
       
   765     if (socketType != QAbstractSocket::TcpSocket)
       
   766         return 0;
       
   767 
       
   768     // proxy type must have been resolved by now
       
   769     if (proxy.type() != QNetworkProxy::HttpProxy)
       
   770         return 0;
       
   771 
       
   772     // we only accept active sockets
       
   773     if (!qobject_cast<QAbstractSocket *>(parent))
       
   774         return 0;
       
   775 
       
   776     QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
       
   777     engine->setProxy(proxy);
       
   778     return engine;
       
   779 }
       
   780 
       
   781 QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(int, QObject *)
       
   782 {
       
   783     return 0;
       
   784 }
       
   785 
       
   786 QT_END_NAMESPACE
       
   787 
       
   788 #endif