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