2015-12-10 05:06:13 +02:00
/****************************************************************************
* *
* * Copyright ( C ) 2015 The Qt Company Ltd .
* * Contact : http : //www.qt.io/licensing/
* *
* * This file is part of the QtNetwork module of the Qt Toolkit .
* *
* * $ QT_BEGIN_LICENSE : LGPL $
* * Commercial License Usage
* * Licensees holding valid commercial Qt licenses may use this file in
* * accordance with the commercial license agreement provided with the
* * Software or , alternatively , in accordance with the terms contained in
* * a written agreement between you and The Qt Company . For licensing terms
* * and conditions see http : //www.qt.io/terms-conditions. For further
* * information use the contact form at http : //www.qt.io/contact-us.
* *
* * GNU Lesser General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU Lesser
* * General Public License version 2.1 or version 3 as published by the Free
* * Software Foundation and appearing in the file LICENSE . LGPLv21 and
* * LICENSE . LGPLv3 included in the packaging of this file . Please review the
* * following information to ensure the GNU Lesser General Public License
* * requirements will be met : https : //www.gnu.org/licenses/lgpl.html and
* * http : //www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
* *
* * As a special exception , The Qt Company gives you certain additional
* * rights . These rights are described in The Qt Company LGPL Exception
* * version 1.1 , included in the file LGPL_EXCEPTION . txt in this package .
* *
* * GNU General Public License Usage
* * Alternatively , this file may be used under the terms of the GNU
* * General Public License version 3.0 as published by the Free Software
* * Foundation and appearing in the file LICENSE . GPL included in the
* * packaging of this file . Please review the following information to
* * ensure the GNU General Public License version 3.0 requirements will be
* * met : http : //www.gnu.org/copyleft/gpl.html.
* *
* * $ QT_END_LICENSE $
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "qhttpnetworkconnection_p.h"
# include "qhttpnetworkconnectionchannel_p.h"
# include "qnoncontiguousbytedevice_p.h"
2016-10-19 05:41:21 +00:00
# include <QtNetwork/qsslkey.h>
# include <QtNetwork/qsslcipher.h>
# include <QtNetwork/qsslconfiguration.h>
2015-12-10 05:06:13 +02:00
# include <qpair.h>
# include <qdebug.h>
# ifndef QT_NO_HTTP
# ifndef QT_NO_BEARERMANAGEMENT
# include "qnetworksession_p.h"
# endif
QT_BEGIN_NAMESPACE
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
QHttpNetworkConnectionChannel : : QHttpNetworkConnectionChannel ( )
: socket ( 0 )
, ssl ( false )
, state ( IdleState )
, reply ( 0 )
, written ( 0 )
, bytesTotal ( 0 )
, resendCurrent ( false )
, lastStatus ( 0 )
, pendingEncrypt ( false )
, reconnectAttempts ( 2 )
, authMethod ( QAuthenticatorPrivate : : None )
, proxyAuthMethod ( QAuthenticatorPrivate : : None )
, authenticationCredentialsSent ( false )
, proxyCredentialsSent ( false )
, ignoreAllSslErrors ( false )
, pipeliningSupported ( PipeliningSupportUnknown )
, connection ( 0 )
{
// Inlining this function in the header leads to compiler error on
// release-armv5, on at least timebox 9.2 and 10.1.
}
void QHttpNetworkConnectionChannel : : init ( )
{
if ( connection - > d_func ( ) - > encrypt )
socket = new QSslSocket ;
else
socket = new QTcpSocket ;
# ifndef QT_NO_BEARERMANAGEMENT
//push session down to socket
if ( networkSession )
socket - > setProperty ( " _q_networksession " , QVariant : : fromValue ( networkSession ) ) ;
# endif
# ifndef QT_NO_NETWORKPROXY
// Set by QNAM anyway, but let's be safe here
socket - > setProxy ( QNetworkProxy : : NoProxy ) ;
# endif
// We want all signals (except the interactive ones) be connected as QueuedConnection
// because else we're falling into cases where we recurse back into the socket code
// and mess up the state. Always going to the event loop (and expecting that when reading/writing)
// is safer.
QObject : : connect ( socket , SIGNAL ( bytesWritten ( qint64 ) ) ,
this , SLOT ( _q_bytesWritten ( qint64 ) ) ,
Qt : : QueuedConnection ) ;
QObject : : connect ( socket , SIGNAL ( connected ( ) ) ,
this , SLOT ( _q_connected ( ) ) ,
Qt : : QueuedConnection ) ;
QObject : : connect ( socket , SIGNAL ( readyRead ( ) ) ,
this , SLOT ( _q_readyRead ( ) ) ,
Qt : : QueuedConnection ) ;
// The disconnected() and error() signals may already come
// while calling connectToHost().
// In case of a cached hostname or an IP this
// will then emit a signal to the user of QNetworkReply
// but cannot be caught because the user did not have a chance yet
// to connect to QNetworkReply's signals.
qRegisterMetaType < QAbstractSocket : : SocketError > ( " QAbstractSocket::SocketError " ) ;
QObject : : connect ( socket , SIGNAL ( disconnected ( ) ) ,
this , SLOT ( _q_disconnected ( ) ) ,
Qt : : QueuedConnection ) ;
QObject : : connect ( socket , SIGNAL ( error ( QAbstractSocket : : SocketError ) ) ,
this , SLOT ( _q_error ( QAbstractSocket : : SocketError ) ) ,
Qt : : QueuedConnection ) ;
# ifndef QT_NO_NETWORKPROXY
QObject : : connect ( socket , SIGNAL ( proxyAuthenticationRequired ( QNetworkProxy , QAuthenticator * ) ) ,
this , SLOT ( _q_proxyAuthenticationRequired ( QNetworkProxy , QAuthenticator * ) ) ,
Qt : : DirectConnection ) ;
# endif
QSslSocket * sslSocket = qobject_cast < QSslSocket * > ( socket ) ;
if ( sslSocket ) {
// won't be a sslSocket if encrypt is false
QObject : : connect ( sslSocket , SIGNAL ( encrypted ( ) ) ,
this , SLOT ( _q_encrypted ( ) ) ,
Qt : : QueuedConnection ) ;
QObject : : connect ( sslSocket , SIGNAL ( sslErrors ( QList < QSslError > ) ) ,
this , SLOT ( _q_sslErrors ( QList < QSslError > ) ) ,
Qt : : DirectConnection ) ;
QObject : : connect ( sslSocket , SIGNAL ( encryptedBytesWritten ( qint64 ) ) ,
this , SLOT ( _q_encryptedBytesWritten ( qint64 ) ) ,
Qt : : QueuedConnection ) ;
}
}
void QHttpNetworkConnectionChannel : : close ( )
{
if ( socket - > state ( ) = = QAbstractSocket : : UnconnectedState )
state = QHttpNetworkConnectionChannel : : IdleState ;
else
state = QHttpNetworkConnectionChannel : : ClosingState ;
if ( socket )
socket - > close ( ) ;
}
bool QHttpNetworkConnectionChannel : : sendRequest ( )
{
if ( ! reply ) {
// heh, how should that happen!
qWarning ( ) < < " QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply " ;
state = QHttpNetworkConnectionChannel : : IdleState ;
return false ;
}
switch ( state ) {
case QHttpNetworkConnectionChannel : : IdleState : { // write the header
if ( ! ensureConnection ( ) ) {
// wait for the connection (and encryption) to be done
// sendRequest will be called again from either
// _q_connected or _q_encrypted
return false ;
}
written = 0 ; // excluding the header
bytesTotal = 0 ;
QHttpNetworkReplyPrivate * replyPrivate = reply - > d_func ( ) ;
replyPrivate - > clear ( ) ;
replyPrivate - > connection = connection ;
replyPrivate - > connectionChannel = this ;
replyPrivate - > autoDecompress = request . d - > autoDecompress ;
replyPrivate - > pipeliningUsed = false ;
// if the url contains authentication parameters, use the new ones
// both channels will use the new authentication parameters
if ( ! request . url ( ) . userInfo ( ) . isEmpty ( ) & & request . withCredentials ( ) ) {
QUrl url = request . url ( ) ;
QAuthenticator & auth = authenticator ;
if ( url . userName ( ) ! = auth . user ( )
| | ( ! url . password ( ) . isEmpty ( ) & & url . password ( ) ! = auth . password ( ) ) ) {
auth . setUser ( url . userName ( ) ) ;
auth . setPassword ( url . password ( ) ) ;
connection - > d_func ( ) - > copyCredentials ( connection - > d_func ( ) - > indexOf ( socket ) , & auth , false ) ;
}
// clear the userinfo, since we use the same request for resending
// userinfo in url can conflict with the one in the authenticator
url . setUserInfo ( QString ( ) ) ;
request . setUrl ( url ) ;
}
// Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
// and withCredentials has not been set to true.
if ( request . withCredentials ( ) )
connection - > d_func ( ) - > createAuthorization ( socket , request ) ;
# ifndef QT_NO_NETWORKPROXY
QByteArray header = QHttpNetworkRequestPrivate : : header ( request ,
( connection - > d_func ( ) - > networkProxy . type ( ) ! = QNetworkProxy : : NoProxy ) ) ;
# else
QByteArray header = QHttpNetworkRequestPrivate : : header ( request , false ) ;
# endif
socket - > write ( header ) ;
// flushing is dangerous (QSslSocket calls transmit which might read or error)
// socket->flush();
QNonContiguousByteDevice * uploadByteDevice = request . uploadByteDevice ( ) ;
if ( uploadByteDevice ) {
// connect the signals so this function gets called again
QObject : : connect ( uploadByteDevice , SIGNAL ( readyRead ( ) ) , this , SLOT ( _q_uploadDataReadyRead ( ) ) ) ;
bytesTotal = request . contentLength ( ) ;
state = QHttpNetworkConnectionChannel : : WritingState ; // start writing data
sendRequest ( ) ; //recurse
} else {
state = QHttpNetworkConnectionChannel : : WaitingState ; // now wait for response
sendRequest ( ) ; //recurse
}
break ;
}
case QHttpNetworkConnectionChannel : : WritingState :
{
// write the data
QNonContiguousByteDevice * uploadByteDevice = request . uploadByteDevice ( ) ;
if ( ! uploadByteDevice | | bytesTotal = = written ) {
if ( uploadByteDevice )
emit reply - > dataSendProgress ( written , bytesTotal ) ;
state = QHttpNetworkConnectionChannel : : WaitingState ; // now wait for response
sendRequest ( ) ; // recurse
break ;
}
// only feed the QTcpSocket buffer when there is less than 32 kB in it
const qint64 socketBufferFill = 32 * 1024 ;
const qint64 socketWriteMaxSize = 16 * 1024 ;
QSslSocket * sslSocket = qobject_cast < QSslSocket * > ( socket ) ;
// if it is really an ssl socket, check more than just bytesToWrite()
while ( ( socket - > bytesToWrite ( ) + ( sslSocket ? sslSocket - > encryptedBytesToWrite ( ) : 0 ) )
2016-10-19 05:41:21 +00:00
< = socketBufferFill & & bytesTotal ! = written ) {
2015-12-10 05:06:13 +02:00
// get pointer to upload data
qint64 currentReadSize = 0 ;
qint64 desiredReadSize = qMin ( socketWriteMaxSize , bytesTotal - written ) ;
const char * readPointer = uploadByteDevice - > readPointer ( desiredReadSize , currentReadSize ) ;
if ( currentReadSize = = - 1 ) {
// premature eof happened
connection - > d_func ( ) - > emitReplyError ( socket , reply , QNetworkReply : : UnknownNetworkError ) ;
return false ;
break ;
} else if ( readPointer = = 0 | | currentReadSize = = 0 ) {
// nothing to read currently, break the loop
break ;
} else {
if ( written ! = uploadByteDevice - > pos ( ) ) {
// Sanity check. This was useful in tracking down an upload corruption.
qWarning ( ) < < " QHttpProtocolHandler: Internal error in sendRequest. Expected to write at position " < < written < < " but read device is at " < < uploadByteDevice - > pos ( ) ;
Q_ASSERT ( written = = uploadByteDevice - > pos ( ) ) ;
connection - > d_func ( ) - > emitReplyError ( socket , reply , QNetworkReply : : ProtocolFailure ) ;
return false ;
}
qint64 currentWriteSize = socket - > write ( readPointer , currentReadSize ) ;
if ( currentWriteSize = = - 1 | | currentWriteSize ! = currentReadSize ) {
// socket broke down
connection - > d_func ( ) - > emitReplyError ( socket , reply , QNetworkReply : : UnknownNetworkError ) ;
return false ;
} else {
written + = currentWriteSize ;
uploadByteDevice - > advanceReadPointer ( currentWriteSize ) ;
emit reply - > dataSendProgress ( written , bytesTotal ) ;
if ( written = = bytesTotal ) {
// make sure this function is called once again
state = QHttpNetworkConnectionChannel : : WaitingState ;
sendRequest ( ) ;
break ;
}
}
}
}
break ;
}
case QHttpNetworkConnectionChannel : : WaitingState :
{
QNonContiguousByteDevice * uploadByteDevice = request . uploadByteDevice ( ) ;
if ( uploadByteDevice ) {
QObject : : disconnect ( uploadByteDevice , SIGNAL ( readyRead ( ) ) , this , SLOT ( _q_uploadDataReadyRead ( ) ) ) ;
}
// HTTP pipelining
//connection->d_func()->fillPipeline(socket);
//socket->flush();
// ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
// this is needed if the sends an reply before we have finished sending the request. In that
// case receiveReply had been called before but ignored the server reply
if ( socket - > bytesAvailable ( ) )
QMetaObject : : invokeMethod ( this , " _q_receiveReply " , Qt : : QueuedConnection ) ;
break ;
}
case QHttpNetworkConnectionChannel : : ReadingState :
// ignore _q_bytesWritten in these states
// fall through
default :
break ;
}
return true ;
}
void QHttpNetworkConnectionChannel : : _q_receiveReply ( )
{
Q_ASSERT ( socket ) ;
if ( ! reply ) {
if ( socket - > bytesAvailable ( ) > 0 )
qWarning ( ) < < " QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply, "
< < socket - > bytesAvailable ( ) < < " bytes on socket. " ;
close ( ) ;
return ;
}
// only run when the QHttpNetworkConnection is not currently being destructed, e.g.
// this function is called from _q_disconnected which is called because
// of ~QHttpNetworkConnectionPrivate
if ( ! qobject_cast < QHttpNetworkConnection * > ( connection ) ) {
return ;
}
QAbstractSocket : : SocketState socketState = socket - > state ( ) ;
// connection might be closed to signal the end of data
if ( socketState = = QAbstractSocket : : UnconnectedState ) {
if ( socket - > bytesAvailable ( ) < = 0 ) {
if ( reply - > d_func ( ) - > state = = QHttpNetworkReplyPrivate : : ReadingDataState ) {
// finish this reply. this case happens when the server did not send a content length
reply - > d_func ( ) - > state = QHttpNetworkReplyPrivate : : AllDoneState ;
allDone ( ) ;
return ;
} else {
handleUnexpectedEOF ( ) ;
return ;
}
} else {
// socket not connected but still bytes for reading.. just continue in this function
}
}
// read loop for the response
qint64 bytes = 0 ;
qint64 lastBytes = bytes ;
do {
lastBytes = bytes ;
QHttpNetworkReplyPrivate : : ReplyState state = reply - > d_func ( ) - > state ;
switch ( state ) {
case QHttpNetworkReplyPrivate : : NothingDoneState : {
state = reply - > d_func ( ) - > state = QHttpNetworkReplyPrivate : : ReadingStatusState ;
// fallthrough
}
case QHttpNetworkReplyPrivate : : ReadingStatusState : {
qint64 statusBytes = reply - > d_func ( ) - > readStatus ( socket ) ;
if ( statusBytes = = - 1 ) {
// connection broke while reading status. also handled if later _q_disconnected is called
handleUnexpectedEOF ( ) ;
return ;
}
bytes + = statusBytes ;
lastStatus = reply - > d_func ( ) - > statusCode ;
break ;
}
case QHttpNetworkReplyPrivate : : ReadingHeaderState : {
QHttpNetworkReplyPrivate * replyPrivate = reply - > d_func ( ) ;
qint64 headerBytes = replyPrivate - > readHeader ( socket ) ;
if ( headerBytes = = - 1 ) {
// connection broke while reading headers. also handled if later _q_disconnected is called
handleUnexpectedEOF ( ) ;
return ;
}
bytes + = headerBytes ;
// If headers were parsed successfully now it is the ReadingDataState
if ( replyPrivate - > state = = QHttpNetworkReplyPrivate : : ReadingDataState ) {
if ( replyPrivate - > isGzipped ( ) & & replyPrivate - > autoDecompress ) {
// remove the Content-Length from header
replyPrivate - > removeAutoDecompressHeader ( ) ;
} else {
replyPrivate - > autoDecompress = false ;
}
if ( replyPrivate - > statusCode = = 100 ) {
replyPrivate - > clearHttpLayerInformation ( ) ;
replyPrivate - > state = QHttpNetworkReplyPrivate : : ReadingStatusState ;
break ; // ignore
}
if ( replyPrivate - > shouldEmitSignals ( ) )
emit reply - > headerChanged ( ) ;
// After headerChanged had been emitted
// we can suddenly have a replyPrivate->userProvidedDownloadBuffer
// this is handled in the ReadingDataState however
if ( ! replyPrivate - > expectContent ( ) ) {
replyPrivate - > state = QHttpNetworkReplyPrivate : : AllDoneState ;
allDone ( ) ;
break ;
}
}
break ;
}
case QHttpNetworkReplyPrivate : : ReadingDataState : {
QHttpNetworkReplyPrivate * replyPrivate = reply - > d_func ( ) ;
if ( socket - > state ( ) = = QAbstractSocket : : ConnectedState & &
replyPrivate - > downstreamLimited & & ! replyPrivate - > responseData . isEmpty ( ) & & replyPrivate - > shouldEmitSignals ( ) ) {
// (only do the following when still connected, not when we have already been disconnected and there is still data)
// We already have some HTTP body data. We don't read more from the socket until
// this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
// we could not limit our read buffer usage.
// We only do this when shouldEmitSignals==true because our HTTP parsing
// always needs to parse the 401/407 replies. Therefore they don't really obey
// to the read buffer maximum size, but we don't care since they should be small.
return ;
}
if ( replyPrivate - > userProvidedDownloadBuffer ) {
// the user provided a direct buffer where we should put all our data in.
// this only works when we can tell the user the content length and he/she can allocate
// the buffer in that size.
// note that this call will read only from the still buffered data
qint64 haveRead = replyPrivate - > readBodyVeryFast ( socket , replyPrivate - > userProvidedDownloadBuffer + replyPrivate - > totalProgress ) ;
bytes + = haveRead ;
replyPrivate - > totalProgress + = haveRead ;
// the user will get notified of it via progress signal
if ( haveRead > 0 )
emit reply - > dataReadProgress ( replyPrivate - > totalProgress , replyPrivate - > bodyLength ) ;
} else if ( ! replyPrivate - > isChunked ( ) & & ! replyPrivate - > autoDecompress
& & replyPrivate - > bodyLength > 0 ) {
// bulk files like images should fulfill these properties and
// we can therefore save on memory copying
qint64 haveRead = replyPrivate - > readBodyFast ( socket , & replyPrivate - > responseData ) ;
bytes + = haveRead ;
replyPrivate - > totalProgress + = haveRead ;
if ( replyPrivate - > shouldEmitSignals ( ) ) {
emit reply - > readyRead ( ) ;
emit reply - > dataReadProgress ( replyPrivate - > totalProgress , replyPrivate - > bodyLength ) ;
}
}
else
{
// use the traditional slower reading (for compressed encoding, chunked encoding,
// no content-length etc)
QByteDataBuffer byteDatas ;
qint64 haveRead = replyPrivate - > readBody ( socket , & byteDatas ) ;
if ( haveRead ) {
bytes + = haveRead ;
if ( replyPrivate - > autoDecompress )
replyPrivate - > appendCompressedReplyData ( byteDatas ) ;
else
replyPrivate - > appendUncompressedReplyData ( byteDatas ) ;
if ( ! replyPrivate - > autoDecompress ) {
replyPrivate - > totalProgress + = bytes ;
if ( replyPrivate - > shouldEmitSignals ( ) ) {
// important: At the point of this readyRead(), the byteDatas list must be empty,
// else implicit sharing will trigger memcpy when the user is reading data!
emit reply - > readyRead ( ) ;
emit reply - > dataReadProgress ( replyPrivate - > totalProgress , replyPrivate - > bodyLength ) ;
}
}
# ifndef QT_NO_COMPRESS
else if ( ! expand ( false ) ) { // expand a chunk if possible
// If expand() failed we can just return, it had already called connection->emitReplyError()
return ;
}
# endif
}
}
// still in ReadingDataState? This function will be called again by the socket's readyRead
if ( replyPrivate - > state = = QHttpNetworkReplyPrivate : : ReadingDataState )
break ;
// everything done, fall through
}
case QHttpNetworkReplyPrivate : : AllDoneState :
allDone ( ) ;
break ;
default :
break ;
}
} while ( bytes ! = lastBytes & & reply ) ;
}
// called when unexpectedly reading a -1 or when data is expected but socket is closed
void QHttpNetworkConnectionChannel : : handleUnexpectedEOF ( )
{
Q_ASSERT ( reply ) ;
if ( reconnectAttempts < = 0 ) {
// too many errors reading/receiving/parsing the status, close the socket and emit error
requeueCurrentlyPipelinedRequests ( ) ;
close ( ) ;
reply - > d_func ( ) - > errorString = connection - > d_func ( ) - > errorDetail ( QNetworkReply : : RemoteHostClosedError , socket ) ;
emit reply - > finishedWithError ( QNetworkReply : : RemoteHostClosedError , reply - > d_func ( ) - > errorString ) ;
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
} else {
reconnectAttempts - - ;
reply - > d_func ( ) - > clear ( ) ;
reply - > d_func ( ) - > connection = connection ;
reply - > d_func ( ) - > connectionChannel = this ;
closeAndResendCurrentRequest ( ) ;
}
}
bool QHttpNetworkConnectionChannel : : ensureConnection ( )
{
QAbstractSocket : : SocketState socketState = socket - > state ( ) ;
// resend this request after we receive the disconnected signal
if ( socketState = = QAbstractSocket : : ClosingState ) {
if ( reply )
resendCurrent = true ;
return false ;
}
// already trying to connect?
if ( socketState = = QAbstractSocket : : HostLookupState | |
socketState = = QAbstractSocket : : ConnectingState ) {
return false ;
}
// make sure that this socket is in a connected state, if not initiate
// connection to the host.
if ( socketState ! = QAbstractSocket : : ConnectedState ) {
// connect to the host if not already connected.
state = QHttpNetworkConnectionChannel : : ConnectingState ;
pendingEncrypt = ssl ;
// reset state
pipeliningSupported = PipeliningSupportUnknown ;
authenticationCredentialsSent = false ;
proxyCredentialsSent = false ;
authenticator . detach ( ) ;
QAuthenticatorPrivate * priv = QAuthenticatorPrivate : : getPrivate ( authenticator ) ;
priv - > hasFailed = false ;
proxyAuthenticator . detach ( ) ;
priv = QAuthenticatorPrivate : : getPrivate ( proxyAuthenticator ) ;
priv - > hasFailed = false ;
// This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
// is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
// last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
// check the "phase" for generating the Authorization header. NTLM authentication is a two stage
// process & needs the "phase". To make sure the QAuthenticator uses the current username/password
// the phase is reset to Start.
priv = QAuthenticatorPrivate : : getPrivate ( authenticator ) ;
if ( priv & & priv - > phase = = QAuthenticatorPrivate : : Done )
priv - > phase = QAuthenticatorPrivate : : Start ;
priv = QAuthenticatorPrivate : : getPrivate ( proxyAuthenticator ) ;
if ( priv & & priv - > phase = = QAuthenticatorPrivate : : Done )
priv - > phase = QAuthenticatorPrivate : : Start ;
QString connectHost = connection - > d_func ( ) - > hostName ;
qint16 connectPort = connection - > d_func ( ) - > port ;
# ifndef QT_NO_NETWORKPROXY
// HTTPS always use transparent proxy.
if ( connection - > d_func ( ) - > networkProxy . type ( ) ! = QNetworkProxy : : NoProxy & & ! ssl ) {
connectHost = connection - > d_func ( ) - > networkProxy . hostName ( ) ;
connectPort = connection - > d_func ( ) - > networkProxy . port ( ) ;
}
if ( socket - > proxy ( ) . type ( ) = = QNetworkProxy : : HttpProxy ) {
// Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
QByteArray value ;
// ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
if ( request . url ( ) . isEmpty ( ) )
value = connection - > d_func ( ) - > predictNextRequest ( ) . headerField ( " user-agent " ) ;
else
value = request . headerField ( " user-agent " ) ;
if ( ! value . isEmpty ( ) )
socket - > setProperty ( " _q_user-agent " , value ) ;
}
# endif
if ( ssl ) {
QSslSocket * sslSocket = qobject_cast < QSslSocket * > ( socket ) ;
sslSocket - > connectToHostEncrypted ( connectHost , connectPort ) ;
if ( ignoreAllSslErrors )
sslSocket - > ignoreSslErrors ( ) ;
sslSocket - > ignoreSslErrors ( ignoreSslErrorsList ) ;
// limit the socket read buffer size. we will read everything into
// the QHttpNetworkReply anyway, so let's grow only that and not
// here and there.
socket - > setReadBufferSize ( 64 * 1024 ) ;
} else {
// In case of no proxy we can use the Unbuffered QTcpSocket
# ifndef QT_NO_NETWORKPROXY
if ( connection - > d_func ( ) - > networkProxy . type ( ) = = QNetworkProxy : : NoProxy
& & connection - > cacheProxy ( ) . type ( ) = = QNetworkProxy : : NoProxy
& & connection - > transparentProxy ( ) . type ( ) = = QNetworkProxy : : NoProxy ) {
# endif
socket - > connectToHost ( connectHost , connectPort , QIODevice : : ReadWrite | QIODevice : : Unbuffered ) ;
// For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
socket - > setReadBufferSize ( 1 * 1024 ) ;
# ifndef QT_NO_NETWORKPROXY
} else {
socket - > connectToHost ( connectHost , connectPort ) ;
// limit the socket read buffer size. we will read everything into
// the QHttpNetworkReply anyway, so let's grow only that and not
// here and there.
socket - > setReadBufferSize ( 64 * 1024 ) ;
}
# endif
}
return false ;
}
// This code path for ConnectedState
if ( pendingEncrypt ) {
// Let's only be really connected when we have received the encrypted() signal. Else the state machine seems to mess up
// and corrupt the things sent to the server.
return false ;
}
return true ;
}
# ifndef QT_NO_COMPRESS
bool QHttpNetworkConnectionChannel : : expand ( bool dataComplete )
{
Q_ASSERT ( socket ) ;
Q_ASSERT ( reply ) ;
qint64 total = reply - > d_func ( ) - > compressedData . size ( ) ;
if ( total > = CHUNK | | dataComplete ) {
// uncompress the data
QByteArray content , inflated ;
content = reply - > d_func ( ) - > compressedData ;
reply - > d_func ( ) - > compressedData . clear ( ) ;
int ret = Z_OK ;
if ( content . size ( ) )
ret = reply - > d_func ( ) - > gunzipBodyPartially ( content , inflated ) ;
if ( ret > = Z_OK ) {
if ( dataComplete & & ret = = Z_OK & & ! reply - > d_func ( ) - > streamEnd ) {
reply - > d_func ( ) - > gunzipBodyPartiallyEnd ( ) ;
reply - > d_func ( ) - > streamEnd = true ;
}
if ( inflated . size ( ) ) {
reply - > d_func ( ) - > totalProgress + = inflated . size ( ) ;
reply - > d_func ( ) - > appendUncompressedReplyData ( inflated ) ;
if ( reply - > d_func ( ) - > shouldEmitSignals ( ) ) {
// important: At the point of this readyRead(), inflated must be cleared,
// else implicit sharing will trigger memcpy when the user is reading data!
emit reply - > readyRead ( ) ;
emit reply - > dataReadProgress ( reply - > d_func ( ) - > totalProgress , 0 ) ;
}
}
} else {
connection - > d_func ( ) - > emitReplyError ( socket , reply , QNetworkReply : : ProtocolFailure ) ;
return false ;
}
}
return true ;
}
# endif
void QHttpNetworkConnectionChannel : : allDone ( )
{
Q_ASSERT ( reply ) ;
# ifndef QT_NO_COMPRESS
// expand the whole data.
if ( reply - > d_func ( ) - > expectContent ( ) & & reply - > d_func ( ) - > autoDecompress & & ! reply - > d_func ( ) - > streamEnd ) {
bool expandResult = expand ( true ) ;
// If expand() failed we can just return, it had already called connection->emitReplyError()
if ( ! expandResult )
return ;
}
# endif
if ( ! reply ) {
qWarning ( ) < < " QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt-project.org/ " ;
return ;
}
// while handling 401 & 407, we might reset the status code, so save this.
bool emitFinished = reply - > d_func ( ) - > shouldEmitSignals ( ) ;
bool connectionCloseEnabled = reply - > d_func ( ) - > isConnectionCloseEnabled ( ) ;
detectPipeliningSupport ( ) ;
handleStatus ( ) ;
// handleStatus() might have removed the reply because it already called connection->emitReplyError()
// queue the finished signal, this is required since we might send new requests from
// slot connected to it. The socket will not fire readyRead signal, if we are already
// in the slot connected to readyRead
if ( reply & & emitFinished )
QMetaObject : : invokeMethod ( reply , " finished " , Qt : : QueuedConnection ) ;
// reset the reconnection attempts after we receive a complete reply.
// in case of failures, each channel will attempt two reconnects before emitting error.
reconnectAttempts = 2 ;
// now the channel can be seen as free/idle again, all signal emissions for the reply have been done
if ( state ! = QHttpNetworkConnectionChannel : : ClosingState )
state = QHttpNetworkConnectionChannel : : IdleState ;
// if it does not need to be sent again we can set it to 0
// the previous code did not do that and we had problems with accidental re-sending of a
// finished request.
// Note that this may trigger a segfault at some other point. But then we can fix the underlying
// problem.
if ( ! resendCurrent ) {
request = QHttpNetworkRequest ( ) ;
reply = 0 ;
}
// move next from pipeline to current request
if ( ! alreadyPipelinedRequests . isEmpty ( ) ) {
if ( resendCurrent | | connectionCloseEnabled | | socket - > state ( ) ! = QAbstractSocket : : ConnectedState ) {
// move the pipelined ones back to the main queue
requeueCurrentlyPipelinedRequests ( ) ;
close ( ) ;
} else {
// there were requests pipelined in and we can continue
HttpMessagePair messagePair = alreadyPipelinedRequests . takeFirst ( ) ;
request = messagePair . first ;
reply = messagePair . second ;
state = QHttpNetworkConnectionChannel : : ReadingState ;
resendCurrent = false ;
written = 0 ; // message body, excluding the header, irrelevant here
bytesTotal = 0 ; // message body total, excluding the header, irrelevant here
// pipeline even more
connection - > d_func ( ) - > fillPipeline ( socket ) ;
// continue reading
//_q_receiveReply();
// this was wrong, allDone gets called from that function anyway.
}
} else if ( alreadyPipelinedRequests . isEmpty ( ) & & socket - > bytesAvailable ( ) > 0 ) {
// this is weird. we had nothing pipelined but still bytes available. better close it.
close ( ) ;
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
} else if ( alreadyPipelinedRequests . isEmpty ( ) ) {
if ( connectionCloseEnabled )
if ( socket - > state ( ) ! = QAbstractSocket : : UnconnectedState )
close ( ) ;
if ( qobject_cast < QHttpNetworkConnection * > ( connection ) )
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
}
}
void QHttpNetworkConnectionChannel : : detectPipeliningSupport ( )
{
Q_ASSERT ( reply ) ;
// detect HTTP Pipelining support
QByteArray serverHeaderField ;
if (
// check for HTTP/1.1
( reply - > d_func ( ) - > majorVersion = = 1 & & reply - > d_func ( ) - > minorVersion = = 1 )
// check for not having connection close
& & ( ! reply - > d_func ( ) - > isConnectionCloseEnabled ( ) )
// check if it is still connected
& & ( socket - > state ( ) = = QAbstractSocket : : ConnectedState )
// check for broken servers in server reply header
// this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
& & ( serverHeaderField = reply - > headerField ( " Server " ) , ! serverHeaderField . contains ( " Microsoft-IIS/4. " ) )
& & ( ! serverHeaderField . contains ( " Microsoft-IIS/5. " ) )
& & ( ! serverHeaderField . contains ( " Netscape-Enterprise/3. " ) )
// this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
& & ( ! serverHeaderField . contains ( " WebLogic " ) )
& & ( ! serverHeaderField . startsWith ( " Rocket " ) ) // a Python Web Server, see Web2py.com
) {
pipeliningSupported = QHttpNetworkConnectionChannel : : PipeliningProbablySupported ;
} else {
pipeliningSupported = QHttpNetworkConnectionChannel : : PipeliningSupportUnknown ;
}
}
// called when the connection broke and we need to queue some pipelined requests again
void QHttpNetworkConnectionChannel : : requeueCurrentlyPipelinedRequests ( )
{
for ( int i = 0 ; i < alreadyPipelinedRequests . length ( ) ; i + + )
connection - > d_func ( ) - > requeueRequest ( alreadyPipelinedRequests . at ( i ) ) ;
alreadyPipelinedRequests . clear ( ) ;
// only run when the QHttpNetworkConnection is not currently being destructed, e.g.
// this function is called from _q_disconnected which is called because
// of ~QHttpNetworkConnectionPrivate
if ( qobject_cast < QHttpNetworkConnection * > ( connection ) )
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
}
void QHttpNetworkConnectionChannel : : handleStatus ( )
{
Q_ASSERT ( socket ) ;
Q_ASSERT ( reply ) ;
int statusCode = reply - > statusCode ( ) ;
bool resend = false ;
switch ( statusCode ) {
case 401 : // auth required
case 407 : // proxy auth required
if ( connection - > d_func ( ) - > handleAuthenticateChallenge ( socket , reply , ( statusCode = = 407 ) , resend ) ) {
if ( resend ) {
if ( ! resetUploadData ( ) )
break ;
reply - > d_func ( ) - > eraseData ( ) ;
if ( alreadyPipelinedRequests . isEmpty ( ) ) {
// this does a re-send without closing the connection
resendCurrent = true ;
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
} else {
// we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
closeAndResendCurrentRequest ( ) ;
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
}
} else {
//authentication cancelled, close the channel.
close ( ) ;
}
} else {
emit reply - > headerChanged ( ) ;
emit reply - > readyRead ( ) ;
QNetworkReply : : NetworkError errorCode = ( statusCode = = 407 )
? QNetworkReply : : ProxyAuthenticationRequiredError
: QNetworkReply : : AuthenticationRequiredError ;
reply - > d_func ( ) - > errorString = connection - > d_func ( ) - > errorDetail ( errorCode , socket ) ;
emit reply - > finishedWithError ( errorCode , reply - > d_func ( ) - > errorString ) ;
}
break ;
default :
if ( qobject_cast < QHttpNetworkConnection * > ( connection ) )
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
}
}
bool QHttpNetworkConnectionChannel : : resetUploadData ( )
{
if ( ! reply ) {
//this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
return false ;
}
QNonContiguousByteDevice * uploadByteDevice = request . uploadByteDevice ( ) ;
if ( ! uploadByteDevice )
return true ;
if ( uploadByteDevice - > reset ( ) ) {
written = 0 ;
return true ;
} else {
connection - > d_func ( ) - > emitReplyError ( socket , reply , QNetworkReply : : ContentReSendError ) ;
return false ;
}
}
void QHttpNetworkConnectionChannel : : pipelineInto ( HttpMessagePair & pair )
{
// this is only called for simple GET
QHttpNetworkRequest & request = pair . first ;
QHttpNetworkReply * reply = pair . second ;
reply - > d_func ( ) - > clear ( ) ;
reply - > d_func ( ) - > connection = connection ;
reply - > d_func ( ) - > connectionChannel = this ;
reply - > d_func ( ) - > autoDecompress = request . d - > autoDecompress ;
reply - > d_func ( ) - > pipeliningUsed = true ;
# ifndef QT_NO_NETWORKPROXY
pipeline . append ( QHttpNetworkRequestPrivate : : header ( request ,
( connection - > d_func ( ) - > networkProxy . type ( ) ! = QNetworkProxy : : NoProxy ) ) ) ;
# else
pipeline . append ( QHttpNetworkRequestPrivate : : header ( request , false ) ) ;
# endif
alreadyPipelinedRequests . append ( pair ) ;
// pipelineFlush() needs to be called at some point afterwards
}
void QHttpNetworkConnectionChannel : : pipelineFlush ( )
{
if ( pipeline . isEmpty ( ) )
return ;
// The goal of this is so that we have everything in one TCP packet.
// For the Unbuffered QTcpSocket this is manually needed, the buffered
// QTcpSocket does it automatically.
// Also, sometimes the OS does it for us (Nagle's algorithm) but that
// happens only sometimes.
socket - > write ( pipeline ) ;
pipeline . clear ( ) ;
}
void QHttpNetworkConnectionChannel : : closeAndResendCurrentRequest ( )
{
requeueCurrentlyPipelinedRequests ( ) ;
close ( ) ;
if ( reply )
resendCurrent = true ;
if ( qobject_cast < QHttpNetworkConnection * > ( connection ) )
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
}
bool QHttpNetworkConnectionChannel : : isSocketBusy ( ) const
{
return ( state & QHttpNetworkConnectionChannel : : BusyState ) ;
}
bool QHttpNetworkConnectionChannel : : isSocketWriting ( ) const
{
return ( state & QHttpNetworkConnectionChannel : : WritingState ) ;
}
bool QHttpNetworkConnectionChannel : : isSocketWaiting ( ) const
{
return ( state & QHttpNetworkConnectionChannel : : WaitingState ) ;
}
bool QHttpNetworkConnectionChannel : : isSocketReading ( ) const
{
return ( state & QHttpNetworkConnectionChannel : : ReadingState ) ;
}
//private slots
void QHttpNetworkConnectionChannel : : _q_readyRead ( )
{
if ( socket - > state ( ) = = QAbstractSocket : : ConnectedState & & socket - > bytesAvailable ( ) = = 0 ) {
// We got a readyRead but no bytes are available..
// This happens for the Unbuffered QTcpSocket
// Also check if socket is in ConnectedState since
// this function may also be invoked via the event loop.
char c ;
qint64 ret = socket - > peek ( & c , 1 ) ;
if ( ret < 0 ) {
_q_error ( socket - > error ( ) ) ;
// We still need to handle the reply so it emits its signals etc.
if ( reply )
_q_receiveReply ( ) ;
return ;
}
}
if ( isSocketWaiting ( ) | | isSocketReading ( ) ) {
state = QHttpNetworkConnectionChannel : : ReadingState ;
if ( reply )
_q_receiveReply ( ) ;
}
}
void QHttpNetworkConnectionChannel : : _q_bytesWritten ( qint64 bytes )
{
Q_UNUSED ( bytes ) ;
if ( ssl ) {
// In the SSL case we want to send data from encryptedBytesWritten signal since that one
// is the one going down to the actual network, not only into some SSL buffer.
return ;
}
// bytes have been written to the socket. write even more of them :)
if ( isSocketWriting ( ) )
sendRequest ( ) ;
// otherwise we do nothing
}
void QHttpNetworkConnectionChannel : : _q_disconnected ( )
{
if ( state = = QHttpNetworkConnectionChannel : : ClosingState ) {
state = QHttpNetworkConnectionChannel : : IdleState ;
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
return ;
}
// read the available data before closing
if ( isSocketWaiting ( ) | | isSocketReading ( ) ) {
if ( reply ) {
state = QHttpNetworkConnectionChannel : : ReadingState ;
_q_receiveReply ( ) ;
}
} else if ( state = = QHttpNetworkConnectionChannel : : IdleState & & resendCurrent ) {
// re-sending request because the socket was in ClosingState
QMetaObject : : invokeMethod ( connection , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
}
state = QHttpNetworkConnectionChannel : : IdleState ;
requeueCurrentlyPipelinedRequests ( ) ;
close ( ) ;
}
void QHttpNetworkConnectionChannel : : _q_connected ( )
{
// improve performance since we get the request sent by the kernel ASAP
//socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
// We have this commented out now. It did not have the effect we wanted. If we want to
// do this properly, Qt has to combine multiple HTTP requests into one buffer
// and send this to the kernel in one syscall and then the kernel immediately sends
// it as one TCP packet because of TCP_NODELAY.
// However, this code is currently not in Qt, so we rely on the kernel combining
// the requests into one TCP packet.
// not sure yet if it helps, but it makes sense
socket - > setSocketOption ( QAbstractSocket : : KeepAliveOption , 1 ) ;
pipeliningSupported = QHttpNetworkConnectionChannel : : PipeliningSupportUnknown ;
// ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
//channels[i].reconnectAttempts = 2;
if ( ! pendingEncrypt & & ! ssl ) { // FIXME: Didn't work properly with pendingEncrypt only, we should refactor this into an EncrypingState
state = QHttpNetworkConnectionChannel : : IdleState ;
if ( ! reply )
connection - > d_func ( ) - > dequeueRequest ( socket ) ;
if ( reply )
sendRequest ( ) ;
}
}
void QHttpNetworkConnectionChannel : : _q_error ( QAbstractSocket : : SocketError socketError )
{
if ( ! socket )
return ;
QNetworkReply : : NetworkError errorCode = QNetworkReply : : UnknownNetworkError ;
switch ( socketError ) {
case QAbstractSocket : : HostNotFoundError :
errorCode = QNetworkReply : : HostNotFoundError ;
break ;
case QAbstractSocket : : ConnectionRefusedError :
errorCode = QNetworkReply : : ConnectionRefusedError ;
break ;
case QAbstractSocket : : RemoteHostClosedError :
// try to reconnect/resend before sending an error.
// while "Reading" the _q_disconnected() will handle this.
if ( state ! = QHttpNetworkConnectionChannel : : IdleState & & state ! = QHttpNetworkConnectionChannel : : ReadingState ) {
if ( reconnectAttempts - - > 0 ) {
closeAndResendCurrentRequest ( ) ;
return ;
} else {
errorCode = QNetworkReply : : RemoteHostClosedError ;
}
} else if ( state = = QHttpNetworkConnectionChannel : : ReadingState ) {
if ( ! reply )
break ;
if ( ! reply - > d_func ( ) - > expectContent ( ) ) {
// No content expected, this is a valid way to have the connection closed by the server
return ;
}
if ( reply - > contentLength ( ) = = - 1 & & ! reply - > d_func ( ) - > isChunked ( ) ) {
// There was no content-length header and it's not chunked encoding,
// so this is a valid way to have the connection closed by the server
return ;
}
// ok, we got a disconnect even though we did not expect it
// Try to read everything from the socket before we emit the error.
if ( socket - > bytesAvailable ( ) ) {
// Read everything from the socket into the reply buffer.
// we can ignore the readbuffersize as the data is already
// in memory and we will not recieve more data on the socket.
reply - > setReadBufferSize ( 0 ) ;
_q_receiveReply ( ) ;
if ( ssl ) {
2016-10-19 05:41:21 +00:00
// The QSslSocket can still have encrypted bytes in the plainsocket.
2015-12-10 05:06:13 +02:00
// So we need to check this if the socket is a QSslSocket. When the socket is flushed
// it will force a decrypt of the encrypted data in the plainsocket.
QSslSocket * sslSocket = static_cast < QSslSocket * > ( socket ) ;
qint64 beforeFlush = sslSocket - > encryptedBytesAvailable ( ) ;
while ( sslSocket - > encryptedBytesAvailable ( ) ) {
sslSocket - > flush ( ) ;
_q_receiveReply ( ) ;
qint64 afterFlush = sslSocket - > encryptedBytesAvailable ( ) ;
if ( afterFlush = = beforeFlush )
break ;
beforeFlush = afterFlush ;
}
}
}
errorCode = QNetworkReply : : RemoteHostClosedError ;
} else {
errorCode = QNetworkReply : : RemoteHostClosedError ;
}
break ;
case QAbstractSocket : : SocketTimeoutError :
// try to reconnect/resend before sending an error.
if ( state = = QHttpNetworkConnectionChannel : : WritingState & & ( reconnectAttempts - - > 0 ) ) {
closeAndResendCurrentRequest ( ) ;
return ;
}
errorCode = QNetworkReply : : TimeoutError ;
break ;
case QAbstractSocket : : ProxyAuthenticationRequiredError :
errorCode = QNetworkReply : : ProxyAuthenticationRequiredError ;
break ;
case QAbstractSocket : : SslHandshakeFailedError :
errorCode = QNetworkReply : : SslHandshakeFailedError ;
break ;
default :
// all other errors are treated as NetworkError
errorCode = QNetworkReply : : UnknownNetworkError ;
break ;
}
QPointer < QHttpNetworkConnection > that = connection ;
QString errorString = connection - > d_func ( ) - > errorDetail ( errorCode , socket , socket - > errorString ( ) ) ;
// Need to dequeu the request so that we can emit the error.
if ( ! reply )
connection - > d_func ( ) - > dequeueRequest ( socket ) ;
if ( reply ) {
reply - > d_func ( ) - > errorString = errorString ;
emit reply - > finishedWithError ( errorCode , errorString ) ;
reply = 0 ;
}
// send the next request
QMetaObject : : invokeMethod ( that , " _q_startNextRequest " , Qt : : QueuedConnection ) ;
if ( that ) //signal emission triggered event loop
close ( ) ;
}
# ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel : : _q_proxyAuthenticationRequired ( const QNetworkProxy & proxy , QAuthenticator * auth )
{
// Need to dequeue the request before we can emit the error.
if ( ! reply )
connection - > d_func ( ) - > dequeueRequest ( socket ) ;
if ( reply )
connection - > d_func ( ) - > emitProxyAuthenticationRequired ( this , proxy , auth ) ;
}
# endif
void QHttpNetworkConnectionChannel : : _q_uploadDataReadyRead ( )
{
if ( reply & & state = = QHttpNetworkConnectionChannel : : WritingState ) {
// There might be timing issues, make sure to only send upload data if really in that state
sendRequest ( ) ;
}
}
void QHttpNetworkConnectionChannel : : _q_encrypted ( )
{
if ( ! socket )
return ; // ### error
state = QHttpNetworkConnectionChannel : : IdleState ;
pendingEncrypt = false ;
if ( ! reply )
connection - > d_func ( ) - > dequeueRequest ( socket ) ;
if ( reply )
sendRequest ( ) ;
}
void QHttpNetworkConnectionChannel : : _q_sslErrors ( const QList < QSslError > & errors )
{
if ( ! socket )
return ;
//QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
// Also pause the connection because socket notifiers may fire while an user
// dialog is displaying
connection - > d_func ( ) - > pauseConnection ( ) ;
if ( pendingEncrypt & & ! reply )
connection - > d_func ( ) - > dequeueRequest ( socket ) ;
if ( reply )
emit reply - > sslErrors ( errors ) ;
connection - > d_func ( ) - > resumeConnection ( ) ;
}
void QHttpNetworkConnectionChannel : : _q_encryptedBytesWritten ( qint64 bytes )
{
Q_UNUSED ( bytes ) ;
// bytes have been written to the socket. write even more of them :)
if ( isSocketWriting ( ) )
sendRequest ( ) ;
// otherwise we do nothing
}
void QHttpNetworkConnectionChannel : : setConnection ( QHttpNetworkConnection * c )
{
// Inlining this function in the header leads to compiler error on
// release-armv5, on at least timebox 9.2 and 10.1.
connection = c ;
}
QT_END_NAMESPACE
# include "moc_qhttpnetworkconnectionchannel_p.h"
# endif // QT_NO_HTTP