1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include <private/qabstractsocket_p.h>
43 #include "qhttpnetworkconnection_p.h"
44 #include "qhttpnetworkconnectionchannel_p.h"
45 #include "private/qnoncontiguousbytedevice_p.h"
46 #include <private/qnetworkrequest_p.h>
47 #include <private/qobject_p.h>
48 #include <private/qauthenticator_p.h>
49 #include "private/qhostinfo_p.h"
50 #include <qnetworkproxy.h>
51 #include <qauthenticator.h>
52 #include <qcoreapplication.h>
61 # include <private/qsslsocket_p.h>
62 # include <QtNetwork/qsslkey.h>
63 # include <QtNetwork/qsslcipher.h>
64 # include <QtNetwork/qsslconfiguration.h>
71 const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
73 // The pipeline length. So there will be 4 requests in flight.
74 const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
75 // Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
76 // This means that there are 2 requests in flight and 2 slots free that will be re-filled.
77 const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
80 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
81 : state(RunningState),
82 networkLayerState(Unknown),
83 hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
84 channelCount(defaultChannelCount)
85 #ifndef QT_NO_NETWORKPROXY
86 , networkProxy(QNetworkProxy::NoProxy)
89 channels = new QHttpNetworkConnectionChannel[channelCount];
92 QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
93 : state(RunningState), networkLayerState(Unknown),
94 hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
95 channelCount(channelCount)
96 #ifndef QT_NO_NETWORKPROXY
97 , networkProxy(QNetworkProxy::NoProxy)
100 channels = new QHttpNetworkConnectionChannel[channelCount];
105 QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
107 for (int i = 0; i < channelCount; ++i) {
108 if (channels[i].socket) {
109 channels[i].socket->close();
110 delete channels[i].socket;
116 void QHttpNetworkConnectionPrivate::init()
118 Q_Q(QHttpNetworkConnection);
119 for (int i = 0; i < channelCount; i++) {
120 channels[i].setConnection(this->q_func());
121 channels[i].ssl = encrypt;
122 #ifndef QT_NO_BEARERMANAGEMENT
123 //push session down to channels
124 channels[i].networkSession = networkSession;
128 delayedConnectionTimer.setSingleShot(true);
129 QObject::connect(&delayedConnectionTimer, SIGNAL(timeout()), q, SLOT(_q_connectDelayedChannel()));
132 void QHttpNetworkConnectionPrivate::pauseConnection()
136 // Disable all socket notifiers
137 for (int i = 0; i < channelCount; i++) {
138 if (channels[i].socket) {
141 QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
144 QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
149 void QHttpNetworkConnectionPrivate::resumeConnection()
151 state = RunningState;
152 // Enable all socket notifiers
153 for (int i = 0; i < channelCount; i++) {
154 if (channels[i].socket) {
157 QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
160 QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
162 // Resume pending upload if needed
163 if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
164 QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
168 // queue _q_startNextRequest
169 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
172 int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
174 for (int i = 0; i < channelCount; ++i)
175 if (channels[i].socket == socket)
178 qFatal("Called with unknown socket object.");
182 // If the connection is in the InProgress state channel errors should not always be
183 // emitted. This function will check the status of the connection channels if we
184 // have not decided the networkLayerState and will return true if the channel error
185 // should be emitted by the channel.
186 bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *socket)
188 Q_Q(QHttpNetworkConnection);
190 bool emitError = true;
191 int i = indexOf(socket);
192 int otherSocket = (i == 0 ? 1 : 0);
194 // If the IPv4 connection still isn't started we need to start it now.
195 if (delayedConnectionTimer.isActive()) {
196 delayedConnectionTimer.stop();
197 channels[otherSocket].ensureConnection();
200 if (channelCount == 1) {
201 if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress)
202 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
206 if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
207 if (channels[otherSocket].isSocketBusy() && (channels[otherSocket].state != QHttpNetworkConnectionChannel::ClosingState)) {
208 // this was the first socket to fail.
213 // Both connection attempts has failed.
214 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
219 if (((networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (channels[i].networkLayerPreference != QAbstractSocket::IPv4Protocol))
220 || ((networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (channels[i].networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
221 // First connection worked so this is the second one to complete and it failed.
223 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
226 if (networkLayerState == QHttpNetworkConnectionPrivate::Unknown)
227 qWarning() << "We got a connection error when networkLayerState is Unknown";
234 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
236 return reply.d_func()->responseData.byteAmount();
239 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
241 return reply.d_func()->responseData.sizeNextBlock();
244 void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
246 QHttpNetworkRequest &request = messagePair.first;
247 QHttpNetworkReply *reply = messagePair.second;
249 // add missing fields for the request
251 // check if Content-Length is provided
252 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
253 if (uploadByteDevice) {
254 if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
255 // both values known, take the smaller one.
256 request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
257 } else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
258 // content length not supplied by user, but the upload device knows it
259 request.setContentLength(uploadByteDevice->size());
260 } else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
261 // everything OK, the user supplied us the contentLength
262 } else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
263 qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
266 // set the Connection/Proxy-Connection: Keep-Alive headers
267 #ifndef QT_NO_NETWORKPROXY
268 if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) {
269 value = request.headerField("proxy-connection");
271 request.setHeaderField("Proxy-Connection", "Keep-Alive");
274 value = request.headerField("connection");
276 request.setHeaderField("Connection", "Keep-Alive");
277 #ifndef QT_NO_NETWORKPROXY
281 // If the request had a accept-encoding set, we better not mess
282 // with it. If it was not set, we announce that we understand gzip
283 // and remember this fact in request.d->autoDecompress so that
284 // we can later decompress the HTTP reply if it has such an
286 value = request.headerField("accept-encoding");
287 if (value.isEmpty()) {
288 #ifndef QT_NO_COMPRESS
289 request.setHeaderField("Accept-Encoding", "gzip, deflate");
290 request.d->autoDecompress = true;
292 // if zlib is not available set this to false always
293 request.d->autoDecompress = false;
297 // some websites mandate an accept-language header and fail
298 // if it is not sent. This is a problem with the website and
299 // not with us, but we work around this by setting
301 value = request.headerField("accept-language");
302 if (value.isEmpty()) {
303 QString systemLocale = QLocale::system().name().replace(QChar::fromLatin1('_'),QChar::fromLatin1('-'));
304 QString acceptLanguage;
305 if (systemLocale == QLatin1String("C"))
306 acceptLanguage = QString::fromLatin1("en,*");
307 else if (systemLocale.startsWith(QLatin1String("en-")))
308 acceptLanguage = QString::fromLatin1("%1,*").arg(systemLocale);
310 acceptLanguage = QString::fromLatin1("%1,en,*").arg(systemLocale);
311 request.setHeaderField("Accept-Language", acceptLanguage.toLatin1());
314 // set the User Agent
315 value = request.headerField("user-agent");
317 request.setHeaderField("User-Agent", "Mozilla/5.0");
319 value = request.headerField("host");
320 if (value.isEmpty()) {
323 if (add.setAddress(hostName)) {
324 if (add.protocol() == QAbstractSocket::IPv6Protocol)
325 host = "[" + hostName.toLatin1() + "]";//format the ipv6 in the standard way
327 host = hostName.toLatin1();
330 host = QUrl::toAce(hostName);
333 int port = request.url().port();
336 host += QByteArray::number(port);
339 request.setHeaderField("Host", host);
342 reply->d_func()->requestIsPrepared = true;
348 void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
349 QHttpNetworkReply *reply,
350 QNetworkReply::NetworkError errorCode)
352 Q_Q(QHttpNetworkConnection);
359 // this error matters only to this reply
360 reply->d_func()->errorString = errorDetail(errorCode, socket);
361 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
362 // remove the corrupt data if any
363 reply->d_func()->eraseData();
367 channels[i].reply = 0;
368 channels[i].request = QHttpNetworkRequest();
370 channels[i].requeueCurrentlyPipelinedRequests();
372 // send the next request
373 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
377 void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
381 // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
382 if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
384 if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
388 // select another channel
389 QAuthenticator* otherAuth = 0;
390 for (int i = 0; i < channelCount; ++i) {
391 if (i == fromChannel)
394 otherAuth = &channels[i].proxyAuthenticator;
396 otherAuth = &channels[i].authenticator;
397 // if the credentials are different, copy them
398 if (otherAuth->user().compare(auth->user()))
399 otherAuth->setUser(auth->user());
400 if (otherAuth->password().compare(auth->password()))
401 otherAuth->setPassword(auth->password());
406 // handles the authentication for one channel and eventually re-starts the other channels
407 bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
408 bool isProxy, bool &resend)
414 //create the response header to be used with QAuthenticatorPrivate.
415 QList<QPair<QByteArray, QByteArray> > fields = reply->header();
417 //find out the type of authentication protocol requested.
418 QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
419 if (authMethod != QAuthenticatorPrivate::None) {
420 int i = indexOf(socket);
421 //Use a single authenticator for all domains. ### change later to use domain/realm
422 QAuthenticator* auth = 0;
424 auth = &channels[i].proxyAuthenticator;
425 channels[i].proxyAuthMethod = authMethod;
427 auth = &channels[i].authenticator;
428 channels[i].authMethod = authMethod;
430 //proceed with the authentication.
433 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
434 priv->parseHttpResponse(fields, isProxy);
436 if (priv->phase == QAuthenticatorPrivate::Done) {
439 if (channels[i].authenticationCredentialsSent) {
441 priv = QAuthenticatorPrivate::getPrivate(*auth);
442 priv->hasFailed = true;
443 priv->phase = QAuthenticatorPrivate::Done;
444 channels[i].authenticationCredentialsSent = false;
446 emit reply->authenticationRequired(reply->request(), auth);
447 #ifndef QT_NO_NETWORKPROXY
449 if (channels[i].proxyCredentialsSent) {
451 priv = QAuthenticatorPrivate::getPrivate(*auth);
452 priv->hasFailed = true;
453 priv->phase = QAuthenticatorPrivate::Done;
454 channels[i].proxyCredentialsSent = false;
456 emit reply->proxyAuthenticationRequired(networkProxy, auth);
461 if (priv->phase != QAuthenticatorPrivate::Done) {
462 // send any pending requests
463 copyCredentials(i, auth, isProxy);
465 } else if (priv->phase == QAuthenticatorPrivate::Start) {
466 // If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
467 // parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
468 // such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
469 emit reply->cacheCredentials(reply->request(), auth);
471 // - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
472 // then nothing was filled in by the user or the cache
473 // - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then
474 // we need to bail out if authentication is required.
475 if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
476 // Reset authenticator so the next request on that channel does not get messed up
479 channels[i].proxyAuthenticator = QAuthenticator();
481 channels[i].authenticator = QAuthenticator();
483 // authentication is cancelled, send the current contents to the user.
484 emit channels[i].reply->headerChanged();
485 emit channels[i].reply->readyRead();
486 QNetworkReply::NetworkError errorCode =
488 ? QNetworkReply::ProxyAuthenticationRequiredError
489 : QNetworkReply::AuthenticationRequiredError;
490 reply->d_func()->errorString = errorDetail(errorCode, socket);
491 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
492 // ### at this point the reply could be deleted
502 void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
506 int i = indexOf(socket);
508 // Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
509 if (channels[i].authMethod != QAuthenticatorPrivate::None) {
510 if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
511 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
512 if (priv && priv->method != QAuthenticatorPrivate::None) {
513 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
514 request.setHeaderField("Authorization", response);
515 channels[i].authenticationCredentialsSent = true;
520 // Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
521 if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
522 if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
523 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
524 if (priv && priv->method != QAuthenticatorPrivate::None) {
525 QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
526 request.setHeaderField("Proxy-Authorization", response);
527 channels[i].proxyCredentialsSent = true;
533 QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
535 Q_Q(QHttpNetworkConnection);
537 // The reply component of the pair is created initially.
538 QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
539 reply->setRequest(request);
540 reply->d_func()->connection = q;
541 reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
542 HttpMessagePair pair = qMakePair(request, reply);
544 switch (request.priority()) {
545 case QHttpNetworkRequest::HighPriority:
546 highPriorityQueue.prepend(pair);
548 case QHttpNetworkRequest::NormalPriority:
549 case QHttpNetworkRequest::LowPriority:
550 lowPriorityQueue.prepend(pair);
554 // For Happy Eyeballs the networkLayerState is set to Unkown
555 // untill we have started the first connection attempt. So no
556 // request will be started untill we know if IPv4 or IPv6
558 if (networkLayerState == Unknown) {
559 startHostInfoLookup();
560 } else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
561 // this used to be called via invokeMethod and a QueuedConnection
562 // It is the only place _q_startNextRequest is called directly without going
563 // through the event loop using a QueuedConnection.
564 // This is dangerous because of recursion that might occur when emitting
565 // signals as DirectConnection from this code path. Therefore all signal
566 // emissions that can come out from this code path need to
567 // be QueuedConnection.
568 // We are currently trying to fine-tune this.
569 _q_startNextRequest();
574 void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
576 Q_Q(QHttpNetworkConnection);
578 QHttpNetworkRequest request = pair.first;
579 switch (request.priority()) {
580 case QHttpNetworkRequest::HighPriority:
581 highPriorityQueue.prepend(pair);
583 case QHttpNetworkRequest::NormalPriority:
584 case QHttpNetworkRequest::LowPriority:
585 lowPriorityQueue.prepend(pair);
589 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
592 bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
598 if (!highPriorityQueue.isEmpty()) {
599 // remove from queue before sendRequest! else we might pipeline the same request again
600 HttpMessagePair messagePair = highPriorityQueue.takeLast();
601 if (!messagePair.second->d_func()->requestIsPrepared)
602 prepareRequest(messagePair);
603 channels[i].request = messagePair.first;
604 channels[i].reply = messagePair.second;
608 if (!lowPriorityQueue.isEmpty()) {
609 // remove from queue before sendRequest! else we might pipeline the same request again
610 HttpMessagePair messagePair = lowPriorityQueue.takeLast();
611 if (!messagePair.second->d_func()->requestIsPrepared)
612 prepareRequest(messagePair);
613 channels[i].request = messagePair.first;
614 channels[i].reply = messagePair.second;
620 QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest()
622 if (!highPriorityQueue.isEmpty())
623 return highPriorityQueue.last().first;
624 if (!lowPriorityQueue.isEmpty())
625 return lowPriorityQueue.last().first;
626 return QHttpNetworkRequest();
629 // this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
630 void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
632 // return fast if there is nothing to pipeline
633 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
636 int i = indexOf(socket);
638 // return fast if there was no reply right now processed
639 if (channels[i].reply == 0)
642 if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
646 if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
649 // the current request that is in must already support pipelining
650 if (!channels[i].request.isPipeliningAllowed())
653 // the current request must be a idempotent (right now we only check GET)
654 if (channels[i].request.operation() != QHttpNetworkRequest::Get)
657 // check if socket is connected
658 if (socket->state() != QAbstractSocket::ConnectedState)
661 // check for resendCurrent
662 if (channels[i].resendCurrent)
665 // we do not like authentication stuff
666 // ### make sure to be OK with this in later releases
667 if (!channels[i].authenticator.isNull()
668 && (!channels[i].authenticator.user().isEmpty()
669 || !channels[i].authenticator.password().isEmpty()))
671 if (!channels[i].proxyAuthenticator.isNull()
672 && (!channels[i].proxyAuthenticator.user().isEmpty()
673 || !channels[i].proxyAuthenticator.password().isEmpty()))
676 // must be in ReadingState or WaitingState
677 if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
678 || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
682 while (!highPriorityQueue.isEmpty()) {
683 lengthBefore = channels[i].alreadyPipelinedRequests.length();
684 fillPipeline(highPriorityQueue, channels[i]);
686 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
687 channels[i].pipelineFlush();
691 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
692 break; // did not process anything, now do the low prio queue
695 while (!lowPriorityQueue.isEmpty()) {
696 lengthBefore = channels[i].alreadyPipelinedRequests.length();
697 fillPipeline(lowPriorityQueue, channels[i]);
699 if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
700 channels[i].pipelineFlush();
704 if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
705 break; // did not process anything
709 channels[i].pipelineFlush();
712 // returns true when the processing of a queue has been done
713 bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
718 for (int i = queue.count() - 1; i >= 0; --i) {
719 HttpMessagePair messagePair = queue.at(i);
720 const QHttpNetworkRequest &request = messagePair.first;
722 // we currently do not support pipelining if HTTP authentication is used
723 if (!request.url().userInfo().isEmpty())
726 // take only GET requests
727 if (request.operation() != QHttpNetworkRequest::Get)
730 if (!request.isPipeliningAllowed())
733 // remove it from the queue
735 // we modify the queue we iterate over here, but since we return from the function
736 // afterwards this is fine.
739 if (!messagePair.second->d_func()->requestIsPrepared)
740 prepareRequest(messagePair);
741 channel.pipelineInto(messagePair);
743 // return false because we processed something and need to process again
747 // return true, the queue has been processed and not changed
752 QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail)
756 case QNetworkReply::HostNotFoundError:
758 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
760 errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName);
762 case QNetworkReply::ConnectionRefusedError:
763 errorString = QCoreApplication::translate("QHttp", "Connection refused");
765 case QNetworkReply::RemoteHostClosedError:
766 errorString = QCoreApplication::translate("QHttp", "Connection closed");
768 case QNetworkReply::TimeoutError:
769 errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
771 case QNetworkReply::ProxyAuthenticationRequiredError:
772 errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
774 case QNetworkReply::AuthenticationRequiredError:
775 errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
777 case QNetworkReply::ProtocolFailure:
778 errorString = QCoreApplication::translate("QHttp", "Data corrupted");
780 case QNetworkReply::ProtocolUnknownError:
781 errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
783 case QNetworkReply::SslHandshakeFailedError:
784 errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
787 // all other errors are treated as QNetworkReply::UnknownNetworkError
788 errorString = extraDetail;
794 // this is called from the destructor of QHttpNetworkReply. It is called when
795 // the reply was finished correctly or when it was aborted.
796 void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
798 Q_Q(QHttpNetworkConnection);
800 // check if the reply is currently being processed or it is pipelined in
801 for (int i = 0; i < channelCount; ++i) {
802 // is the reply associated the currently processing of this channel?
803 if (channels[i].reply == reply) {
804 channels[i].reply = 0;
805 channels[i].request = QHttpNetworkRequest();
806 channels[i].resendCurrent = false;
808 if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
809 // the reply had to be prematurely removed, e.g. it was not finished
810 // therefore we have to requeue the already pipelined requests.
811 channels[i].requeueCurrentlyPipelinedRequests();
814 // if HTTP mandates we should close
815 // or the reply is not finished yet, e.g. it was aborted
816 // we have to close that connection
817 if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
820 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
824 // is the reply inside the pipeline of this channel already?
825 for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
826 if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
827 // Remove that HttpMessagePair
828 channels[i].alreadyPipelinedRequests.removeAt(j);
830 channels[i].requeueCurrentlyPipelinedRequests();
832 // Since some requests had already been pipelined, but we removed
833 // one and re-queued the others
834 // we must force a connection close after the request that is
835 // currently in processing has been finished.
836 if (channels[i].reply)
837 channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
839 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
844 // remove from the high priority queue
845 if (!highPriorityQueue.isEmpty()) {
846 for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
847 HttpMessagePair messagePair = highPriorityQueue.at(j);
848 if (messagePair.second == reply) {
849 highPriorityQueue.removeAt(j);
850 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
855 // remove from the low priority queue
856 if (!lowPriorityQueue.isEmpty()) {
857 for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
858 HttpMessagePair messagePair = lowPriorityQueue.at(j);
859 if (messagePair.second == reply) {
860 lowPriorityQueue.removeAt(j);
861 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
870 // This function must be called from the event loop. The only
871 // exception is documented in QHttpNetworkConnectionPrivate::queueRequest
872 // although it is called _q_startNextRequest, it will actually start multiple requests when possible
873 void QHttpNetworkConnectionPrivate::_q_startNextRequest()
875 // If there is no network layer state decided we should not start any new requests.
876 if (networkLayerState == Unknown || networkLayerState == InProgress)
879 // If the QHttpNetworkConnection is currently paused then bail out immediately
880 if (state == PausedState)
883 //resend the necessary ones.
884 for (int i = 0; i < channelCount; ++i) {
885 if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
886 channels[i].resendCurrent = false;
887 channels[i].state = QHttpNetworkConnectionChannel::IdleState;
889 // if this is not possible, error will be emitted and connection terminated
890 if (!channels[i].resetUploadData())
892 channels[i].sendRequest();
898 // return fast if there is nothing to do
899 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
901 // try to get a free AND connected socket
902 for (int i = 0; i < channelCount; ++i) {
903 if (channels[i].socket) {
904 if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
905 if (dequeueRequest(channels[i].socket))
906 channels[i].sendRequest();
911 // try to push more into all sockets
912 // ### FIXME we should move this to the beginning of the function
913 // as soon as QtWebkit is properly using the pipelining
914 // (e.g. not for XMLHttpRequest or the first page load)
915 // ### FIXME we should also divide the requests more even
916 // on the connected sockets
917 //tryToFillPipeline(socket);
918 // return fast if there is nothing to pipeline
919 if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
921 for (int i = 0; i < channelCount; i++)
922 if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
923 fillPipeline(channels[i].socket);
925 // If there is not already any connected channels we need to connect a new one.
926 // We do not pair the channel with the request until we know if it is
927 // connected or not. This is to reuse connected channels before we connect new once.
928 int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count();
929 for (int i = 0; i < channelCount; ++i) {
930 bool connectChannel = false;
931 if (channels[i].socket) {
932 if ((channels[i].socket->state() == QAbstractSocket::ConnectingState) || (channels[i].socket->state() == QAbstractSocket::HostLookupState))
934 if ( queuedRequest <=0 )
936 if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState))
937 connectChannel = true;
938 } else { // not previously used channel
939 connectChannel = true;
942 if (connectChannel) {
943 if (networkLayerState == IPv4)
944 channels[i].networkLayerPreference = QAbstractSocket::IPv4Protocol;
945 else if (networkLayerState == IPv6)
946 channels[i].networkLayerPreference = QAbstractSocket::IPv6Protocol;
947 channels[i].ensureConnection();
951 if ( queuedRequest <=0 )
957 void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
959 for (int i = 0 ; i < channelCount; ++i) {
960 if (channels[i].reply == reply) {
961 // emulate a readyRead() from the socket
962 QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
970 // The first time we start the connection is used we do not know if we
971 // should use IPv4 or IPv6. So we start a hostlookup to figure this out.
972 // Later when we do the connection the socket will not need to do another
973 // lookup as then the hostinfo will already be in the cache.
974 void QHttpNetworkConnectionPrivate::startHostInfoLookup()
976 networkLayerState = InProgress;
978 // check if we already now can descide if this is IPv4 or IPv6
979 QString lookupHost = hostName;
980 #ifndef QT_NO_NETWORKPROXY
981 if (networkProxy.capabilities() & QNetworkProxy::HostNameLookupCapability) {
982 lookupHost = networkProxy.hostName();
983 } else if (channels[0].proxy.capabilities() & QNetworkProxy::HostNameLookupCapability) {
984 lookupHost = channels[0].proxy.hostName();
988 if (temp.setAddress(lookupHost)) {
989 if (temp.protocol() == QAbstractSocket::IPv4Protocol) {
990 networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
991 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
993 } else if (temp.protocol() == QAbstractSocket::IPv6Protocol) {
994 networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
995 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1000 bool immediateResultValid = false;
1001 QHostInfo hostInfo = qt_qhostinfo_lookup(lookupHost,
1003 SLOT(_q_hostLookupFinished(QHostInfo)),
1004 &immediateResultValid,
1006 if (immediateResultValid) {
1007 _q_hostLookupFinished(hostInfo);
1013 void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(QHostInfo info)
1017 bool foundAddress = false;
1019 foreach (const QHostAddress &address, info.addresses()) {
1020 if (address.protocol() == QAbstractSocket::IPv4Protocol) {
1021 if (!foundAddress) {
1022 foundAddress = true;
1026 } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
1027 if (!foundAddress) {
1028 foundAddress = true;
1036 startNetworkLayerStateLookup();
1038 networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1039 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1041 networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1042 QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1044 if (dequeueRequest(channels[0].socket)) {
1045 emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
1046 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1048 // Should not happen
1049 qWarning() << "QHttpNetworkConnectionPrivate::_q_hostLookupFinished could not dequeu request";
1050 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1056 // This will be used if the host lookup found both and Ipv4 and
1057 // Ipv6 address. Then we will start up two connections and pick
1058 // the network layer of the one that finish first. The second
1059 // connection will then be disconnected.
1060 void QHttpNetworkConnectionPrivate::startNetworkLayerStateLookup()
1062 if (channelCount > 1) {
1063 // At this time all channels should be unconnected.
1064 Q_ASSERT(!channels[0].isSocketBusy());
1065 Q_ASSERT(!channels[1].isSocketBusy());
1067 networkLayerState = InProgress;
1069 channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1070 channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1073 #ifndef QT_NO_BEARERMANAGEMENT
1074 if (networkSession) {
1075 if (networkSession->configuration().bearerType() == QNetworkConfiguration::Bearer2G)
1077 else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerCDMA2000)
1079 else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerWCDMA)
1081 else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerHSPA)
1085 delayedConnectionTimer.start(timeout);
1087 channels[1].ensureConnection();
1089 channels[0].ensureConnection();
1091 networkLayerState = InProgress;
1092 channels[0].networkLayerPreference = QAbstractSocket::AnyIPProtocol;
1093 channels[0].ensureConnection();
1097 void QHttpNetworkConnectionPrivate::networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
1099 for (int i = 0 ; i < channelCount; ++i) {
1100 if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1101 channels[i].close();
1106 void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
1109 channels[0].ensureConnection();
1111 channels[1].ensureConnection();
1114 #ifndef QT_NO_BEARERMANAGEMENT
1115 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
1116 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
1118 Q_D(QHttpNetworkConnection);
1119 d->networkSession = networkSession;
1123 QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent, QSharedPointer<QNetworkSession> networkSession)
1124 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
1126 Q_D(QHttpNetworkConnection);
1127 d->networkSession = networkSession;
1131 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
1132 : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
1134 Q_D(QHttpNetworkConnection);
1138 QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
1139 : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
1141 Q_D(QHttpNetworkConnection);
1146 QHttpNetworkConnection::~QHttpNetworkConnection()
1150 QString QHttpNetworkConnection::hostName() const
1152 Q_D(const QHttpNetworkConnection);
1156 quint16 QHttpNetworkConnection::port() const
1158 Q_D(const QHttpNetworkConnection);
1162 QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
1164 Q_D(QHttpNetworkConnection);
1165 return d->queueRequest(request);
1168 bool QHttpNetworkConnection::isSsl() const
1170 Q_D(const QHttpNetworkConnection);
1174 QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
1176 return d_func()->channels;
1179 #ifndef QT_NO_NETWORKPROXY
1180 void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
1182 Q_D(QHttpNetworkConnection);
1183 d->networkProxy = networkProxy;
1184 // update the authenticator
1185 if (!d->networkProxy.user().isEmpty()) {
1186 for (int i = 0; i < d->channelCount; ++i) {
1187 d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
1188 d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
1193 QNetworkProxy QHttpNetworkConnection::cacheProxy() const
1195 Q_D(const QHttpNetworkConnection);
1196 return d->networkProxy;
1199 void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
1201 Q_D(QHttpNetworkConnection);
1202 for (int i = 0; i < d->channelCount; ++i)
1203 d->channels[i].setProxy(networkProxy);
1206 QNetworkProxy QHttpNetworkConnection::transparentProxy() const
1208 Q_D(const QHttpNetworkConnection);
1209 return d->channels[0].proxy;
1214 // SSL support below
1216 void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
1218 Q_D(QHttpNetworkConnection);
1222 // set the config on all channels
1223 for (int i = 0; i < d->channelCount; ++i)
1224 d->channels[i].setSslConfiguration(config);
1227 void QHttpNetworkConnection::ignoreSslErrors(int channel)
1229 Q_D(QHttpNetworkConnection);
1233 if (channel == -1) { // ignore for all channels
1234 for (int i = 0; i < d->channelCount; ++i) {
1235 d->channels[i].ignoreSslErrors();
1239 d->channels[channel].ignoreSslErrors();
1243 void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
1245 Q_D(QHttpNetworkConnection);
1249 if (channel == -1) { // ignore for all channels
1250 for (int i = 0; i < d->channelCount; ++i) {
1251 d->channels[i].ignoreSslErrors(errors);
1255 d->channels[channel].ignoreSslErrors(errors);
1261 #ifndef QT_NO_NETWORKPROXY
1262 // only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
1263 // from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
1264 // e.g. it is for SOCKS proxies which require authentication.
1265 void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
1267 // Also pause the connection because socket notifiers may fire while an user
1268 // dialog is displaying
1270 emit chan->reply->proxyAuthenticationRequired(proxy, auth);
1272 int i = indexOf(chan->socket);
1273 copyCredentials(i, auth, true);
1280 #include "moc_qhttpnetworkconnection_p.cpp"
1282 #endif // QT_NO_HTTP