choke uploadProgress signals
[profile/ivi/qtbase.git] / src / network / access / qhttpnetworkconnectionchannel.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 "qhttpnetworkconnection_p.h"
43 #include "qhttpnetworkconnectionchannel_p.h"
44 #include "private/qnoncontiguousbytedevice_p.h"
45
46 #include <qpair.h>
47 #include <qdebug.h>
48
49 #ifndef QT_NO_HTTP
50
51 #ifndef QT_NO_SSL
52 #    include <QtNetwork/qsslkey.h>
53 #    include <QtNetwork/qsslcipher.h>
54 #    include <QtNetwork/qsslconfiguration.h>
55 #endif
56
57 #ifndef QT_NO_BEARERMANAGEMENT
58 #include "private/qnetworksession_p.h"
59 #endif
60
61 QT_BEGIN_NAMESPACE
62
63 // TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
64
65 QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
66     : socket(0)
67     , ssl(false)
68     , isInitialized(false)
69     , state(IdleState)
70     , reply(0)
71     , written(0)
72     , bytesTotal(0)
73     , resendCurrent(false)
74     , lastStatus(0)
75     , pendingEncrypt(false)
76     , reconnectAttempts(2)
77     , authMethod(QAuthenticatorPrivate::None)
78     , proxyAuthMethod(QAuthenticatorPrivate::None)
79     , authenticationCredentialsSent(false)
80     , proxyCredentialsSent(false)
81 #ifndef QT_NO_SSL
82     , ignoreAllSslErrors(false)
83 #endif
84     , pipeliningSupported(PipeliningSupportUnknown)
85     , networkLayerPreference(QAbstractSocket::AnyIPProtocol)
86     , connection(0)
87 {
88     // Inlining this function in the header leads to compiler error on
89     // release-armv5, on at least timebox 9.2 and 10.1.
90 }
91
92 void QHttpNetworkConnectionChannel::init()
93 {
94 #ifndef QT_NO_SSL
95     if (connection->d_func()->encrypt)
96         socket = new QSslSocket;
97     else
98         socket = new QTcpSocket;
99 #else
100     socket = new QTcpSocket;
101 #endif
102 #ifndef QT_NO_BEARERMANAGEMENT
103     //push session down to socket
104     if (networkSession)
105         socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
106 #endif
107 #ifndef QT_NO_NETWORKPROXY
108     // Set by QNAM anyway, but let's be safe here
109     socket->setProxy(QNetworkProxy::NoProxy);
110 #endif
111
112     QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
113                      this, SLOT(_q_bytesWritten(qint64)),
114                      Qt::DirectConnection);
115     QObject::connect(socket, SIGNAL(connected()),
116                      this, SLOT(_q_connected()),
117                      Qt::DirectConnection);
118     QObject::connect(socket, SIGNAL(readyRead()),
119                      this, SLOT(_q_readyRead()),
120                      Qt::DirectConnection);
121
122     // The disconnected() and error() signals may already come
123     // while calling connectToHost().
124     // In case of a cached hostname or an IP this
125     // will then emit a signal to the user of QNetworkReply
126     // but cannot be caught because the user did not have a chance yet
127     // to connect to QNetworkReply's signals.
128     qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
129     QObject::connect(socket, SIGNAL(disconnected()),
130                      this, SLOT(_q_disconnected()),
131                      Qt::QueuedConnection);
132     QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
133                      this, SLOT(_q_error(QAbstractSocket::SocketError)),
134                      Qt::QueuedConnection);
135
136
137 #ifndef QT_NO_NETWORKPROXY
138     QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
139                      this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
140                      Qt::DirectConnection);
141 #endif
142
143 #ifndef QT_NO_SSL
144     QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
145     if (sslSocket) {
146         // won't be a sslSocket if encrypt is false
147         QObject::connect(sslSocket, SIGNAL(encrypted()),
148                          this, SLOT(_q_encrypted()),
149                          Qt::DirectConnection);
150         QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
151                          this, SLOT(_q_sslErrors(QList<QSslError>)),
152                          Qt::DirectConnection);
153         QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
154                          this, SLOT(_q_encryptedBytesWritten(qint64)),
155                          Qt::DirectConnection);
156
157         if (ignoreAllSslErrors)
158             sslSocket->ignoreSslErrors();
159
160         if (!ignoreSslErrorsList.isEmpty())
161             sslSocket->ignoreSslErrors(ignoreSslErrorsList);
162
163         if (!sslConfiguration.isNull())
164            sslSocket->setSslConfiguration(sslConfiguration);
165     }
166
167 #endif
168
169 #ifndef QT_NO_NETWORKPROXY
170     if (proxy.type() != QNetworkProxy::NoProxy)
171         socket->setProxy(proxy);
172 #endif
173     isInitialized = true;
174 }
175
176
177 void QHttpNetworkConnectionChannel::close()
178 {
179     if (!socket)
180         state = QHttpNetworkConnectionChannel::IdleState;
181     else if (socket->state() == QAbstractSocket::UnconnectedState)
182         state = QHttpNetworkConnectionChannel::IdleState;
183     else
184         state = QHttpNetworkConnectionChannel::ClosingState;
185
186     if (socket)
187         socket->close();
188 }
189
190
191 bool QHttpNetworkConnectionChannel::sendRequest()
192 {
193     if (!reply) {
194         // heh, how should that happen!
195         qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
196         state = QHttpNetworkConnectionChannel::IdleState;
197         return false;
198     }
199
200     switch (state) {
201     case QHttpNetworkConnectionChannel::IdleState: { // write the header
202         if (!ensureConnection()) {
203             // wait for the connection (and encryption) to be done
204             // sendRequest will be called again from either
205             // _q_connected or _q_encrypted
206             return false;
207         }
208         written = 0; // excluding the header
209         bytesTotal = 0;
210
211         QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
212         replyPrivate->clear();
213         replyPrivate->connection = connection;
214         replyPrivate->connectionChannel = this;
215         replyPrivate->autoDecompress = request.d->autoDecompress;
216         replyPrivate->pipeliningUsed = false;
217
218         // if the url contains authentication parameters, use the new ones
219         // both channels will use the new authentication parameters
220         if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
221             QUrl url = request.url();
222             QAuthenticator &auth = authenticator;
223             if (url.userName() != auth.user()
224                 || (!url.password().isEmpty() && url.password() != auth.password())) {
225                 auth.setUser(url.userName());
226                 auth.setPassword(url.password());
227                 connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
228             }
229             // clear the userinfo,  since we use the same request for resending
230             // userinfo in url can conflict with the one in the authenticator
231             url.setUserInfo(QString());
232             request.setUrl(url);
233         }
234         // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
235         // and withCredentials has not been set to true.
236         if (request.withCredentials())
237             connection->d_func()->createAuthorization(socket, request);
238 #ifndef QT_NO_NETWORKPROXY
239         QByteArray header = QHttpNetworkRequestPrivate::header(request,
240             (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
241 #else
242         QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
243 #endif
244         socket->write(header);
245         // flushing is dangerous (QSslSocket calls transmit which might read or error)
246 //        socket->flush();
247         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
248         if (uploadByteDevice) {
249             // connect the signals so this function gets called again
250             QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
251
252             bytesTotal = request.contentLength();
253
254             state = QHttpNetworkConnectionChannel::WritingState; // start writing data
255             sendRequest(); //recurse
256         } else {
257             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
258             sendRequest(); //recurse
259         }
260
261         break;
262     }
263     case QHttpNetworkConnectionChannel::WritingState:
264     {
265         // write the data
266         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
267         if (!uploadByteDevice || bytesTotal == written) {
268             if (uploadByteDevice)
269                 emit reply->dataSendProgress(written, bytesTotal);
270             state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
271             sendRequest(); // recurse
272             break;
273         }
274
275         // only feed the QTcpSocket buffer when there is less than 32 kB in it
276         const qint64 socketBufferFill = 32*1024;
277         const qint64 socketWriteMaxSize = 16*1024;
278
279
280 #ifndef QT_NO_SSL
281         QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
282         // if it is really an ssl socket, check more than just bytesToWrite()
283         while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
284                 <= socketBufferFill && bytesTotal != written)
285 #else
286         while (socket->bytesToWrite() <= socketBufferFill
287                && bytesTotal != written)
288 #endif
289         {
290             // get pointer to upload data
291             qint64 currentReadSize = 0;
292             qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
293             const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
294
295             if (currentReadSize == -1) {
296                 // premature eof happened
297                 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
298                 return false;
299                 break;
300             } else if (readPointer == 0 || currentReadSize == 0) {
301                 // nothing to read currently, break the loop
302                 break;
303             } else {
304                 qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
305                 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
306                     // socket broke down
307                     connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
308                     return false;
309                 } else {
310                     written += currentWriteSize;
311                     uploadByteDevice->advanceReadPointer(currentWriteSize);
312
313                     emit reply->dataSendProgress(written, bytesTotal);
314
315                     if (written == bytesTotal) {
316                         // make sure this function is called once again
317                         state = QHttpNetworkConnectionChannel::WaitingState;
318                         sendRequest();
319                         break;
320                     }
321                 }
322             }
323         }
324         break;
325     }
326
327     case QHttpNetworkConnectionChannel::WaitingState:
328     {
329         QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
330         if (uploadByteDevice) {
331             QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
332         }
333
334         // HTTP pipelining
335         //connection->d_func()->fillPipeline(socket);
336         //socket->flush();
337
338         // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
339         // this is needed if the sends an reply before we have finished sending the request. In that
340         // case receiveReply had been called before but ignored the server reply
341         if (socket->bytesAvailable())
342             QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
343         break;
344     }
345     case QHttpNetworkConnectionChannel::ReadingState:
346         // ignore _q_bytesWritten in these states
347         // fall through
348     default:
349         break;
350     }
351     return true;
352 }
353
354
355 void QHttpNetworkConnectionChannel::_q_receiveReply()
356 {
357     Q_ASSERT(socket);
358
359     if (!reply) {
360         if (socket->bytesAvailable() > 0)
361             qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
362                        << socket->bytesAvailable() << "bytes on socket.";
363         close();
364         return;
365     }
366
367     // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
368     // this function is called from _q_disconnected which is called because
369     // of ~QHttpNetworkConnectionPrivate
370     if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
371         return;
372     }
373
374     QAbstractSocket::SocketState socketState = socket->state();
375
376     // connection might be closed to signal the end of data
377     if (socketState == QAbstractSocket::UnconnectedState) {
378         if (socket->bytesAvailable() <= 0) {
379             if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
380                 // finish this reply. this case happens when the server did not send a content length
381                 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
382                 allDone();
383                 return;
384             } else {
385                 handleUnexpectedEOF();
386                 return;
387             }
388         } else {
389             // socket not connected but still bytes for reading.. just continue in this function
390         }
391     }
392
393     // read loop for the response
394     qint64 bytes = 0;
395     qint64 lastBytes = bytes;
396     do {
397         lastBytes = bytes;
398
399         QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
400         switch (state) {
401         case QHttpNetworkReplyPrivate::NothingDoneState: {
402             state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
403             // fallthrough
404         }
405         case QHttpNetworkReplyPrivate::ReadingStatusState: {
406             qint64 statusBytes = reply->d_func()->readStatus(socket);
407             if (statusBytes == -1) {
408                 // connection broke while reading status. also handled if later _q_disconnected is called
409                 handleUnexpectedEOF();
410                 return;
411             }
412             bytes += statusBytes;
413             lastStatus = reply->d_func()->statusCode;
414             break;
415         }
416         case QHttpNetworkReplyPrivate::ReadingHeaderState: {
417             QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
418             qint64 headerBytes = replyPrivate->readHeader(socket);
419             if (headerBytes == -1) {
420                 // connection broke while reading headers. also handled if later _q_disconnected is called
421                 handleUnexpectedEOF();
422                 return;
423             }
424             bytes += headerBytes;
425             // If headers were parsed successfully now it is the ReadingDataState
426             if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
427                 if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
428                     // remove the Content-Length from header
429                     replyPrivate->removeAutoDecompressHeader();
430                 } else {
431                     replyPrivate->autoDecompress = false;
432                 }
433                 if (replyPrivate->statusCode == 100) {
434                     replyPrivate->clearHttpLayerInformation();
435                     replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
436                     break; // ignore
437                 }
438                 if (replyPrivate->shouldEmitSignals())
439                     emit reply->headerChanged();
440                 // After headerChanged had been emitted
441                 // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
442                 // this is handled in the ReadingDataState however
443
444                 if (!replyPrivate->expectContent()) {
445                     replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
446                     allDone();
447                     break;
448                 }
449             }
450             break;
451         }
452         case QHttpNetworkReplyPrivate::ReadingDataState: {
453            QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
454            if (socket->state() == QAbstractSocket::ConnectedState &&
455                replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
456                // (only do the following when still connected, not when we have already been disconnected and there is still data)
457                // We already have some HTTP body data. We don't read more from the socket until
458                // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
459                // we could not limit our read buffer usage.
460                // We only do this when shouldEmitSignals==true because our HTTP parsing
461                // always needs to parse the 401/407 replies. Therefore they don't really obey
462                // to the read buffer maximum size, but we don't care since they should be small.
463                return;
464            }
465
466            if (replyPrivate->userProvidedDownloadBuffer) {
467                // the user provided a direct buffer where we should put all our data in.
468                // this only works when we can tell the user the content length and he/she can allocate
469                // the buffer in that size.
470                // note that this call will read only from the still buffered data
471                qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
472                if (haveRead > 0) {
473                    bytes += haveRead;
474                    replyPrivate->totalProgress += haveRead;
475                    // the user will get notified of it via progress signal
476                    emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
477                } else if (haveRead == 0) {
478                    // Happens since this called in a loop. Currently no bytes available.
479                } else if (haveRead < 0) {
480                    connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
481                    break;
482                }
483            } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
484                  && replyPrivate->bodyLength > 0) {
485                  // bulk files like images should fulfill these properties and
486                  // we can therefore save on memory copying
487                 qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
488                 bytes += haveRead;
489                 replyPrivate->totalProgress += haveRead;
490                 if (replyPrivate->shouldEmitSignals()) {
491                     emit reply->readyRead();
492                     emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
493                 }
494             }
495             else
496             {
497                 // use the traditional slower reading (for compressed encoding, chunked encoding,
498                 // no content-length etc)
499                 qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
500                 if (haveRead > 0) {
501                     bytes += haveRead;
502                     replyPrivate->totalProgress += haveRead;
503                     if (replyPrivate->shouldEmitSignals()) {
504                         emit reply->readyRead();
505                         emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
506                     }
507                 } else if (haveRead == -1) {
508                     // Some error occured
509                     connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
510                     break;
511                 }
512             }
513             // still in ReadingDataState? This function will be called again by the socket's readyRead
514             if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
515                 break;
516
517             // everything done, fall through
518             }
519       case QHttpNetworkReplyPrivate::AllDoneState:
520             allDone();
521             break;
522         default:
523             break;
524         }
525     } while (bytes != lastBytes && reply);
526 }
527
528 // called when unexpectedly reading a -1 or when data is expected but socket is closed
529 void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
530 {
531     Q_ASSERT(reply);
532     if (reconnectAttempts <= 0) {
533         // too many errors reading/receiving/parsing the status, close the socket and emit error
534         requeueCurrentlyPipelinedRequests();
535         close();
536         reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
537         emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
538         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
539     } else {
540         reconnectAttempts--;
541         reply->d_func()->clear();
542         reply->d_func()->connection = connection;
543         reply->d_func()->connectionChannel = this;
544         closeAndResendCurrentRequest();
545     }
546 }
547
548 bool QHttpNetworkConnectionChannel::ensureConnection()
549 {
550     if (!isInitialized)
551         init();
552
553     QAbstractSocket::SocketState socketState = socket->state();
554
555     // resend this request after we receive the disconnected signal
556     if (socketState == QAbstractSocket::ClosingState) {
557         if (reply)
558             resendCurrent = true;
559         return false;
560     }
561
562     // already trying to connect?
563     if (socketState == QAbstractSocket::HostLookupState ||
564         socketState == QAbstractSocket::ConnectingState) {
565         return false;
566     }
567
568     // make sure that this socket is in a connected state, if not initiate
569     // connection to the host.
570     if (socketState != QAbstractSocket::ConnectedState) {
571         // connect to the host if not already connected.
572         state = QHttpNetworkConnectionChannel::ConnectingState;
573         pendingEncrypt = ssl;
574
575         // reset state
576         pipeliningSupported = PipeliningSupportUnknown;
577         authenticationCredentialsSent = false;
578         proxyCredentialsSent = false;
579         authenticator.detach();
580         QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
581         priv->hasFailed = false;
582         proxyAuthenticator.detach();
583         priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
584         priv->hasFailed = false;
585
586         // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
587         // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
588         // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
589         // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
590         // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
591         // the phase is reset to Start.
592         priv = QAuthenticatorPrivate::getPrivate(authenticator);
593         if (priv && priv->phase == QAuthenticatorPrivate::Done)
594             priv->phase = QAuthenticatorPrivate::Start;
595         priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
596         if (priv && priv->phase == QAuthenticatorPrivate::Done)
597             priv->phase = QAuthenticatorPrivate::Start;
598
599         QString connectHost = connection->d_func()->hostName;
600         qint16 connectPort = connection->d_func()->port;
601
602 #ifndef QT_NO_NETWORKPROXY
603         // HTTPS always use transparent proxy.
604         if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
605             connectHost = connection->d_func()->networkProxy.hostName();
606             connectPort = connection->d_func()->networkProxy.port();
607         }
608         if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
609             // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
610             QByteArray value;
611             // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
612             if (request.url().isEmpty())
613                 value = connection->d_func()->predictNextRequest().headerField("user-agent");
614             else
615                 value = request.headerField("user-agent");
616             if (!value.isEmpty()) {
617                 QNetworkProxy proxy(socket->proxy());
618                 proxy.setRawHeader("User-Agent", value); //detaches
619                 socket->setProxy(proxy);
620             }
621         }
622 #endif
623         if (ssl) {
624 #ifndef QT_NO_SSL
625             QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
626             sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
627             if (ignoreAllSslErrors)
628                 sslSocket->ignoreSslErrors();
629             sslSocket->ignoreSslErrors(ignoreSslErrorsList);
630
631             // limit the socket read buffer size. we will read everything into
632             // the QHttpNetworkReply anyway, so let's grow only that and not
633             // here and there.
634             socket->setReadBufferSize(64*1024);
635 #else
636             // Need to dequeue the request so that we can emit the error.
637             if (!reply)
638                 connection->d_func()->dequeueRequest(socket);
639             connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
640 #endif
641         } else {
642             // In case of no proxy we can use the Unbuffered QTcpSocket
643 #ifndef QT_NO_NETWORKPROXY
644             if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
645                     && connection->cacheProxy().type() == QNetworkProxy::NoProxy
646                     && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
647 #endif
648                 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
649                 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
650                 socket->setReadBufferSize(1*1024);
651 #ifndef QT_NO_NETWORKPROXY
652             } else {
653                 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
654
655                 // limit the socket read buffer size. we will read everything into
656                 // the QHttpNetworkReply anyway, so let's grow only that and not
657                 // here and there.
658                 socket->setReadBufferSize(64*1024);
659             }
660 #endif
661         }
662         return false;
663     }
664     return true;
665 }
666
667 void QHttpNetworkConnectionChannel::allDone()
668 {
669     Q_ASSERT(reply);
670
671     if (!reply) {
672         qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt-project.org/";
673         return;
674     }
675
676     // while handling 401 & 407, we might reset the status code, so save this.
677     bool emitFinished = reply->d_func()->shouldEmitSignals();
678     bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
679     detectPipeliningSupport();
680
681     handleStatus();
682     // handleStatus() might have removed the reply because it already called connection->emitReplyError()
683
684     // queue the finished signal, this is required since we might send new requests from
685     // slot connected to it. The socket will not fire readyRead signal, if we are already
686     // in the slot connected to readyRead
687     if (reply && emitFinished)
688         QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
689
690
691     // reset the reconnection attempts after we receive a complete reply.
692     // in case of failures, each channel will attempt two reconnects before emitting error.
693     reconnectAttempts = 2;
694
695     // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
696     if (state != QHttpNetworkConnectionChannel::ClosingState)
697         state = QHttpNetworkConnectionChannel::IdleState;
698
699     // if it does not need to be sent again we can set it to 0
700     // the previous code did not do that and we had problems with accidental re-sending of a
701     // finished request.
702     // Note that this may trigger a segfault at some other point. But then we can fix the underlying
703     // problem.
704     if (!resendCurrent) {
705         request = QHttpNetworkRequest();
706         reply = 0;
707     }
708
709     // move next from pipeline to current request
710     if (!alreadyPipelinedRequests.isEmpty()) {
711         if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
712             // move the pipelined ones back to the main queue
713             requeueCurrentlyPipelinedRequests();
714             close();
715         } else {
716             // there were requests pipelined in and we can continue
717             HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
718
719             request = messagePair.first;
720             reply = messagePair.second;
721             state = QHttpNetworkConnectionChannel::ReadingState;
722             resendCurrent = false;
723
724             written = 0; // message body, excluding the header, irrelevant here
725             bytesTotal = 0; // message body total, excluding the header, irrelevant here
726
727             // pipeline even more
728             connection->d_func()->fillPipeline(socket);
729
730             // continue reading
731             //_q_receiveReply();
732             // this was wrong, allDone gets called from that function anyway.
733         }
734     } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
735         // this is weird. we had nothing pipelined but still bytes available. better close it.
736         close();
737
738         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
739     } else if (alreadyPipelinedRequests.isEmpty()) {
740         if (connectionCloseEnabled)
741             if (socket->state() != QAbstractSocket::UnconnectedState)
742                 close();
743         if (qobject_cast<QHttpNetworkConnection*>(connection))
744             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
745     }
746 }
747
748 void QHttpNetworkConnectionChannel::detectPipeliningSupport()
749 {
750     Q_ASSERT(reply);
751     // detect HTTP Pipelining support
752     QByteArray serverHeaderField;
753     if (
754             // check for HTTP/1.1
755             (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
756             // check for not having connection close
757             && (!reply->d_func()->isConnectionCloseEnabled())
758             // check if it is still connected
759             && (socket->state() == QAbstractSocket::ConnectedState)
760             // check for broken servers in server reply header
761             // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
762             && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
763             && (!serverHeaderField.contains("Microsoft-IIS/5."))
764             && (!serverHeaderField.contains("Netscape-Enterprise/3."))
765             // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
766             && (!serverHeaderField.contains("WebLogic"))
767             && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
768             ) {
769         pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
770     } else {
771         pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
772     }
773 }
774
775 // called when the connection broke and we need to queue some pipelined requests again
776 void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
777 {
778     for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
779         connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
780     alreadyPipelinedRequests.clear();
781
782     // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
783     // this function is called from _q_disconnected which is called because
784     // of ~QHttpNetworkConnectionPrivate
785     if (qobject_cast<QHttpNetworkConnection*>(connection))
786         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
787 }
788
789 void QHttpNetworkConnectionChannel::handleStatus()
790 {
791     Q_ASSERT(socket);
792     Q_ASSERT(reply);
793
794     int statusCode = reply->statusCode();
795     bool resend = false;
796
797     switch (statusCode) {
798     case 401: // auth required
799     case 407: // proxy auth required
800         if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
801             if (resend) {
802                 if (!resetUploadData())
803                     break;
804
805                 reply->d_func()->eraseData();
806
807                 if (alreadyPipelinedRequests.isEmpty()) {
808                     // this does a re-send without closing the connection
809                     resendCurrent = true;
810                     QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
811                 } else {
812                     // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
813                     closeAndResendCurrentRequest();
814                     QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
815                 }
816             } else {
817                 //authentication cancelled, close the channel.
818                 close();
819             }
820         } else {
821             emit reply->headerChanged();
822             emit reply->readyRead();
823             QNetworkReply::NetworkError errorCode = (statusCode == 407)
824                 ? QNetworkReply::ProxyAuthenticationRequiredError
825                 : QNetworkReply::AuthenticationRequiredError;
826             reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
827             emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
828         }
829         break;
830     default:
831         if (qobject_cast<QHttpNetworkConnection*>(connection))
832             QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
833     }
834 }
835
836 bool QHttpNetworkConnectionChannel::resetUploadData()
837 {
838     if (!reply) {
839         //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
840         return false;
841     }
842     QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
843     if (!uploadByteDevice)
844         return true;
845
846     if (uploadByteDevice->reset()) {
847         written = 0;
848         return true;
849     } else {
850         connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
851         return false;
852     }
853 }
854
855 #ifndef QT_NO_NETWORKPROXY
856
857 void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
858 {
859     if (socket)
860         socket->setProxy(networkProxy);
861
862     proxy = networkProxy;
863 }
864
865 #endif
866
867 #ifndef QT_NO_SSL
868
869 void QHttpNetworkConnectionChannel::ignoreSslErrors()
870 {
871     if (socket)
872         static_cast<QSslSocket *>(socket)->ignoreSslErrors();
873
874     ignoreAllSslErrors = true;
875 }
876
877
878 void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
879 {
880     if (socket)
881         static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
882
883     ignoreSslErrorsList = errors;
884 }
885
886 void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config)
887 {
888     if (socket)
889         static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
890
891     sslConfiguration = config;
892 }
893
894 #endif
895
896 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
897 {
898     // this is only called for simple GET
899
900     QHttpNetworkRequest &request = pair.first;
901     QHttpNetworkReply *reply = pair.second;
902     reply->d_func()->clear();
903     reply->d_func()->connection = connection;
904     reply->d_func()->connectionChannel = this;
905     reply->d_func()->autoDecompress = request.d->autoDecompress;
906     reply->d_func()->pipeliningUsed = true;
907
908 #ifndef QT_NO_NETWORKPROXY
909     pipeline.append(QHttpNetworkRequestPrivate::header(request,
910                                                            (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
911 #else
912     pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
913 #endif
914
915     alreadyPipelinedRequests.append(pair);
916
917     // pipelineFlush() needs to be called at some point afterwards
918 }
919
920 void QHttpNetworkConnectionChannel::pipelineFlush()
921 {
922     if (pipeline.isEmpty())
923         return;
924
925     // The goal of this is so that we have everything in one TCP packet.
926     // For the Unbuffered QTcpSocket this is manually needed, the buffered
927     // QTcpSocket does it automatically.
928     // Also, sometimes the OS does it for us (Nagle's algorithm) but that
929     // happens only sometimes.
930     socket->write(pipeline);
931     pipeline.clear();
932 }
933
934
935 void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
936 {
937     requeueCurrentlyPipelinedRequests();
938     close();
939     if (reply)
940         resendCurrent = true;
941     if (qobject_cast<QHttpNetworkConnection*>(connection))
942         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
943 }
944
945 bool QHttpNetworkConnectionChannel::isSocketBusy() const
946 {
947     return (state & QHttpNetworkConnectionChannel::BusyState);
948 }
949
950 bool QHttpNetworkConnectionChannel::isSocketWriting() const
951 {
952     return (state & QHttpNetworkConnectionChannel::WritingState);
953 }
954
955 bool QHttpNetworkConnectionChannel::isSocketWaiting() const
956 {
957     return (state & QHttpNetworkConnectionChannel::WaitingState);
958 }
959
960 bool QHttpNetworkConnectionChannel::isSocketReading() const
961 {
962     return (state & QHttpNetworkConnectionChannel::ReadingState);
963 }
964
965 //private slots
966 void QHttpNetworkConnectionChannel::_q_readyRead()
967 {
968     if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
969         // We got a readyRead but no bytes are available..
970         // This happens for the Unbuffered QTcpSocket
971         // Also check if socket is in ConnectedState since
972         // this function may also be invoked via the event loop.
973         char c;
974         qint64  ret = socket->peek(&c, 1);
975         if (ret < 0) {
976             _q_error(socket->error());
977             // We still need to handle the reply so it emits its signals etc.
978             if (reply)
979                 _q_receiveReply();
980             return;
981         }
982     }
983
984     if (isSocketWaiting() || isSocketReading()) {
985         state = QHttpNetworkConnectionChannel::ReadingState;
986         if (reply)
987             _q_receiveReply();
988     }
989 }
990
991 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
992 {
993     Q_UNUSED(bytes);
994     // bytes have been written to the socket. write even more of them :)
995     if (isSocketWriting())
996         sendRequest();
997     // otherwise we do nothing
998 }
999
1000 void QHttpNetworkConnectionChannel::_q_disconnected()
1001 {
1002     if (state == QHttpNetworkConnectionChannel::ClosingState) {
1003         state = QHttpNetworkConnectionChannel::IdleState;
1004         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1005         return;
1006     }
1007
1008     // read the available data before closing
1009     if (isSocketWaiting() || isSocketReading()) {
1010         if (reply) {
1011             state = QHttpNetworkConnectionChannel::ReadingState;
1012             _q_receiveReply();
1013         }
1014     } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
1015         // re-sending request because the socket was in ClosingState
1016         QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1017     }
1018     state = QHttpNetworkConnectionChannel::IdleState;
1019
1020     requeueCurrentlyPipelinedRequests();
1021     close();
1022 }
1023
1024
1025 void QHttpNetworkConnectionChannel::_q_connected()
1026 {
1027     // For the Happy Eyeballs we need to check if this is the first channel to connect.
1028     if (!pendingEncrypt) {
1029         if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
1030             if (connection->d_func()->delayedConnectionTimer.isActive())
1031                 connection->d_func()->delayedConnectionTimer.stop();
1032             if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
1033                 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1034             else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
1035                 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1036             else {
1037                 if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
1038                     connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1039                 else
1040                     connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1041             }
1042             connection->d_func()->networkLayerDetected(networkLayerPreference);
1043         } else {
1044             if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (networkLayerPreference != QAbstractSocket::IPv4Protocol))
1045                 || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
1046                 close();
1047                 // This is the second connection so it has to be closed and we can schedule it for another request.
1048                 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1049                 return;
1050             }
1051             //The connections networkLayerState had already been decided.
1052         }
1053     }
1054
1055     // improve performance since we get the request sent by the kernel ASAP
1056     //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
1057     // We have this commented out now. It did not have the effect we wanted. If we want to
1058     // do this properly, Qt has to combine multiple HTTP requests into one buffer
1059     // and send this to the kernel in one syscall and then the kernel immediately sends
1060     // it as one TCP packet because of TCP_NODELAY.
1061     // However, this code is currently not in Qt, so we rely on the kernel combining
1062     // the requests into one TCP packet.
1063
1064     // not sure yet if it helps, but it makes sense
1065     socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
1066
1067     pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
1068
1069     // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
1070     //channels[i].reconnectAttempts = 2;
1071     if (!pendingEncrypt) {
1072         state = QHttpNetworkConnectionChannel::IdleState;
1073         if (!reply)
1074             connection->d_func()->dequeueRequest(socket);
1075         if (reply)
1076             sendRequest();
1077     }
1078 }
1079
1080
1081 void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1082 {
1083     if (!socket)
1084         return;
1085     QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1086
1087     switch (socketError) {
1088     case QAbstractSocket::HostNotFoundError:
1089         errorCode = QNetworkReply::HostNotFoundError;
1090         break;
1091     case QAbstractSocket::ConnectionRefusedError:
1092         errorCode = QNetworkReply::ConnectionRefusedError;
1093         break;
1094     case QAbstractSocket::RemoteHostClosedError:
1095         // try to reconnect/resend before sending an error.
1096         // while "Reading" the _q_disconnected() will handle this.
1097         if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1098             if (reconnectAttempts-- > 0) {
1099                 closeAndResendCurrentRequest();
1100                 return;
1101             } else {
1102                 errorCode = QNetworkReply::RemoteHostClosedError;
1103             }
1104         } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1105             if (!reply->d_func()->expectContent()) {
1106                 // No content expected, this is a valid way to have the connection closed by the server
1107                 return;
1108             }
1109             if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1110                 // There was no content-length header and it's not chunked encoding,
1111                 // so this is a valid way to have the connection closed by the server
1112                 return;
1113             }
1114             // ok, we got a disconnect even though we did not expect it
1115             // Try to read everything from the socket before we emit the error.
1116             if (socket->bytesAvailable()) {
1117                 // Read everything from the socket into the reply buffer.
1118                 // we can ignore the readbuffersize as the data is already
1119                 // in memory and we will not recieve more data on the socket.
1120                 reply->setReadBufferSize(0);
1121                 _q_receiveReply();
1122 #ifndef QT_NO_SSL
1123                 if (ssl) {
1124                     // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
1125                     // So we need to check this if the socket is a QSslSocket. When the socket is flushed
1126                     // it will force a decrypt of the encrypted data in the plainsocket.
1127                     QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
1128                     qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
1129                     while (sslSocket->encryptedBytesAvailable()) {
1130                         sslSocket->flush();
1131                         _q_receiveReply();
1132                         qint64 afterFlush = sslSocket->encryptedBytesAvailable();
1133                         if (afterFlush == beforeFlush)
1134                             break;
1135                         beforeFlush = afterFlush;
1136                     }
1137                 }
1138 #endif
1139             }
1140
1141             errorCode = QNetworkReply::RemoteHostClosedError;
1142         } else {
1143             errorCode = QNetworkReply::RemoteHostClosedError;
1144         }
1145         break;
1146     case QAbstractSocket::SocketTimeoutError:
1147         // try to reconnect/resend before sending an error.
1148         if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1149             closeAndResendCurrentRequest();
1150             return;
1151         }
1152         errorCode = QNetworkReply::TimeoutError;
1153         break;
1154     case QAbstractSocket::ProxyAuthenticationRequiredError:
1155         errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1156         break;
1157     case QAbstractSocket::SslHandshakeFailedError:
1158         errorCode = QNetworkReply::SslHandshakeFailedError;
1159         break;
1160     default:
1161         // all other errors are treated as NetworkError
1162         errorCode = QNetworkReply::UnknownNetworkError;
1163         break;
1164     }
1165     QPointer<QHttpNetworkConnection> that = connection;
1166     QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1167
1168     // In the InProgress state the channel should not emit the error.
1169     // This will instead be handled by the connection.
1170     if (!connection->d_func()->shouldEmitChannelError(socket))
1171         return;
1172
1173     // Need to dequeu the request so that we can emit the error.
1174     if (!reply)
1175         connection->d_func()->dequeueRequest(socket);
1176     if (reply) {
1177         reply->d_func()->errorString = errorString;
1178         emit reply->finishedWithError(errorCode, errorString);
1179         reply = 0;
1180     }
1181     // send the next request
1182     QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1183
1184     if (that) //signal emission triggered event loop
1185         close();
1186 }
1187
1188 #ifndef QT_NO_NETWORKPROXY
1189 void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1190 {
1191     // Need to dequeue the request before we can emit the error.
1192     if (!reply)
1193         connection->d_func()->dequeueRequest(socket);
1194     if (reply)
1195         connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1196 }
1197 #endif
1198
1199 void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1200 {
1201     sendRequest();
1202 }
1203
1204 #ifndef QT_NO_SSL
1205 void QHttpNetworkConnectionChannel::_q_encrypted()
1206 {
1207     if (!socket)
1208         return; // ### error
1209     state = QHttpNetworkConnectionChannel::IdleState;
1210     pendingEncrypt = false;
1211     if (!reply)
1212         connection->d_func()->dequeueRequest(socket);
1213     if (reply)
1214         sendRequest();
1215 }
1216
1217 void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1218 {
1219     if (!socket)
1220         return;
1221     //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1222     // Also pause the connection because socket notifiers may fire while an user
1223     // dialog is displaying
1224     connection->d_func()->pauseConnection();
1225     if (pendingEncrypt && !reply)
1226         connection->d_func()->dequeueRequest(socket);
1227     if (reply)
1228         emit reply->sslErrors(errors);
1229     connection->d_func()->resumeConnection();
1230 }
1231
1232 void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1233 {
1234     Q_UNUSED(bytes);
1235     // bytes have been written to the socket. write even more of them :)
1236     if (isSocketWriting())
1237         sendRequest();
1238     // otherwise we do nothing
1239 }
1240
1241 #endif
1242
1243 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1244 {
1245     // Inlining this function in the header leads to compiler error on
1246     // release-armv5, on at least timebox 9.2 and 10.1.
1247     connection = c;
1248 }
1249
1250 QT_END_NAMESPACE
1251
1252 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
1253
1254 #endif // QT_NO_HTTP