src/network/access/qhttpnetworkconnection.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 "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 a
       
   198     // universal one always.
       
   199     value = request.headerField("accept-language");
       
   200     if (value.isEmpty())
       
   201         request.setHeaderField("accept-language", "en,*");
       
   202 
       
   203     // set the User Agent
       
   204     value = request.headerField("user-agent");
       
   205     if (value.isEmpty())
       
   206         request.setHeaderField("User-Agent", "Mozilla/5.0");
       
   207     // set the host
       
   208     value = request.headerField("host");
       
   209     if (value.isEmpty()) {
       
   210         QByteArray host = QUrl::toAce(hostName);
       
   211 
       
   212         int port = request.url().port();
       
   213         if (port != -1) {
       
   214             host += ':';
       
   215             host += QByteArray::number(port);
       
   216         }
       
   217 
       
   218         request.setHeaderField("Host", host);
       
   219     }
       
   220 
       
   221     reply->d_func()->requestIsPrepared = true;
       
   222 }
       
   223 
       
   224 
       
   225 
       
   226 
       
   227 void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
       
   228                                                    QHttpNetworkReply *reply,
       
   229                                                    QNetworkReply::NetworkError errorCode)
       
   230 {
       
   231     Q_Q(QHttpNetworkConnection);
       
   232     if (socket && reply) {
       
   233         // this error matters only to this reply
       
   234         reply->d_func()->errorString = errorDetail(errorCode, socket);
       
   235         emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
       
   236         int i = indexOf(socket);
       
   237         // remove the corrupt data if any
       
   238         reply->d_func()->eraseData();
       
   239         channels[i].close();
       
   240         // send the next request
       
   241         QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   242     }
       
   243 }
       
   244 
       
   245 void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
       
   246 {
       
   247     Q_ASSERT(auth);
       
   248 
       
   249     // select another channel
       
   250     QAuthenticator* otherAuth = 0;
       
   251     for (int i = 0; i < channelCount; ++i) {
       
   252         if (i == fromChannel)
       
   253             continue;
       
   254         if (isProxy)
       
   255             otherAuth = &channels[i].proxyAuthenticator;
       
   256         else
       
   257             otherAuth = &channels[i].authenticator;
       
   258         // if the credentials are different, copy them
       
   259         if (otherAuth->user().compare(auth->user()))
       
   260             otherAuth->setUser(auth->user());
       
   261         if (otherAuth->password().compare(auth->password()))
       
   262             otherAuth->setPassword(auth->password());
       
   263     }
       
   264 }
       
   265 
       
   266 
       
   267 // handles the authentication for one channel and eventually re-starts the other channels
       
   268 bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
       
   269                                                                 bool isProxy, bool &resend)
       
   270 {
       
   271     Q_ASSERT(socket);
       
   272     Q_ASSERT(reply);
       
   273 
       
   274     Q_Q(QHttpNetworkConnection);
       
   275 
       
   276     resend = false;
       
   277     //create the response header to be used with QAuthenticatorPrivate.
       
   278     QHttpResponseHeader responseHeader;
       
   279     QList<QPair<QByteArray, QByteArray> > fields = reply->header();
       
   280     QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
       
   281     while (it != fields.constEnd()) {
       
   282         responseHeader.addValue(QString::fromLatin1(it->first), QString::fromUtf8(it->second));
       
   283         it++;
       
   284     }
       
   285     //find out the type of authentication protocol requested.
       
   286     QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
       
   287     if (authMethod != QAuthenticatorPrivate::None) {
       
   288         int i = indexOf(socket);
       
   289         //Use a single authenticator for all domains. ### change later to use domain/realm
       
   290         QAuthenticator* auth = 0;
       
   291         if (isProxy) {
       
   292             auth = &channels[i].proxyAuthenticator;
       
   293             channels[i].proxyAuthMehtod = authMethod;
       
   294         } else {
       
   295             auth = &channels[i].authenticator;
       
   296             channels[i].authMehtod = authMethod;
       
   297         }
       
   298         //proceed with the authentication.
       
   299         if (auth->isNull())
       
   300             auth->detach();
       
   301         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
       
   302         priv->parseHttpResponse(responseHeader, isProxy);
       
   303 
       
   304         if (priv->phase == QAuthenticatorPrivate::Done) {
       
   305             if ((isProxy && pendingProxyAuthSignal) ||(!isProxy && pendingAuthSignal)) {
       
   306                 // drop the request
       
   307                 reply->d_func()->eraseData();
       
   308                 channels[i].close();
       
   309                 channels[i].lastStatus = 0;
       
   310                 channels[i].state =  QHttpNetworkConnectionChannel::Wait4AuthState;
       
   311                 return false;
       
   312             }
       
   313             // cannot use this socket until the slot returns
       
   314             channels[i].state = QHttpNetworkConnectionChannel::WaitingState;
       
   315             socket->blockSignals(true);
       
   316             if (!isProxy) {
       
   317                 pendingAuthSignal = true;
       
   318                 emit q->authenticationRequired(reply->request(), auth, q);
       
   319                 pendingAuthSignal = false;
       
   320 #ifndef QT_NO_NETWORKPROXY
       
   321             } else {
       
   322                 pendingProxyAuthSignal = true;
       
   323                 emit q->proxyAuthenticationRequired(networkProxy, auth, q);
       
   324                 pendingProxyAuthSignal = false;
       
   325 #endif
       
   326             }
       
   327             socket->blockSignals(false);
       
   328             // socket free to use
       
   329             channels[i].state = QHttpNetworkConnectionChannel::IdleState;
       
   330             if (priv->phase != QAuthenticatorPrivate::Done) {
       
   331                 // send any pending requests
       
   332                 copyCredentials(i,  auth, isProxy);
       
   333                 QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection);
       
   334             }
       
   335         }
       
   336         // changing values in QAuthenticator will reset the 'phase'
       
   337         if (priv->phase == QAuthenticatorPrivate::Done) {
       
   338             // authentication is cancelled, send the current contents to the user.
       
   339             emit channels[i].reply->headerChanged();
       
   340             emit channels[i].reply->readyRead();
       
   341             QNetworkReply::NetworkError errorCode =
       
   342                 isProxy
       
   343                 ? QNetworkReply::ProxyAuthenticationRequiredError
       
   344                 : QNetworkReply::AuthenticationRequiredError;
       
   345             reply->d_func()->errorString = errorDetail(errorCode, socket);
       
   346             emit q->error(errorCode, reply->d_func()->errorString);
       
   347             emit channels[i].reply->finished();
       
   348             // ### at this point the reply could be deleted
       
   349             socket->close();
       
   350             // remove pending request on the other channels
       
   351             for (int j = 0; j < channelCount; ++j) {
       
   352                 if (j != i && channels[j].state ==  QHttpNetworkConnectionChannel::Wait4AuthState)
       
   353                     channels[j].state = QHttpNetworkConnectionChannel::IdleState;
       
   354             }
       
   355             return true;
       
   356         }
       
   357         //resend the request
       
   358         resend = true;
       
   359         return true;
       
   360     }
       
   361     return false;
       
   362 }
       
   363 
       
   364 void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
       
   365 {
       
   366     Q_ASSERT(socket);
       
   367 
       
   368     int i = indexOf(socket);
       
   369 
       
   370     if (channels[i].authMehtod != QAuthenticatorPrivate::None) {
       
   371         if (!(channels[i].authMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
       
   372             QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
       
   373             if (priv && priv->method != QAuthenticatorPrivate::None) {
       
   374                 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
       
   375                 request.setHeaderField("authorization", response);
       
   376             }
       
   377         }
       
   378     }
       
   379     if (channels[i].proxyAuthMehtod != QAuthenticatorPrivate::None) {
       
   380         if (!(channels[i].proxyAuthMehtod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
       
   381             QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
       
   382             if (priv && priv->method != QAuthenticatorPrivate::None) {
       
   383                 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
       
   384                 request.setHeaderField("proxy-authorization", response);
       
   385             }
       
   386         }
       
   387     }
       
   388 }
       
   389 
       
   390 QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
       
   391 {
       
   392     Q_Q(QHttpNetworkConnection);
       
   393 
       
   394     // The reply component of the pair is created initially.
       
   395     QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
       
   396     reply->setRequest(request);
       
   397     reply->d_func()->connection = q;
       
   398     HttpMessagePair pair = qMakePair(request, reply);
       
   399 
       
   400     switch (request.priority()) {
       
   401     case QHttpNetworkRequest::HighPriority:
       
   402         highPriorityQueue.prepend(pair);
       
   403         break;
       
   404     case QHttpNetworkRequest::NormalPriority:
       
   405     case QHttpNetworkRequest::LowPriority:
       
   406         lowPriorityQueue.prepend(pair);
       
   407         break;
       
   408     }
       
   409     QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   410     return reply;
       
   411 }
       
   412 
       
   413 void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
       
   414 {
       
   415     Q_Q(QHttpNetworkConnection);
       
   416 
       
   417     QHttpNetworkRequest request = pair.first;
       
   418     switch (request.priority()) {
       
   419     case QHttpNetworkRequest::HighPriority:
       
   420         highPriorityQueue.prepend(pair);
       
   421         break;
       
   422     case QHttpNetworkRequest::NormalPriority:
       
   423     case QHttpNetworkRequest::LowPriority:
       
   424         lowPriorityQueue.prepend(pair);
       
   425         break;
       
   426     }
       
   427     QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   428 }
       
   429 
       
   430 void QHttpNetworkConnectionPrivate::dequeueAndSendRequest(QAbstractSocket *socket)
       
   431 {
       
   432     Q_ASSERT(socket);
       
   433 
       
   434     int i = indexOf(socket);
       
   435 
       
   436     if (!highPriorityQueue.isEmpty()) {
       
   437         // remove from queue before sendRequest! else we might pipeline the same request again
       
   438         HttpMessagePair messagePair = highPriorityQueue.takeLast();
       
   439         if (!messagePair.second->d_func()->requestIsPrepared)
       
   440             prepareRequest(messagePair);
       
   441         channels[i].request = messagePair.first;
       
   442         channels[i].reply = messagePair.second;
       
   443         channels[i].sendRequest();
       
   444         return;
       
   445     }
       
   446 
       
   447     if (!lowPriorityQueue.isEmpty()) {
       
   448         // remove from queue before sendRequest! else we might pipeline the same request again
       
   449         HttpMessagePair messagePair = lowPriorityQueue.takeLast();
       
   450         if (!messagePair.second->d_func()->requestIsPrepared)
       
   451             prepareRequest(messagePair);
       
   452         channels[i].request = messagePair.first;
       
   453         channels[i].reply = messagePair.second;
       
   454         channels[i].sendRequest();
       
   455         return;
       
   456     }
       
   457 }
       
   458 
       
   459 // this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
       
   460 void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
       
   461 {
       
   462     // return fast if there is nothing to pipeline
       
   463     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
       
   464         return;
       
   465 
       
   466     int i = indexOf(socket);
       
   467 
       
   468     bool highPriorityQueueProcessingDone = false;
       
   469     bool lowPriorityQueueProcessingDone = false;
       
   470 
       
   471     while (!highPriorityQueueProcessingDone && !lowPriorityQueueProcessingDone) {
       
   472         // this loop runs once per request we intend to pipeline in.
       
   473 
       
   474         if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
       
   475             return;
       
   476 
       
   477         // the current request that is in must already support pipelining
       
   478         if (!channels[i].request.isPipeliningAllowed())
       
   479             return;
       
   480 
       
   481         // the current request must be a idempotent (right now we only check GET)
       
   482         if (channels[i].request.operation() != QHttpNetworkRequest::Get)
       
   483             return;
       
   484 
       
   485         // check if socket is connected
       
   486         if (socket->state() != QAbstractSocket::ConnectedState)
       
   487             return;
       
   488 
       
   489         // check for resendCurrent
       
   490         if (channels[i].resendCurrent)
       
   491             return;
       
   492 
       
   493         // we do not like authentication stuff
       
   494         // ### make sure to be OK with this in later releases
       
   495         if (!channels[i].authenticator.isNull() || !channels[i].authenticator.user().isEmpty())
       
   496             return;
       
   497         if (!channels[i].proxyAuthenticator.isNull() || !channels[i].proxyAuthenticator.user().isEmpty())
       
   498             return;
       
   499 
       
   500         // check for pipeline length
       
   501         if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength)
       
   502             return;
       
   503 
       
   504         // must be in ReadingState or WaitingState
       
   505         if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
       
   506                || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
       
   507             return;
       
   508 
       
   509         highPriorityQueueProcessingDone = fillPipeline(highPriorityQueue, channels[i]);
       
   510         // not finished with highPriorityQueue? then loop again
       
   511         if (!highPriorityQueueProcessingDone)
       
   512             continue;
       
   513         // highPriorityQueue was processed, now deal with the lowPriorityQueue
       
   514         lowPriorityQueueProcessingDone = fillPipeline(lowPriorityQueue, channels[i]);
       
   515     }
       
   516 }
       
   517 
       
   518 // returns true when the processing of a queue has been done
       
   519 bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
       
   520 {
       
   521     if (queue.isEmpty())
       
   522         return true;
       
   523 
       
   524     for (int i = queue.count() - 1; i >= 0; --i) {
       
   525         HttpMessagePair messagePair = queue.at(i);
       
   526         const QHttpNetworkRequest &request = messagePair.first;
       
   527 
       
   528         // we currently do not support pipelining if HTTP authentication is used
       
   529         if (!request.url().userInfo().isEmpty())
       
   530             continue;
       
   531 
       
   532         // take only GET requests
       
   533         if (request.operation() != QHttpNetworkRequest::Get)
       
   534             continue;
       
   535 
       
   536         if (!request.isPipeliningAllowed())
       
   537             continue;
       
   538 
       
   539         // remove it from the queue
       
   540         queue.takeAt(i);
       
   541         // we modify the queue we iterate over here, but since we return from the function
       
   542         // afterwards this is fine.
       
   543 
       
   544         // actually send it
       
   545         if (!messagePair.second->d_func()->requestIsPrepared)
       
   546             prepareRequest(messagePair);
       
   547         channel.pipelineInto(messagePair);
       
   548 
       
   549         // return false because we processed something and need to process again
       
   550         return false;
       
   551     }
       
   552 
       
   553     // return true, the queue has been processed and not changed
       
   554     return true;
       
   555 }
       
   556 
       
   557 
       
   558 QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket)
       
   559 {
       
   560     Q_ASSERT(socket);
       
   561 
       
   562     QString errorString;
       
   563     switch (errorCode) {
       
   564     case QNetworkReply::HostNotFoundError:
       
   565         errorString = QString::fromLatin1(QT_TRANSLATE_NOOP("QHttp", "Host %1 not found"))
       
   566                               .arg(socket->peerName());
       
   567         break;
       
   568     case QNetworkReply::ConnectionRefusedError:
       
   569         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection refused"));
       
   570         break;
       
   571     case QNetworkReply::RemoteHostClosedError:
       
   572         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Connection closed"));
       
   573         break;
       
   574     case QNetworkReply::TimeoutError:
       
   575         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTP request failed"));
       
   576         break;
       
   577     case QNetworkReply::ProxyAuthenticationRequiredError:
       
   578         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Proxy requires authentication"));
       
   579         break;
       
   580     case QNetworkReply::AuthenticationRequiredError:
       
   581         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Host requires authentication"));
       
   582         break;
       
   583     case QNetworkReply::ProtocolFailure:
       
   584         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Data corrupted"));
       
   585         break;
       
   586     case QNetworkReply::ProtocolUnknownError:
       
   587         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "Unknown protocol specified"));
       
   588         break;
       
   589     case QNetworkReply::SslHandshakeFailedError:
       
   590         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "SSL handshake failed"));
       
   591         break;
       
   592     default:
       
   593         // all other errors are treated as QNetworkReply::UnknownNetworkError
       
   594         errorString = QLatin1String(QT_TRANSLATE_NOOP("QHttp", "HTTP request failed"));
       
   595         break;
       
   596     }
       
   597     return errorString;
       
   598 }
       
   599 
       
   600 // this is called from the destructor of QHttpNetworkReply. It is called when
       
   601 // the reply was finished correctly or when it was aborted.
       
   602 void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
       
   603 {
       
   604     Q_Q(QHttpNetworkConnection);
       
   605 
       
   606     // check if the reply is currently being processed or it is pipelined in
       
   607     for (int i = 0; i < channelCount; ++i) {
       
   608         // is the reply associated the currently processing of this channel?
       
   609         if (channels[i].reply == reply) {
       
   610             channels[i].reply = 0;
       
   611 
       
   612             if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
       
   613                 // the reply had to be prematurely removed, e.g. it was not finished
       
   614                 // therefore we have to requeue the already pipelined requests.
       
   615                 channels[i].requeueCurrentlyPipelinedRequests();
       
   616             }
       
   617 
       
   618             // if HTTP mandates we should close
       
   619             // or the reply is not finished yet, e.g. it was aborted
       
   620             // we have to close that connection
       
   621             if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
       
   622                 channels[i].close();
       
   623 
       
   624             QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   625             return;
       
   626         }
       
   627 
       
   628         // is the reply inside the pipeline of this channel already?
       
   629         for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
       
   630             if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
       
   631                // Remove that HttpMessagePair
       
   632                channels[i].alreadyPipelinedRequests.removeAt(j);
       
   633 
       
   634                channels[i].requeueCurrentlyPipelinedRequests();
       
   635 
       
   636                // Since some requests had already been pipelined, but we removed
       
   637                // one and re-queued the others
       
   638                // we must force a connection close after the request that is
       
   639                // currently in processing has been finished.
       
   640                if (channels[i].reply)
       
   641                    channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
       
   642 
       
   643                QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   644                return;
       
   645             }
       
   646         }
       
   647     }
       
   648     // remove from the high priority queue
       
   649     if (!highPriorityQueue.isEmpty()) {
       
   650         for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
       
   651             HttpMessagePair messagePair = highPriorityQueue.at(j);
       
   652             if (messagePair.second == reply) {
       
   653                 highPriorityQueue.removeAt(j);
       
   654                 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   655                 return;
       
   656             }
       
   657         }
       
   658     }
       
   659     // remove from the low priority queue
       
   660     if (!lowPriorityQueue.isEmpty()) {
       
   661         for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
       
   662             HttpMessagePair messagePair = lowPriorityQueue.at(j);
       
   663             if (messagePair.second == reply) {
       
   664                 lowPriorityQueue.removeAt(j);
       
   665                 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
       
   666                 return;
       
   667             }
       
   668         }
       
   669     }
       
   670 }
       
   671 
       
   672 
       
   673 
       
   674 void QHttpNetworkConnectionPrivate::_q_startNextRequest()
       
   675 {
       
   676     //resend the necessary ones.
       
   677     for (int i = 0; i < channelCount; ++i) {
       
   678         if (channels[i].resendCurrent) {
       
   679             channels[i].resendCurrent = false;
       
   680             channels[i].state = QHttpNetworkConnectionChannel::IdleState;
       
   681             if (channels[i].reply)
       
   682                 channels[i].sendRequest();
       
   683         }
       
   684     }
       
   685     QAbstractSocket *socket = 0;
       
   686     for (int i = 0; i < channelCount; ++i) {
       
   687         QAbstractSocket *chSocket = channels[i].socket;
       
   688         // send the request using the idle socket
       
   689         if (!channels[i].isSocketBusy()) {
       
   690             socket = chSocket;
       
   691             break;
       
   692         }
       
   693     }
       
   694 
       
   695     // this socket is free,
       
   696     if (socket)
       
   697         dequeueAndSendRequest(socket);
       
   698 
       
   699     // try to push more into all sockets
       
   700     // ### FIXME we should move this to the beginning of the function
       
   701     // as soon as QtWebkit is properly using the pipelining
       
   702     // (e.g. not for XMLHttpRequest or the first page load)
       
   703     // ### FIXME we should also divide the requests more even
       
   704     // on the connected sockets
       
   705     //tryToFillPipeline(socket);
       
   706     // return fast if there is nothing to pipeline
       
   707     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
       
   708         return;
       
   709     for (int j = 0; j < channelCount; j++)
       
   710         fillPipeline(channels[j].socket);
       
   711 }
       
   712 
       
   713 void QHttpNetworkConnectionPrivate::_q_restartAuthPendingRequests()
       
   714 {
       
   715     // send the request using the idle socket
       
   716     for (int i = 0 ; i < channelCount; ++i) {
       
   717         if (channels[i].state ==  QHttpNetworkConnectionChannel::Wait4AuthState) {
       
   718             channels[i].state = QHttpNetworkConnectionChannel::IdleState;
       
   719             if (channels[i].reply)
       
   720                 channels[i].sendRequest();
       
   721         }
       
   722     }
       
   723 }
       
   724 
       
   725 
       
   726 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
       
   727     : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
       
   728 {
       
   729     Q_D(QHttpNetworkConnection);
       
   730     d->init();
       
   731 }
       
   732 
       
   733 QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
       
   734      : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
       
   735 {
       
   736     Q_D(QHttpNetworkConnection);
       
   737     d->init();
       
   738 }
       
   739 
       
   740 QHttpNetworkConnection::~QHttpNetworkConnection()
       
   741 {
       
   742 }
       
   743 
       
   744 QString QHttpNetworkConnection::hostName() const
       
   745 {
       
   746     Q_D(const QHttpNetworkConnection);
       
   747     return d->hostName;
       
   748 }
       
   749 
       
   750 quint16 QHttpNetworkConnection::port() const
       
   751 {
       
   752     Q_D(const QHttpNetworkConnection);
       
   753     return d->port;
       
   754 }
       
   755 
       
   756 QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
       
   757 {
       
   758     Q_D(QHttpNetworkConnection);
       
   759     return d->queueRequest(request);
       
   760 }
       
   761 
       
   762 void QHttpNetworkConnection::enableEncryption()
       
   763 {
       
   764     Q_D(QHttpNetworkConnection);
       
   765     d->encrypt = true;
       
   766 }
       
   767 
       
   768 bool QHttpNetworkConnection::isEncrypted() const
       
   769 {
       
   770     Q_D(const QHttpNetworkConnection);
       
   771     return d->encrypt;
       
   772 }
       
   773 
       
   774 void QHttpNetworkConnection::setProxyAuthentication(QAuthenticator *authenticator)
       
   775 {
       
   776     Q_D(QHttpNetworkConnection);
       
   777     for (int i = 0; i < d->channelCount; ++i)
       
   778         d->channels[i].proxyAuthenticator = *authenticator;
       
   779 }
       
   780 
       
   781 void QHttpNetworkConnection::setAuthentication(const QString &domain, QAuthenticator *authenticator)
       
   782 {
       
   783     Q_UNUSED(domain); // ### domain ?
       
   784     Q_D(QHttpNetworkConnection);
       
   785     for (int i = 0; i < d->channelCount; ++i)
       
   786         d->channels[i].authenticator = *authenticator;
       
   787 }
       
   788 
       
   789 #ifndef QT_NO_NETWORKPROXY
       
   790 void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
       
   791 {
       
   792     Q_D(QHttpNetworkConnection);
       
   793     d->networkProxy = networkProxy;
       
   794     // update the authenticator
       
   795     if (!d->networkProxy.user().isEmpty()) {
       
   796         for (int i = 0; i < d->channelCount; ++i) {
       
   797             d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
       
   798             d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
       
   799         }
       
   800     }
       
   801 }
       
   802 
       
   803 QNetworkProxy QHttpNetworkConnection::cacheProxy() const
       
   804 {
       
   805     Q_D(const QHttpNetworkConnection);
       
   806     return d->networkProxy;
       
   807 }
       
   808 
       
   809 void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
       
   810 {
       
   811     Q_D(QHttpNetworkConnection);
       
   812     for (int i = 0; i < d->channelCount; ++i)
       
   813         d->channels[i].socket->setProxy(networkProxy);
       
   814 }
       
   815 
       
   816 QNetworkProxy QHttpNetworkConnection::transparentProxy() const
       
   817 {
       
   818     Q_D(const QHttpNetworkConnection);
       
   819     return d->channels[0].socket->proxy();
       
   820 }
       
   821 #endif
       
   822 
       
   823 
       
   824 // SSL support below
       
   825 #ifndef QT_NO_OPENSSL
       
   826 QSslConfiguration QHttpNetworkConnectionPrivate::sslConfiguration(const QHttpNetworkReply &reply) const
       
   827 {
       
   828     if (!encrypt)
       
   829         return QSslConfiguration();
       
   830 
       
   831     for (int i = 0; i < channelCount; ++i)
       
   832         if (channels[i].reply == &reply)
       
   833             return static_cast<QSslSocket *>(channels[0].socket)->sslConfiguration();
       
   834     return QSslConfiguration(); // pending or done request
       
   835 }
       
   836 
       
   837 void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
       
   838 {
       
   839     Q_D(QHttpNetworkConnection);
       
   840     if (!d->encrypt)
       
   841         return;
       
   842 
       
   843     // set the config on all channels
       
   844     for (int i = 0; i < d->channelCount; ++i)
       
   845         static_cast<QSslSocket *>(d->channels[i].socket)->setSslConfiguration(config);
       
   846 }
       
   847 
       
   848 void QHttpNetworkConnection::ignoreSslErrors(int channel)
       
   849 {
       
   850     Q_D(QHttpNetworkConnection);
       
   851     if (!d->encrypt)
       
   852         return;
       
   853 
       
   854     if (channel == -1) { // ignore for all channels
       
   855         for (int i = 0; i < d->channelCount; ++i) {
       
   856             static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
       
   857             d->channels[i].ignoreAllSslErrors = true;
       
   858         }
       
   859 
       
   860     } else {
       
   861         static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
       
   862         d->channels[channel].ignoreAllSslErrors = true;
       
   863     }
       
   864 }
       
   865 
       
   866 void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
       
   867 {
       
   868     Q_D(QHttpNetworkConnection);
       
   869     if (!d->encrypt)
       
   870         return;
       
   871 
       
   872     if (channel == -1) { // ignore for all channels
       
   873         for (int i = 0; i < d->channelCount; ++i) {
       
   874             static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
       
   875             d->channels[i].ignoreSslErrorsList = errors;
       
   876         }
       
   877 
       
   878     } else {
       
   879         static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
       
   880         d->channels[channel].ignoreSslErrorsList = errors;
       
   881     }
       
   882 }
       
   883 
       
   884 #endif //QT_NO_OPENSSL
       
   885 
       
   886 #ifndef QT_NO_NETWORKPROXY
       
   887 // only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
       
   888 // from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
       
   889 // e.g. it is for SOCKS proxies which require authentication.
       
   890 void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
       
   891 {
       
   892     Q_Q(QHttpNetworkConnection);
       
   893     emit q->proxyAuthenticationRequired(proxy, auth, q);
       
   894     int i = indexOf(chan->socket);
       
   895     copyCredentials(i, auth, true);
       
   896 }
       
   897 #endif
       
   898 
       
   899 
       
   900 QT_END_NAMESPACE
       
   901 
       
   902 #include "moc_qhttpnetworkconnection_p.cpp"
       
   903 
       
   904 #endif // QT_NO_HTTP