choke uploadProgress signals
[profile/ivi/qtbase.git] / src / network / access / qhttpnetworkconnection.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
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>
53
54 #include <qbuffer.h>
55 #include <qpair.h>
56 #include <qdebug.h>
57
58 #ifndef QT_NO_HTTP
59
60 #ifndef QT_NO_SSL
61 #    include <private/qsslsocket_p.h>
62 #    include <QtNetwork/qsslkey.h>
63 #    include <QtNetwork/qsslcipher.h>
64 #    include <QtNetwork/qsslconfiguration.h>
65 #endif
66
67
68
69 QT_BEGIN_NAMESPACE
70
71 const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
72
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;
78
79
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)
87 #endif
88 {
89     channels = new QHttpNetworkConnectionChannel[channelCount];
90 }
91
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)
98 #endif
99 {
100     channels = new QHttpNetworkConnectionChannel[channelCount];
101 }
102
103
104
105 QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
106 {
107     for (int i = 0; i < channelCount; ++i) {
108         if (channels[i].socket) {
109             channels[i].socket->close();
110             delete channels[i].socket;
111         }
112     }
113     delete []channels;
114 }
115
116 void QHttpNetworkConnectionPrivate::init()
117 {
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;
125 #endif
126     }
127
128     delayedConnectionTimer.setSingleShot(true);
129     QObject::connect(&delayedConnectionTimer, SIGNAL(timeout()), q, SLOT(_q_connectDelayedChannel()));
130 }
131
132 void QHttpNetworkConnectionPrivate::pauseConnection()
133 {
134     state = PausedState;
135
136     // Disable all socket notifiers
137     for (int i = 0; i < channelCount; i++) {
138         if (channels[i].socket) {
139 #ifndef QT_NO_SSL
140             if (encrypt)
141                 QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
142             else
143 #endif
144                 QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
145         }
146     }
147 }
148
149 void QHttpNetworkConnectionPrivate::resumeConnection()
150 {
151     state = RunningState;
152     // Enable all socket notifiers
153     for (int i = 0; i < channelCount; i++) {
154         if (channels[i].socket) {
155 #ifndef QT_NO_SSL
156             if (encrypt)
157                 QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
158             else
159 #endif
160                 QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
161
162             // Resume pending upload if needed
163             if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
164                 QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
165         }
166     }
167
168     // queue _q_startNextRequest
169     QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
170 }
171
172 int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
173 {
174     for (int i = 0; i < channelCount; ++i)
175         if (channels[i].socket == socket)
176             return i;
177
178     qFatal("Called with unknown socket object.");
179     return 0;
180 }
181
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)
187 {
188     Q_Q(QHttpNetworkConnection);
189
190     bool emitError = true;
191     int i = indexOf(socket);
192     int otherSocket = (i == 0 ? 1 : 0);
193
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();
198     }
199
200     if (channelCount == 1) {
201         if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress)
202             networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
203         channels[0].close();
204         emitError = true;
205     } else {
206         if (networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
207             if (channels[otherSocket].isSocketBusy() && (channels[otherSocket].state != QHttpNetworkConnectionChannel::ClosingState)) {
208                 // this was the first socket to fail.
209                 channels[i].close();
210                 emitError = false;
211             }
212             else {
213                 // Both connection attempts has failed.
214                 networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
215                 channels[i].close();
216                 emitError = true;
217             }
218         } else {
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.
222                 channels[i].close();
223                 QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
224                 emitError = false;
225             }
226             if (networkLayerState == QHttpNetworkConnectionPrivate::Unknown)
227                 qWarning() << "We got a connection error when networkLayerState is Unknown";
228         }
229     }
230     return emitError;
231 }
232
233
234 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
235 {
236     return reply.d_func()->responseData.byteAmount();
237 }
238
239 qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
240 {
241     return reply.d_func()->responseData.sizeNextBlock();
242 }
243
244 void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
245 {
246     QHttpNetworkRequest &request = messagePair.first;
247     QHttpNetworkReply *reply = messagePair.second;
248
249     // add missing fields for the request
250     QByteArray value;
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");
264         }
265     }
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");
270         if (value.isEmpty())
271             request.setHeaderField("Proxy-Connection", "Keep-Alive");
272     } else {
273 #endif
274         value = request.headerField("connection");
275         if (value.isEmpty())
276             request.setHeaderField("Connection", "Keep-Alive");
277 #ifndef QT_NO_NETWORKPROXY
278     }
279 #endif
280
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
285     // encoding.
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;
291 #else
292         // if zlib is not available set this to false always
293         request.d->autoDecompress = false;
294 #endif
295     }
296
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
300     // one always.
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);
309         else
310             acceptLanguage = QString::fromLatin1("%1,en,*").arg(systemLocale);
311         request.setHeaderField("Accept-Language", acceptLanguage.toLatin1());
312     }
313
314     // set the User Agent
315     value = request.headerField("user-agent");
316     if (value.isEmpty())
317         request.setHeaderField("User-Agent", "Mozilla/5.0");
318     // set the host
319     value = request.headerField("host");
320     if (value.isEmpty()) {
321         QHostAddress add;
322         QByteArray host;
323         if (add.setAddress(hostName)) {
324             if (add.protocol() == QAbstractSocket::IPv6Protocol)
325                 host = "[" + hostName.toLatin1() + "]";//format the ipv6 in the standard way
326             else
327                 host = hostName.toLatin1();
328
329         } else {
330             host = QUrl::toAce(hostName);
331         }
332
333         int port = request.url().port();
334         if (port != -1) {
335             host += ':';
336             host += QByteArray::number(port);
337         }
338
339         request.setHeaderField("Host", host);
340     }
341
342     reply->d_func()->requestIsPrepared = true;
343 }
344
345
346
347
348 void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
349                                                    QHttpNetworkReply *reply,
350                                                    QNetworkReply::NetworkError errorCode)
351 {
352     Q_Q(QHttpNetworkConnection);
353
354     int i = 0;
355     if (socket)
356         i = indexOf(socket);
357
358     if (reply) {
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();
364
365         // Clean the channel
366         channels[i].close();
367         channels[i].reply = 0;
368         channels[i].request = QHttpNetworkRequest();
369         if (socket)
370             channels[i].requeueCurrentlyPipelinedRequests();
371
372         // send the next request
373         QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
374     }
375 }
376
377 void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
378 {
379     Q_ASSERT(auth);
380
381     // NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
382     if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
383         return;
384     if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
385         return;
386
387
388     // select another channel
389     QAuthenticator* otherAuth = 0;
390     for (int i = 0; i < channelCount; ++i) {
391         if (i == fromChannel)
392             continue;
393         if (isProxy)
394             otherAuth = &channels[i].proxyAuthenticator;
395         else
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());
402     }
403 }
404
405
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)
409 {
410     Q_ASSERT(socket);
411     Q_ASSERT(reply);
412
413     resend = false;
414     //create the response header to be used with QAuthenticatorPrivate.
415     QList<QPair<QByteArray, QByteArray> > fields = reply->header();
416
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;
423         if (isProxy) {
424             auth = &channels[i].proxyAuthenticator;
425             channels[i].proxyAuthMethod = authMethod;
426         } else {
427             auth = &channels[i].authenticator;
428             channels[i].authMethod = authMethod;
429         }
430         //proceed with the authentication.
431         if (auth->isNull())
432             auth->detach();
433         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
434         priv->parseHttpResponse(fields, isProxy);
435
436         if (priv->phase == QAuthenticatorPrivate::Done) {
437             pauseConnection();
438             if (!isProxy) {
439                 if (channels[i].authenticationCredentialsSent) {
440                     auth->detach();
441                     priv = QAuthenticatorPrivate::getPrivate(*auth);
442                     priv->hasFailed = true;
443                     priv->phase = QAuthenticatorPrivate::Done;
444                     channels[i].authenticationCredentialsSent = false;
445                 }
446                 emit reply->authenticationRequired(reply->request(), auth);
447 #ifndef QT_NO_NETWORKPROXY
448             } else {
449                 if (channels[i].proxyCredentialsSent) {
450                     auth->detach();
451                     priv = QAuthenticatorPrivate::getPrivate(*auth);
452                     priv->hasFailed = true;
453                     priv->phase = QAuthenticatorPrivate::Done;
454                     channels[i].proxyCredentialsSent = false;
455                 }
456                 emit reply->proxyAuthenticationRequired(networkProxy, auth);
457 #endif
458             }
459             resumeConnection();
460
461             if (priv->phase != QAuthenticatorPrivate::Done) {
462                 // send any pending requests
463                 copyCredentials(i,  auth, isProxy);
464             }
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);
470         }
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
477             auth = 0;
478             if (isProxy)
479                 channels[i].proxyAuthenticator = QAuthenticator();
480             else
481                 channels[i].authenticator = QAuthenticator();
482
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 =
487                 isProxy
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
493             return true;
494         }
495         //resend the request
496         resend = true;
497         return true;
498     }
499     return false;
500 }
501
502 void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
503 {
504     Q_ASSERT(socket);
505
506     int i = indexOf(socket);
507
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;
516             }
517         }
518     }
519
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;
528             }
529         }
530     }
531 }
532
533 QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
534 {
535     Q_Q(QHttpNetworkConnection);
536
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);
543
544     switch (request.priority()) {
545     case QHttpNetworkRequest::HighPriority:
546         highPriorityQueue.prepend(pair);
547         break;
548     case QHttpNetworkRequest::NormalPriority:
549     case QHttpNetworkRequest::LowPriority:
550         lowPriorityQueue.prepend(pair);
551         break;
552     }
553
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
557     // should be used.
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();
570     }
571     return reply;
572 }
573
574 void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
575 {
576     Q_Q(QHttpNetworkConnection);
577
578     QHttpNetworkRequest request = pair.first;
579     switch (request.priority()) {
580     case QHttpNetworkRequest::HighPriority:
581         highPriorityQueue.prepend(pair);
582         break;
583     case QHttpNetworkRequest::NormalPriority:
584     case QHttpNetworkRequest::LowPriority:
585         lowPriorityQueue.prepend(pair);
586         break;
587     }
588
589     QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
590 }
591
592 bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
593 {
594     int i = 0;
595     if (socket)
596         i = indexOf(socket);
597
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;
605         return true;
606     }
607
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;
615         return true;
616     }
617     return false;
618 }
619
620 QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest()
621 {
622     if (!highPriorityQueue.isEmpty())
623         return highPriorityQueue.last().first;
624     if (!lowPriorityQueue.isEmpty())
625         return lowPriorityQueue.last().first;
626     return QHttpNetworkRequest();
627 }
628
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)
631 {
632     // return fast if there is nothing to pipeline
633     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
634         return;
635
636     int i = indexOf(socket);
637
638     // return fast if there was no reply right now processed
639     if (channels[i].reply == 0)
640         return;
641
642     if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
643         return;
644     }
645
646     if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
647         return;
648
649     // the current request that is in must already support pipelining
650     if (!channels[i].request.isPipeliningAllowed())
651         return;
652
653     // the current request must be a idempotent (right now we only check GET)
654     if (channels[i].request.operation() != QHttpNetworkRequest::Get)
655         return;
656
657     // check if socket is connected
658     if (socket->state() != QAbstractSocket::ConnectedState)
659         return;
660
661     // check for resendCurrent
662     if (channels[i].resendCurrent)
663         return;
664
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()))
670         return;
671     if (!channels[i].proxyAuthenticator.isNull()
672         && (!channels[i].proxyAuthenticator.user().isEmpty()
673             || !channels[i].proxyAuthenticator.password().isEmpty()))
674         return;
675
676     // must be in ReadingState or WaitingState
677     if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
678            || channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
679         return;
680
681     int lengthBefore;
682     while (!highPriorityQueue.isEmpty()) {
683         lengthBefore = channels[i].alreadyPipelinedRequests.length();
684         fillPipeline(highPriorityQueue, channels[i]);
685
686         if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
687             channels[i].pipelineFlush();
688             return;
689         }
690
691         if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
692             break; // did not process anything, now do the low prio queue
693     }
694
695     while (!lowPriorityQueue.isEmpty()) {
696         lengthBefore = channels[i].alreadyPipelinedRequests.length();
697         fillPipeline(lowPriorityQueue, channels[i]);
698
699         if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
700             channels[i].pipelineFlush();
701             return;
702         }
703
704         if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
705             break; // did not process anything
706     }
707
708
709     channels[i].pipelineFlush();
710 }
711
712 // returns true when the processing of a queue has been done
713 bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
714 {
715     if (queue.isEmpty())
716         return true;
717
718     for (int i = queue.count() - 1; i >= 0; --i) {
719         HttpMessagePair messagePair = queue.at(i);
720         const QHttpNetworkRequest &request = messagePair.first;
721
722         // we currently do not support pipelining if HTTP authentication is used
723         if (!request.url().userInfo().isEmpty())
724             continue;
725
726         // take only GET requests
727         if (request.operation() != QHttpNetworkRequest::Get)
728             continue;
729
730         if (!request.isPipeliningAllowed())
731             continue;
732
733         // remove it from the queue
734         queue.takeAt(i);
735         // we modify the queue we iterate over here, but since we return from the function
736         // afterwards this is fine.
737
738         // actually send it
739         if (!messagePair.second->d_func()->requestIsPrepared)
740             prepareRequest(messagePair);
741         channel.pipelineInto(messagePair);
742
743         // return false because we processed something and need to process again
744         return false;
745     }
746
747     // return true, the queue has been processed and not changed
748     return true;
749 }
750
751
752 QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail)
753 {
754     QString errorString;
755     switch (errorCode) {
756     case QNetworkReply::HostNotFoundError:
757         if (socket)
758             errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
759         else
760             errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName);
761         break;
762     case QNetworkReply::ConnectionRefusedError:
763         errorString = QCoreApplication::translate("QHttp", "Connection refused");
764         break;
765     case QNetworkReply::RemoteHostClosedError:
766         errorString = QCoreApplication::translate("QHttp", "Connection closed");
767         break;
768     case QNetworkReply::TimeoutError:
769         errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
770         break;
771     case QNetworkReply::ProxyAuthenticationRequiredError:
772         errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
773         break;
774     case QNetworkReply::AuthenticationRequiredError:
775         errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
776         break;
777     case QNetworkReply::ProtocolFailure:
778         errorString = QCoreApplication::translate("QHttp", "Data corrupted");
779         break;
780     case QNetworkReply::ProtocolUnknownError:
781         errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
782         break;
783     case QNetworkReply::SslHandshakeFailedError:
784         errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
785         break;
786     default:
787         // all other errors are treated as QNetworkReply::UnknownNetworkError
788         errorString = extraDetail;
789         break;
790     }
791     return errorString;
792 }
793
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)
797 {
798     Q_Q(QHttpNetworkConnection);
799
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;
807
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();
812             }
813
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())
818                 channels[i].close();
819
820             QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
821             return;
822         }
823
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);
829
830                channels[i].requeueCurrentlyPipelinedRequests();
831
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;
838
839                QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
840                return;
841             }
842         }
843     }
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);
851                 return;
852             }
853         }
854     }
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);
862                 return;
863             }
864         }
865     }
866 }
867
868
869
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()
874 {
875     // If there is no network layer state decided we should not start any new requests.
876     if (networkLayerState == Unknown || networkLayerState == InProgress)
877         return;
878
879     // If the QHttpNetworkConnection is currently paused then bail out immediately
880     if (state == PausedState)
881         return;
882
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;
888
889             // if this is not possible, error will be emitted and connection terminated
890             if (!channels[i].resetUploadData())
891                 continue;
892             channels[i].sendRequest();
893         }
894     }
895
896     // dequeue new ones
897
898     // return fast if there is nothing to do
899     if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
900         return;
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();
907             }
908         }
909     }
910
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())
920         return;
921     for (int i = 0; i < channelCount; i++)
922         if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
923             fillPipeline(channels[i].socket);
924
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))
933                 queuedRequest--;
934             if ( queuedRequest <=0 )
935                 break;
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;
940         }
941
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();
948             queuedRequest--;
949         }
950
951         if ( queuedRequest <=0 )
952             break;
953     }
954 }
955
956
957 void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
958 {
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);
963             return;
964         }
965     }
966 }
967
968
969
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()
975 {
976     networkLayerState = InProgress;
977
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();
985     }
986 #endif
987     QHostAddress temp;
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);
992             return;
993         } else if (temp.protocol() == QAbstractSocket::IPv6Protocol) {
994             networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
995             QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
996             return;
997         }
998     } else {
999         int hostLookupId;
1000         bool immediateResultValid = false;
1001         QHostInfo hostInfo = qt_qhostinfo_lookup(lookupHost,
1002                                                  this->q_func(),
1003                                                  SLOT(_q_hostLookupFinished(QHostInfo)),
1004                                                  &immediateResultValid,
1005                                                  &hostLookupId);
1006         if (immediateResultValid) {
1007             _q_hostLookupFinished(hostInfo);
1008         }
1009     }
1010 }
1011
1012
1013 void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(QHostInfo info)
1014 {
1015     bool bIpv4 = false;
1016     bool bIpv6 = false;
1017     bool foundAddress = false;
1018
1019     foreach (const QHostAddress &address, info.addresses()) {
1020         if (address.protocol() == QAbstractSocket::IPv4Protocol) {
1021             if (!foundAddress) {
1022                 foundAddress = true;
1023                 delayIpv4 = false;
1024             }
1025             bIpv4 = true;
1026         } else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
1027             if (!foundAddress) {
1028                 foundAddress = true;
1029                 delayIpv4 = true;
1030             }
1031             bIpv6 = true;
1032         }
1033     }
1034
1035     if (bIpv4 && bIpv6)
1036         startNetworkLayerStateLookup();
1037     else if (bIpv4) {
1038         networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1039         QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1040     } else if (bIpv6) {
1041         networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1042         QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
1043     } else {
1044         if (dequeueRequest(channels[0].socket)) {
1045             emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
1046             networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1047         } else {
1048             // Should not happen
1049             qWarning() << "QHttpNetworkConnectionPrivate::_q_hostLookupFinished could not dequeu request";
1050             networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
1051         }
1052     }
1053 }
1054
1055
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()
1061 {
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());
1066
1067         networkLayerState = InProgress;
1068
1069         channels[0].networkLayerPreference = QAbstractSocket::IPv4Protocol;
1070         channels[1].networkLayerPreference = QAbstractSocket::IPv6Protocol;
1071
1072         int timeout = 300;
1073 #ifndef QT_NO_BEARERMANAGEMENT
1074         if (networkSession) {
1075             if (networkSession->configuration().bearerType() == QNetworkConfiguration::Bearer2G)
1076                 timeout = 800;
1077             else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerCDMA2000)
1078                 timeout = 500;
1079             else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerWCDMA)
1080                 timeout = 500;
1081             else if (networkSession->configuration().bearerType() == QNetworkConfiguration::BearerHSPA)
1082                 timeout = 400;
1083         }
1084 #endif
1085         delayedConnectionTimer.start(timeout);
1086         if (delayIpv4)
1087             channels[1].ensureConnection();
1088         else
1089             channels[0].ensureConnection();
1090     } else {
1091         networkLayerState = InProgress;
1092         channels[0].networkLayerPreference = QAbstractSocket::AnyIPProtocol;
1093         channels[0].ensureConnection();
1094     }
1095 }
1096
1097 void QHttpNetworkConnectionPrivate::networkLayerDetected(QAbstractSocket::NetworkLayerProtocol protocol)
1098 {
1099     for (int i = 0 ; i < channelCount; ++i) {
1100         if ((channels[i].networkLayerPreference != protocol) && (channels[i].state == QHttpNetworkConnectionChannel::ConnectingState)) {
1101             channels[i].close();
1102         }
1103     }
1104 }
1105
1106 void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
1107 {
1108     if (delayIpv4)
1109         channels[0].ensureConnection();
1110     else
1111         channels[1].ensureConnection();
1112 }
1113
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)
1117 {
1118     Q_D(QHttpNetworkConnection);
1119     d->networkSession = networkSession;
1120     d->init();
1121 }
1122
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)
1125 {
1126     Q_D(QHttpNetworkConnection);
1127     d->networkSession = networkSession;
1128     d->init();
1129 }
1130 #else
1131 QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
1132     : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
1133 {
1134     Q_D(QHttpNetworkConnection);
1135     d->init();
1136 }
1137
1138 QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
1139      : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
1140 {
1141     Q_D(QHttpNetworkConnection);
1142     d->init();
1143 }
1144 #endif
1145
1146 QHttpNetworkConnection::~QHttpNetworkConnection()
1147 {
1148 }
1149
1150 QString QHttpNetworkConnection::hostName() const
1151 {
1152     Q_D(const QHttpNetworkConnection);
1153     return d->hostName;
1154 }
1155
1156 quint16 QHttpNetworkConnection::port() const
1157 {
1158     Q_D(const QHttpNetworkConnection);
1159     return d->port;
1160 }
1161
1162 QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
1163 {
1164     Q_D(QHttpNetworkConnection);
1165     return d->queueRequest(request);
1166 }
1167
1168 bool QHttpNetworkConnection::isSsl() const
1169 {
1170     Q_D(const QHttpNetworkConnection);
1171     return d->encrypt;
1172 }
1173
1174 QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
1175 {
1176     return d_func()->channels;
1177 }
1178
1179 #ifndef QT_NO_NETWORKPROXY
1180 void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
1181 {
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());
1189         }
1190     }
1191 }
1192
1193 QNetworkProxy QHttpNetworkConnection::cacheProxy() const
1194 {
1195     Q_D(const QHttpNetworkConnection);
1196     return d->networkProxy;
1197 }
1198
1199 void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
1200 {
1201     Q_D(QHttpNetworkConnection);
1202     for (int i = 0; i < d->channelCount; ++i)
1203         d->channels[i].setProxy(networkProxy);
1204 }
1205
1206 QNetworkProxy QHttpNetworkConnection::transparentProxy() const
1207 {
1208     Q_D(const QHttpNetworkConnection);
1209     return d->channels[0].proxy;
1210 }
1211 #endif
1212
1213
1214 // SSL support below
1215 #ifndef QT_NO_SSL
1216 void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
1217 {
1218     Q_D(QHttpNetworkConnection);
1219     if (!d->encrypt)
1220         return;
1221
1222     // set the config on all channels
1223     for (int i = 0; i < d->channelCount; ++i)
1224         d->channels[i].setSslConfiguration(config);
1225 }
1226
1227 void QHttpNetworkConnection::ignoreSslErrors(int channel)
1228 {
1229     Q_D(QHttpNetworkConnection);
1230     if (!d->encrypt)
1231         return;
1232
1233     if (channel == -1) { // ignore for all channels
1234         for (int i = 0; i < d->channelCount; ++i) {
1235             d->channels[i].ignoreSslErrors();
1236         }
1237
1238     } else {
1239         d->channels[channel].ignoreSslErrors();
1240     }
1241 }
1242
1243 void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
1244 {
1245     Q_D(QHttpNetworkConnection);
1246     if (!d->encrypt)
1247         return;
1248
1249     if (channel == -1) { // ignore for all channels
1250         for (int i = 0; i < d->channelCount; ++i) {
1251             d->channels[i].ignoreSslErrors(errors);
1252         }
1253
1254     } else {
1255         d->channels[channel].ignoreSslErrors(errors);
1256     }
1257 }
1258
1259 #endif //QT_NO_SSL
1260
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)
1266 {
1267     // Also pause the connection because socket notifiers may fire while an user
1268     // dialog is displaying
1269     pauseConnection();
1270     emit chan->reply->proxyAuthenticationRequired(proxy, auth);
1271     resumeConnection();
1272     int i = indexOf(chan->socket);
1273     copyCredentials(i, auth, true);
1274 }
1275 #endif
1276
1277
1278 QT_END_NAMESPACE
1279
1280 #include "moc_qhttpnetworkconnection_p.cpp"
1281
1282 #endif // QT_NO_HTTP