util/src/network/access/qhttpnetworkconnection.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 "qhttpnetworkconnection_p.h"
       
    43 #include "qhttpnetworkconnectionchannel_p.h"
       
    44 #include "private/qnoncontiguousbytedevice_p.h"
       
    45 #include <private/qnetworkrequest_p.h>
       
    46 #include <private/qobject_p.h>
       
    47 #include <private/qauthenticator_p.h>
       
    48 #include <qnetworkproxy.h>
       
    49 #include <qauthenticator.h>
       
    50 
       
    51 #include <qbuffer.h>
       
    52 #include <qpair.h>
       
    53 #include <qhttp.h>
       
    54 #include <qdebug.h>
       
    55 
       
    56 #ifndef QT_NO_HTTP
       
    57 
       
    58 #ifndef QT_NO_OPENSSL
       
    59 #    include <QtNetwork/qsslkey.h>
       
    60 #    include <QtNetwork/qsslcipher.h>
       
    61 #    include <QtNetwork/qsslconfiguration.h>
       
    62 #endif
       
    63 
       
    64 
       
    65 
       
    66 QT_BEGIN_NAMESPACE
       
    67 
       
    68 #ifdef Q_OS_SYMBIAN
       
    69 const int QHttpNetworkConnectionPrivate::defaultChannelCount = 3;
       
    70 #else
       
    71 const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
       
    72 #endif
       
    73 
       
    74 // the maximum amount of requests that might be pipelined into a socket
       
    75 // from what was suggested, 3 seems to be OK
       
    76 const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
       
    77 
       
    78 
       
    79 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
       
    80 : hostName(hostName), port(port), encrypt(encrypt),
       
    81   channelCount(defaultChannelCount),
       
    82   pendingAuthSignal(false), pendingProxyAuthSignal(false)
       
    83 #ifndef QT_NO_NETWORKPROXY
       
    84   , networkProxy(QNetworkProxy::NoProxy)
       
    85 #endif
       
    86 {
       
    87     channels = new QHttpNetworkConnectionChannel[channelCount];
       
    88 }
       
    89 
       
    90 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
       
    91 : hostName(hostName), port(port), encrypt(encrypt),
       
    92   channelCount(channelCount),
       
    93   pendingAuthSignal(false), pendingProxyAuthSignal(false)
       
    94 #ifndef QT_NO_NETWORKPROXY
       
    95   , networkProxy(QNetworkProxy::NoProxy)
       
    96 #endif
       
    97 {
       
    98     channels = new QHttpNetworkConnectionChannel[channelCount];
       
    99 }
       
   100 
       
   101 
       
   102 
       
   103 QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
       
   104 {
       
   105     for (int i = 0; i < channelCount; ++i) {
       
   106         if (channels[i].socket) {
       
   107             channels[i].socket->close();
       
   108             delete channels[i].socket;
       
   109         }
       
   110     }
       
   111     delete []channels;
       
   112 }
       
   113 
       
   114 void QHttpNetworkConnectionPrivate::init()
       
   115 {
       
   116     for (int i = 0; i < channelCount; i++) {
       
   117         channels[i].setConnection(this->q_func());
       
   118         channels[i].init();
       
   119     }
       
   120 }
       
   121 
       
   122 int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
       
   123 {
       
   124     for (int i = 0; i < channelCount; ++i)
       
   125         if (channels[i].socket == socket)
       
   126             return i;
       
   127 
       
   128     qFatal("Called with unknown socket object.");
       
   129     return 0;
       
   130 }
       
   131 
       
   132 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
       
   133 {
       
   134     return reply.d_func()->responseData.byteAmount();
       
   135 }
       
   136 
       
   137 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
       
   138 {
       
   139     return reply.d_func()->responseData.sizeNextBlock();
       
   140 }
       
   141 
       
   142 void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
       
   143 {
       
   144     QHttpNetworkRequest &request = messagePair.first;
       
   145     QHttpNetworkReply *reply = messagePair.second;
       
   146 
       
   147     // add missing fields for the request
       
   148     QByteArray value;
       
   149     // check if Content-Length is provided
       
   150     QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
       
   151     if (uploadByteDevice) {
       
   152         if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
       
   153             // both values known, take the smaller one.
       
   154             request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
       
   155         } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
       
   156             // content length not supplied by user, but the upload device knows it
       
   157             request.setContentLength(uploadByteDevice->size());
       
   158         } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
       
   159             // everything OK, the user supplied us the contentLength
       
   160         } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
       
   161             qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
       
   162         }
       
   163     }
       
   164     // set the Connection/Proxy-Connection: Keep-Alive headers
       
   165 #ifndef QT_NO_NETWORKPROXY
       
   166     if (networkProxy.type() == QNetworkProxy::HttpCachingProxy)  {
       
   167         value = request.headerField("proxy-connection");
       
   168         if (value.isEmpty())
       
   169             request.setHeaderField("Proxy-Connection", "Keep-Alive");
       
   170     } else {
       
   171 #endif
       
   172         value = request.headerField("connection");
       
   173         if (value.isEmpty())
       
   174             request.setHeaderField("Connection", "Keep-Alive");
       
   175 #ifndef QT_NO_NETWORKPROXY
       
   176     }
       
   177 #endif
       
   178 
       
   179     // If the request had a accept-encoding set, we better not mess
       
   180     // with it. If it was not set, we announce that we understand gzip
       
   181     // and remember this fact in request.d->autoDecompress so that
       
   182     // we can later decompress the HTTP reply if it has such an
       
   183     // encoding.
       
   184     value = request.headerField("accept-encoding");
       
   185     if (value.isEmpty()) {
       
   186 #ifndef QT_NO_COMPRESS
       
   187         request.setHeaderField("Accept-Encoding", "gzip");
       
   188         request.d->autoDecompress = true;
       
   189 #else
       
   190         // if zlib is not available set this to false always
       
   191         request.d->autoDecompress = false;
       
   192 #endif
       
   193     }
       
   194 
       
   195     // some websites mandate an accept-language header and fail
       
   196     // if it is not sent. This is a problem with the website and
       
   197     // not with us, but we work around this by setting
       
   198     // one always.
       
   199     value = request.headerField("accept-language");
       
   200     if (value.isEmpty()) {
       
   201         QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-'));
       
   202         QString acceptLanguage;
       
   203         if (systemLocale == QLatin1String("C"))
       
   204             acceptLanguage = QString::fromAscii("en,*");
       
   205         else if (systemLocale.startsWith(QLatin1String("en-")))
       
   206             acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale);
       
   207         else
       
   208             acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale);
       
   209         request.setHeaderField("Accept-Language", acceptLanguage.toAscii());
       
   210     }
       
   211 
       
   212     // set the User Agent
       
   213     value = request.headerField("user-agent");
       
   214     if (value.isEmpty())
       
   215         request.setHeaderField("User-Agent", "Mozilla/5.0");
       
   216     // set the host
       
   217     value = request.headerField("host");
       
   218     if (value.isEmpty()) {
       
   219         QByteArray host = QUrl::toAce(hostName);
       
   220 
       
   221         int port = request.url().port();
       
   222         if (port != -1) {
       
   223             host += ':';
       
   224             host += QByteArray::number(port);
       
   225         }
       
   226 
       
   227         request.setHeaderField("Host", host);
       
   228     }
       
   229 
       
   230     reply->d_func()->requestIsPrepared = true;
       
   231 }
       
   232 
       
   233 
       
   234 
       
   235 
       
   236 void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
       
   237                                                    QHttpNetworkReply *reply,
       
   238                                                    QNetworkReply::NetworkError errorCode)
       
   239 {
       
   240     Q_Q(QHttpNetworkConnection);
       
   241     if (socket && reply) {
       
   242         // this error matters only to this reply
       
   243         reply->d_func()->errorString = errorDetail(errorCode, socket);
       
   244         emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
       
   245         int i = indexOf(socket);
       
   246         // remove the corrupt data if any
       
   247         reply->d_func()->eraseData();
       
   248         channels[i].close();
       
   249         // send the next request
       
   250         QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   251     }
       
   252 }
       
   253 
       
   254 void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
       
   255 {
       
   256     Q_ASSERT(auth);
       
   257 
       
   258     // select another channel
       
   259     QAuthenticator* otherAuth = 0;
       
   260     for (int i = 0; i < channelCount; ++i) {
       
   261         if (i == fromChannel)
       
   262             continue;
       
   263         if (isProxy)
       
   264             otherAuth = &channels[i].proxyAuthenticator;
       
   265         else
       
   266             otherAuth = &channels[i].authenticator;
       
   267         // if the credentials are different, copy them
       
   268         if (otherAuth->user().compare(auth->user()))
       
   269             otherAuth->setUser(auth->user());
       
   270         if (otherAuth->password().compare(auth->password()))
       
   271             otherAuth->setPassword(auth->password());
       
   272     }
       
   273 }
       
   274 
       
   275 
       
   276 // handles the authentication for one channel and eventually re-starts the other channels
       
   277 bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
       
   278                                                                 bool isProxy, bool &resend)
       
   279 {
       
   280     Q_ASSERT(socket);
       
   281     Q_ASSERT(reply);
       
   282 
       
   283     Q_Q(QHttpNetworkConnection);
       
   284 
       
   285     resend = false;
       
   286     //create the response header to be used with QAuthenticatorPrivate.
       
   287     QHttpResponseHeader responseHeader;
       
   288     QList<QPair<QByteArray, QByteArray> > fields = reply->header();
       
   289     QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
       
   290     while (it != fields.constEnd()) {
       
   291         responseHeader.addValue(QString::fromLatin1(it->first), QString::fromUtf8(it->second));
       
   292         it++;
       
   293     }
       
   294     //find out the type of authentication protocol requested.
       
   295     QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
       
   296     if (authMethod != QAuthenticatorPrivate::None) {
       
   297         int i = indexOf(socket);
       
   298         //Use a single authenticator for all domains. ### change later to use domain/realm
       
   299         QAuthenticator* auth = 0;
       
   300         if (isProxy) {
       
   301             auth = &channels[i].proxyAuthenticator;
       
   302             channels[i].proxyAuthMehtod = authMethod;
       
   303         } else {
       
   304             auth = &channels[i].authenticator;
       
   305             channels[i].authMehtod = authMethod;
       
   306         }
       
   307         //proceed with the authentication.
       
   308         if (auth->isNull())
       
   309             auth->detach();
       
   310         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
       
   311         priv->parseHttpResponse(responseHeader, isProxy);
       
   312 
       
   313         if (priv->phase == QAuthenticatorPrivate::Done) {
       
   314             if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) {
       
   315                 // drop the request
       
   316                 reply->d_func()->eraseData();
       
   317                 channels[i].close();
       
   318                 channels[i].lastStatus = 0;
       
   319                 channels[i].state =  QHttpNetworkConnectionChannel::Wait4AuthState;
       
   320                 return false;
       
   321             }
       
   322             // cannot use this socket until the slot returns
       
   323             channels[i].state = QHttpNetworkConnectionChannel::WaitingState;
       
   324             socket->blockSignals(true);
       
   325             if (!isProxy) {
       
   326                 pendingAuthSignal = true;
       
   327                 emit q->authenticationRequired(reply->request(), auth, q);
       
   328                 pendingAuthSignal = false;
       
   329 #ifndef QT_NO_NETWORKPROXY
       
   330             } else {
       
   331                 pendingProxyAuthSignal = true;
       
   332                 emit q->proxyAuthenticationRequired(networkProxy, auth, q);
       
   333                 pendingProxyAuthSignal = false;
       
   334 #endif
       
   335             }
       
   336             socket->blockSignals(false);
       
   337             // socket free to use
       
   338             channels[i].state = QHttpNetworkConnectionChannel::IdleState;
       
   339             if (priv->phase != QAuthenticatorPrivate::Done) {
       
   340                 // send any pending requests
       
   341                 copyCredentials(i,  auth, isProxy);
       
   342                 QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection);
       
   343             }
       
   344         }
       
   345         // changing values in QAuthenticator will reset the 'phase'
       
   346         if (priv->phase == QAuthenticatorPrivate::Done) {
       
   347             // authentication is cancelled, send the current contents to the user.
       
   348             emit channels[i].reply->headerChanged();
       
   349             emit channels[i].reply->readyRead();
       
   350             QNetworkReply::NetworkError errorCode =
       
   351                 isProxy
       
   352                 ? QNetworkReply::ProxyAuthenticationRequiredError
       
   353                 : QNetworkReply::AuthenticationRequiredError;
       
   354             reply->d_func()->errorString = errorDetail(errorCode, socket);
       
   355             emit q->error(errorCode, reply->d_func()->errorString);
       
   356             emit channels[i].reply->finished();
       
   357             // ### at this point the reply could be deleted
       
   358             socket->close();
       
   359             // remove pending request on the other channels
       
   360             for (int j = 0; j < channelCount; ++j) {
       
   361                 if (j != i && channels[j].state ==  QHttpNetworkConnectionChannel::Wait4AuthState)
       
   362                     channels[j].state = QHttpNetworkConnectionChannel::IdleState;
       
   363             }
       
   364             return true;
       
   365         }
       
   366         //resend the request
       
   367         resend = true;
       
   368         return true;
       
   369     }
       
   370     return false;
       
   371 }
       
   372 
       
   373 void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
       
   374 {
       
   375     Q_ASSERT(socket);
       
   376 
       
   377     int i = indexOf(socket);
       
   378 
       
   379     if (channels[i].authMehtod != QAuthenticatorPrivate::None) {
       
   380         if (!(channels[i].authMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
       
   381             QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
       
   382             if (priv && priv->method != QAuthenticatorPrivate::None) {
       
   383                 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
       
   384                 request.setHeaderField("Authorization", response);
       
   385             }
       
   386         }
       
   387     }
       
   388     if (channels[i].proxyAuthMehtod != QAuthenticatorPrivate::None) {
       
   389         if (!(channels[i].proxyAuthMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
       
   390             QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
       
   391             if (priv && priv->method != QAuthenticatorPrivate::None) {
       
   392                 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
       
   393                 request.setHeaderField("Proxy-Authorization", response);
       
   394             }
       
   395         }
       
   396     }
       
   397 }
       
   398 
       
   399 QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
       
   400 {
       
   401     Q_Q(QHttpNetworkConnection);
       
   402 
       
   403     // The reply component of the pair is created initially.
       
   404     QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
       
   405     reply->setRequest(request);
       
   406     reply->d_func()->connection = q;
       
   407     reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
       
   408     HttpMessagePair pair = qMakePair(request, reply);
       
   409 
       
   410     switch (request.priority()) {
       
   411     case QHttpNetworkRequest::HighPriority:
       
   412         highPriorityQueue.prepend(pair);
       
   413         break;
       
   414     case QHttpNetworkRequest::NormalPriority:
       
   415     case QHttpNetworkRequest::LowPriority:
       
   416         lowPriorityQueue.prepend(pair);
       
   417         break;
       
   418     }
       
   419 
       
   420     // this used to be called via invokeMethod and a QueuedConnection
       
   421     // It is the only place _q_startNextRequest is called directly without going
       
   422     // through the event loop using a QueuedConnection.
       
   423     // This is dangerous because of recursion that might occur when emitting
       
   424     // signals as DirectConnection from this code path. Therefore all signal
       
   425     // emissions that can come out from this code path need to
       
   426     // be QueuedConnection.
       
   427     // We are currently trying to fine-tune this.
       
   428     _q_startNextRequest();
       
   429 
       
   430 
       
   431     return reply;
       
   432 }
       
   433 
       
   434 void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
       
   435 {
       
   436     Q_Q(QHttpNetworkConnection);
       
   437 
       
   438     QHttpNetworkRequest request = pair.first;
       
   439     switch (request.priority()) {
       
   440     case QHttpNetworkRequest::HighPriority:
       
   441         highPriorityQueue.prepend(pair);
       
   442         break;
       
   443     case QHttpNetworkRequest::NormalPriority:
       
   444     case QHttpNetworkRequest::LowPriority:
       
   445         lowPriorityQueue.prepend(pair);
       
   446         break;
       
   447     }
       
   448 
       
   449     QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   450 }
       
   451 
       
   452 void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket)
       
   453 {
       
   454     Q_ASSERT(socket);
       
   455 
       
   456     int i = indexOf(socket);
       
   457 
       
   458     if (!highPriorityQueue.isEmpty()) {
       
   459         // remove from queue before sendRequest! else we might pipeline the same request again
       
   460         HttpMessagePair messagePair = highPriorityQueue.takeLast();
       
   461         if (!messagePair.second->d_func()->requestIsPrepared)
       
   462             prepareRequest(messagePair);
       
   463         channels[i].request = messagePair.first;
       
   464         channels[i].reply = messagePair.second;
       
   465         channels[i].sendRequest();
       
   466         return;
       
   467     }
       
   468 
       
   469     if (!lowPriorityQueue.isEmpty()) {
       
   470         // remove from queue before sendRequest! else we might pipeline the same request again
       
   471         HttpMessagePair messagePair = lowPriorityQueue.takeLast();
       
   472         if (!messagePair.second->d_func()->requestIsPrepared)
       
   473             prepareRequest(messagePair);
       
   474         channels[i].request = messagePair.first;
       
   475         channels[i].reply = messagePair.second;
       
   476         channels[i].sendRequest();
       
   477         return;
       
   478     }
       
   479 }
       
   480 
       
   481 // this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
       
   482 void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
       
   483 {
       
   484     // return fast if there is nothing to pipeline
       
   485     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
       
   486         return;
       
   487 
       
   488     int i = indexOf(socket);
       
   489 
       
   490     bool highPriorityQueueProcessingDone = false;
       
   491     bool lowPriorityQueueProcessingDone = false;
       
   492 
       
   493     while (!highPriorityQueueProcessingDone && !lowPriorityQueueProcessingDone) {
       
   494         // this loop runs once per request we intend to pipeline in.
       
   495 
       
   496         if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
       
   497             return;
       
   498 
       
   499         // the current request that is in must already support pipelining
       
   500         if (!channels[i].request.isPipeliningAllowed())
       
   501             return;
       
   502 
       
   503         // the current request must be a idempotent (right now we only check GET)
       
   504         if (channels[i].request.operation() != QHttpNetworkRequest::Get)
       
   505             return;
       
   506 
       
   507         // check if socket is connected
       
   508         if (socket->state() != QAbstractSocket::ConnectedState)
       
   509             return;
       
   510 
       
   511         // check for resendCurrent
       
   512         if (channels[i].resendCurrent)
       
   513             return;
       
   514 
       
   515         // we do not like authentication stuff
       
   516         // ### make sure to be OK with this in later releases
       
   517         if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty())
       
   518             return;
       
   519         if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty())
       
   520             return;
       
   521 
       
   522         // check for pipeline length
       
   523         if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
       
   524             return;
       
   525 
       
   526         // must be in ReadingState or WaitingState
       
   527         if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
       
   528                || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
       
   529             return;
       
   530 
       
   531         highPriorityQueueProcessingDone = fillPipeline(highPriorityQueue, channels[i]);
       
   532         // not finished with highPriorityQueue? then loop again
       
   533         if (!highPriorityQueueProcessingDone)
       
   534             continue;
       
   535         // highPriorityQueue was processed, now deal with the lowPriorityQueue
       
   536         lowPriorityQueueProcessingDone = fillPipeline(lowPriorityQueue, channels[i]);
       
   537     }
       
   538 }
       
   539 
       
   540 // returns true when the processing of a queue has been done
       
   541 bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
       
   542 {
       
   543     if (queue.isEmpty())
       
   544         return true;
       
   545 
       
   546     for (int i = queue.count() - 1; i >= 0; --i) {
       
   547         HttpMessagePair messagePair = queue.at(i);
       
   548         const QHttpNetworkRequest &request = messagePair.first;
       
   549 
       
   550         // we currently do not support pipelining if HTTP authentication is used
       
   551         if (!request.url().userInfo().isEmpty())
       
   552             continue;
       
   553 
       
   554         // take only GET requests
       
   555         if (request.operation() != QHttpNetworkRequest::Get)
       
   556             continue;
       
   557 
       
   558         if (!request.isPipeliningAllowed())
       
   559             continue;
       
   560 
       
   561         // remove it from the queue
       
   562         queue.takeAt(i);
       
   563         // we modify the queue we iterate over here, but since we return from the function
       
   564         // afterwards this is fine.
       
   565 
       
   566         // actually send it
       
   567         if (!messagePair.second->d_func()->requestIsPrepared)
       
   568             prepareRequest(messagePair);
       
   569         channel.pipelineInto(messagePair);
       
   570 
       
   571         // return false because we processed something and need to process again
       
   572         return false;
       
   573     }
       
   574 
       
   575     // return true, the queue has been processed and not changed
       
   576     return true;
       
   577 }
       
   578 
       
   579 
       
   580 QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket,
       
   581                                                    const QString &extraDetail)
       
   582 {
       
   583     Q_ASSERT(socket);
       
   584 
       
   585     QString errorString;
       
   586     switch (errorCode) {
       
   587     case QNetworkReply::HostNotFoundError:
       
   588         errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
       
   589                               .arg(socket->peerName());
       
   590         break;
       
   591     case QNetworkReply::ConnectionRefusedError:
       
   592         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused"));
       
   593         break;
       
   594     case QNetworkReply::RemoteHostClosedError:
       
   595         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection closed"));
       
   596         break;
       
   597     case QNetworkReply::TimeoutError:
       
   598         errorString = QLatin1String(QT_TRANSLATE_NOOP("QAbstractSocket", "Socket operation timed out"));
       
   599         break;
       
   600     case QNetworkReply::ProxyAuthenticationRequiredError:
       
   601         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy requires authentication"));
       
   602         break;
       
   603     case QNetworkReply::AuthenticationRequiredError:
       
   604         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Host requires authentication"));
       
   605         break;
       
   606     case QNetworkReply::ProtocolFailure:
       
   607         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Data corrupted"));
       
   608         break;
       
   609     case QNetworkReply::ProtocolUnknownError:
       
   610         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown protocol specified"));
       
   611         break;
       
   612     case QNetworkReply::SslHandshakeFailedError:
       
   613         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "SSL handshake failed"));
       
   614         break;
       
   615     default:
       
   616         // all other errors are treated as QNetworkReply::UnknownNetworkError
       
   617         errorString = extraDetail;
       
   618         break;
       
   619     }
       
   620     return errorString;
       
   621 }
       
   622 
       
   623 // this is called from the destructor of QHttpNetworkReply. It is called when
       
   624 // the reply was finished correctly or when it was aborted.
       
   625 void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
       
   626 {
       
   627     Q_Q(QHttpNetworkConnection);
       
   628 
       
   629     // check if the reply is currently being processed or it is pipelined in
       
   630     for (int i = 0; i < channelCount; ++i) {
       
   631         // is the reply associated the currently processing of this channel?
       
   632         if (channels[i].reply == reply) {
       
   633             channels[i].reply = 0;
       
   634 
       
   635             if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
       
   636                 // the reply had to be prematurely removed, e.g. it was not finished
       
   637                 // therefore we have to requeue the already pipelined requests.
       
   638                 channels[i].requeueCurrentlyPipelinedRequests();
       
   639             }
       
   640 
       
   641             // if HTTP mandates we should close
       
   642             // or the reply is not finished yet, e.g. it was aborted
       
   643             // we have to close that connection
       
   644             if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
       
   645                 channels[i].close();
       
   646 
       
   647             QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   648             return;
       
   649         }
       
   650 
       
   651         // is the reply inside the pipeline of this channel already?
       
   652         for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
       
   653             if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
       
   654                // Remove that HttpMessagePair
       
   655                channels[i].alreadyPipelinedRequests.removeAt(j);
       
   656 
       
   657                channels[i].requeueCurrentlyPipelinedRequests();
       
   658 
       
   659                // Since some requests had already been pipelined, but we removed
       
   660                // one and re-queued the others
       
   661                // we must force a connection close after the request that is
       
   662                // currently in processing has been finished.
       
   663                if (channels[i].reply)
       
   664                    channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
       
   665 
       
   666                QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   667                return;
       
   668             }
       
   669         }
       
   670     }
       
   671     // remove from the high priority queue
       
   672     if (!highPriorityQueue.isEmpty()) {
       
   673         for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
       
   674             HttpMessagePair messagePair = highPriorityQueue.at(j);
       
   675             if (messagePair.second == reply) {
       
   676                 highPriorityQueue.removeAt(j);
       
   677                 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   678                 return;
       
   679             }
       
   680         }
       
   681     }
       
   682     // remove from the low priority queue
       
   683     if (!lowPriorityQueue.isEmpty()) {
       
   684         for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
       
   685             HttpMessagePair messagePair = lowPriorityQueue.at(j);
       
   686             if (messagePair.second == reply) {
       
   687                 lowPriorityQueue.removeAt(j);
       
   688                 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   689                 return;
       
   690             }
       
   691         }
       
   692     }
       
   693 }
       
   694 
       
   695 
       
   696 
       
   697 // This function must be called from the event loop. The only
       
   698 // exception is documented in QHttpNetworkConnectionPrivate::queueRequest
       
   699 void QHttpNetworkConnectionPrivate::_q_startNextRequest()
       
   700 {
       
   701     //resend the necessary ones.
       
   702     for (int i = 0; i < channelCount; ++i) {
       
   703         if (channels[i].resendCurrent) {
       
   704             channels[i].resendCurrent = false;
       
   705             channels[i].state = QHttpNetworkConnectionChannel::IdleState;
       
   706 
       
   707             // if this is not possible, error will be emitted and connection terminated
       
   708             if (!channels[i].resetUploadData())
       
   709                 continue;
       
   710 
       
   711             channels[i].sendRequest();
       
   712         }
       
   713     }
       
   714 
       
   715     // dequeue new ones
       
   716 
       
   717     QAbstractSocket *socket = 0;
       
   718     for (int i = 0; i < channelCount; ++i) {
       
   719         QAbstractSocket *chSocket = channels[i].socket;
       
   720         // try to get a free AND connected socket
       
   721         if (!channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
       
   722             socket = chSocket;
       
   723             dequeueAndSendRequest(socket);
       
   724             break;
       
   725         }
       
   726     }
       
   727 
       
   728     if (!socket) {
       
   729         for (int i = 0; i < channelCount; ++i) {
       
   730             QAbstractSocket *chSocket = channels[i].socket;
       
   731             // try to get a free unconnected socket
       
   732             if (!channels[i].isSocketBusy()) {
       
   733                 socket = chSocket;
       
   734                 dequeueAndSendRequest(socket);
       
   735                 break;
       
   736             }
       
   737         }
       
   738     }
       
   739 
       
   740     // try to push more into all sockets
       
   741     // ### FIXME we should move this to the beginning of the function
       
   742     // as soon as QtWebkit is properly using the pipelining
       
   743     // (e.g. not for XMLHttpRequest or the first page load)
       
   744     // ### FIXME we should also divide the requests more even
       
   745     // on the connected sockets
       
   746     //tryToFillPipeline(socket);
       
   747     // return fast if there is nothing to pipeline
       
   748     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
       
   749         return;
       
   750     for (int j = 0; j < channelCount; j++)
       
   751         fillPipeline(channels[j].socket);
       
   752 }
       
   753 
       
   754 void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests()
       
   755 {
       
   756     // send the request using the idle socket
       
   757     for (int i = 0 ; i < channelCount; ++i) {
       
   758         if (channels[i].state ==  QHttpNetworkConnectionChannel::Wait4AuthState) {
       
   759             channels[i].state = QHttpNetworkConnectionChannel::IdleState;
       
   760             if (channels[i].reply)
       
   761                 channels[i].sendRequest();
       
   762         }
       
   763     }
       
   764 }
       
   765 
       
   766 void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
       
   767 {
       
   768     for (int i = 0 ; i < channelCount; ++i) {
       
   769         if (channels[i].reply ==  reply) {
       
   770             // emulate a readyRead() from the socket
       
   771             QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
       
   772             return;
       
   773         }
       
   774     }
       
   775 }
       
   776 
       
   777 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
       
   778     : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
       
   779 {
       
   780     Q_D(QHttpNetworkConnection);
       
   781     d->init();
       
   782 }
       
   783 
       
   784 QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
       
   785      : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
       
   786 {
       
   787     Q_D(QHttpNetworkConnection);
       
   788     d->init();
       
   789 }
       
   790 
       
   791 QHttpNetworkConnection::~QHttpNetworkConnection()
       
   792 {
       
   793 }
       
   794 
       
   795 QString QHttpNetworkConnection::hostName() const
       
   796 {
       
   797     Q_D(const QHttpNetworkConnection);
       
   798     return d->hostName;
       
   799 }
       
   800 
       
   801 quint16 QHttpNetworkConnection::port() const
       
   802 {
       
   803     Q_D(const QHttpNetworkConnection);
       
   804     return d->port;
       
   805 }
       
   806 
       
   807 QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
       
   808 {
       
   809     Q_D(QHttpNetworkConnection);
       
   810     return d->queueRequest(request);
       
   811 }
       
   812 
       
   813 void QHttpNetworkConnection::enableEncryption()
       
   814 {
       
   815     Q_D(QHttpNetworkConnection);
       
   816     d->encrypt = true;
       
   817 }
       
   818 
       
   819 bool QHttpNetworkConnection::isEncrypted() const
       
   820 {
       
   821     Q_D(const QHttpNetworkConnection);
       
   822     return d->encrypt;
       
   823 }
       
   824 
       
   825 void QHttpNetworkConnection::setProxyAuthentication(QAuthenticator *authenticator)
       
   826 {
       
   827     Q_D(QHttpNetworkConnection);
       
   828     for (int i = 0; i < d->channelCount; ++i)
       
   829         d->channels[i].proxyAuthenticator = *authenticator;
       
   830 }
       
   831 
       
   832 void QHttpNetworkConnection::setAuthentication(const QString &domain, QAuthenticator *authenticator)
       
   833 {
       
   834     Q_UNUSED(domain); // ### domain ?
       
   835     Q_D(QHttpNetworkConnection);
       
   836     for (int i = 0; i < d->channelCount; ++i)
       
   837         d->channels[i].authenticator = *authenticator;
       
   838 }
       
   839 
       
   840 #ifndef QT_NO_NETWORKPROXY
       
   841 void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
       
   842 {
       
   843     Q_D(QHttpNetworkConnection);
       
   844     d->networkProxy = networkProxy;
       
   845     // update the authenticator
       
   846     if (!d->networkProxy.user().isEmpty()) {
       
   847         for (int i = 0; i < d->channelCount; ++i) {
       
   848             d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
       
   849             d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
       
   850         }
       
   851     }
       
   852 }
       
   853 
       
   854 QNetworkProxy QHttpNetworkConnection::cacheProxy() const
       
   855 {
       
   856     Q_D(const QHttpNetworkConnection);
       
   857     return d->networkProxy;
       
   858 }
       
   859 
       
   860 void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
       
   861 {
       
   862     Q_D(QHttpNetworkConnection);
       
   863     for (int i = 0; i < d->channelCount; ++i)
       
   864         d->channels[i].socket->setProxy(networkProxy);
       
   865 }
       
   866 
       
   867 QNetworkProxy QHttpNetworkConnection::transparentProxy() const
       
   868 {
       
   869     Q_D(const QHttpNetworkConnection);
       
   870     return d->channels[0].socket->proxy();
       
   871 }
       
   872 #endif
       
   873 
       
   874 
       
   875 // SSL support below
       
   876 #ifndef QT_NO_OPENSSL
       
   877 void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
       
   878 {
       
   879     Q_D(QHttpNetworkConnection);
       
   880     if (!d->encrypt)
       
   881         return;
       
   882 
       
   883     // set the config on all channels
       
   884     for (int i = 0; i < d->channelCount; ++i)
       
   885         static_cast<QSslSocket *>(d->channels[i].socket)->setSslConfiguration(config);
       
   886 }
       
   887 
       
   888 void QHttpNetworkConnection::ignoreSslErrors(int channel)
       
   889 {
       
   890     Q_D(QHttpNetworkConnection);
       
   891     if (!d->encrypt)
       
   892         return;
       
   893 
       
   894     if (channel == -1) { // ignore for all channels
       
   895         for (int i = 0; i < d->channelCount; ++i) {
       
   896             static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
       
   897             d->channels[i].ignoreAllSslErrors = true;
       
   898         }
       
   899 
       
   900     } else {
       
   901         static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
       
   902         d->channels[channel].ignoreAllSslErrors = true;
       
   903     }
       
   904 }
       
   905 
       
   906 void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
       
   907 {
       
   908     Q_D(QHttpNetworkConnection);
       
   909     if (!d->encrypt)
       
   910         return;
       
   911 
       
   912     if (channel == -1) { // ignore for all channels
       
   913         for (int i = 0; i < d->channelCount; ++i) {
       
   914             static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
       
   915             d->channels[i].ignoreSslErrorsList = errors;
       
   916         }
       
   917 
       
   918     } else {
       
   919         static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
       
   920         d->channels[channel].ignoreSslErrorsList = errors;
       
   921     }
       
   922 }
       
   923 
       
   924 #endif //QT_NO_OPENSSL
       
   925 
       
   926 #ifndef QT_NO_NETWORKPROXY
       
   927 // only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
       
   928 // from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
       
   929 // e.g. it is for SOCKS proxies which require authentication.
       
   930 void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
       
   931 {
       
   932     Q_Q(QHttpNetworkConnection);
       
   933     emit q->proxyAuthenticationRequired(proxy, auth, q);
       
   934     int i = indexOf(chan->socket);
       
   935     copyCredentials(i, auth, true);
       
   936 }
       
   937 #endif
       
   938 
       
   939 
       
   940 QT_END_NAMESPACE
       
   941 
       
   942 #include "moc_qhttpnetworkconnection_p.cpp"
       
   943 
       
   944 #endif // QT_NO_HTTP