mirror of
https://bitbucket.org/smil3y/katie.git
synced 2025-02-23 18:32:55 +00:00
drop network access classes [ci reset]
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
parent
5e74cf5e11
commit
59da27ca99
105 changed files with 78 additions and 27890 deletions
|
@ -668,10 +668,6 @@ katie_generate_obsolete(QHBoxLayout QtGui qboxlayout.h)
|
|||
katie_generate_obsolete(QHelpEvent QtGui qevent.h)
|
||||
katie_generate_obsolete(QHideEvent QtGui qevent.h)
|
||||
katie_generate_obsolete(QHoverEvent QtGui qevent.h)
|
||||
katie_generate_obsolete(QHttpHeader QtNetwork qhttp.h)
|
||||
katie_generate_obsolete(QHttpPart QtNetwork qhttpmultipart.h)
|
||||
katie_generate_obsolete(QHttpRequestHeader QtNetwork qhttp.h)
|
||||
katie_generate_obsolete(QHttpResponseHeader QtNetwork qhttp.h)
|
||||
katie_generate_obsolete(QIconEngineFactoryInterface QtGui qiconengineplugin.h)
|
||||
katie_generate_obsolete(QIconEngineFactoryInterfaceV2 QtGui qiconengineplugin.h)
|
||||
katie_generate_obsolete(QIconEnginePluginV2 QtGui qiconengineplugin.h)
|
||||
|
@ -730,7 +726,6 @@ katie_generate_obsolete(QMutableStringListIterator QtCore qstringlist.h)
|
|||
katie_generate_obsolete(QMutableVectorIterator QtCore qvector.h)
|
||||
katie_generate_obsolete(QMutexLocker QtCore qmutex.h)
|
||||
katie_generate_obsolete(QNetworkAddressEntry QtNetwork qnetworkinterface.h)
|
||||
katie_generate_obsolete(QNetworkCacheMetaData QtNetwork qabstractnetworkcache.h)
|
||||
katie_generate_obsolete(QNetworkProxyFactory QtNetwork qnetworkproxy.h)
|
||||
katie_generate_obsolete(QNetworkProxyQuery QtNetwork qnetworkproxy.h)
|
||||
katie_generate_obsolete(QNoDebug QtCore qdebug.h)
|
||||
|
|
|
@ -1120,34 +1120,19 @@ include/katie/QtGui/qwizard.h
|
|||
include/katie/QtGui/qworkspace.h
|
||||
include/katie/QtGui/qx11embed_x11.h
|
||||
include/katie/QtGui/qx11info_x11.h
|
||||
include/katie/QtNetwork/QAbstractNetworkCache
|
||||
include/katie/QtNetwork/QAbstractSocket
|
||||
include/katie/QtNetwork/QAuthenticator
|
||||
include/katie/QtNetwork/QCryptographicHash
|
||||
include/katie/QtNetwork/QFtp
|
||||
include/katie/QtNetwork/QHostAddress
|
||||
include/katie/QtNetwork/QHostInfo
|
||||
include/katie/QtNetwork/QHttp
|
||||
include/katie/QtNetwork/QHttpHeader
|
||||
include/katie/QtNetwork/QHttpMultiPart
|
||||
include/katie/QtNetwork/QHttpPart
|
||||
include/katie/QtNetwork/QHttpRequestHeader
|
||||
include/katie/QtNetwork/QHttpResponseHeader
|
||||
include/katie/QtNetwork/QIPv6Address
|
||||
include/katie/QtNetwork/QLocalServer
|
||||
include/katie/QtNetwork/QLocalSocket
|
||||
include/katie/QtNetwork/QNetworkAccessManager
|
||||
include/katie/QtNetwork/QNetworkAddressEntry
|
||||
include/katie/QtNetwork/QNetworkCacheMetaData
|
||||
include/katie/QtNetwork/QNetworkCookie
|
||||
include/katie/QtNetwork/QNetworkCookieJar
|
||||
include/katie/QtNetwork/QNetworkDiskCache
|
||||
include/katie/QtNetwork/QNetworkInterface
|
||||
include/katie/QtNetwork/QNetworkProxy
|
||||
include/katie/QtNetwork/QNetworkProxyFactory
|
||||
include/katie/QtNetwork/QNetworkProxyQuery
|
||||
include/katie/QtNetwork/QNetworkReply
|
||||
include/katie/QtNetwork/QNetworkRequest
|
||||
include/katie/QtNetwork/QSsl
|
||||
include/katie/QtNetwork/QSslCertificate
|
||||
include/katie/QtNetwork/QSslCipher
|
||||
|
@ -1161,25 +1146,15 @@ include/katie/QtNetwork/QUdpSocket
|
|||
include/katie/QtNetwork/QUrlInfo
|
||||
include/katie/QtNetwork/Q_IPV6ADDR
|
||||
include/katie/QtNetwork/QtNetwork
|
||||
include/katie/QtNetwork/qabstractnetworkcache.h
|
||||
include/katie/QtNetwork/qabstractsocket.h
|
||||
include/katie/QtNetwork/qauthenticator.h
|
||||
include/katie/QtNetwork/qcryptographichash.h
|
||||
include/katie/QtNetwork/qftp.h
|
||||
include/katie/QtNetwork/qhostaddress.h
|
||||
include/katie/QtNetwork/qhostinfo.h
|
||||
include/katie/QtNetwork/qhttp.h
|
||||
include/katie/QtNetwork/qhttpmultipart.h
|
||||
include/katie/QtNetwork/qlocalserver.h
|
||||
include/katie/QtNetwork/qlocalsocket.h
|
||||
include/katie/QtNetwork/qnetworkaccessmanager.h
|
||||
include/katie/QtNetwork/qnetworkcookie.h
|
||||
include/katie/QtNetwork/qnetworkcookiejar.h
|
||||
include/katie/QtNetwork/qnetworkdiskcache.h
|
||||
include/katie/QtNetwork/qnetworkinterface.h
|
||||
include/katie/QtNetwork/qnetworkproxy.h
|
||||
include/katie/QtNetwork/qnetworkreply.h
|
||||
include/katie/QtNetwork/qnetworkrequest.h
|
||||
include/katie/QtNetwork/qssl.h
|
||||
include/katie/QtNetwork/qsslcertificate.h
|
||||
include/katie/QtNetwork/qsslcipher.h
|
||||
|
|
|
@ -1123,34 +1123,19 @@ include/katie/QtGui/qwizard.h
|
|||
include/katie/QtGui/qworkspace.h
|
||||
include/katie/QtGui/qx11embed_x11.h
|
||||
include/katie/QtGui/qx11info_x11.h
|
||||
include/katie/QtNetwork/QAbstractNetworkCache
|
||||
include/katie/QtNetwork/QAbstractSocket
|
||||
include/katie/QtNetwork/QAuthenticator
|
||||
include/katie/QtNetwork/QCryptographicHash
|
||||
include/katie/QtNetwork/QFtp
|
||||
include/katie/QtNetwork/QHostAddress
|
||||
include/katie/QtNetwork/QHostInfo
|
||||
include/katie/QtNetwork/QHttp
|
||||
include/katie/QtNetwork/QHttpHeader
|
||||
include/katie/QtNetwork/QHttpMultiPart
|
||||
include/katie/QtNetwork/QHttpPart
|
||||
include/katie/QtNetwork/QHttpRequestHeader
|
||||
include/katie/QtNetwork/QHttpResponseHeader
|
||||
include/katie/QtNetwork/QIPv6Address
|
||||
include/katie/QtNetwork/QLocalServer
|
||||
include/katie/QtNetwork/QLocalSocket
|
||||
include/katie/QtNetwork/QNetworkAccessManager
|
||||
include/katie/QtNetwork/QNetworkAddressEntry
|
||||
include/katie/QtNetwork/QNetworkCacheMetaData
|
||||
include/katie/QtNetwork/QNetworkCookie
|
||||
include/katie/QtNetwork/QNetworkCookieJar
|
||||
include/katie/QtNetwork/QNetworkDiskCache
|
||||
include/katie/QtNetwork/QNetworkInterface
|
||||
include/katie/QtNetwork/QNetworkProxy
|
||||
include/katie/QtNetwork/QNetworkProxyFactory
|
||||
include/katie/QtNetwork/QNetworkProxyQuery
|
||||
include/katie/QtNetwork/QNetworkReply
|
||||
include/katie/QtNetwork/QNetworkRequest
|
||||
include/katie/QtNetwork/QSsl
|
||||
include/katie/QtNetwork/QSslCertificate
|
||||
include/katie/QtNetwork/QSslCipher
|
||||
|
@ -1164,25 +1149,15 @@ include/katie/QtNetwork/QUdpSocket
|
|||
include/katie/QtNetwork/QUrlInfo
|
||||
include/katie/QtNetwork/Q_IPV6ADDR
|
||||
include/katie/QtNetwork/QtNetwork
|
||||
include/katie/QtNetwork/qabstractnetworkcache.h
|
||||
include/katie/QtNetwork/qabstractsocket.h
|
||||
include/katie/QtNetwork/qauthenticator.h
|
||||
include/katie/QtNetwork/qcryptographichash.h
|
||||
include/katie/QtNetwork/qftp.h
|
||||
include/katie/QtNetwork/qhostaddress.h
|
||||
include/katie/QtNetwork/qhostinfo.h
|
||||
include/katie/QtNetwork/qhttp.h
|
||||
include/katie/QtNetwork/qhttpmultipart.h
|
||||
include/katie/QtNetwork/qlocalserver.h
|
||||
include/katie/QtNetwork/qlocalsocket.h
|
||||
include/katie/QtNetwork/qnetworkaccessmanager.h
|
||||
include/katie/QtNetwork/qnetworkcookie.h
|
||||
include/katie/QtNetwork/qnetworkcookiejar.h
|
||||
include/katie/QtNetwork/qnetworkdiskcache.h
|
||||
include/katie/QtNetwork/qnetworkinterface.h
|
||||
include/katie/QtNetwork/qnetworkproxy.h
|
||||
include/katie/QtNetwork/qnetworkreply.h
|
||||
include/katie/QtNetwork/qnetworkrequest.h
|
||||
include/katie/QtNetwork/qssl.h
|
||||
include/katie/QtNetwork/qsslcertificate.h
|
||||
include/katie/QtNetwork/qsslcipher.h
|
||||
|
|
|
@ -1129,34 +1129,19 @@ include/katie/QtGui/qworkspace.h
|
|||
include/katie/QtGui/qx11embed_x11.h
|
||||
include/katie/QtGui/qx11info_x11.h
|
||||
include/katie/QtNetwork/
|
||||
include/katie/QtNetwork/QAbstractNetworkCache
|
||||
include/katie/QtNetwork/QAbstractSocket
|
||||
include/katie/QtNetwork/QAuthenticator
|
||||
include/katie/QtNetwork/QCryptographicHash
|
||||
include/katie/QtNetwork/QFtp
|
||||
include/katie/QtNetwork/QHostAddress
|
||||
include/katie/QtNetwork/QHostInfo
|
||||
include/katie/QtNetwork/QHttp
|
||||
include/katie/QtNetwork/QHttpHeader
|
||||
include/katie/QtNetwork/QHttpMultiPart
|
||||
include/katie/QtNetwork/QHttpPart
|
||||
include/katie/QtNetwork/QHttpRequestHeader
|
||||
include/katie/QtNetwork/QHttpResponseHeader
|
||||
include/katie/QtNetwork/QIPv6Address
|
||||
include/katie/QtNetwork/QLocalServer
|
||||
include/katie/QtNetwork/QLocalSocket
|
||||
include/katie/QtNetwork/QNetworkAccessManager
|
||||
include/katie/QtNetwork/QNetworkAddressEntry
|
||||
include/katie/QtNetwork/QNetworkCacheMetaData
|
||||
include/katie/QtNetwork/QNetworkCookie
|
||||
include/katie/QtNetwork/QNetworkCookieJar
|
||||
include/katie/QtNetwork/QNetworkDiskCache
|
||||
include/katie/QtNetwork/QNetworkInterface
|
||||
include/katie/QtNetwork/QNetworkProxy
|
||||
include/katie/QtNetwork/QNetworkProxyFactory
|
||||
include/katie/QtNetwork/QNetworkProxyQuery
|
||||
include/katie/QtNetwork/QNetworkReply
|
||||
include/katie/QtNetwork/QNetworkRequest
|
||||
include/katie/QtNetwork/QSsl
|
||||
include/katie/QtNetwork/QSslCertificate
|
||||
include/katie/QtNetwork/QSslCipher
|
||||
|
@ -1170,25 +1155,15 @@ include/katie/QtNetwork/QUdpSocket
|
|||
include/katie/QtNetwork/QUrlInfo
|
||||
include/katie/QtNetwork/Q_IPV6ADDR
|
||||
include/katie/QtNetwork/QtNetwork
|
||||
include/katie/QtNetwork/qabstractnetworkcache.h
|
||||
include/katie/QtNetwork/qabstractsocket.h
|
||||
include/katie/QtNetwork/qauthenticator.h
|
||||
include/katie/QtNetwork/qcryptographichash.h
|
||||
include/katie/QtNetwork/qftp.h
|
||||
include/katie/QtNetwork/qhostaddress.h
|
||||
include/katie/QtNetwork/qhostinfo.h
|
||||
include/katie/QtNetwork/qhttp.h
|
||||
include/katie/QtNetwork/qhttpmultipart.h
|
||||
include/katie/QtNetwork/qlocalserver.h
|
||||
include/katie/QtNetwork/qlocalsocket.h
|
||||
include/katie/QtNetwork/qnetworkaccessmanager.h
|
||||
include/katie/QtNetwork/qnetworkcookie.h
|
||||
include/katie/QtNetwork/qnetworkcookiejar.h
|
||||
include/katie/QtNetwork/qnetworkdiskcache.h
|
||||
include/katie/QtNetwork/qnetworkinterface.h
|
||||
include/katie/QtNetwork/qnetworkproxy.h
|
||||
include/katie/QtNetwork/qnetworkreply.h
|
||||
include/katie/QtNetwork/qnetworkrequest.h
|
||||
include/katie/QtNetwork/qssl.h
|
||||
include/katie/QtNetwork/qsslcertificate.h
|
||||
include/katie/QtNetwork/qsslcipher.h
|
||||
|
|
|
@ -332,13 +332,8 @@ incmap = {
|
|||
'QtEvents': 'qevent.h',
|
||||
},
|
||||
'QtNetwork': {
|
||||
'QHttpHeader': 'qhttp.h',
|
||||
'QHttpPart': 'qhttpmultipart.h',
|
||||
'QHttpRequestHeader': 'qhttp.h',
|
||||
'QHttpResponseHeader': 'qhttp.h',
|
||||
'QIPv6Address': 'qhostaddress.h',
|
||||
'QNetworkAddressEntry': 'qnetworkinterface.h',
|
||||
'QNetworkCacheMetaData': 'qabstractnetworkcache.h',
|
||||
'QNetworkProxyFactory': 'qnetworkproxy.h',
|
||||
'QNetworkProxyQuery': 'qnetworkproxy.h',
|
||||
'Q_IPV6ADDR': 'qhostaddress.h',
|
||||
|
|
|
@ -19,7 +19,6 @@ classlist = [
|
|||
"QAbstractItemModel",
|
||||
"QAbstractItemView",
|
||||
"QAbstractListModel",
|
||||
"QAbstractNetworkCache",
|
||||
"QAbstractPageSetupDialog",
|
||||
"QAbstractPrintDialog",
|
||||
"QAbstractProxyModel",
|
||||
|
@ -225,7 +224,6 @@ classlist = [
|
|||
"QFormLayout",
|
||||
"QFrame",
|
||||
"QFSFileEngine",
|
||||
"QFtp",
|
||||
"QFuture",
|
||||
"QFutureInterface",
|
||||
"QFutureInterfaceBase",
|
||||
|
@ -291,12 +289,6 @@ classlist = [
|
|||
"QHostAddress",
|
||||
"QHostInfo",
|
||||
"QHoverEvent",
|
||||
"QHttp",
|
||||
"QHttpHeader",
|
||||
"QHttpMultiPart",
|
||||
"QHttpPart",
|
||||
"QHttpRequestHeader",
|
||||
"QHttpResponseHeader",
|
||||
"QIcon",
|
||||
"QIconEngine",
|
||||
"QIconEngineFactoryInterface",
|
||||
|
@ -398,18 +390,11 @@ classlist = [
|
|||
"QMutableVectorIterator",
|
||||
"QMutex",
|
||||
"QMutexLocker",
|
||||
"QNetworkAccessManager",
|
||||
"QNetworkAddressEntry",
|
||||
"QNetworkCacheMetaData",
|
||||
"QNetworkCookie",
|
||||
"QNetworkCookieJar",
|
||||
"QNetworkDiskCache",
|
||||
"QNetworkInterface",
|
||||
"QNetworkProxy",
|
||||
"QNetworkProxyFactory",
|
||||
"QNetworkProxyQuery",
|
||||
"QNetworkReply",
|
||||
"QNetworkRequest",
|
||||
"QNoDebug",
|
||||
"QObject",
|
||||
"QObjectCleanupHandler",
|
||||
|
|
|
@ -115,6 +115,9 @@
|
|||
#define QT_NO_SVGGENERATOR
|
||||
#define QT_NO_SVGWIDGET
|
||||
#define QT_NO_GRAPHICSSVGITEM
|
||||
#define QT_NO_FTP
|
||||
#define QT_NO_HTTP
|
||||
#define QT_NO_NETWORKDISKCACHE
|
||||
|
||||
// Not supported, used to bootstrap
|
||||
#cmakedefine QT_NO_QOBJECT
|
||||
|
@ -203,11 +206,9 @@
|
|||
#cmakedefine QT_NO_FRAME
|
||||
#cmakedefine QT_NO_FSCOMPLETER
|
||||
#cmakedefine QT_NO_FSFILEENGINE
|
||||
#cmakedefine QT_NO_FTP
|
||||
#cmakedefine QT_NO_GRAPHICSEFFECT
|
||||
#cmakedefine QT_NO_GRAPHICSVIEW
|
||||
#cmakedefine QT_NO_GROUPBOX
|
||||
#cmakedefine QT_NO_HTTP
|
||||
#cmakedefine QT_NO_ICON
|
||||
#cmakedefine QT_NO_IDENTITYPROXYMODEL
|
||||
#cmakedefine QT_NO_IMAGEFORMAT_PPM
|
||||
|
@ -233,7 +234,6 @@
|
|||
#cmakedefine QT_NO_MENUBAR
|
||||
#cmakedefine QT_NO_MESSAGEBOX
|
||||
#cmakedefine QT_NO_MOVIE
|
||||
#cmakedefine QT_NO_NETWORKDISKCACHE
|
||||
#cmakedefine QT_NO_NETWORKINTERFACE
|
||||
#cmakedefine QT_NO_NETWORKPROXY
|
||||
#cmakedefine QT_NO_PDF
|
||||
|
@ -356,11 +356,6 @@
|
|||
# define QT_NO_MENU
|
||||
#endif
|
||||
|
||||
// QNetworkDiskCache
|
||||
#if !defined(QT_NO_NETWORKDISKCACHE) && defined(QT_NO_TEMPORARYFILE)
|
||||
# define QT_NO_NETWORKDISKCACHE
|
||||
#endif
|
||||
|
||||
// QProgressDialog
|
||||
#if !defined(QT_NO_PROGRESSDIALOG) && defined(QT_NO_PROGRESSBAR)
|
||||
# define QT_NO_PROGRESSDIALOG
|
||||
|
@ -416,11 +411,6 @@
|
|||
# define QT_NO_DBUS
|
||||
#endif
|
||||
|
||||
// File Transfer Protocol
|
||||
#if !defined(QT_NO_FTP) && (defined(QT_NO_URLINFO) || defined(QT_NO_TEXTDATE))
|
||||
# define QT_NO_FTP
|
||||
#endif
|
||||
|
||||
// QScrollArea
|
||||
#if !defined(QT_NO_SCROLLAREA) && defined(QT_NO_SCROLLBAR)
|
||||
# define QT_NO_SCROLLAREA
|
||||
|
@ -451,11 +441,6 @@
|
|||
# define QT_NO_GRAPHICSVIEW
|
||||
#endif
|
||||
|
||||
// Hyper Text Transfer Protocol
|
||||
#if !defined(QT_NO_HTTP) && defined(QT_NO_HOSTINFO)
|
||||
# define QT_NO_HTTP
|
||||
#endif
|
||||
|
||||
// QMdiArea
|
||||
#if !defined(QT_NO_MDIAREA) && defined(QT_NO_SCROLLAREA)
|
||||
# define QT_NO_MDIAREA
|
||||
|
|
|
@ -122,12 +122,9 @@ QByteArray QBufferPrivate::peek(qint64 maxSize)
|
|||
|
||||
QBuffer emits readyRead() when new data has arrived in the
|
||||
buffer. By connecting to this signal, you can use QBuffer to
|
||||
store temporary data before processing it. For example, you can
|
||||
pass the buffer to QFtp when downloading a file from an FTP
|
||||
server. Whenever a new payload of data has been downloaded,
|
||||
readyRead() is emitted, and you can process the data that just
|
||||
arrived. QBuffer also emits bytesWritten() every time new data
|
||||
has been written to the buffer.
|
||||
store temporary data before processing it. QBuffer also
|
||||
emits bytesWritten() every time new data has been written to
|
||||
the buffer.
|
||||
|
||||
\sa QFile, QDataStream, QTextStream, QByteArray
|
||||
*/
|
||||
|
|
|
@ -333,11 +333,7 @@ void QProcessPrivate::Channel::clear()
|
|||
write to the process's standard input by calling write(), and
|
||||
read the standard output by calling read(), readLine(), and
|
||||
getChar(). Because it inherits QIODevice, QProcess can also be
|
||||
used as an input source for QXmlReader, or for generating data to
|
||||
be uploaded using QFtp.
|
||||
|
||||
\note On Windows CE and Symbian, reading and writing to a process
|
||||
is not supported.
|
||||
used as an input source for QXmlReader.
|
||||
|
||||
When the process exits, QProcess reenters the \l NotRunning state
|
||||
(the initial state), and emits finished().
|
||||
|
|
|
@ -204,7 +204,6 @@ QT_BEGIN_NAMESPACE
|
|||
\omitvalue ShowWindowRequest
|
||||
\omitvalue Style
|
||||
\omitvalue ThreadChange
|
||||
\omitvalue NetworkReplyUpdated
|
||||
\omitvalue FutureCallOut
|
||||
*/
|
||||
|
||||
|
|
|
@ -176,17 +176,15 @@ public:
|
|||
CursorChange = 112,
|
||||
ToolTipChange = 113,
|
||||
|
||||
NetworkReplyUpdated = 114, // Internal for QNetworkReply
|
||||
GrabMouse = 114,
|
||||
UngrabMouse = 115,
|
||||
GrabKeyboard = 116,
|
||||
UngrabKeyboard = 117,
|
||||
|
||||
GrabMouse = 115,
|
||||
UngrabMouse = 116,
|
||||
GrabKeyboard = 117,
|
||||
UngrabKeyboard = 118,
|
||||
RequestSoftwareInputPanel = 118,
|
||||
CloseSoftwareInputPanel = 119,
|
||||
|
||||
RequestSoftwareInputPanel = 119,
|
||||
CloseSoftwareInputPanel = 120,
|
||||
|
||||
WinIdChange = 121,
|
||||
WinIdChange = 120,
|
||||
|
||||
User = 1000, // first user event id
|
||||
MaxUser = 65535 // last user event id
|
||||
|
|
|
@ -165,10 +165,7 @@ bool QProgressBarPrivate::repaintRequired() const
|
|||
beginning with reset().
|
||||
|
||||
If minimum and maximum both are set to 0, the bar shows a busy
|
||||
indicator instead of a percentage of steps. This is useful, for
|
||||
example, when using QFtp or QNetworkAccessManager to download
|
||||
items when they are unable to determine the size of the item being
|
||||
downloaded.
|
||||
indicator instead of a percentage of steps.
|
||||
|
||||
\table
|
||||
\row \o \inlineimage macintosh-progressbar.png Screenshot of a Macintosh style progress bar
|
||||
|
|
|
@ -5,23 +5,14 @@ set(EXTRA_NETWORK_LIBS
|
|||
)
|
||||
|
||||
set(NETWORK_PUBLIC_HEADERS
|
||||
QAbstractNetworkCache
|
||||
QAbstractSocket
|
||||
QAuthenticator
|
||||
QFtp
|
||||
QHostAddress
|
||||
QHostInfo
|
||||
QHttp
|
||||
QLocalServer
|
||||
QLocalSocket
|
||||
QNetworkAccessManager
|
||||
QNetworkCookie
|
||||
QNetworkCookieJar
|
||||
QNetworkDiskCache
|
||||
QNetworkInterface
|
||||
QNetworkProxy
|
||||
QNetworkReply
|
||||
QNetworkRequest
|
||||
QSsl
|
||||
QSslCertificate
|
||||
QSslCipher
|
||||
|
@ -33,7 +24,6 @@ set(NETWORK_PUBLIC_HEADERS
|
|||
QTcpSocket
|
||||
QUdpSocket
|
||||
QUrlInfo
|
||||
QHttpMultiPart
|
||||
QCryptographicHash
|
||||
)
|
||||
|
||||
|
@ -46,41 +36,6 @@ include_directories(
|
|||
)
|
||||
|
||||
set(NETWORK_HEADERS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qftp.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttp.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkheader_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkrequest_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkreply_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkconnection_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkconnectionchannel_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessauthenticationmanager_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessmanager.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessmanager_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccesscache_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessbackend_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessdebugpipebackend_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccesshttpbackend_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessfilebackend_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccesscachebackend_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessftpbackend_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkcookie.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkcookie_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkcookiejar.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkcookiejar_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkrequest.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkrequest_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreply.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreply_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreplyimpl_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreplydataimpl_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreplyfileimpl_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qabstractnetworkcache_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qabstractnetworkcache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkdiskcache_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkdiskcache.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpthreaddelegate_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpmultipart.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpmultipart_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qauthenticator.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qauthenticator_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qcryptographichash.h
|
||||
|
@ -93,7 +48,6 @@ set(NETWORK_HEADERS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qnetworkinterface.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qnetworkinterface_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qabstractsocketengine_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qhttpsocketengine_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qsocks5socketengine_p.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qabstractsocket.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qabstractsocket_p.h
|
||||
|
@ -123,33 +77,6 @@ set(NETWORK_HEADERS
|
|||
)
|
||||
|
||||
set(NETWORK_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qftp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttp.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkheader.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkrequest.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkreply.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkconnection.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpnetworkconnectionchannel.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessauthenticationmanager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessmanager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccesscache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessbackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessdebugpipebackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessfilebackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccesscachebackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccessftpbackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkaccesshttpbackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkcookie.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkcookiejar.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkrequest.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreply.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreplyimpl.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreplydataimpl.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkreplyfileimpl.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qabstractnetworkcache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qnetworkdiskcache.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpthreaddelegate.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/access/qhttpmultipart.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qauthenticator.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qcryptographichash.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qhostaddress.cpp
|
||||
|
@ -160,7 +87,6 @@ set(NETWORK_SOURCES
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qhostinfo_unix.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/kernel/qnetworkinterface_unix.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qabstractsocketengine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qhttpsocketengine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qsocks5socketengine.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qabstractsocket.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/socket/qtcpsocket.cpp
|
||||
|
|
|
@ -1,520 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qabstractnetworkcache.h"
|
||||
#include "qabstractnetworkcache_p.h"
|
||||
|
||||
#include "qdatetime.h"
|
||||
#include "qurl.h"
|
||||
|
||||
#include "qdebug.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkCacheMetaDataPrivate : public QSharedData
|
||||
{
|
||||
|
||||
public:
|
||||
QNetworkCacheMetaDataPrivate()
|
||||
: QSharedData()
|
||||
, saveToDisk(true)
|
||||
{}
|
||||
|
||||
bool operator==(const QNetworkCacheMetaDataPrivate &other) const
|
||||
{
|
||||
return
|
||||
url == other.url
|
||||
&& lastModified == other.lastModified
|
||||
&& expirationDate == other.expirationDate
|
||||
&& headers == other.headers
|
||||
&& saveToDisk == other.saveToDisk;
|
||||
}
|
||||
|
||||
QUrl url;
|
||||
QDateTime lastModified;
|
||||
QDateTime expirationDate;
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
QNetworkCacheMetaData::AttributesMap attributes;
|
||||
bool saveToDisk;
|
||||
|
||||
static void save(QDataStream &out, const QNetworkCacheMetaData &metaData);
|
||||
static void load(QDataStream &in, QNetworkCacheMetaData &metaData);
|
||||
};
|
||||
Q_GLOBAL_STATIC(QNetworkCacheMetaDataPrivate, metadata_shared_invalid)
|
||||
|
||||
/*!
|
||||
\class QNetworkCacheMetaData
|
||||
\since 4.5
|
||||
\inmodule QtNetwork
|
||||
|
||||
\brief The QNetworkCacheMetaData class provides cache information.
|
||||
|
||||
QNetworkCacheMetaData provides information about a cache file including
|
||||
the url, when it was last modified, when the cache file was created, headers
|
||||
for file and if the file should be saved onto a disk.
|
||||
|
||||
\sa QAbstractNetworkCache
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QNetworkCacheMetaData::RawHeader
|
||||
|
||||
Synonym for QPair<QByteArray, QByteArray>
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QNetworkCacheMetaData::RawHeaderList
|
||||
|
||||
Synonym for QList<RawHeader>
|
||||
*/
|
||||
|
||||
/*!
|
||||
\typedef QNetworkCacheMetaData::AttributesMap
|
||||
|
||||
Synonym for QHash<QNetworkRequest::Attribute, QVariant>
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs an invalid network cache meta data.
|
||||
|
||||
\sa isValid()
|
||||
*/
|
||||
QNetworkCacheMetaData::QNetworkCacheMetaData()
|
||||
: d(new QNetworkCacheMetaDataPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the network cache meta data.
|
||||
*/
|
||||
QNetworkCacheMetaData::~QNetworkCacheMetaData()
|
||||
{
|
||||
// QSharedDataPointer takes care of freeing d
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a copy of the \a other QNetworkCacheMetaData.
|
||||
*/
|
||||
QNetworkCacheMetaData::QNetworkCacheMetaData(const QNetworkCacheMetaData &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Makes a copy of the \a other QNetworkCacheMetaData and returns a reference to the copy.
|
||||
*/
|
||||
QNetworkCacheMetaData &QNetworkCacheMetaData::operator=(const QNetworkCacheMetaData &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if this meta data is equal to the \a other meta data; otherwise returns false.
|
||||
|
||||
\sa operator!=()
|
||||
*/
|
||||
bool QNetworkCacheMetaData::operator==(const QNetworkCacheMetaData &other) const
|
||||
{
|
||||
if (d == other.d)
|
||||
return true;
|
||||
if (d && other.d)
|
||||
return *d == *other.d;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QNetworkCacheMetaData::operator!=(const QNetworkCacheMetaData &other) const
|
||||
|
||||
Returns true if this meta data is not equal to the \a other meta data; otherwise returns false.
|
||||
|
||||
\sa operator==()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns true if this network cache meta data has attributes that have been set otherwise false.
|
||||
*/
|
||||
bool QNetworkCacheMetaData::isValid() const
|
||||
{
|
||||
return !(*d == *metadata_shared_invalid());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns is this cache should be allowed to be stored on disk.
|
||||
|
||||
Some cache implementations can keep these cache items in memory for performance reasons,
|
||||
but for security reasons they should not be written to disk.
|
||||
|
||||
Specifically with http, documents marked with Pragma: no-cache, or have a Cache-control set to
|
||||
no-store or no-cache or any https document that doesn't have "Cache-control: public" set will
|
||||
set the saveToDisk to false.
|
||||
|
||||
\sa setSaveToDisk()
|
||||
*/
|
||||
bool QNetworkCacheMetaData::saveToDisk() const
|
||||
{
|
||||
return d->saveToDisk;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets whether this network cache meta data and associated content should be
|
||||
allowed to be stored on disk to \a allow.
|
||||
|
||||
\sa saveToDisk()
|
||||
*/
|
||||
void QNetworkCacheMetaData::setSaveToDisk(bool allow)
|
||||
{
|
||||
d->saveToDisk = allow;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the URL this network cache meta data is referring to.
|
||||
|
||||
\sa setUrl()
|
||||
*/
|
||||
QUrl QNetworkCacheMetaData::url() const
|
||||
{
|
||||
return d->url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the URL this network cache meta data to to be \a url.
|
||||
|
||||
The password and fragment are removed from the url.
|
||||
|
||||
\sa url()
|
||||
*/
|
||||
void QNetworkCacheMetaData::setUrl(const QUrl &url)
|
||||
{
|
||||
d->url = url;
|
||||
d->url.setPassword(QString());
|
||||
d->url.setFragment(QString());
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of all raw headers that are set in this meta data.
|
||||
The list is in the same order that the headers were set.
|
||||
|
||||
\sa setRawHeaders()
|
||||
*/
|
||||
QNetworkCacheMetaData::RawHeaderList QNetworkCacheMetaData::rawHeaders() const
|
||||
{
|
||||
return d->headers;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the raw headers to \a list.
|
||||
|
||||
\sa rawHeaders()
|
||||
*/
|
||||
void QNetworkCacheMetaData::setRawHeaders(const RawHeaderList &list)
|
||||
{
|
||||
d->headers = list;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date and time when the meta data was last modified.
|
||||
*/
|
||||
QDateTime QNetworkCacheMetaData::lastModified() const
|
||||
{
|
||||
return d->lastModified;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the date and time when the meta data was last modified to \a dateTime.
|
||||
*/
|
||||
void QNetworkCacheMetaData::setLastModified(const QDateTime &dateTime)
|
||||
{
|
||||
d->lastModified = dateTime;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date and time when the meta data expires.
|
||||
*/
|
||||
QDateTime QNetworkCacheMetaData::expirationDate() const
|
||||
{
|
||||
return d->expirationDate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the date and time when the meta data expires to \a dateTime.
|
||||
*/
|
||||
void QNetworkCacheMetaData::setExpirationDate(const QDateTime &dateTime)
|
||||
{
|
||||
d->expirationDate = dateTime;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Returns all the attributes stored with this cache item.
|
||||
|
||||
\sa setAttributes(), QNetworkRequest::Attribute
|
||||
*/
|
||||
QNetworkCacheMetaData::AttributesMap QNetworkCacheMetaData::attributes() const
|
||||
{
|
||||
return d->attributes;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Sets all attributes of this cache item to be the map \a attributes.
|
||||
|
||||
\sa attributes(), QNetworkRequest::setAttribute()
|
||||
*/
|
||||
void QNetworkCacheMetaData::setAttributes(const AttributesMap &attributes)
|
||||
{
|
||||
d->attributes = attributes;
|
||||
}
|
||||
|
||||
/*!
|
||||
\relates QNetworkCacheMetaData
|
||||
\since 4.5
|
||||
|
||||
Writes \a metaData to the \a out stream.
|
||||
|
||||
\sa {Serializing Qt Data Types}
|
||||
*/
|
||||
QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData &metaData)
|
||||
{
|
||||
QNetworkCacheMetaDataPrivate::save(out, metaData);
|
||||
return out;
|
||||
}
|
||||
|
||||
static inline QDataStream &operator<<(QDataStream &out, const QNetworkCacheMetaData::AttributesMap &hash)
|
||||
{
|
||||
out << quint32(hash.size());
|
||||
QNetworkCacheMetaData::AttributesMap::ConstIterator it = hash.end();
|
||||
QNetworkCacheMetaData::AttributesMap::ConstIterator begin = hash.begin();
|
||||
while (it != begin) {
|
||||
--it;
|
||||
out << int(it.key()) << it.value();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void QNetworkCacheMetaDataPrivate::save(QDataStream &out, const QNetworkCacheMetaData &metaData)
|
||||
{
|
||||
// note: if you change the contents of the meta data here
|
||||
// remember to bump the cache version in qnetworkdiskcache.cpp CurrentCacheVersion
|
||||
out << metaData.url();
|
||||
out << metaData.expirationDate();
|
||||
out << metaData.lastModified();
|
||||
out << metaData.saveToDisk();
|
||||
out << metaData.attributes();
|
||||
out << metaData.rawHeaders();
|
||||
}
|
||||
|
||||
/*!
|
||||
\relates QNetworkCacheMetaData
|
||||
\since 4.5
|
||||
|
||||
Reads a QNetworkCacheMetaData from the stream \a in into \a metaData.
|
||||
|
||||
\sa {Serializing Qt Data Types}
|
||||
*/
|
||||
QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData &metaData)
|
||||
{
|
||||
QNetworkCacheMetaDataPrivate::load(in, metaData);
|
||||
return in;
|
||||
}
|
||||
|
||||
static inline QDataStream &operator>>(QDataStream &in, QNetworkCacheMetaData::AttributesMap &hash)
|
||||
{
|
||||
hash.clear();
|
||||
QDataStream::DataStatus oldStatus = in.status();
|
||||
in.resetStatus();
|
||||
hash.clear();
|
||||
|
||||
quint32 n;
|
||||
in >> n;
|
||||
|
||||
for (quint32 i = 0; i < n; ++i) {
|
||||
if (in.status() != QDataStream::Ok)
|
||||
break;
|
||||
|
||||
int k;
|
||||
QVariant t;
|
||||
in >> k >> t;
|
||||
hash.insertMulti(QNetworkRequest::Attribute(k), t);
|
||||
}
|
||||
|
||||
if (in.status() != QDataStream::Ok)
|
||||
hash.clear();
|
||||
if (oldStatus != QDataStream::Ok)
|
||||
in.setStatus(oldStatus);
|
||||
return in;
|
||||
}
|
||||
|
||||
void QNetworkCacheMetaDataPrivate::load(QDataStream &in, QNetworkCacheMetaData &metaData)
|
||||
{
|
||||
in >> metaData.d->url;
|
||||
in >> metaData.d->expirationDate;
|
||||
in >> metaData.d->lastModified;
|
||||
in >> metaData.d->saveToDisk;
|
||||
in >> metaData.d->attributes;
|
||||
in >> metaData.d->headers;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QAbstractNetworkCache
|
||||
\since 4.5
|
||||
\inmodule QtNetwork
|
||||
|
||||
\brief The QAbstractNetworkCache class provides the interface for cache implementations.
|
||||
|
||||
QAbstractNetworkCache is the base class for every standard cache that is used be
|
||||
QNetworkAccessManager. QAbstractNetworkCache is an abstract class and cannot be
|
||||
instantiated.
|
||||
|
||||
\sa QNetworkDiskCache
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs an abstract network cache with the given \a parent.
|
||||
*/
|
||||
QAbstractNetworkCache::QAbstractNetworkCache(QObject *parent)
|
||||
: QObject(*new QAbstractNetworkCachePrivate, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QAbstractNetworkCache::QAbstractNetworkCache(QAbstractNetworkCachePrivate &dd, QObject *parent)
|
||||
: QObject(dd, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the cache.
|
||||
|
||||
Any operations that have not been inserted are discarded.
|
||||
|
||||
\sa insert()
|
||||
*/
|
||||
QAbstractNetworkCache::~QAbstractNetworkCache()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QNetworkCacheMetaData QAbstractNetworkCache::metaData(const QUrl &url) = 0
|
||||
Returns the meta data for the url \a url.
|
||||
|
||||
If the url is valid and the cache contains the data for url,
|
||||
a valid QNetworkCacheMetaData is returned.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa updateMetaData(), data()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QAbstractNetworkCache::updateMetaData(const QNetworkCacheMetaData &metaData) = 0
|
||||
Updates the cache meta date for the metaData's url to \a metaData
|
||||
|
||||
If the cache does not contains a cache item for the url then no action is taken.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa metaData(), prepare()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QIODevice *QAbstractNetworkCache::data(const QUrl &url) = 0
|
||||
Returns the data associated with \a url.
|
||||
|
||||
It is up to the application that requests the data to delete
|
||||
the QIODevice when done with it.
|
||||
|
||||
If there is no cache for \a url, the url is invalid, or if there
|
||||
is an internal cache error 0 is returned.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa metaData(), prepare()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool QAbstractNetworkCache::remove(const QUrl &url) = 0
|
||||
Removes the cache entry for \a url, returning true if success otherwise false.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa clear(), prepare()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn QIODevice *QAbstractNetworkCache::prepare(const QNetworkCacheMetaData &metaData) = 0
|
||||
Returns the device that should be populated with the data for
|
||||
the cache item \a metaData. When all of the data has been written
|
||||
insert() should be called. If metaData is invalid or the url in
|
||||
the metadata is invalid 0 is returned.
|
||||
|
||||
The cache owns the device and will take care of deleting it when
|
||||
it is inserted or removed.
|
||||
|
||||
To cancel a prepared inserted call remove() on the metadata's url.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa remove(), updateMetaData(), insert()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QAbstractNetworkCache::insert(QIODevice *device) = 0
|
||||
Inserts the data in \a device and the prepared meta data into the cache.
|
||||
After this function is called the data and meta data should be retrievable
|
||||
using data() and metaData().
|
||||
|
||||
To cancel a prepared inserted call remove() on the metadata's url.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa prepare(), remove()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn qint64 QAbstractNetworkCache::cacheSize() const = 0
|
||||
Returns the current size taken up by the cache. Depending upon
|
||||
the cache implementation this might be disk or memory size.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa clear()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QAbstractNetworkCache::clear() = 0
|
||||
Removes all items from the cache. Unless there was failures
|
||||
clearing the cache cacheSize() should return 0 after a call to clear.
|
||||
|
||||
In the base class this is a pure virtual function.
|
||||
|
||||
\sa cacheSize(), remove()
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#include "moc_qabstractnetworkcache.h"
|
||||
|
|
@ -1,118 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTNETWORKCACHE_H
|
||||
#define QABSTRACTNETWORKCACHE_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qshareddata.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QIODevice;
|
||||
class QDateTime;
|
||||
class QUrl;
|
||||
template<class T> class QList;
|
||||
|
||||
class QNetworkCacheMetaDataPrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkCacheMetaData
|
||||
{
|
||||
|
||||
public:
|
||||
typedef QPair<QByteArray, QByteArray> RawHeader;
|
||||
typedef QList<RawHeader> RawHeaderList;
|
||||
typedef QHash<QNetworkRequest::Attribute, QVariant> AttributesMap;
|
||||
|
||||
QNetworkCacheMetaData();
|
||||
QNetworkCacheMetaData(const QNetworkCacheMetaData &other);
|
||||
~QNetworkCacheMetaData();
|
||||
|
||||
QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other);
|
||||
bool operator==(const QNetworkCacheMetaData &other) const;
|
||||
inline bool operator!=(const QNetworkCacheMetaData &other) const
|
||||
{ return !(*this == other); }
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
QUrl url() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
RawHeaderList rawHeaders() const;
|
||||
void setRawHeaders(const RawHeaderList &headers);
|
||||
|
||||
QDateTime lastModified() const;
|
||||
void setLastModified(const QDateTime &dateTime);
|
||||
|
||||
QDateTime expirationDate() const;
|
||||
void setExpirationDate(const QDateTime &dateTime);
|
||||
|
||||
bool saveToDisk() const;
|
||||
void setSaveToDisk(bool allow);
|
||||
|
||||
AttributesMap attributes() const;
|
||||
void setAttributes(const AttributesMap &attributes);
|
||||
|
||||
private:
|
||||
friend class QNetworkCacheMetaDataPrivate;
|
||||
QSharedDataPointer<QNetworkCacheMetaDataPrivate> d;
|
||||
};
|
||||
|
||||
Q_NETWORK_EXPORT QDataStream &operator<<(QDataStream &, const QNetworkCacheMetaData &);
|
||||
Q_NETWORK_EXPORT QDataStream &operator>>(QDataStream &, QNetworkCacheMetaData &);
|
||||
|
||||
|
||||
class QAbstractNetworkCachePrivate;
|
||||
class Q_NETWORK_EXPORT QAbstractNetworkCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~QAbstractNetworkCache();
|
||||
|
||||
virtual QNetworkCacheMetaData metaData(const QUrl &url) = 0;
|
||||
virtual void updateMetaData(const QNetworkCacheMetaData &metaData) = 0;
|
||||
virtual QIODevice *data(const QUrl &url) = 0;
|
||||
virtual bool remove(const QUrl &url) = 0;
|
||||
virtual qint64 cacheSize() const = 0;
|
||||
|
||||
virtual QIODevice *prepare(const QNetworkCacheMetaData &metaData) = 0;
|
||||
virtual void insert(QIODevice *device) = 0;
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void clear() = 0;
|
||||
|
||||
protected:
|
||||
explicit QAbstractNetworkCache(QObject *parent = nullptr);
|
||||
QAbstractNetworkCache(QAbstractNetworkCachePrivate &dd, QObject *parent);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QAbstractNetworkCache)
|
||||
Q_DISABLE_COPY(QAbstractNetworkCache)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif
|
|
@ -1,46 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QABSTRACTNETWORKCACHE_P_H
|
||||
#define QABSTRACTNETWORKCACHE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access framework. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qobject_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAbstractNetworkCachePrivate: public QObjectPrivate
|
||||
{
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,149 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QFTP_H
|
||||
#define QFTP_H
|
||||
|
||||
#include <QtCore/qstring.h>
|
||||
#include <QtNetwork/qurlinfo.h>
|
||||
#include <QtCore/qobject.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
#ifndef QT_NO_FTP
|
||||
|
||||
class QFtpPrivate;
|
||||
|
||||
class Q_NETWORK_EXPORT QFtp : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QFtp(QObject *parent = nullptr);
|
||||
virtual ~QFtp();
|
||||
|
||||
enum State {
|
||||
Unconnected,
|
||||
HostLookup,
|
||||
Connecting,
|
||||
Connected,
|
||||
LoggedIn,
|
||||
Closing
|
||||
};
|
||||
enum Error {
|
||||
NoError,
|
||||
UnknownError,
|
||||
HostNotFound,
|
||||
ConnectionRefused,
|
||||
NotConnected
|
||||
};
|
||||
enum Command {
|
||||
None,
|
||||
SetTransferMode,
|
||||
SetProxy,
|
||||
ConnectToHost,
|
||||
Login,
|
||||
Close,
|
||||
List,
|
||||
Cd,
|
||||
Get,
|
||||
Put,
|
||||
Remove,
|
||||
Mkdir,
|
||||
Rmdir,
|
||||
Rename,
|
||||
RawCommand
|
||||
};
|
||||
enum TransferMode {
|
||||
Active,
|
||||
Passive
|
||||
};
|
||||
enum TransferType {
|
||||
Binary,
|
||||
Ascii
|
||||
};
|
||||
|
||||
int setProxy(const QString &host, quint16 port);
|
||||
int connectToHost(const QString &host, quint16 port=21);
|
||||
int login(const QString &user = QString(), const QString &password = QString());
|
||||
int close();
|
||||
int setTransferMode(TransferMode mode);
|
||||
int list(const QString &dir = QString());
|
||||
int cd(const QString &dir);
|
||||
int get(const QString &file, QIODevice *dev=0, TransferType type = Binary);
|
||||
int put(const QByteArray &data, const QString &file, TransferType type = Binary);
|
||||
int put(QIODevice *dev, const QString &file, TransferType type = Binary);
|
||||
int remove(const QString &file);
|
||||
int mkdir(const QString &dir);
|
||||
int rmdir(const QString &dir);
|
||||
int rename(const QString &oldname, const QString &newname);
|
||||
|
||||
int rawCommand(const QString &command);
|
||||
|
||||
qint64 bytesAvailable() const;
|
||||
qint64 read(char *data, qint64 maxlen);
|
||||
QByteArray readAll();
|
||||
|
||||
int currentId() const;
|
||||
QIODevice* currentDevice() const;
|
||||
Command currentCommand() const;
|
||||
bool hasPendingCommands() const;
|
||||
void clearPendingCommands();
|
||||
|
||||
State state() const;
|
||||
|
||||
Error error() const;
|
||||
QString errorString() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void abort();
|
||||
|
||||
Q_SIGNALS:
|
||||
void stateChanged(int);
|
||||
void listInfo(const QUrlInfo&);
|
||||
void readyRead();
|
||||
void dataTransferProgress(qint64, qint64);
|
||||
void rawCommandReply(int, const QString&);
|
||||
|
||||
void commandStarted(int);
|
||||
void commandFinished(int, bool);
|
||||
void done(bool);
|
||||
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QFtp)
|
||||
Q_DECLARE_PRIVATE(QFtp)
|
||||
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_startNextCommand())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_piFinished(const QString&))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_piError(int, const QString&))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_piConnectState(int))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_piFtpReply(int, const QString&))
|
||||
};
|
||||
|
||||
#endif // QT_NO_FTP
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif // QFTP_H
|
File diff suppressed because it is too large
Load diff
|
@ -1,282 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTP_H
|
||||
#define QHTTP_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qmap.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
class QTcpSocket;
|
||||
class QTimerEvent;
|
||||
class QIODevice;
|
||||
class QAuthenticator;
|
||||
class QNetworkProxy;
|
||||
class QSslError;
|
||||
|
||||
class QHttpPrivate;
|
||||
|
||||
class QHttpHeaderPrivate;
|
||||
class Q_NETWORK_EXPORT QHttpHeader
|
||||
{
|
||||
public:
|
||||
QHttpHeader();
|
||||
QHttpHeader(const QHttpHeader &header);
|
||||
QHttpHeader(const QString &str);
|
||||
virtual ~QHttpHeader();
|
||||
|
||||
QHttpHeader &operator=(const QHttpHeader &h);
|
||||
|
||||
void setValue(const QString &key, const QString &value);
|
||||
void setValues(const QList<QPair<QString, QString> > &values);
|
||||
void addValue(const QString &key, const QString &value);
|
||||
QList<QPair<QString, QString> > values() const;
|
||||
bool hasKey(const QString &key) const;
|
||||
QStringList keys() const;
|
||||
QString value(const QString &key) const;
|
||||
QStringList allValues(const QString &key) const;
|
||||
void removeValue(const QString &key);
|
||||
void removeAllValues(const QString &key);
|
||||
|
||||
// ### Qt 5: change to qint64
|
||||
bool hasContentLength() const;
|
||||
uint contentLength() const;
|
||||
void setContentLength(int len);
|
||||
|
||||
bool hasContentType() const;
|
||||
QString contentType() const;
|
||||
void setContentType(const QString &type);
|
||||
|
||||
virtual QString toString() const;
|
||||
bool isValid() const;
|
||||
|
||||
virtual int majorVersion() const = 0;
|
||||
virtual int minorVersion() const = 0;
|
||||
|
||||
protected:
|
||||
virtual bool parseLine(const QString &line, int number);
|
||||
bool parse(const QString &str);
|
||||
void setValid(bool);
|
||||
|
||||
QHttpHeader(QHttpHeaderPrivate &dd, const QString &str = QString());
|
||||
QHttpHeader(QHttpHeaderPrivate &dd, const QHttpHeader &header);
|
||||
QScopedPointer<QHttpHeaderPrivate> d_ptr;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QHttpHeader)
|
||||
};
|
||||
|
||||
class QHttpResponseHeaderPrivate;
|
||||
class Q_NETWORK_EXPORT QHttpResponseHeader : public QHttpHeader
|
||||
{
|
||||
public:
|
||||
QHttpResponseHeader();
|
||||
QHttpResponseHeader(const QHttpResponseHeader &header);
|
||||
QHttpResponseHeader(const QString &str);
|
||||
QHttpResponseHeader(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
|
||||
QHttpResponseHeader &operator=(const QHttpResponseHeader &header);
|
||||
|
||||
void setStatusLine(int code, const QString &text = QString(), int majorVer = 1, int minorVer = 1);
|
||||
|
||||
int statusCode() const;
|
||||
QString reasonPhrase() const;
|
||||
|
||||
int majorVersion() const;
|
||||
int minorVersion() const;
|
||||
|
||||
QString toString() const;
|
||||
|
||||
protected:
|
||||
bool parseLine(const QString &line, int number);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QHttpResponseHeader)
|
||||
friend class QHttpPrivate;
|
||||
};
|
||||
|
||||
class QHttpRequestHeaderPrivate;
|
||||
class Q_NETWORK_EXPORT QHttpRequestHeader : public QHttpHeader
|
||||
{
|
||||
public:
|
||||
QHttpRequestHeader();
|
||||
QHttpRequestHeader(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
|
||||
QHttpRequestHeader(const QHttpRequestHeader &header);
|
||||
QHttpRequestHeader(const QString &str);
|
||||
QHttpRequestHeader &operator=(const QHttpRequestHeader &header);
|
||||
|
||||
void setRequest(const QString &method, const QString &path, int majorVer = 1, int minorVer = 1);
|
||||
|
||||
QString method() const;
|
||||
QString path() const;
|
||||
|
||||
int majorVersion() const;
|
||||
int minorVersion() const;
|
||||
|
||||
QString toString() const;
|
||||
|
||||
protected:
|
||||
bool parseLine(const QString &line, int number);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QHttpRequestHeader)
|
||||
};
|
||||
|
||||
class Q_NETWORK_EXPORT QHttp : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum ConnectionMode {
|
||||
ConnectionModeHttp,
|
||||
ConnectionModeHttps
|
||||
};
|
||||
|
||||
explicit QHttp(QObject *parent = nullptr);
|
||||
QHttp(const QString &hostname, quint16 port = 80, QObject *parent = nullptr);
|
||||
QHttp(const QString &hostname, ConnectionMode mode, quint16 port = 0, QObject *parent = nullptr);
|
||||
virtual ~QHttp();
|
||||
|
||||
enum State {
|
||||
Unconnected,
|
||||
HostLookup,
|
||||
Connecting,
|
||||
Sending,
|
||||
Reading,
|
||||
Connected,
|
||||
Closing
|
||||
};
|
||||
enum Error {
|
||||
NoError,
|
||||
UnknownError,
|
||||
HostNotFound,
|
||||
ConnectionRefused,
|
||||
UnexpectedClose,
|
||||
InvalidResponseHeader,
|
||||
WrongContentLength,
|
||||
Aborted,
|
||||
AuthenticationRequiredError,
|
||||
ProxyAuthenticationRequiredError
|
||||
};
|
||||
|
||||
int setHost(const QString &hostname, quint16 port = 80);
|
||||
int setHost(const QString &hostname, ConnectionMode mode, quint16 port = 0);
|
||||
|
||||
int setSocket(QTcpSocket *socket);
|
||||
int setUser(const QString &username, const QString &password = QString());
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
int setProxy(const QString &host, int port,
|
||||
const QString &username = QString(),
|
||||
const QString &password = QString());
|
||||
int setProxy(const QNetworkProxy &proxy);
|
||||
#endif
|
||||
|
||||
int get(const QString &path, QIODevice *to=0);
|
||||
int post(const QString &path, QIODevice *data, QIODevice *to=0 );
|
||||
int post(const QString &path, const QByteArray &data, QIODevice *to=0);
|
||||
int head(const QString &path);
|
||||
int request(const QHttpRequestHeader &header, QIODevice *device=0, QIODevice *to=0);
|
||||
int request(const QHttpRequestHeader &header, const QByteArray &data, QIODevice *to=0);
|
||||
|
||||
int closeConnection();
|
||||
int close();
|
||||
|
||||
qint64 bytesAvailable() const;
|
||||
qint64 read(char *data, qint64 maxlen);
|
||||
QByteArray readAll();
|
||||
|
||||
int currentId() const;
|
||||
QIODevice *currentSourceDevice() const;
|
||||
QIODevice *currentDestinationDevice() const;
|
||||
QHttpRequestHeader currentRequest() const;
|
||||
QHttpResponseHeader lastResponse() const;
|
||||
bool hasPendingRequests() const;
|
||||
void clearPendingRequests();
|
||||
|
||||
State state() const;
|
||||
|
||||
Error error() const;
|
||||
QString errorString() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void abort();
|
||||
|
||||
void ignoreSslErrors();
|
||||
|
||||
Q_SIGNALS:
|
||||
void stateChanged(int);
|
||||
void responseHeaderReceived(const QHttpResponseHeader &resp);
|
||||
void readyRead(const QHttpResponseHeader &resp);
|
||||
|
||||
// ### Qt 5: change to qint64
|
||||
void dataSendProgress(int, int);
|
||||
void dataReadProgress(int, int);
|
||||
|
||||
void requestStarted(int);
|
||||
void requestFinished(int, bool);
|
||||
void done(bool);
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *);
|
||||
#endif
|
||||
void authenticationRequired(const QString &hostname, quint16 port, QAuthenticator *);
|
||||
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(QHttp)
|
||||
Q_DECLARE_PRIVATE(QHttp)
|
||||
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotReadyRead())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotConnected())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotError(QAbstractSocket::SocketError))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotClosed())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotBytesWritten(qint64 numBytes))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotEncryptedBytesWritten(qint64 numBytes))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotDoFinished())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_slotSendRequest())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_continuePost())
|
||||
|
||||
friend class QHttpNormalRequest;
|
||||
friend class QHttpSetHostRequest;
|
||||
friend class QHttpSetSocketRequest;
|
||||
friend class QHttpSetUserRequest;
|
||||
friend class QHttpSetProxyRequest;
|
||||
friend class QHttpCloseRequest;
|
||||
friend class QHttpPGHRequest;
|
||||
};
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif // QHTTP_H
|
|
@ -1,523 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpmultipart.h"
|
||||
#include "qhttpmultipart_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QHttpPart
|
||||
\brief The QHttpPart class holds a body part to be used inside a
|
||||
HTTP multipart MIME message.
|
||||
\since 4.8
|
||||
|
||||
\ingroup network
|
||||
\inmodule QtNetwork
|
||||
|
||||
The QHttpPart class holds a body part to be used inside a HTTP
|
||||
multipart MIME message (which is represented by the QHttpMultiPart class).
|
||||
A QHttpPart consists of a header block
|
||||
and a data block, which are separated by each other by two
|
||||
consecutive new lines. An example for one part would be:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 0
|
||||
|
||||
For setting headers, use setHeader() and setRawHeader(), which behave
|
||||
exactly like QNetworkRequest::setHeader() and QNetworkRequest::setRawHeader().
|
||||
|
||||
For reading small pieces of data, use setBody(); for larger data blocks
|
||||
like e.g. images, use setBodyDevice(). The latter method saves memory by
|
||||
not copying the data internally, but reading directly from the device.
|
||||
This means that the device must be opened and readable at the moment when
|
||||
the multipart message containing the body part is sent on the network via
|
||||
QNetworkAccessManager::post().
|
||||
|
||||
To construct a QHttpPart with a small body, consider the following snippet
|
||||
(this produces the data shown in the example above):
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 1
|
||||
|
||||
To construct a QHttpPart reading from a device (e.g. a file), the following
|
||||
can be applied:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qhttppart.cpp 2
|
||||
|
||||
Be aware that QHttpPart does not take ownership of the device when set, so
|
||||
it is the developer's responsibility to destroy it when it is not needed anymore.
|
||||
A good idea might be to set the multipart message as parent object for the device,
|
||||
as documented at the documentation for QHttpMultiPart.
|
||||
|
||||
\sa QHttpMultiPart, QNetworkAccessManager
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
Constructs an empty QHttpPart object.
|
||||
*/
|
||||
QHttpPart::QHttpPart() : d(new QHttpPartPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a copy of \a other.
|
||||
*/
|
||||
QHttpPart::QHttpPart(const QHttpPart &other) : d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys this QHttpPart.
|
||||
*/
|
||||
QHttpPart::~QHttpPart()
|
||||
{
|
||||
d = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a copy of \a other.
|
||||
*/
|
||||
QHttpPart &QHttpPart::operator=(const QHttpPart &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if this object is the same as \a other (i.e., if they
|
||||
have the same headers and body).
|
||||
|
||||
\sa operator!=()
|
||||
*/
|
||||
bool QHttpPart::operator==(const QHttpPart &other) const
|
||||
{
|
||||
return d == other.d || *d == *other.d;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QHttpPart::operator!=(const QHttpPart &other) const
|
||||
|
||||
Returns true if this object is not the same as \a other.
|
||||
|
||||
\sa operator==()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Sets the value of the known header \a header to be \a value,
|
||||
overriding any previously set headers.
|
||||
|
||||
\sa QNetworkRequest::KnownHeaders, setRawHeader(), QNetworkRequest::setHeader()
|
||||
*/
|
||||
void QHttpPart::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
d->setCookedHeader(header, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the header \a headerName to be of value \a headerValue. If \a
|
||||
headerName corresponds to a known header (see
|
||||
QNetworkRequest::KnownHeaders), the raw format will be parsed and
|
||||
the corresponding "cooked" header will be set as well.
|
||||
|
||||
Note: setting the same header twice overrides the previous
|
||||
setting. To accomplish the behaviour of multiple HTTP headers of
|
||||
the same name, you should concatenate the two values, separating
|
||||
them with a comma (",") and set one single raw header.
|
||||
|
||||
\sa QNetworkRequest::KnownHeaders, setHeader(), QNetworkRequest::setRawHeader()
|
||||
*/
|
||||
void QHttpPart::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
|
||||
{
|
||||
d->setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the body of this MIME part to \a body. The body set with this method
|
||||
will be used unless the device is set via setBodyDevice(). For a large
|
||||
amount of data (e.g. an image), use setBodyDevice(), which will not copy
|
||||
the data internally.
|
||||
|
||||
\sa setBodyDevice()
|
||||
*/
|
||||
void QHttpPart::setBody(const QByteArray &body)
|
||||
{
|
||||
d->setBody(body);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the device to read the content from to \a device. For large amounts of data
|
||||
this method should be preferred over setBody(),
|
||||
because the content is not copied when using this method, but read
|
||||
directly from the device.
|
||||
\a device must be open and readable. QHttpPart does not take ownership
|
||||
of \a device, i.e. the device must be closed and destroyed if necessary.
|
||||
if \a device is sequential (e.g. sockets, but not files),
|
||||
QNetworkAccessManager::post() should be called after \a device has
|
||||
emitted finished().
|
||||
For unsetting the device and using data set via setBody(), use
|
||||
"setBodyDevice(0)".
|
||||
|
||||
\sa setBody(), QNetworkAccessManager::post()
|
||||
*/
|
||||
void QHttpPart::setBodyDevice(QIODevice *device)
|
||||
{
|
||||
d->setBodyDevice(device);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*!
|
||||
\class QHttpMultiPart
|
||||
\brief The QHttpMultiPart class resembles a MIME multipart message to be sent over HTTP.
|
||||
\since 4.8
|
||||
|
||||
\ingroup network
|
||||
\inmodule QtNetwork
|
||||
|
||||
The QHttpMultiPart resembles a MIME multipart message, as described in RFC 2046,
|
||||
which is to be sent over HTTP.
|
||||
A multipart message consists of an arbitrary number of body parts (see QHttpPart),
|
||||
which are separated by a unique boundary. The boundary of the QHttpMultiPart is
|
||||
constructed with the string "boundary_.oOo._" followed by random characters,
|
||||
and provides enough uniqueness to make sure it does not occur inside the parts itself.
|
||||
If desired, the boundary can still be set via setBoundary().
|
||||
|
||||
As an example, consider the following code snippet, which constructs a multipart
|
||||
message containing a text part followed by an image part:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qhttpmultipart.cpp 0
|
||||
|
||||
\sa QHttpPart, QNetworkAccessManager::post()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QHttpMultiPart::ContentType
|
||||
|
||||
List of known content types for a multipart subtype as described
|
||||
in RFC 2046 and others.
|
||||
|
||||
\value MixedType corresponds to the "multipart/mixed" subtype,
|
||||
meaning the body parts are independent of each other, as described
|
||||
in RFC 2046.
|
||||
|
||||
\value RelatedType corresponds to the "multipart/related" subtype,
|
||||
meaning the body parts are related to each other, as described in RFC 2387.
|
||||
|
||||
\value FormDataType corresponds to the "multipart/form-data"
|
||||
subtype, meaning the body parts contain form elements, as described in RFC 2388.
|
||||
|
||||
\value AlternativeType corresponds to the "multipart/alternative"
|
||||
subtype, meaning the body parts are alternative representations of
|
||||
the same information, as described in RFC 2046.
|
||||
|
||||
\sa setContentType()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Constructs a QHttpMultiPart with content type MixedType and sets
|
||||
\a parent as the parent object.
|
||||
|
||||
\sa QHttpMultiPart::ContentType
|
||||
*/
|
||||
QHttpMultiPart::QHttpMultiPart(QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
|
||||
{
|
||||
Q_D(QHttpMultiPart);
|
||||
d->contentType = MixedType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Constructs a QHttpMultiPart with content type \a contentType and
|
||||
sets parent as the parent object.
|
||||
|
||||
\sa QHttpMultiPart::ContentType
|
||||
*/
|
||||
QHttpMultiPart::QHttpMultiPart(QHttpMultiPart::ContentType contentType, QObject *parent) : QObject(*new QHttpMultiPartPrivate, parent)
|
||||
{
|
||||
Q_D(QHttpMultiPart);
|
||||
d->contentType = contentType;
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the multipart.
|
||||
*/
|
||||
QHttpMultiPart::~QHttpMultiPart()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Appends \a httpPart to this multipart.
|
||||
*/
|
||||
void QHttpMultiPart::append(const QHttpPart &httpPart)
|
||||
{
|
||||
d_func()->parts.append(httpPart);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the content type to \a contentType. The content type will be used
|
||||
in the HTTP header section when sending the multipart message via
|
||||
QNetworkAccessManager::post().
|
||||
In case you want to use a multipart subtype not contained in
|
||||
QHttpMultiPart::ContentType,
|
||||
you can add the "Content-Type" header field to the QNetworkRequest
|
||||
by hand, and then use this request together with the multipart
|
||||
message for posting.
|
||||
|
||||
\sa QHttpMultiPart::ContentType, QNetworkAccessManager::post()
|
||||
*/
|
||||
void QHttpMultiPart::setContentType(QHttpMultiPart::ContentType contentType)
|
||||
{
|
||||
d_func()->contentType = contentType;
|
||||
}
|
||||
|
||||
/*!
|
||||
returns the boundary.
|
||||
|
||||
\sa setBoundary()
|
||||
*/
|
||||
QByteArray QHttpMultiPart::boundary() const
|
||||
{
|
||||
return d_func()->boundary;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the boundary to \a boundary.
|
||||
|
||||
Usually, you do not need to generate a boundary yourself; upon construction
|
||||
the boundary is initiated with the string "boundary_.oOo._" followed by random
|
||||
characters, and provides enough uniqueness to make sure it does not occur
|
||||
inside the parts itself.
|
||||
|
||||
\sa boundary()
|
||||
*/
|
||||
void QHttpMultiPart::setBoundary(const QByteArray &boundary)
|
||||
{
|
||||
d_func()->boundary = boundary;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ----------- implementations of private classes: ------------------
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
qint64 QHttpPartPrivate::bytesAvailable() const
|
||||
{
|
||||
checkHeaderCreated();
|
||||
qint64 bytesAvailable = header.count();
|
||||
if (bodyDevice) {
|
||||
bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
|
||||
} else {
|
||||
bytesAvailable += body.count() - readPointer;
|
||||
}
|
||||
// the device might have closed etc., so make sure we do not return a negative value
|
||||
return qMax(bytesAvailable, (qint64) 0);
|
||||
}
|
||||
|
||||
qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
checkHeaderCreated();
|
||||
qint64 bytesRead = 0;
|
||||
qint64 headerDataCount = header.count();
|
||||
|
||||
// read header if it has not been read yet
|
||||
if (readPointer < headerDataCount) {
|
||||
bytesRead = qMin(headerDataCount - readPointer, maxSize);
|
||||
const char *headerData = header.constData();
|
||||
memcpy(data, headerData + readPointer, bytesRead);
|
||||
readPointer += bytesRead;
|
||||
}
|
||||
// read content if there is still space
|
||||
if (bytesRead < maxSize) {
|
||||
if (bodyDevice) {
|
||||
qint64 dataBytesRead = bodyDevice->read(data + bytesRead, maxSize - bytesRead);
|
||||
if (dataBytesRead == -1)
|
||||
return -1;
|
||||
bytesRead += dataBytesRead;
|
||||
readPointer += dataBytesRead;
|
||||
} else {
|
||||
qint64 contentBytesRead = qMin(body.count() - readPointer + headerDataCount, maxSize - bytesRead);
|
||||
const char *contentData = body.constData();
|
||||
// if this method is called several times, we need to find the
|
||||
// right offset in the content ourselves:
|
||||
memcpy(data + bytesRead, contentData + readPointer - headerDataCount, contentBytesRead);
|
||||
bytesRead += contentBytesRead;
|
||||
readPointer += contentBytesRead;
|
||||
}
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
qint64 QHttpPartPrivate::size() const
|
||||
{
|
||||
checkHeaderCreated();
|
||||
qint64 size = header.count();
|
||||
if (bodyDevice) {
|
||||
size += bodyDevice->size();
|
||||
} else {
|
||||
size += body.count();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
bool QHttpPartPrivate::reset()
|
||||
{
|
||||
bool ret = true;
|
||||
if (bodyDevice)
|
||||
if (!bodyDevice->reset())
|
||||
ret = false;
|
||||
readPointer = 0;
|
||||
return ret;
|
||||
}
|
||||
void QHttpPartPrivate::checkHeaderCreated() const
|
||||
{
|
||||
if (!headerCreated) {
|
||||
// copied from QHttpNetworkRequestPrivate::header() and adapted
|
||||
QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
|
||||
QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
|
||||
for (; it != fields.constEnd(); ++it)
|
||||
header += it->first + QLatin1String(": ") + it->second + QLatin1String("\r\n");
|
||||
header += "\r\n";
|
||||
headerCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
|
||||
{
|
||||
boundary = QByteArray("boundary_.oOo._")
|
||||
+ QByteArray::number(qrand()).toBase64()
|
||||
+ QByteArray::number(qrand()).toBase64()
|
||||
+ QByteArray::number(qrand()).toBase64();
|
||||
|
||||
// boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
|
||||
if (boundary.count() > 70)
|
||||
boundary = boundary.left(70);
|
||||
}
|
||||
|
||||
qint64 QHttpMultiPartIODevice::size() const
|
||||
{
|
||||
// if not done yet, we calculate the size and the offsets of each part,
|
||||
// including boundary (needed later in readData)
|
||||
if (deviceSize == -1) {
|
||||
qint64 currentSize = 0;
|
||||
qint64 boundaryCount = multiPart->boundary.count();
|
||||
for (int a = 0; a < multiPart->parts.count(); a++) {
|
||||
partOffsets.append(currentSize);
|
||||
// 4 additional bytes for the "--" before and the "\r\n" after the boundary,
|
||||
// and 2 bytes for the "\r\n" after the content
|
||||
currentSize += boundaryCount + 4 + multiPart->parts.at(a).d->size() + 2;
|
||||
}
|
||||
currentSize += boundaryCount + 6; // size for ending boundary, 2 beginning and ending dashes and "\r\n"
|
||||
deviceSize = currentSize;
|
||||
}
|
||||
return deviceSize;
|
||||
}
|
||||
|
||||
bool QHttpMultiPartIODevice::isSequential() const
|
||||
{
|
||||
for (int a = 0; a < multiPart->parts.count(); a++) {
|
||||
QIODevice *device = multiPart->parts.at(a).d->bodyDevice;
|
||||
// we are sequential if any of the bodyDevices of our parts are sequential;
|
||||
// when reading from a byte array, we are not sequential
|
||||
if (device && device->isSequential())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHttpMultiPartIODevice::reset()
|
||||
{
|
||||
for (int a = 0; a < multiPart->parts.count(); a++)
|
||||
if (!multiPart->parts[a].d->reset())
|
||||
return false;
|
||||
readPointer = 0;
|
||||
return true;
|
||||
}
|
||||
qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
|
||||
{
|
||||
qint64 bytesRead = 0, index = 0;
|
||||
|
||||
// skip the parts we have already read
|
||||
while (index < multiPart->parts.count() &&
|
||||
readPointer >= partOffsets.at(index) + multiPart->parts.at(index).d->size()
|
||||
+ multiPart->boundary.count() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
|
||||
index++;
|
||||
|
||||
// read the data
|
||||
while (bytesRead < maxSize && index < multiPart->parts.count()) {
|
||||
|
||||
// check whether we need to read the boundary of the current part
|
||||
QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
|
||||
qint64 boundaryCount = boundaryData.count();
|
||||
qint64 partIndex = readPointer - partOffsets.at(index);
|
||||
if (partIndex < boundaryCount) {
|
||||
qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
|
||||
memcpy(data + bytesRead, boundaryData.constData() + partIndex, boundaryBytesRead);
|
||||
bytesRead += boundaryBytesRead;
|
||||
readPointer += boundaryBytesRead;
|
||||
partIndex += boundaryBytesRead;
|
||||
}
|
||||
|
||||
// check whether we need to read the data of the current part
|
||||
if (bytesRead < maxSize && partIndex >= boundaryCount && partIndex < boundaryCount + multiPart->parts.at(index).d->size()) {
|
||||
qint64 dataBytesRead = multiPart->parts[index].d->readData(data + bytesRead, maxSize - bytesRead);
|
||||
if (dataBytesRead == -1)
|
||||
return -1;
|
||||
bytesRead += dataBytesRead;
|
||||
readPointer += dataBytesRead;
|
||||
partIndex += dataBytesRead;
|
||||
}
|
||||
|
||||
// check whether we need to read the ending CRLF of the current part
|
||||
if (bytesRead < maxSize && partIndex >= boundaryCount + multiPart->parts.at(index).d->size()) {
|
||||
if (bytesRead == maxSize - 1)
|
||||
return bytesRead;
|
||||
memcpy(data + bytesRead, "\r\n", 2);
|
||||
bytesRead += 2;
|
||||
readPointer += 2;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
// check whether we need to return the final boundary
|
||||
if (bytesRead < maxSize && index == multiPart->parts.count()) {
|
||||
QByteArray finalBoundary = "--" + multiPart->boundary + "--\r\n";
|
||||
qint64 boundaryIndex = readPointer + finalBoundary.count() - size();
|
||||
qint64 lastBoundaryBytesRead = qMin(finalBoundary.count() - boundaryIndex, maxSize - bytesRead);
|
||||
memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
|
||||
bytesRead += lastBoundaryBytesRead;
|
||||
readPointer += lastBoundaryBytesRead;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
|
||||
{
|
||||
Q_UNUSED(data);
|
||||
Q_UNUSED(maxSize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#include "moc_qhttpmultipart.h"
|
|
@ -1,96 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPMULTIPART_H
|
||||
#define QHTTPMULTIPART_H
|
||||
|
||||
#include <QtCore/QSharedData>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QHttpPartPrivate;
|
||||
class QHttpMultiPart;
|
||||
|
||||
class Q_NETWORK_EXPORT QHttpPart
|
||||
{
|
||||
public:
|
||||
QHttpPart();
|
||||
QHttpPart(const QHttpPart &other);
|
||||
~QHttpPart();
|
||||
QHttpPart &operator=(const QHttpPart &other);
|
||||
bool operator==(const QHttpPart &other) const;
|
||||
inline bool operator!=(const QHttpPart &other) const
|
||||
{ return !operator==(other); }
|
||||
|
||||
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
|
||||
void setRawHeader(const QByteArray &headerName, const QByteArray &headerValue);
|
||||
|
||||
void setBody(const QByteArray &body);
|
||||
void setBodyDevice(QIODevice *device);
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QHttpPartPrivate> d;
|
||||
|
||||
friend class QHttpMultiPartIODevice;
|
||||
};
|
||||
|
||||
class QHttpMultiPartPrivate;
|
||||
|
||||
class Q_NETWORK_EXPORT QHttpMultiPart : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum ContentType {
|
||||
MixedType,
|
||||
RelatedType,
|
||||
FormDataType,
|
||||
AlternativeType
|
||||
};
|
||||
|
||||
QHttpMultiPart(QObject *parent = nullptr);
|
||||
QHttpMultiPart(ContentType contentType, QObject *parent = nullptr);
|
||||
~QHttpMultiPart();
|
||||
|
||||
void append(const QHttpPart &httpPart);
|
||||
|
||||
void setContentType(ContentType contentType);
|
||||
|
||||
QByteArray boundary() const;
|
||||
void setBoundary(const QByteArray &boundary);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QHttpMultiPart)
|
||||
Q_DISABLE_COPY(QHttpMultiPart)
|
||||
|
||||
friend class QNetworkAccessManager;
|
||||
friend class QNetworkAccessManagerPrivate;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif // QHTTPMULTIPART_H
|
|
@ -1,162 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPMULTIPART_P_H
|
||||
#define QHTTPMULTIPART_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "QtCore/qshareddata.h"
|
||||
#include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate
|
||||
#include "qobject_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QHttpPartPrivate: public QSharedData, public QNetworkHeadersPrivate
|
||||
{
|
||||
public:
|
||||
inline QHttpPartPrivate() : bodyDevice(0), headerCreated(false), readPointer(0)
|
||||
{
|
||||
}
|
||||
~QHttpPartPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
QHttpPartPrivate(const QHttpPartPrivate &other)
|
||||
: QSharedData(other), QNetworkHeadersPrivate(other), body(other.body),
|
||||
header(other.header), headerCreated(other.headerCreated), readPointer(other.readPointer)
|
||||
{
|
||||
bodyDevice = other.bodyDevice;
|
||||
}
|
||||
|
||||
inline bool operator==(const QHttpPartPrivate &other) const
|
||||
{
|
||||
return rawHeaders == other.rawHeaders && body == other.body &&
|
||||
bodyDevice == other.bodyDevice && readPointer == other.readPointer;
|
||||
}
|
||||
|
||||
void setBodyDevice(QIODevice *device) {
|
||||
bodyDevice = device;
|
||||
readPointer = 0;
|
||||
}
|
||||
void setBody(const QByteArray &newBody) {
|
||||
body = newBody;
|
||||
readPointer = 0;
|
||||
}
|
||||
|
||||
// QIODevice-style methods called by QHttpMultiPartIODevice (but this class is
|
||||
// not a QIODevice):
|
||||
qint64 bytesAvailable() const;
|
||||
qint64 readData(char *data, qint64 maxSize);
|
||||
qint64 size() const;
|
||||
bool reset();
|
||||
|
||||
QByteArray body;
|
||||
QIODevice *bodyDevice;
|
||||
|
||||
private:
|
||||
void checkHeaderCreated() const;
|
||||
|
||||
mutable QByteArray header;
|
||||
mutable bool headerCreated;
|
||||
qint64 readPointer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class QHttpMultiPartPrivate;
|
||||
|
||||
class Q_AUTOTEST_EXPORT QHttpMultiPartIODevice : public QIODevice
|
||||
{
|
||||
public:
|
||||
QHttpMultiPartIODevice(QHttpMultiPartPrivate *parentMultiPart) :
|
||||
QIODevice(), multiPart(parentMultiPart), readPointer(0), deviceSize(-1) {
|
||||
}
|
||||
|
||||
~QHttpMultiPartIODevice() {
|
||||
}
|
||||
|
||||
virtual bool atEnd() const {
|
||||
return readPointer == size();
|
||||
}
|
||||
|
||||
virtual qint64 bytesAvailable() const {
|
||||
return size() - readPointer;
|
||||
}
|
||||
|
||||
virtual void close() {
|
||||
readPointer = 0;
|
||||
partOffsets.clear();
|
||||
deviceSize = -1;
|
||||
QIODevice::close();
|
||||
}
|
||||
|
||||
virtual qint64 bytesToWrite() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual qint64 size() const;
|
||||
virtual bool isSequential() const;
|
||||
virtual bool reset();
|
||||
virtual qint64 readData(char *data, qint64 maxSize);
|
||||
virtual qint64 writeData(const char *data, qint64 maxSize);
|
||||
|
||||
QHttpMultiPartPrivate *multiPart;
|
||||
qint64 readPointer;
|
||||
mutable QList<qint64> partOffsets;
|
||||
mutable qint64 deviceSize;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class QHttpMultiPartPrivate: public QObjectPrivate
|
||||
{
|
||||
public:
|
||||
|
||||
QHttpMultiPartPrivate();
|
||||
|
||||
~QHttpMultiPartPrivate()
|
||||
{
|
||||
delete device;
|
||||
}
|
||||
|
||||
QList<QHttpPart> parts;
|
||||
QByteArray boundary;
|
||||
QHttpMultiPart::ContentType contentType;
|
||||
QHttpMultiPartIODevice *device;
|
||||
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif // QHTTPMULTIPART_P_H
|
|
@ -1,992 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qabstractsocket_p.h"
|
||||
#include "qhttpnetworkconnection_p.h"
|
||||
#include "qhttpnetworkconnectionchannel_p.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
#include "qnetworkrequest_p.h"
|
||||
#include "qobject_p.h"
|
||||
#include "qauthenticator_p.h"
|
||||
#include "qnetworkproxy.h"
|
||||
#include "qauthenticator.h"
|
||||
#include "qcoreapplication.h"
|
||||
|
||||
#include "qbuffer.h"
|
||||
#include "qpair.h"
|
||||
#include "qhttp.h"
|
||||
#include "qdebug.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include "qsslsocket_p.h"
|
||||
#include <QtNetwork/qsslkey.h>
|
||||
#include <QtNetwork/qsslcipher.h>
|
||||
#include <QtNetwork/qsslconfiguration.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
const int QHttpNetworkConnectionPrivate::defaultChannelCount = 6;
|
||||
|
||||
// The pipeline length. So there will be 4 requests in flight.
|
||||
const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
|
||||
// Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
|
||||
// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
|
||||
const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
|
||||
|
||||
|
||||
QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt)
|
||||
: state(RunningState),
|
||||
hostName(hostName), port(port), encrypt(encrypt),
|
||||
channelCount(defaultChannelCount)
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
, networkProxy(QNetworkProxy::NoProxy)
|
||||
#endif
|
||||
{
|
||||
channels = new QHttpNetworkConnectionChannel[channelCount];
|
||||
}
|
||||
|
||||
QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt)
|
||||
: state(RunningState),
|
||||
hostName(hostName), port(port), encrypt(encrypt),
|
||||
channelCount(channelCount)
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
, networkProxy(QNetworkProxy::NoProxy)
|
||||
#endif
|
||||
{
|
||||
channels = new QHttpNetworkConnectionChannel[channelCount];
|
||||
}
|
||||
|
||||
|
||||
|
||||
QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate()
|
||||
{
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].socket) {
|
||||
channels[i].socket->close();
|
||||
delete channels[i].socket;
|
||||
}
|
||||
}
|
||||
delete []channels;
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::init()
|
||||
{
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
channels[i].setConnection(this->q_func());
|
||||
channels[i].ssl = encrypt;
|
||||
channels[i].init();
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::pauseConnection()
|
||||
{
|
||||
state = PausedState;
|
||||
|
||||
// Disable all socket notifiers
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
if (encrypt)
|
||||
QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
|
||||
else
|
||||
QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::resumeConnection()
|
||||
{
|
||||
state = RunningState;
|
||||
// Enable all socket notifiers
|
||||
for (int i = 0; i < channelCount; i++) {
|
||||
if (encrypt)
|
||||
QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
|
||||
else
|
||||
QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
|
||||
|
||||
// Resume pending upload if needed
|
||||
if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
|
||||
QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// queue _q_startNextRequest
|
||||
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
|
||||
{
|
||||
for (int i = 0; i < channelCount; ++i)
|
||||
if (channels[i].socket == socket)
|
||||
return i;
|
||||
|
||||
qFatal("Called with unknown socket object.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailable(const QHttpNetworkReply &reply) const
|
||||
{
|
||||
return reply.d_func()->responseData.byteAmount();
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const
|
||||
{
|
||||
return reply.d_func()->responseData.sizeNextBlock();
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
|
||||
{
|
||||
QHttpNetworkRequest &request = messagePair.first;
|
||||
QHttpNetworkReply *reply = messagePair.second;
|
||||
|
||||
// add missing fields for the request
|
||||
QByteArray value;
|
||||
// check if Content-Length is provided
|
||||
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
|
||||
if (uploadByteDevice) {
|
||||
if (request.contentLength() != -1 && uploadByteDevice->size() != -1) {
|
||||
// both values known, take the smaller one.
|
||||
request.setContentLength(qMin(uploadByteDevice->size(), request.contentLength()));
|
||||
} else if (request.contentLength() == -1 && uploadByteDevice->size() != -1) {
|
||||
// content length not supplied by user, but the upload device knows it
|
||||
request.setContentLength(uploadByteDevice->size());
|
||||
} else if (request.contentLength() != -1 && uploadByteDevice->size() == -1) {
|
||||
// everything OK, the user supplied us the contentLength
|
||||
} else if (request.contentLength() == -1 && uploadByteDevice->size() == -1) {
|
||||
qFatal("QHttpNetworkConnectionPrivate: Neither content-length nor upload device size were given");
|
||||
}
|
||||
}
|
||||
// set the Connection/Proxy-Connection: Keep-Alive headers
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
if (networkProxy.type() == QNetworkProxy::HttpCachingProxy) {
|
||||
value = request.headerField("proxy-connection");
|
||||
if (value.isEmpty())
|
||||
request.setHeaderField("Proxy-Connection", "Keep-Alive");
|
||||
} else {
|
||||
#endif
|
||||
value = request.headerField("connection");
|
||||
if (value.isEmpty())
|
||||
request.setHeaderField("Connection", "Keep-Alive");
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the request had a accept-encoding set, we better not mess
|
||||
// with it. If it was not set, we announce that we understand gzip
|
||||
// and remember this fact in request.d->autoDecompress so that
|
||||
// we can later decompress the HTTP reply if it has such an
|
||||
// encoding.
|
||||
value = request.headerField("accept-encoding");
|
||||
if (value.isEmpty()) {
|
||||
request.setHeaderField("Accept-Encoding", "gzip");
|
||||
request.d->autoDecompress = true;
|
||||
}
|
||||
|
||||
// some websites mandate an accept-language header and fail
|
||||
// if it is not sent. This is a problem with the website and
|
||||
// not with us, but we work around this by setting
|
||||
// one always.
|
||||
value = request.headerField("accept-language");
|
||||
if (value.isEmpty()) {
|
||||
QString systemLocale = QLocale::system().name().replace(QChar::fromAscii('_'),QChar::fromAscii('-'));
|
||||
QString acceptLanguage;
|
||||
if (systemLocale == QLatin1String("C"))
|
||||
acceptLanguage = QString::fromAscii("en,*");
|
||||
else if (systemLocale.startsWith(QLatin1String("en-")))
|
||||
acceptLanguage = QString::fromAscii("%1,*").arg(systemLocale);
|
||||
else
|
||||
acceptLanguage = QString::fromAscii("%1,en,*").arg(systemLocale);
|
||||
request.setHeaderField("Accept-Language", acceptLanguage.toAscii());
|
||||
}
|
||||
|
||||
// set the User Agent
|
||||
value = request.headerField("user-agent");
|
||||
if (value.isEmpty())
|
||||
request.setHeaderField("User-Agent", "Mozilla/5.0");
|
||||
// set the host
|
||||
value = request.headerField("host");
|
||||
if (value.isEmpty()) {
|
||||
QHostAddress add;
|
||||
QByteArray host;
|
||||
if(add.setAddress(hostName)) {
|
||||
if(add.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
host = "[" + hostName.toAscii() + "]";//format the ipv6 in the standard way
|
||||
} else {
|
||||
host = QUrl::toAce(hostName);
|
||||
}
|
||||
} else {
|
||||
host = QUrl::toAce(hostName);
|
||||
}
|
||||
|
||||
int port = request.url().port();
|
||||
if (port != -1) {
|
||||
host += ':';
|
||||
host += QByteArray::number(port);
|
||||
}
|
||||
|
||||
request.setHeaderField("Host", host);
|
||||
}
|
||||
|
||||
reply->d_func()->requestIsPrepared = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
|
||||
QHttpNetworkReply *reply,
|
||||
QNetworkReply::NetworkError errorCode)
|
||||
{
|
||||
Q_Q(QHttpNetworkConnection);
|
||||
if (socket && reply) {
|
||||
// this error matters only to this reply
|
||||
reply->d_func()->errorString = errorDetail(errorCode, socket);
|
||||
emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
|
||||
int i = indexOf(socket);
|
||||
// remove the corrupt data if any
|
||||
reply->d_func()->eraseData();
|
||||
|
||||
// Clean the channel
|
||||
channels[i].close();
|
||||
channels[i].reply = 0;
|
||||
channels[i].request = QHttpNetworkRequest();
|
||||
channels[i].requeueCurrentlyPipelinedRequests();
|
||||
|
||||
// send the next request
|
||||
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy)
|
||||
{
|
||||
Q_ASSERT(auth);
|
||||
|
||||
// NTLM is a multi phase authentication. Copying credentials between authenticators would mess things up.
|
||||
if (!isProxy && channels[fromChannel].authMethod == QAuthenticatorPrivate::Ntlm)
|
||||
return;
|
||||
if (isProxy && channels[fromChannel].proxyAuthMethod == QAuthenticatorPrivate::Ntlm)
|
||||
return;
|
||||
|
||||
|
||||
// select another channel
|
||||
QAuthenticator* otherAuth = 0;
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (i == fromChannel)
|
||||
continue;
|
||||
if (isProxy)
|
||||
otherAuth = &channels[i].proxyAuthenticator;
|
||||
else
|
||||
otherAuth = &channels[i].authenticator;
|
||||
// if the credentials are different, copy them
|
||||
if (otherAuth->user().compare(auth->user()))
|
||||
otherAuth->setUser(auth->user());
|
||||
if (otherAuth->password().compare(auth->password()))
|
||||
otherAuth->setPassword(auth->password());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// handles the authentication for one channel and eventually re-starts the other channels
|
||||
bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
|
||||
bool isProxy, bool &resend)
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
Q_ASSERT(reply);
|
||||
|
||||
resend = false;
|
||||
//create the response header to be used with QAuthenticatorPrivate.
|
||||
QList<QPair<QByteArray, QByteArray> > fields = reply->header();
|
||||
|
||||
//find out the type of authentication protocol requested.
|
||||
QAuthenticatorPrivate::Method authMethod = reply->d_func()->authenticationMethod(isProxy);
|
||||
if (authMethod != QAuthenticatorPrivate::None) {
|
||||
int i = indexOf(socket);
|
||||
//Use a single authenticator for all domains. ### change later to use domain/realm
|
||||
QAuthenticator* auth = 0;
|
||||
if (isProxy) {
|
||||
auth = &channels[i].proxyAuthenticator;
|
||||
channels[i].proxyAuthMethod = authMethod;
|
||||
} else {
|
||||
auth = &channels[i].authenticator;
|
||||
channels[i].authMethod = authMethod;
|
||||
}
|
||||
//proceed with the authentication.
|
||||
if (auth->isNull())
|
||||
auth->detach();
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
|
||||
priv->parseHttpResponse(fields, isProxy);
|
||||
|
||||
if (priv->phase == QAuthenticatorPrivate::Done) {
|
||||
pauseConnection();
|
||||
if (!isProxy) {
|
||||
if (channels[i].authenticationCredentialsSent) {
|
||||
auth->detach();
|
||||
priv = QAuthenticatorPrivate::getPrivate(*auth);
|
||||
priv->hasFailed = true;
|
||||
priv->phase = QAuthenticatorPrivate::Done;
|
||||
channels[i].authenticationCredentialsSent = false;
|
||||
}
|
||||
emit reply->authenticationRequired(reply->request(), auth);
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
} else {
|
||||
if (channels[i].proxyCredentialsSent) {
|
||||
auth->detach();
|
||||
priv = QAuthenticatorPrivate::getPrivate(*auth);
|
||||
priv->hasFailed = true;
|
||||
priv->phase = QAuthenticatorPrivate::Done;
|
||||
channels[i].proxyCredentialsSent = false;
|
||||
}
|
||||
emit reply->proxyAuthenticationRequired(networkProxy, auth);
|
||||
#endif
|
||||
}
|
||||
resumeConnection();
|
||||
|
||||
if (priv->phase != QAuthenticatorPrivate::Done) {
|
||||
// send any pending requests
|
||||
copyCredentials(i, auth, isProxy);
|
||||
}
|
||||
} else if (priv->phase == QAuthenticatorPrivate::Start) {
|
||||
// If the url's authenticator has a 'user' set we will end up here (phase is only set to 'Done' by
|
||||
// parseHttpResponse above if 'user' is empty). So if credentials were supplied with the request,
|
||||
// such as in the case of an XMLHttpRequest, this is our only opportunity to cache them.
|
||||
emit reply->cacheCredentials(reply->request(), auth);
|
||||
}
|
||||
// - Changing values in QAuthenticator will reset the 'phase'. Therefore if it is still "Done"
|
||||
// then nothing was filled in by the user or the cache
|
||||
// - If withCredentials has been set to false (e.g. by QtWebKit for a cross-origin XMLHttpRequest) then
|
||||
// we need to bail out if authentication is required.
|
||||
if (priv->phase == QAuthenticatorPrivate::Done || !reply->request().withCredentials()) {
|
||||
// Reset authenticator so the next request on that channel does not get messed up
|
||||
auth = 0;
|
||||
if (isProxy)
|
||||
channels[i].proxyAuthenticator = QAuthenticator();
|
||||
else
|
||||
channels[i].authenticator = QAuthenticator();
|
||||
|
||||
// authentication is cancelled, send the current contents to the user.
|
||||
emit channels[i].reply->headerChanged();
|
||||
emit channels[i].reply->readyRead();
|
||||
QNetworkReply::NetworkError errorCode =
|
||||
isProxy
|
||||
? QNetworkReply::ProxyAuthenticationRequiredError
|
||||
: QNetworkReply::AuthenticationRequiredError;
|
||||
reply->d_func()->errorString = errorDetail(errorCode, socket);
|
||||
emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
|
||||
// ### at this point the reply could be deleted
|
||||
return true;
|
||||
}
|
||||
//resend the request
|
||||
resend = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
|
||||
int i = indexOf(socket);
|
||||
|
||||
// Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
|
||||
if (channels[i].authMethod != QAuthenticatorPrivate::None) {
|
||||
if (!(channels[i].authMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 401)) {
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].authenticator);
|
||||
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
||||
QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
|
||||
request.setHeaderField("Authorization", response);
|
||||
channels[i].authenticationCredentialsSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
|
||||
if (channels[i].proxyAuthMethod != QAuthenticatorPrivate::None) {
|
||||
if (!(channels[i].proxyAuthMethod == QAuthenticatorPrivate::Ntlm && channels[i].lastStatus != 407)) {
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(channels[i].proxyAuthenticator);
|
||||
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
||||
QByteArray response = priv->calculateResponse(request.d->methodName(), request.d->uri(false));
|
||||
request.setHeaderField("Proxy-Authorization", response);
|
||||
channels[i].proxyCredentialsSent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetworkRequest &request)
|
||||
{
|
||||
Q_Q(QHttpNetworkConnection);
|
||||
|
||||
// The reply component of the pair is created initially.
|
||||
QHttpNetworkReply *reply = new QHttpNetworkReply(request.url());
|
||||
reply->setRequest(request);
|
||||
reply->d_func()->connection = q;
|
||||
reply->d_func()->connectionChannel = &channels[0]; // will have the correct one set later
|
||||
HttpMessagePair pair = qMakePair(request, reply);
|
||||
|
||||
switch (request.priority()) {
|
||||
case QHttpNetworkRequest::HighPriority:
|
||||
highPriorityQueue.prepend(pair);
|
||||
break;
|
||||
case QHttpNetworkRequest::NormalPriority:
|
||||
case QHttpNetworkRequest::LowPriority:
|
||||
lowPriorityQueue.prepend(pair);
|
||||
break;
|
||||
}
|
||||
|
||||
// this used to be called via invokeMethod and a QueuedConnection
|
||||
// It is the only place _q_startNextRequest is called directly without going
|
||||
// through the event loop using a QueuedConnection.
|
||||
// This is dangerous because of recursion that might occur when emitting
|
||||
// signals as DirectConnection from this code path. Therefore all signal
|
||||
// emissions that can come out from this code path need to
|
||||
// be QueuedConnection.
|
||||
// We are currently trying to fine-tune this.
|
||||
_q_startNextRequest();
|
||||
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
|
||||
{
|
||||
Q_Q(QHttpNetworkConnection);
|
||||
|
||||
QHttpNetworkRequest request = pair.first;
|
||||
switch (request.priority()) {
|
||||
case QHttpNetworkRequest::HighPriority:
|
||||
highPriorityQueue.prepend(pair);
|
||||
break;
|
||||
case QHttpNetworkRequest::NormalPriority:
|
||||
case QHttpNetworkRequest::LowPriority:
|
||||
lowPriorityQueue.prepend(pair);
|
||||
break;
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
|
||||
int i = indexOf(socket);
|
||||
|
||||
if (!highPriorityQueue.isEmpty()) {
|
||||
// remove from queue before sendRequest! else we might pipeline the same request again
|
||||
HttpMessagePair messagePair = highPriorityQueue.takeLast();
|
||||
if (!messagePair.second->d_func()->requestIsPrepared)
|
||||
prepareRequest(messagePair);
|
||||
channels[i].request = messagePair.first;
|
||||
channels[i].reply = messagePair.second;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lowPriorityQueue.isEmpty()) {
|
||||
// remove from queue before sendRequest! else we might pipeline the same request again
|
||||
HttpMessagePair messagePair = lowPriorityQueue.takeLast();
|
||||
if (!messagePair.second->d_func()->requestIsPrepared)
|
||||
prepareRequest(messagePair);
|
||||
channels[i].request = messagePair.first;
|
||||
channels[i].reply = messagePair.second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
QHttpNetworkRequest QHttpNetworkConnectionPrivate::predictNextRequest()
|
||||
{
|
||||
if (!highPriorityQueue.isEmpty())
|
||||
return highPriorityQueue.last().first;
|
||||
if (!lowPriorityQueue.isEmpty())
|
||||
return lowPriorityQueue.last().first;
|
||||
return QHttpNetworkRequest();
|
||||
}
|
||||
|
||||
// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
|
||||
void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
|
||||
{
|
||||
// return fast if there is nothing to pipeline
|
||||
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
|
||||
return;
|
||||
|
||||
int i = indexOf(socket);
|
||||
|
||||
// return fast if there was no reply right now processed
|
||||
if (channels[i].reply == 0)
|
||||
return;
|
||||
|
||||
if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (channels[i].pipeliningSupported != QHttpNetworkConnectionChannel::PipeliningProbablySupported)
|
||||
return;
|
||||
|
||||
// the current request that is in must already support pipelining
|
||||
if (!channels[i].request.isPipeliningAllowed())
|
||||
return;
|
||||
|
||||
// the current request must be a idempotent (right now we only check GET)
|
||||
if (channels[i].request.operation() != QHttpNetworkRequest::Get)
|
||||
return;
|
||||
|
||||
// check if socket is connected
|
||||
if (socket->state() != QAbstractSocket::ConnectedState)
|
||||
return;
|
||||
|
||||
// check for resendCurrent
|
||||
if (channels[i].resendCurrent)
|
||||
return;
|
||||
|
||||
// we do not like authentication stuff
|
||||
// ### make sure to be OK with this in later releases
|
||||
if (!channels[i].authenticator.isNull()
|
||||
&& (!channels[i].authenticator.user().isEmpty()
|
||||
|| !channels[i].authenticator.password().isEmpty()))
|
||||
return;
|
||||
if (!channels[i].proxyAuthenticator.isNull()
|
||||
&& (!channels[i].proxyAuthenticator.user().isEmpty()
|
||||
|| !channels[i].proxyAuthenticator.password().isEmpty()))
|
||||
return;
|
||||
|
||||
// must be in ReadingState or WaitingState
|
||||
if (! (channels[i].state == QHttpNetworkConnectionChannel::WaitingState
|
||||
|| channels[i].state == QHttpNetworkConnectionChannel::ReadingState))
|
||||
return;
|
||||
|
||||
int lengthBefore;
|
||||
while (!highPriorityQueue.isEmpty()) {
|
||||
lengthBefore = channels[i].alreadyPipelinedRequests.length();
|
||||
fillPipeline(highPriorityQueue, channels[i]);
|
||||
|
||||
if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
|
||||
channels[i].pipelineFlush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
|
||||
break; // did not process anything, now do the low prio queue
|
||||
}
|
||||
|
||||
while (!lowPriorityQueue.isEmpty()) {
|
||||
lengthBefore = channels[i].alreadyPipelinedRequests.length();
|
||||
fillPipeline(lowPriorityQueue, channels[i]);
|
||||
|
||||
if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
|
||||
channels[i].pipelineFlush();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
|
||||
break; // did not process anything
|
||||
}
|
||||
|
||||
|
||||
channels[i].pipelineFlush();
|
||||
}
|
||||
|
||||
// returns true when the processing of a queue has been done
|
||||
bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel)
|
||||
{
|
||||
if (queue.isEmpty())
|
||||
return true;
|
||||
|
||||
for (int i = queue.count() - 1; i >= 0; --i) {
|
||||
HttpMessagePair messagePair = queue.at(i);
|
||||
const QHttpNetworkRequest &request = messagePair.first;
|
||||
|
||||
// we currently do not support pipelining if HTTP authentication is used
|
||||
if (!request.url().userInfo().isEmpty())
|
||||
continue;
|
||||
|
||||
// take only GET requests
|
||||
if (request.operation() != QHttpNetworkRequest::Get)
|
||||
continue;
|
||||
|
||||
if (!request.isPipeliningAllowed())
|
||||
continue;
|
||||
|
||||
// remove it from the queue
|
||||
queue.takeAt(i);
|
||||
// we modify the queue we iterate over here, but since we return from the function
|
||||
// afterwards this is fine.
|
||||
|
||||
// actually send it
|
||||
if (!messagePair.second->d_func()->requestIsPrepared)
|
||||
prepareRequest(messagePair);
|
||||
channel.pipelineInto(messagePair);
|
||||
|
||||
// return false because we processed something and need to process again
|
||||
return false;
|
||||
}
|
||||
|
||||
// return true, the queue has been processed and not changed
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket* socket,
|
||||
const QString &extraDetail)
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
|
||||
QString errorString;
|
||||
switch (errorCode) {
|
||||
case QNetworkReply::HostNotFoundError:
|
||||
errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
|
||||
break;
|
||||
case QNetworkReply::ConnectionRefusedError:
|
||||
errorString = QCoreApplication::translate("QHttp", "Connection refused");
|
||||
break;
|
||||
case QNetworkReply::RemoteHostClosedError:
|
||||
errorString = QCoreApplication::translate("QHttp", "Connection closed");
|
||||
break;
|
||||
case QNetworkReply::TimeoutError:
|
||||
errorString = QCoreApplication::translate("QAbstractSocket", "Socket operation timed out");
|
||||
break;
|
||||
case QNetworkReply::ProxyAuthenticationRequiredError:
|
||||
errorString = QCoreApplication::translate("QHttp", "Proxy requires authentication");
|
||||
break;
|
||||
case QNetworkReply::AuthenticationRequiredError:
|
||||
errorString = QCoreApplication::translate("QHttp", "Host requires authentication");
|
||||
break;
|
||||
case QNetworkReply::ProtocolFailure:
|
||||
errorString = QCoreApplication::translate("QHttp", "Data corrupted");
|
||||
break;
|
||||
case QNetworkReply::ProtocolUnknownError:
|
||||
errorString = QCoreApplication::translate("QHttp", "Unknown protocol specified");
|
||||
break;
|
||||
case QNetworkReply::SslHandshakeFailedError:
|
||||
errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
|
||||
break;
|
||||
default:
|
||||
// all other errors are treated as QNetworkReply::UnknownNetworkError
|
||||
errorString = extraDetail;
|
||||
break;
|
||||
}
|
||||
return errorString;
|
||||
}
|
||||
|
||||
// this is called from the destructor of QHttpNetworkReply. It is called when
|
||||
// the reply was finished correctly or when it was aborted.
|
||||
void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
|
||||
{
|
||||
Q_Q(QHttpNetworkConnection);
|
||||
|
||||
// check if the reply is currently being processed or it is pipelined in
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
// is the reply associated the currently processing of this channel?
|
||||
if (channels[i].reply == reply) {
|
||||
channels[i].reply = 0;
|
||||
channels[i].request = QHttpNetworkRequest();
|
||||
channels[i].resendCurrent = false;
|
||||
|
||||
if (!reply->isFinished() && !channels[i].alreadyPipelinedRequests.isEmpty()) {
|
||||
// the reply had to be prematurely removed, e.g. it was not finished
|
||||
// therefore we have to requeue the already pipelined requests.
|
||||
channels[i].requeueCurrentlyPipelinedRequests();
|
||||
}
|
||||
|
||||
// if HTTP mandates we should close
|
||||
// or the reply is not finished yet, e.g. it was aborted
|
||||
// we have to close that connection
|
||||
if (reply->d_func()->isConnectionCloseEnabled() || !reply->isFinished())
|
||||
channels[i].close();
|
||||
|
||||
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
// is the reply inside the pipeline of this channel already?
|
||||
for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
|
||||
if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
|
||||
// Remove that HttpMessagePair
|
||||
channels[i].alreadyPipelinedRequests.removeAt(j);
|
||||
|
||||
channels[i].requeueCurrentlyPipelinedRequests();
|
||||
|
||||
// Since some requests had already been pipelined, but we removed
|
||||
// one and re-queued the others
|
||||
// we must force a connection close after the request that is
|
||||
// currently in processing has been finished.
|
||||
if (channels[i].reply)
|
||||
channels[i].reply->d_func()->forceConnectionCloseEnabled = true;
|
||||
|
||||
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove from the high priority queue
|
||||
if (!highPriorityQueue.isEmpty()) {
|
||||
for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
|
||||
HttpMessagePair messagePair = highPriorityQueue.at(j);
|
||||
if (messagePair.second == reply) {
|
||||
highPriorityQueue.removeAt(j);
|
||||
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// remove from the low priority queue
|
||||
if (!lowPriorityQueue.isEmpty()) {
|
||||
for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
|
||||
HttpMessagePair messagePair = lowPriorityQueue.at(j);
|
||||
if (messagePair.second == reply) {
|
||||
lowPriorityQueue.removeAt(j);
|
||||
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This function must be called from the event loop. The only
|
||||
// exception is documented in QHttpNetworkConnectionPrivate::queueRequest
|
||||
// although it is called _q_startNextRequest, it will actually start multiple requests when possible
|
||||
void QHttpNetworkConnectionPrivate::_q_startNextRequest()
|
||||
{
|
||||
// If the QHttpNetworkConnection is currently paused then bail out immediately
|
||||
if (state == PausedState)
|
||||
return;
|
||||
|
||||
//resend the necessary ones.
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
|
||||
channels[i].resendCurrent = false;
|
||||
channels[i].state = QHttpNetworkConnectionChannel::IdleState;
|
||||
|
||||
// if this is not possible, error will be emitted and connection terminated
|
||||
if (!channels[i].resetUploadData())
|
||||
continue;
|
||||
channels[i].sendRequest();
|
||||
}
|
||||
}
|
||||
|
||||
// dequeue new ones
|
||||
|
||||
// return fast if there is nothing to do
|
||||
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
|
||||
return;
|
||||
// try to get a free AND connected socket
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
|
||||
if (dequeueRequest(channels[i].socket))
|
||||
channels[i].sendRequest();
|
||||
}
|
||||
}
|
||||
|
||||
// try to push more into all sockets
|
||||
// ### FIXME we should move this to the beginning of the function
|
||||
// as soon as QtWebkit is properly using the pipelining
|
||||
// (e.g. not for XMLHttpRequest or the first page load)
|
||||
// ### FIXME we should also divide the requests more even
|
||||
// on the connected sockets
|
||||
//tryToFillPipeline(socket);
|
||||
// return fast if there is nothing to pipeline
|
||||
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
|
||||
return;
|
||||
for (int i = 0; i < channelCount; i++)
|
||||
if (channels[i].socket->state() == QAbstractSocket::ConnectedState)
|
||||
fillPipeline(channels[i].socket);
|
||||
|
||||
// If there is not already any connected channels we need to connect a new one.
|
||||
// We do not pair the channel with the request until we know if it is
|
||||
// connected or not. This is to reuse connected channels before we connect new once.
|
||||
int queuedRequest = highPriorityQueue.count() + lowPriorityQueue.count();
|
||||
for (int i = 0; i < channelCount; ++i) {
|
||||
if (channels[i].socket->state() == QAbstractSocket::ConnectingState)
|
||||
queuedRequest--;
|
||||
if ( queuedRequest <=0 )
|
||||
break;
|
||||
if (!channels[i].reply && !channels[i].isSocketBusy() && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
|
||||
channels[i].ensureConnection();
|
||||
queuedRequest--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void QHttpNetworkConnectionPrivate::readMoreLater(QHttpNetworkReply *reply)
|
||||
{
|
||||
for (int i = 0 ; i < channelCount; ++i) {
|
||||
if (channels[i].reply == reply) {
|
||||
// emulate a readyRead() from the socket
|
||||
QMetaObject::invokeMethod(&channels[i], "_q_readyRead", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt, QObject *parent)
|
||||
: QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt)), parent)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
d->init();
|
||||
}
|
||||
|
||||
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, QObject *parent)
|
||||
: QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt)), parent)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
d->init();
|
||||
}
|
||||
|
||||
QHttpNetworkConnection::~QHttpNetworkConnection()
|
||||
{
|
||||
}
|
||||
|
||||
QString QHttpNetworkConnection::hostName() const
|
||||
{
|
||||
Q_D(const QHttpNetworkConnection);
|
||||
return d->hostName;
|
||||
}
|
||||
|
||||
quint16 QHttpNetworkConnection::port() const
|
||||
{
|
||||
Q_D(const QHttpNetworkConnection);
|
||||
return d->port;
|
||||
}
|
||||
|
||||
QHttpNetworkReply* QHttpNetworkConnection::sendRequest(const QHttpNetworkRequest &request)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
return d->queueRequest(request);
|
||||
}
|
||||
|
||||
bool QHttpNetworkConnection::isSsl() const
|
||||
{
|
||||
Q_D(const QHttpNetworkConnection);
|
||||
return d->encrypt;
|
||||
}
|
||||
|
||||
QHttpNetworkConnectionChannel *QHttpNetworkConnection::channels() const
|
||||
{
|
||||
return d_func()->channels;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
d->networkProxy = networkProxy;
|
||||
// update the authenticator
|
||||
if (!d->networkProxy.user().isEmpty()) {
|
||||
for (int i = 0; i < d->channelCount; ++i) {
|
||||
d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
|
||||
d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkProxy QHttpNetworkConnection::cacheProxy() const
|
||||
{
|
||||
Q_D(const QHttpNetworkConnection);
|
||||
return d->networkProxy;
|
||||
}
|
||||
|
||||
void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
for (int i = 0; i < d->channelCount; ++i)
|
||||
d->channels[i].socket->setProxy(networkProxy);
|
||||
}
|
||||
|
||||
QNetworkProxy QHttpNetworkConnection::transparentProxy() const
|
||||
{
|
||||
Q_D(const QHttpNetworkConnection);
|
||||
return d->channels[0].socket->proxy();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// SSL support below
|
||||
void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
if (!d->encrypt)
|
||||
return;
|
||||
|
||||
// set the config on all channels
|
||||
for (int i = 0; i < d->channelCount; ++i)
|
||||
static_cast<QSslSocket *>(d->channels[i].socket)->setSslConfiguration(config);
|
||||
}
|
||||
|
||||
void QHttpNetworkConnection::ignoreSslErrors(int channel)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
if (!d->encrypt)
|
||||
return;
|
||||
|
||||
if (channel == -1) { // ignore for all channels
|
||||
for (int i = 0; i < d->channelCount; ++i) {
|
||||
static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors();
|
||||
d->channels[i].ignoreAllSslErrors = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors();
|
||||
d->channels[channel].ignoreAllSslErrors = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpNetworkConnection::ignoreSslErrors(const QList<QSslError> &errors, int channel)
|
||||
{
|
||||
Q_D(QHttpNetworkConnection);
|
||||
if (!d->encrypt)
|
||||
return;
|
||||
|
||||
if (channel == -1) { // ignore for all channels
|
||||
for (int i = 0; i < d->channelCount; ++i) {
|
||||
static_cast<QSslSocket *>(d->channels[i].socket)->ignoreSslErrors(errors);
|
||||
d->channels[i].ignoreSslErrorsList = errors;
|
||||
}
|
||||
|
||||
} else {
|
||||
static_cast<QSslSocket *>(d->channels[channel].socket)->ignoreSslErrors(errors);
|
||||
d->channels[channel].ignoreSslErrorsList = errors;
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
// only called from QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired, not
|
||||
// from QHttpNetworkConnectionChannel::handleAuthenticationChallenge
|
||||
// e.g. it is for SOCKS proxies which require authentication.
|
||||
void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth)
|
||||
{
|
||||
// Also pause the connection because socket notifiers may fire while an user
|
||||
// dialog is displaying
|
||||
pauseConnection();
|
||||
emit chan->reply->proxyAuthenticationRequired(proxy, auth);
|
||||
resumeConnection();
|
||||
int i = indexOf(chan->socket);
|
||||
copyCredentials(i, auth, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qhttpnetworkconnection_p.h"
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPNETWORKCONNECTION_H
|
||||
#define QHTTPNETWORKCONNECTION_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qabstractsocket.h>
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qsslerror.h>
|
||||
|
||||
#include "qobject_p.h"
|
||||
#include "qauthenticator.h"
|
||||
#include "qnetworkproxy.h"
|
||||
#include "qbuffer.h"
|
||||
|
||||
#include "qhttpnetworkheader_p.h"
|
||||
#include "qhttpnetworkrequest_p.h"
|
||||
#include "qhttpnetworkreply_p.h"
|
||||
#include "qhttpnetworkconnectionchannel_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpNetworkRequest;
|
||||
class QHttpNetworkReply;
|
||||
class QByteArray;
|
||||
|
||||
class QHttpNetworkConnectionPrivate;
|
||||
class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = nullptr);
|
||||
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80, bool encrypt = false, QObject *parent = nullptr);
|
||||
~QHttpNetworkConnection();
|
||||
|
||||
//The hostname to which this is connected to.
|
||||
QString hostName() const;
|
||||
//The HTTP port in use.
|
||||
quint16 port() const;
|
||||
|
||||
//add a new HTTP request through this connection
|
||||
QHttpNetworkReply* sendRequest(const QHttpNetworkRequest &request);
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
//set the proxy for this connection
|
||||
void setCacheProxy(const QNetworkProxy &networkProxy);
|
||||
QNetworkProxy cacheProxy() const;
|
||||
void setTransparentProxy(const QNetworkProxy &networkProxy);
|
||||
QNetworkProxy transparentProxy() const;
|
||||
#endif
|
||||
|
||||
bool isSsl() const;
|
||||
|
||||
QHttpNetworkConnectionChannel *channels() const;
|
||||
|
||||
void setSslConfiguration(const QSslConfiguration &config);
|
||||
void ignoreSslErrors(int channel = -1);
|
||||
void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QHttpNetworkConnection)
|
||||
Q_DISABLE_COPY(QHttpNetworkConnection)
|
||||
friend class QHttpNetworkReply;
|
||||
friend class QHttpNetworkReplyPrivate;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
|
||||
};
|
||||
|
||||
|
||||
// private classes
|
||||
typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
|
||||
|
||||
|
||||
class QHttpNetworkConnectionPrivate : public QObjectPrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpNetworkConnection)
|
||||
public:
|
||||
static const int defaultChannelCount;
|
||||
static const int defaultPipelineLength;
|
||||
static const int defaultRePipelineLength;
|
||||
|
||||
enum ConnectionState {
|
||||
RunningState = 0,
|
||||
PausedState = 1,
|
||||
};
|
||||
|
||||
QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt);
|
||||
QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt);
|
||||
~QHttpNetworkConnectionPrivate();
|
||||
void init();
|
||||
|
||||
void pauseConnection();
|
||||
void resumeConnection();
|
||||
ConnectionState state;
|
||||
|
||||
enum { ChunkSize = 4096 };
|
||||
|
||||
int indexOf(QAbstractSocket *socket) const;
|
||||
|
||||
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
|
||||
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
|
||||
bool dequeueRequest(QAbstractSocket *socket);
|
||||
void prepareRequest(HttpMessagePair &request);
|
||||
QHttpNetworkRequest predictNextRequest();
|
||||
|
||||
void fillPipeline(QAbstractSocket *socket);
|
||||
bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
|
||||
|
||||
// read more HTTP body after the next event loop spin
|
||||
void readMoreLater(QHttpNetworkReply *reply);
|
||||
|
||||
void copyCredentials(int fromChannel, QAuthenticator *auth, bool isProxy);
|
||||
|
||||
// private slots
|
||||
void _q_startNextRequest(); // send the next request from the queue
|
||||
|
||||
void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
|
||||
|
||||
QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
|
||||
const QString &extraDetail = QString());
|
||||
|
||||
bool expand(QAbstractSocket *socket, QHttpNetworkReply *reply, bool dataComplete);
|
||||
void removeReply(QHttpNetworkReply *reply);
|
||||
|
||||
QString hostName;
|
||||
quint16 port;
|
||||
bool encrypt;
|
||||
|
||||
const int channelCount;
|
||||
QHttpNetworkConnectionChannel *channels; // parallel connections to the server
|
||||
|
||||
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
|
||||
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
|
||||
|
||||
|
||||
void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
|
||||
bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QNetworkProxy networkProxy;
|
||||
void emitProxyAuthenticationRequired(const QHttpNetworkConnectionChannel *chan, const QNetworkProxy &proxy, QAuthenticator* auth);
|
||||
#endif
|
||||
|
||||
//The request queues
|
||||
QList<HttpMessagePair> highPriorityQueue;
|
||||
QList<HttpMessagePair> lowPriorityQueue;
|
||||
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
};
|
||||
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,160 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPNETWORKCONNECTIONCHANNEL_H
|
||||
#define QHTTPNETWORKCONNECTIONCHANNEL_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qabstractsocket.h>
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qsslerror.h>
|
||||
|
||||
#include "qobject_p.h"
|
||||
#include "qauthenticator.h"
|
||||
#include "qnetworkproxy.h"
|
||||
#include "qbuffer.h"
|
||||
|
||||
#include "qhttpnetworkheader_p.h"
|
||||
#include "qhttpnetworkrequest_p.h"
|
||||
#include "qhttpnetworkreply_p.h"
|
||||
|
||||
#include "qhttpnetworkconnection_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpNetworkRequest;
|
||||
class QHttpNetworkReply;
|
||||
class QByteArray;
|
||||
|
||||
#ifndef HttpMessagePair
|
||||
typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
|
||||
#endif
|
||||
|
||||
class QHttpNetworkConnectionChannel : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum ChannelState {
|
||||
IdleState = 0, // ready to send request
|
||||
ConnectingState = 1, // connecting to host
|
||||
WritingState = 2, // writing the data
|
||||
WaitingState = 4, // waiting for reply
|
||||
ReadingState = 8, // reading the reply
|
||||
ClosingState = 16,
|
||||
BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|ClosingState)
|
||||
};
|
||||
QAbstractSocket *socket;
|
||||
bool ssl;
|
||||
ChannelState state;
|
||||
QHttpNetworkRequest request; // current request
|
||||
QHttpNetworkReply *reply; // current reply for this request
|
||||
qint64 written;
|
||||
qint64 bytesTotal;
|
||||
bool resendCurrent;
|
||||
int lastStatus; // last status received on this channel
|
||||
bool pendingEncrypt; // for https (send after encrypted)
|
||||
int reconnectAttempts; // maximum 2 reconnection attempts
|
||||
QAuthenticatorPrivate::Method authMethod;
|
||||
QAuthenticatorPrivate::Method proxyAuthMethod;
|
||||
QAuthenticator authenticator;
|
||||
QAuthenticator proxyAuthenticator;
|
||||
bool authenticationCredentialsSent;
|
||||
bool proxyCredentialsSent;
|
||||
bool ignoreAllSslErrors;
|
||||
QList<QSslError> ignoreSslErrorsList;
|
||||
|
||||
// HTTP pipelining -> http://en.wikipedia.org/wiki/Http_pipelining
|
||||
enum PipeliningSupport {
|
||||
PipeliningSupportUnknown, // default for a new connection
|
||||
PipeliningProbablySupported, // after having received a server response that indicates support
|
||||
PipeliningNotSupported // currently not used
|
||||
};
|
||||
PipeliningSupport pipeliningSupported;
|
||||
QList<HttpMessagePair> alreadyPipelinedRequests;
|
||||
QByteArray pipeline; // temporary buffer that gets sent to socket in pipelineFlush
|
||||
void pipelineInto(HttpMessagePair &pair);
|
||||
void pipelineFlush();
|
||||
void requeueCurrentlyPipelinedRequests();
|
||||
void detectPipeliningSupport();
|
||||
|
||||
QHttpNetworkConnectionChannel();
|
||||
|
||||
void setConnection(QHttpNetworkConnection *c);
|
||||
QPointer<QHttpNetworkConnection> connection;
|
||||
|
||||
void init();
|
||||
void close();
|
||||
|
||||
bool sendRequest();
|
||||
|
||||
bool ensureConnection();
|
||||
|
||||
bool expand(bool dataComplete);
|
||||
void allDone(); // reply header + body have been read
|
||||
void handleStatus(); // called from allDone()
|
||||
|
||||
bool resetUploadData(); // return true if resetting worked or there is no upload data
|
||||
|
||||
void handleUnexpectedEOF();
|
||||
void closeAndResendCurrentRequest();
|
||||
|
||||
bool isSocketBusy() const;
|
||||
bool isSocketWriting() const;
|
||||
bool isSocketWaiting() const;
|
||||
bool isSocketReading() const;
|
||||
|
||||
friend class QNetworkAccessHttpBackend;
|
||||
|
||||
protected slots:
|
||||
void _q_receiveReply();
|
||||
void _q_bytesWritten(qint64 bytes); // proceed sending
|
||||
void _q_readyRead(); // pending data to read
|
||||
void _q_disconnected(); // disconnected from host
|
||||
void _q_connected(); // start sending request
|
||||
void _q_error(QAbstractSocket::SocketError); // error from socket
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void _q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth); // from transparent proxy
|
||||
#endif
|
||||
|
||||
void _q_uploadDataReadyRead();
|
||||
|
||||
void _q_encrypted(); // start sending request (https)
|
||||
void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
|
||||
void _q_encryptedBytesWritten(qint64 bytes); // proceed sending
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#endif
|
|
@ -1,116 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpnetworkheader_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QUrl &newUrl)
|
||||
:url(newUrl)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpNetworkHeaderPrivate::QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other)
|
||||
:QSharedData(other)
|
||||
{
|
||||
url = other.url;
|
||||
fields = other.fields;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkHeaderPrivate::contentLength() const
|
||||
{
|
||||
bool ok = false;
|
||||
// We are not using the headerField() method here because servers might send us multiple content-length
|
||||
// headers which is crap (see QTBUG-15311). Therefore just take the first content-length header field.
|
||||
QByteArray value;
|
||||
QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
|
||||
end = fields.constEnd();
|
||||
for ( ; it != end; ++it)
|
||||
if (qstricmp("content-length", it->first) == 0) {
|
||||
value = it->second;
|
||||
break;
|
||||
}
|
||||
|
||||
qint64 length = value.toULongLong(&ok);
|
||||
if (ok)
|
||||
return length;
|
||||
return -1; // the header field is not set
|
||||
}
|
||||
|
||||
void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
|
||||
{
|
||||
setHeaderField("Content-Length", QByteArray::number(length));
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
|
||||
{
|
||||
QList<QByteArray> allValues = headerFieldValues(name);
|
||||
if (allValues.isEmpty())
|
||||
return defaultValue;
|
||||
|
||||
QByteArray result;
|
||||
bool first = true;
|
||||
foreach (const QByteArray &value, allValues) {
|
||||
if (!first)
|
||||
result += ", ";
|
||||
first = false;
|
||||
result += value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
|
||||
{
|
||||
QList<QByteArray> result;
|
||||
QList<QPair<QByteArray, QByteArray> >::ConstIterator it = fields.constBegin(),
|
||||
end = fields.constEnd();
|
||||
for ( ; it != end; ++it)
|
||||
if (qstricmp(name.constData(), it->first) == 0)
|
||||
result += it->second;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QHttpNetworkHeaderPrivate::setHeaderField(const QByteArray &name, const QByteArray &data)
|
||||
{
|
||||
QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin();
|
||||
while (it != fields.end()) {
|
||||
if (qstricmp(name.constData(), it->first) == 0)
|
||||
it = fields.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
fields.append(qMakePair(name, data));
|
||||
}
|
||||
|
||||
bool QHttpNetworkHeaderPrivate::operator==(const QHttpNetworkHeaderPrivate &other) const
|
||||
{
|
||||
return (url == other.url);
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPNETWORKHEADER_H
|
||||
#define QHTTPNETWORKHEADER_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include "qshareddata.h"
|
||||
#include "qurl.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_AUTOTEST_EXPORT QHttpNetworkHeader
|
||||
{
|
||||
public:
|
||||
virtual ~QHttpNetworkHeader() {};
|
||||
virtual QUrl url() const = 0;
|
||||
virtual void setUrl(const QUrl &url) = 0;
|
||||
|
||||
virtual int majorVersion() const = 0;
|
||||
virtual int minorVersion() const = 0;
|
||||
|
||||
virtual qint64 contentLength() const = 0;
|
||||
virtual void setContentLength(qint64 length) = 0;
|
||||
|
||||
virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
|
||||
virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
|
||||
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
|
||||
};
|
||||
|
||||
class QHttpNetworkHeaderPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
QUrl url;
|
||||
QList<QPair<QByteArray, QByteArray> > fields;
|
||||
|
||||
QHttpNetworkHeaderPrivate(const QUrl &newUrl = QUrl());
|
||||
QHttpNetworkHeaderPrivate(const QHttpNetworkHeaderPrivate &other);
|
||||
qint64 contentLength() const;
|
||||
void setContentLength(qint64 length);
|
||||
|
||||
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
|
||||
QList<QByteArray> headerFieldValues(const QByteArray &name) const;
|
||||
void setHeaderField(const QByteArray &name, const QByteArray &data);
|
||||
bool operator==(const QHttpNetworkHeaderPrivate &other) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
|
||||
#endif // QHTTPNETWORKHEADER_H
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,866 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpnetworkreply_p.h"
|
||||
#include "qhttpnetworkconnection_p.h"
|
||||
#include "qbytearraymatcher.h"
|
||||
#include "qcorecommon_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include <QtNetwork/qsslkey.h>
|
||||
#include <QtNetwork/qsslcipher.h>
|
||||
#include <QtNetwork/qsslconfiguration.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
|
||||
: QObject(*new QHttpNetworkReplyPrivate(url), parent)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpNetworkReply::~QHttpNetworkReply()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
if (d->connection) {
|
||||
d->connection->d_func()->removeReply(this);
|
||||
}
|
||||
}
|
||||
|
||||
QUrl QHttpNetworkReply::url() const
|
||||
{
|
||||
return d_func()->url;
|
||||
}
|
||||
void QHttpNetworkReply::setUrl(const QUrl &url)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->url = url;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReply::contentLength() const
|
||||
{
|
||||
return d_func()->contentLength();
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setContentLength(qint64 length)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->setContentLength(length);
|
||||
}
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
|
||||
{
|
||||
return d_func()->fields;
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
|
||||
{
|
||||
return d_func()->headerField(name, defaultValue);
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setHeaderField(const QByteArray &name, const QByteArray &data)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->setHeaderField(name, data);
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::parseHeader(const QByteArray &header)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->parseHeader(header);
|
||||
}
|
||||
|
||||
QHttpNetworkRequest QHttpNetworkReply::request() const
|
||||
{
|
||||
return d_func()->request;
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setRequest(const QHttpNetworkRequest &request)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->request = request;
|
||||
d->ssl = request.isSsl();
|
||||
}
|
||||
|
||||
int QHttpNetworkReply::statusCode() const
|
||||
{
|
||||
return d_func()->statusCode;
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setStatusCode(int code)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->statusCode = code;
|
||||
}
|
||||
|
||||
QString QHttpNetworkReply::errorString() const
|
||||
{
|
||||
return d_func()->errorString;
|
||||
}
|
||||
|
||||
QString QHttpNetworkReply::reasonPhrase() const
|
||||
{
|
||||
return d_func()->reasonPhrase;
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setErrorString(const QString &error)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->errorString = error;
|
||||
}
|
||||
|
||||
int QHttpNetworkReply::majorVersion() const
|
||||
{
|
||||
return d_func()->majorVersion;
|
||||
}
|
||||
|
||||
int QHttpNetworkReply::minorVersion() const
|
||||
{
|
||||
return d_func()->minorVersion;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReply::bytesAvailable() const
|
||||
{
|
||||
Q_D(const QHttpNetworkReply);
|
||||
if (d->connection)
|
||||
return d->connection->d_func()->uncompressedBytesAvailable(*this);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReply::bytesAvailableNextBlock() const
|
||||
{
|
||||
Q_D(const QHttpNetworkReply);
|
||||
if (d->connection)
|
||||
return d->connection->d_func()->uncompressedBytesAvailableNextBlock(*this);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReply::readAnyAvailable() const
|
||||
{
|
||||
Q_D(const QHttpNetworkReply);
|
||||
return (d->responseData.bufferCount() > 0);
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkReply::readAny()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
if (d->responseData.bufferCount() == 0)
|
||||
return QByteArray();
|
||||
|
||||
// we'll take the last buffer, so schedule another read from http
|
||||
if (d->downstreamLimited && d->responseData.bufferCount() == 1)
|
||||
d->connection->d_func()->readMoreLater(this);
|
||||
return d->responseData.read();
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkReply::readAll()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
return d->responseData.readAll();
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkReply::read(qint64 amount)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
return d->responseData.read(amount);
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReply::sizeNextBlock()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
return d->responseData.sizeNextBlock();
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setDownstreamLimited(bool dsl)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->downstreamLimited = dsl;
|
||||
d->connection->d_func()->readMoreLater(this);
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setReadBufferSize(qint64 size)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
d->readBufferMaxSize = size;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReply::supportsUserProvidedDownloadBuffer()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
return (!d->isChunked() && !d->autoDecompress && d->bodyLength > 0);
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setUserProvidedDownloadBuffer(char* b)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
if (supportsUserProvidedDownloadBuffer())
|
||||
d->userProvidedDownloadBuffer = b;
|
||||
}
|
||||
|
||||
char* QHttpNetworkReply::userProvidedDownloadBuffer()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
return d->userProvidedDownloadBuffer;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReply::isFinished() const
|
||||
{
|
||||
return d_func()->state == QHttpNetworkReplyPrivate::AllDoneState;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReply::isPipeliningUsed() const
|
||||
{
|
||||
return d_func()->pipeliningUsed;
|
||||
}
|
||||
|
||||
QHttpNetworkConnection* QHttpNetworkReply::connection()
|
||||
{
|
||||
return d_func()->connection;
|
||||
}
|
||||
|
||||
|
||||
QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
|
||||
: QHttpNetworkHeaderPrivate(newUrl)
|
||||
, state(NothingDoneState)
|
||||
, ssl(false)
|
||||
, statusCode(100),
|
||||
majorVersion(0), minorVersion(0), bodyLength(0), contentRead(0), totalProgress(0),
|
||||
chunkedTransferEncoding(false),
|
||||
connectionCloseEnabled(true),
|
||||
forceConnectionCloseEnabled(false),
|
||||
lastChunkRead(false),
|
||||
currentChunkSize(0), currentChunkRead(0), readBufferMaxSize(0), connection(0),
|
||||
autoDecompress(false), responseData(), requestIsPrepared(false)
|
||||
,pipeliningUsed(false), downstreamLimited(false)
|
||||
,userProvidedDownloadBuffer(0)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpNetworkReplyPrivate::~QHttpNetworkReplyPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::clearHttpLayerInformation()
|
||||
{
|
||||
state = NothingDoneState;
|
||||
statusCode = 100;
|
||||
bodyLength = 0;
|
||||
contentRead = 0;
|
||||
totalProgress = 0;
|
||||
currentChunkSize = 0;
|
||||
currentChunkRead = 0;
|
||||
lastChunkRead = false;
|
||||
connectionCloseEnabled = true;
|
||||
fields.clear();
|
||||
}
|
||||
|
||||
// TODO: Isn't everything HTTP layer related? We don't need to set connection and connectionChannel to 0 at all
|
||||
void QHttpNetworkReplyPrivate::clear()
|
||||
{
|
||||
connection = 0;
|
||||
connectionChannel = 0;
|
||||
autoDecompress = false;
|
||||
clearHttpLayerInformation();
|
||||
}
|
||||
|
||||
// QHttpNetworkReplyPrivate
|
||||
qint64 QHttpNetworkReplyPrivate::bytesAvailable() const
|
||||
{
|
||||
return (state != ReadingDataState ? 0 : fragment.size());
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::isGzipped()
|
||||
{
|
||||
QByteArray encoding = headerField("content-encoding");
|
||||
return qstricmp(encoding.constData(), "gzip") == 0;
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
|
||||
{
|
||||
// The header "Content-Encoding = gzip" is retained.
|
||||
// Content-Length is removed since the actual one send by the server is for compressed data
|
||||
QByteArray name("content-length");
|
||||
QList<QPair<QByteArray, QByteArray> >::Iterator it = fields.begin(),
|
||||
end = fields.end();
|
||||
while (it != end) {
|
||||
if (qstricmp(name.constData(), it->first.constData()) == 0) {
|
||||
fields.erase(it);
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
|
||||
{
|
||||
challenge.clear();
|
||||
// find out the type of authentication protocol requested.
|
||||
QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
|
||||
// pick the best protocol (has to match parsing in QAuthenticatorPrivate)
|
||||
QList<QByteArray> challenges = headerFieldValues(header);
|
||||
for (int i = 0; i<challenges.size(); i++) {
|
||||
QByteArray line = challenges.at(i);
|
||||
if (qstrnicmp(line.constData(), "negotiate", 9) != 0)
|
||||
challenge = line;
|
||||
}
|
||||
return !challenge.isEmpty();
|
||||
}
|
||||
|
||||
QAuthenticatorPrivate::Method QHttpNetworkReplyPrivate::authenticationMethod(bool isProxy) const
|
||||
{
|
||||
// The logic is same as the one used in void QAuthenticatorPrivate::parseHttpResponse()
|
||||
QAuthenticatorPrivate::Method method = QAuthenticatorPrivate::None;
|
||||
QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
|
||||
QList<QByteArray> challenges = headerFieldValues(header);
|
||||
for (int i = 0; i<challenges.size(); i++) {
|
||||
QByteArray line = challenges.at(i).trimmed().toLower();
|
||||
if (method < QAuthenticatorPrivate::Basic
|
||||
&& line.startsWith("basic")) {
|
||||
method = QAuthenticatorPrivate::Basic;
|
||||
} else if (method < QAuthenticatorPrivate::Ntlm
|
||||
&& line.startsWith("ntlm")) {
|
||||
method = QAuthenticatorPrivate::Ntlm;
|
||||
} else if (method < QAuthenticatorPrivate::DigestMd5
|
||||
&& line.startsWith("digest")) {
|
||||
method = QAuthenticatorPrivate::DigestMd5;
|
||||
}
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::gunzipBody(QByteArray &compressed, QByteArray &inflated)
|
||||
{
|
||||
// check the header
|
||||
if (compressed.size() < 3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const unsigned char gz_magic[2] = { 0x1f, 0x8b }; // gzip magic header
|
||||
if (compressed.at(0) != char(gz_magic[0]) ||
|
||||
compressed.at(1) != char(gz_magic[1])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// expand until end
|
||||
inflated = qUncompress(compressed.constData(), compressed.size());
|
||||
return !inflated.isEmpty();
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
|
||||
{
|
||||
if (fragment.isEmpty()) {
|
||||
// reserve bytes for the status line. This is better than always append() which reallocs the byte array
|
||||
fragment.reserve(32);
|
||||
}
|
||||
|
||||
qint64 bytes = 0;
|
||||
char c;
|
||||
qint64 haveRead = 0;
|
||||
|
||||
do {
|
||||
haveRead = socket->read(&c, 1);
|
||||
if (haveRead == -1)
|
||||
return -1; // unexpected EOF
|
||||
else if (haveRead == 0)
|
||||
break; // read more later
|
||||
else if (haveRead == 1 && fragment.size() == 0 && (c == 11 || c == '\n' || c == '\r' || c == ' ' || c == 31))
|
||||
continue; // Ignore all whitespace that was trailing froma previous request on that socket
|
||||
|
||||
bytes++;
|
||||
|
||||
// allow both CRLF & LF (only) line endings
|
||||
if (c == '\n') {
|
||||
// remove the CR at the end
|
||||
if (fragment.endsWith('\r')) {
|
||||
fragment.truncate(fragment.length()-1);
|
||||
}
|
||||
bool ok = parseStatus(fragment);
|
||||
state = ReadingHeaderState;
|
||||
fragment.clear();
|
||||
if (!ok) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
fragment.append(c);
|
||||
}
|
||||
|
||||
// is this a valid reply?
|
||||
if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
|
||||
{
|
||||
fragment.clear();
|
||||
return -1;
|
||||
}
|
||||
} while (haveRead == 1);
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
|
||||
{
|
||||
// from RFC 2616:
|
||||
// Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
|
||||
// HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
|
||||
// that makes: 'HTTP/n.n xxx Message'
|
||||
// byte count: 0123456789012
|
||||
|
||||
static const int minLength = 11;
|
||||
static const int dotPos = 6;
|
||||
static const int spacePos = 8;
|
||||
static const char httpMagic[] = "HTTP/";
|
||||
|
||||
if (status.length() < minLength
|
||||
|| !status.startsWith(httpMagic)
|
||||
|| status.at(dotPos) != '.'
|
||||
|| status.at(spacePos) != ' ') {
|
||||
// I don't know how to parse this status line
|
||||
return false;
|
||||
}
|
||||
|
||||
// optimize for the valid case: defer checking until the end
|
||||
majorVersion = status.at(dotPos - 1) - '0';
|
||||
minorVersion = status.at(dotPos + 1) - '0';
|
||||
|
||||
int i = spacePos;
|
||||
int j = status.indexOf(' ', i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length()
|
||||
const QByteArray code = status.mid(i + 1, j - i - 1);
|
||||
|
||||
bool ok;
|
||||
statusCode = code.toInt(&ok);
|
||||
reasonPhrase = QString::fromLatin1(status.constData() + j + 1);
|
||||
|
||||
return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
|
||||
{
|
||||
if (fragment.isEmpty()) {
|
||||
// according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
|
||||
// block is 381 bytes.
|
||||
// reserve bytes. This is better than always append() which reallocs the byte array.
|
||||
fragment.reserve(512);
|
||||
}
|
||||
|
||||
qint64 bytes = 0;
|
||||
char c = 0;
|
||||
bool allHeaders = false;
|
||||
qint64 haveRead = 0;
|
||||
do {
|
||||
haveRead = socket->read(&c, 1);
|
||||
if (haveRead == 0) {
|
||||
// read more later
|
||||
break;
|
||||
} else if (haveRead == -1) {
|
||||
// connection broke down
|
||||
return -1;
|
||||
} else {
|
||||
fragment.append(c);
|
||||
bytes++;
|
||||
|
||||
if (c == '\n') {
|
||||
// check for possible header endings. As per HTTP rfc,
|
||||
// the header endings will be marked by CRLFCRLF. But
|
||||
// we will allow CRLFCRLF, CRLFLF, LFLF
|
||||
if (fragment.endsWith("\r\n\r\n")
|
||||
|| fragment.endsWith("\r\n\n")
|
||||
|| fragment.endsWith("\n\n"))
|
||||
allHeaders = true;
|
||||
|
||||
// there is another case: We have no headers. Then the fragment equals just the line ending
|
||||
if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
|
||||
|| (fragment.length() == 1 && fragment.endsWith("\n")))
|
||||
allHeaders = true;
|
||||
}
|
||||
}
|
||||
} while (!allHeaders && haveRead > 0);
|
||||
|
||||
// we received all headers now parse them
|
||||
if (allHeaders) {
|
||||
parseHeader(fragment);
|
||||
state = ReadingDataState;
|
||||
fragment.clear(); // next fragment
|
||||
bodyLength = contentLength(); // cache the length
|
||||
|
||||
// cache isChunked() since it is called often
|
||||
chunkedTransferEncoding = headerField("transfer-encoding").toLower().contains("chunked");
|
||||
|
||||
// cache isConnectionCloseEnabled since it is called often
|
||||
QByteArray connectionHeaderField = headerField("connection");
|
||||
// check for explicit indication of close or the implicit connection close of HTTP/1.0
|
||||
connectionCloseEnabled = (connectionHeaderField.toLower().contains("close") ||
|
||||
headerField("proxy-connection").toLower().contains("close")) ||
|
||||
(majorVersion == 1 && minorVersion == 0 &&
|
||||
(connectionHeaderField.isEmpty() && !headerField("proxy-connection").toLower().contains("keep-alive")));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
|
||||
{
|
||||
// see rfc2616, sec 4 for information about HTTP/1.1 headers.
|
||||
// allows relaxed parsing here, accepts both CRLF & LF line endings
|
||||
const QByteArrayMatcher lf("\n");
|
||||
const QByteArrayMatcher colon(":");
|
||||
int i = 0;
|
||||
while (i < header.count()) {
|
||||
int j = colon.indexIn(header, i); // field-name
|
||||
if (j == -1)
|
||||
break;
|
||||
const QByteArray field = header.mid(i, j - i).trimmed();
|
||||
j++;
|
||||
// any number of LWS is allowed before and after the value
|
||||
QByteArray value;
|
||||
do {
|
||||
i = lf.indexIn(header, j);
|
||||
if (i == -1)
|
||||
break;
|
||||
if (!value.isEmpty())
|
||||
value += ' ';
|
||||
// check if we have CRLF or only LF
|
||||
bool hasCR = (i && header[i-1] == '\r');
|
||||
int length = i -(hasCR ? 1: 0) - j;
|
||||
value += header.mid(j, length).trimmed();
|
||||
j = ++i;
|
||||
} while (i < header.count() && (header.at(i) == ' ' || header.at(i) == '\t'));
|
||||
if (i == -1)
|
||||
break; // something is wrong
|
||||
|
||||
fields.append(qMakePair(field, value));
|
||||
}
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::isChunked()
|
||||
{
|
||||
return chunkedTransferEncoding;
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
|
||||
{
|
||||
return connectionCloseEnabled || forceConnectionCloseEnabled;
|
||||
}
|
||||
|
||||
// note this function can only be used for non-chunked, non-compressed with
|
||||
// known content length
|
||||
qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
|
||||
{
|
||||
// This first read is to flush the buffer inside the socket
|
||||
qint64 haveRead = socket->read(b, bodyLength - contentRead);
|
||||
if (haveRead == -1) {
|
||||
return 0; // ### error checking here;
|
||||
}
|
||||
contentRead += haveRead;
|
||||
|
||||
if (contentRead == bodyLength) {
|
||||
state = AllDoneState;
|
||||
}
|
||||
|
||||
return haveRead;
|
||||
}
|
||||
|
||||
// note this function can only be used for non-chunked, non-compressed with
|
||||
// known content length
|
||||
qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
|
||||
{
|
||||
|
||||
qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
|
||||
if (readBufferMaxSize)
|
||||
toBeRead = qMin(toBeRead, readBufferMaxSize);
|
||||
QByteArray bd(toBeRead, Qt::Uninitialized);
|
||||
qint64 haveRead = socket->read(bd.data(), toBeRead);
|
||||
if (haveRead == -1) {
|
||||
bd.clear();
|
||||
return 0; // ### error checking here;
|
||||
}
|
||||
bd.resize(haveRead);
|
||||
|
||||
rb->append(bd);
|
||||
|
||||
if (contentRead + haveRead == bodyLength) {
|
||||
state = AllDoneState;
|
||||
}
|
||||
|
||||
contentRead += haveRead;
|
||||
return haveRead;
|
||||
}
|
||||
|
||||
|
||||
qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
|
||||
{
|
||||
qint64 bytes = 0;
|
||||
if (isChunked()) {
|
||||
// chunked transfer encoding (rfc 2616, sec 3.6)
|
||||
bytes += readReplyBodyChunked(socket, out);
|
||||
} else if (bodyLength > 0) {
|
||||
// we have a Content-Length
|
||||
bytes += readReplyBodyRaw(socket, out, bodyLength - contentRead);
|
||||
if (contentRead + bytes == bodyLength)
|
||||
state = AllDoneState;
|
||||
} else {
|
||||
// no content length. just read what's possible
|
||||
bytes += readReplyBodyRaw(socket, out, socket->bytesAvailable());
|
||||
}
|
||||
contentRead += bytes;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
|
||||
{
|
||||
// FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
|
||||
qint64 bytes = 0;
|
||||
Q_ASSERT(socket);
|
||||
Q_ASSERT(out);
|
||||
|
||||
int toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
|
||||
if (readBufferMaxSize)
|
||||
toBeRead = qMin<qint64>(toBeRead, readBufferMaxSize);
|
||||
|
||||
while (toBeRead > 0) {
|
||||
QByteArray byteData(toBeRead, Qt::Uninitialized);
|
||||
qint64 haveRead = socket->read(byteData.data(), byteData.size());
|
||||
if (haveRead <= 0) {
|
||||
// ### error checking here
|
||||
byteData.clear();
|
||||
return bytes;
|
||||
}
|
||||
|
||||
byteData.resize(haveRead);
|
||||
out->append(byteData);
|
||||
bytes += haveRead;
|
||||
size -= haveRead;
|
||||
|
||||
toBeRead = qMin<qint64>(128*1024, qMin<qint64>(size, socket->bytesAvailable()));
|
||||
}
|
||||
return bytes;
|
||||
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
|
||||
{
|
||||
qint64 bytes = 0;
|
||||
while (socket->bytesAvailable()) {
|
||||
|
||||
if (readBufferMaxSize && (bytes > readBufferMaxSize))
|
||||
break;
|
||||
|
||||
if (!lastChunkRead && currentChunkRead >= currentChunkSize) {
|
||||
// For the first chunk and when we're done with a chunk
|
||||
currentChunkSize = 0;
|
||||
currentChunkRead = 0;
|
||||
if (bytes) {
|
||||
// After a chunk
|
||||
QSTACKARRAY(char, crlf, 2);
|
||||
// read the "\r\n" after the chunk
|
||||
qint64 haveRead = socket->read(crlf, sizeof(crlf));
|
||||
// FIXME: This code is slightly broken and not optimal. What if the 2 bytes are not available yet?!
|
||||
// For nice reasons (the toLong in getChunkSize accepting \n at the beginning
|
||||
// it right now still works, but we should definitely fix this.
|
||||
|
||||
if (haveRead != 2)
|
||||
return bytes; // FIXME
|
||||
bytes += haveRead;
|
||||
}
|
||||
// Note that chunk size gets stored in currentChunkSize, what is returned is the bytes read
|
||||
bytes += getChunkSize(socket, ¤tChunkSize);
|
||||
if (currentChunkSize == -1)
|
||||
break;
|
||||
}
|
||||
// if the chunk size is 0, end of the stream
|
||||
if (currentChunkSize == 0 || lastChunkRead) {
|
||||
lastChunkRead = true;
|
||||
// try to read the "\r\n" after the chunk
|
||||
QSTACKARRAY(char, crlf, 2);
|
||||
qint64 haveRead = socket->read(crlf, sizeof(crlf));
|
||||
if (haveRead > 0)
|
||||
bytes += haveRead;
|
||||
|
||||
if ((haveRead == 2 && crlf[0] == '\r' && crlf[1] == '\n') || (haveRead == 1 && crlf[0] == '\n'))
|
||||
state = AllDoneState;
|
||||
else if (haveRead == 1 && crlf[0] == '\r')
|
||||
break; // Still waiting for the last \n
|
||||
else if (haveRead > 0) {
|
||||
// If we read something else then CRLF, we need to close the channel.
|
||||
forceConnectionCloseEnabled = true;
|
||||
state = AllDoneState;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// otherwise, try to begin reading this chunk / to read what is missing for this chunk
|
||||
qint64 haveRead = readReplyBodyRaw (socket, out, currentChunkSize - currentChunkRead);
|
||||
currentChunkRead += haveRead;
|
||||
bytes += haveRead;
|
||||
|
||||
// ### error checking here
|
||||
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
|
||||
{
|
||||
qint64 bytes = 0;
|
||||
QSTACKARRAY(char, crlf, 2);
|
||||
*chunkSize = -1;
|
||||
|
||||
int bytesAvailable = socket->bytesAvailable();
|
||||
// FIXME rewrite to permanent loop without bytesAvailable
|
||||
while (bytesAvailable > bytes) {
|
||||
qint64 sniffedBytes = socket->peek(crlf, sizeof(crlf));
|
||||
int fragmentSize = fragment.size();
|
||||
|
||||
// check the next two bytes for a "\r\n", skip blank lines
|
||||
if ((fragmentSize && sniffedBytes == 2 && crlf[0] == '\r' && crlf[1] == '\n')
|
||||
||(fragmentSize > 1 && fragment.endsWith('\r') && crlf[0] == '\n'))
|
||||
{
|
||||
bytes += socket->read(crlf, 1); // read the \r or \n
|
||||
if (crlf[0] == '\r')
|
||||
bytes += socket->read(crlf, 1); // read the \n
|
||||
bool ok = false;
|
||||
// ignore the chunk-extension
|
||||
fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
|
||||
*chunkSize = fragment.toLong(&ok, 16);
|
||||
fragment.clear();
|
||||
break; // size done
|
||||
} else {
|
||||
// read the fragment to the buffer
|
||||
char c = 0;
|
||||
qint64 haveRead = socket->read(&c, 1);
|
||||
if (haveRead < 0) {
|
||||
return -1; // FIXME
|
||||
}
|
||||
bytes += haveRead;
|
||||
fragment.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteArray &qba)
|
||||
{
|
||||
responseData.append(qba);
|
||||
|
||||
// clear the original! helps with implicit sharing and
|
||||
// avoiding memcpy when the user is reading the data
|
||||
qba.clear();
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::appendUncompressedReplyData(QByteDataBuffer &data)
|
||||
{
|
||||
responseData.append(data);
|
||||
|
||||
// clear the original! helps with implicit sharing and
|
||||
// avoiding memcpy when the user is reading the data
|
||||
data.clear();
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::appendCompressedReplyData(QByteDataBuffer &data)
|
||||
{
|
||||
// Work in progress: Later we will directly use a list of QByteArray or a QRingBuffer
|
||||
// instead of one QByteArray.
|
||||
for(int i = 0; i < data.bufferCount(); i++) {
|
||||
QByteArray &byteData = data[i];
|
||||
compressedData.append(byteData.constData(), byteData.size());
|
||||
}
|
||||
data.clear();
|
||||
}
|
||||
|
||||
|
||||
bool QHttpNetworkReplyPrivate::shouldEmitSignals()
|
||||
{
|
||||
// for 401 & 407 don't emit the data signals. Content along with these
|
||||
// responses are send only if the authentication fails.
|
||||
return (statusCode != 401 && statusCode != 407);
|
||||
}
|
||||
|
||||
bool QHttpNetworkReplyPrivate::expectContent()
|
||||
{
|
||||
// check whether we can expect content after the headers (rfc 2616, sec4.4)
|
||||
if ((statusCode >= 100 && statusCode < 200)
|
||||
|| statusCode == 204 || statusCode == 304)
|
||||
return false;
|
||||
if (request.operation() == QHttpNetworkRequest::Head)
|
||||
return false; // no body expected for HEAD request
|
||||
qint64 expectedContentLength = contentLength();
|
||||
if (expectedContentLength == 0)
|
||||
return false;
|
||||
if (expectedContentLength == -1 && bodyLength == 0) {
|
||||
// The content-length header was stripped, but its value was 0.
|
||||
// This would be the case for an explicitly zero-length compressed response.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void QHttpNetworkReplyPrivate::eraseData()
|
||||
{
|
||||
compressedData.clear();
|
||||
responseData.clear();
|
||||
}
|
||||
|
||||
|
||||
// SSL support below
|
||||
QSslConfiguration QHttpNetworkReply::sslConfiguration() const
|
||||
{
|
||||
Q_D(const QHttpNetworkReply);
|
||||
|
||||
if (!d->connectionChannel)
|
||||
return QSslConfiguration();
|
||||
|
||||
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(d->connectionChannel->socket);
|
||||
if (!sslSocket)
|
||||
return QSslConfiguration();
|
||||
|
||||
return sslSocket->sslConfiguration();
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::setSslConfiguration(const QSslConfiguration &config)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
if (d->connection)
|
||||
d->connection->setSslConfiguration(config);
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::ignoreSslErrors()
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
if (d->connection)
|
||||
d->connection->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_D(QHttpNetworkReply);
|
||||
if (d->connection)
|
||||
d->connection->ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#include "moc_qhttpnetworkreply_p.h"
|
||||
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPNETWORKREPLY_H
|
||||
#define QHTTPNETWORKREPLY_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#include <qplatformdefs.h>
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
// it's safe to include these even if SSL support is not enabled
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qsslerror.h>
|
||||
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include "qbuffer.h"
|
||||
|
||||
#include "qobject_p.h"
|
||||
#include "qhttpnetworkheader_p.h"
|
||||
#include "qhttpnetworkrequest_p.h"
|
||||
#include "qauthenticator_p.h"
|
||||
#include "qbytedata_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpNetworkConnection;
|
||||
class QHttpNetworkConnectionChannel;
|
||||
class QHttpNetworkRequest;
|
||||
class QHttpNetworkConnectionPrivate;
|
||||
class QHttpNetworkReplyPrivate;
|
||||
class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
explicit QHttpNetworkReply(const QUrl &url = QUrl(), QObject *parent = nullptr);
|
||||
virtual ~QHttpNetworkReply();
|
||||
|
||||
QUrl url() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
int majorVersion() const;
|
||||
int minorVersion() const;
|
||||
|
||||
qint64 contentLength() const;
|
||||
void setContentLength(qint64 length);
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> > header() const;
|
||||
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
|
||||
void setHeaderField(const QByteArray &name, const QByteArray &data);
|
||||
void parseHeader(const QByteArray &header); // mainly for testing
|
||||
|
||||
QHttpNetworkRequest request() const;
|
||||
void setRequest(const QHttpNetworkRequest &request);
|
||||
|
||||
int statusCode() const;
|
||||
void setStatusCode(int code);
|
||||
|
||||
QString errorString() const;
|
||||
void setErrorString(const QString &error);
|
||||
|
||||
QString reasonPhrase() const;
|
||||
|
||||
qint64 bytesAvailable() const;
|
||||
qint64 bytesAvailableNextBlock() const;
|
||||
bool readAnyAvailable() const;
|
||||
QByteArray readAny();
|
||||
QByteArray readAll();
|
||||
QByteArray read(qint64 amount);
|
||||
qint64 sizeNextBlock();
|
||||
void setDownstreamLimited(bool t);
|
||||
void setReadBufferSize(qint64 size);
|
||||
|
||||
bool supportsUserProvidedDownloadBuffer();
|
||||
void setUserProvidedDownloadBuffer(char*);
|
||||
char* userProvidedDownloadBuffer();
|
||||
|
||||
bool isFinished() const;
|
||||
|
||||
bool isPipeliningUsed() const;
|
||||
|
||||
QHttpNetworkConnection* connection();
|
||||
|
||||
QSslConfiguration sslConfiguration() const;
|
||||
void setSslConfiguration(const QSslConfiguration &config);
|
||||
void ignoreSslErrors();
|
||||
void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
Q_SIGNALS:
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
Q_SIGNALS:
|
||||
void readyRead();
|
||||
void finished();
|
||||
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
|
||||
void headerChanged();
|
||||
// FIXME we need to change this to qint64!
|
||||
void dataReadProgress(int done, int total);
|
||||
void dataSendProgress(qint64 done, qint64 total);
|
||||
void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
|
||||
#endif
|
||||
void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QHttpNetworkReply)
|
||||
friend class QHttpNetworkConnection;
|
||||
friend class QHttpNetworkConnectionPrivate;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
};
|
||||
|
||||
|
||||
class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
|
||||
{
|
||||
public:
|
||||
QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
|
||||
~QHttpNetworkReplyPrivate();
|
||||
qint64 readStatus(QAbstractSocket *socket);
|
||||
bool parseStatus(const QByteArray &status);
|
||||
qint64 readHeader(QAbstractSocket *socket);
|
||||
void parseHeader(const QByteArray &header);
|
||||
qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
|
||||
qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
|
||||
qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
|
||||
bool findChallenge(bool forProxy, QByteArray &challenge) const;
|
||||
QAuthenticatorPrivate::Method authenticationMethod(bool isProxy) const;
|
||||
void clear();
|
||||
void clearHttpLayerInformation();
|
||||
|
||||
qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
|
||||
qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
|
||||
qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
|
||||
|
||||
void appendUncompressedReplyData(QByteArray &qba);
|
||||
void appendUncompressedReplyData(QByteDataBuffer &data);
|
||||
void appendCompressedReplyData(QByteDataBuffer &data);
|
||||
|
||||
bool shouldEmitSignals();
|
||||
bool expectContent();
|
||||
void eraseData();
|
||||
|
||||
qint64 bytesAvailable() const;
|
||||
bool isChunked();
|
||||
bool isConnectionCloseEnabled();
|
||||
bool isGzipped();
|
||||
bool gunzipBody(QByteArray &compressed, QByteArray &inflated);
|
||||
void removeAutoDecompressHeader();
|
||||
|
||||
enum ReplyState {
|
||||
NothingDoneState,
|
||||
ReadingStatusState,
|
||||
ReadingHeaderState,
|
||||
ReadingDataState,
|
||||
AllDoneState
|
||||
} state;
|
||||
|
||||
QHttpNetworkRequest request;
|
||||
bool ssl;
|
||||
int statusCode;
|
||||
int majorVersion;
|
||||
int minorVersion;
|
||||
QString errorString;
|
||||
QString reasonPhrase;
|
||||
qint64 bodyLength;
|
||||
qint64 contentRead;
|
||||
qint64 totalProgress;
|
||||
QByteArray fragment; // used for header, status, chunk header etc, not for reply data
|
||||
bool chunkedTransferEncoding;
|
||||
bool connectionCloseEnabled;
|
||||
bool forceConnectionCloseEnabled;
|
||||
bool lastChunkRead;
|
||||
qint64 currentChunkSize;
|
||||
qint64 currentChunkRead;
|
||||
qint64 readBufferMaxSize;
|
||||
QPointer<QHttpNetworkConnection> connection;
|
||||
QPointer<QHttpNetworkConnectionChannel> connectionChannel;
|
||||
bool autoDecompress;
|
||||
|
||||
QByteDataBuffer responseData; // uncompressed body
|
||||
QByteArray compressedData; // compressed body (temporary)
|
||||
bool requestIsPrepared;
|
||||
|
||||
bool pipeliningUsed;
|
||||
bool downstreamLimited;
|
||||
|
||||
char* userProvidedDownloadBuffer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
//Q_DECLARE_METATYPE(QHttpNetworkReply)
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
|
||||
#endif // QHTTPNETWORKREPLY_H
|
|
@ -1,300 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpnetworkrequest_p.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
|
||||
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
|
||||
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
|
||||
autoDecompress(false), pipeliningAllowed(false), withCredentials(true)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other)
|
||||
: QHttpNetworkHeaderPrivate(other)
|
||||
{
|
||||
operation = other.operation;
|
||||
priority = other.priority;
|
||||
uploadByteDevice = other.uploadByteDevice;
|
||||
autoDecompress = other.autoDecompress;
|
||||
pipeliningAllowed = other.pipeliningAllowed;
|
||||
customVerb = other.customVerb;
|
||||
withCredentials = other.withCredentials;
|
||||
ssl = other.ssl;
|
||||
}
|
||||
|
||||
QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &other) const
|
||||
{
|
||||
return QHttpNetworkHeaderPrivate::operator==(other)
|
||||
&& (operation == other.operation)
|
||||
&& (ssl == other.ssl)
|
||||
&& (uploadByteDevice == other.uploadByteDevice);
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkRequestPrivate::methodName() const
|
||||
{
|
||||
switch (operation) {
|
||||
case QHttpNetworkRequest::Get:
|
||||
return "GET";
|
||||
case QHttpNetworkRequest::Head:
|
||||
return "HEAD";
|
||||
case QHttpNetworkRequest::Post:
|
||||
return "POST";
|
||||
case QHttpNetworkRequest::Options:
|
||||
return "OPTIONS";
|
||||
case QHttpNetworkRequest::Put:
|
||||
return "PUT";
|
||||
case QHttpNetworkRequest::Delete:
|
||||
return "DELETE";
|
||||
case QHttpNetworkRequest::Trace:
|
||||
return "TRACE";
|
||||
case QHttpNetworkRequest::Connect:
|
||||
return "CONNECT";
|
||||
case QHttpNetworkRequest::Custom:
|
||||
return customVerb;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkRequestPrivate::uri(bool throughProxy) const
|
||||
{
|
||||
QUrl::FormattingOptions format(QUrl::RemoveFragment);
|
||||
|
||||
// for POST, query data is send as content
|
||||
if (operation == QHttpNetworkRequest::Post && !uploadByteDevice)
|
||||
format |= QUrl::RemoveQuery;
|
||||
// for requests through proxy, the Request-URI contains full url
|
||||
if (throughProxy)
|
||||
format |= QUrl::RemoveUserInfo;
|
||||
else
|
||||
format |= QUrl::RemoveScheme | QUrl::RemoveAuthority;
|
||||
QByteArray uri = url.toEncoded(format);
|
||||
if (uri.isEmpty() || (throughProxy && url.path().isEmpty()))
|
||||
uri += '/';
|
||||
return uri;
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
|
||||
{
|
||||
QList<QPair<QByteArray, QByteArray> > fields = request.header();
|
||||
QByteArray ba;
|
||||
ba.reserve(40 + fields.length()*25); // very rough lower bound estimation
|
||||
|
||||
ba += request.d->methodName();
|
||||
ba += ' ';
|
||||
ba += request.d->uri(throughProxy);
|
||||
|
||||
ba += " HTTP/";
|
||||
ba += QByteArray::number(request.majorVersion());
|
||||
ba += '.';
|
||||
ba += QByteArray::number(request.minorVersion());
|
||||
ba += "\r\n";
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
|
||||
QList<QPair<QByteArray, QByteArray> >::const_iterator endIt = fields.constEnd();
|
||||
for (; it != endIt; ++it) {
|
||||
ba += it->first;
|
||||
ba += ": ";
|
||||
ba += it->second;
|
||||
ba += "\r\n";
|
||||
}
|
||||
if (request.d->operation == QHttpNetworkRequest::Post) {
|
||||
// add content type, if not set in the request
|
||||
if (request.headerField("content-type").isEmpty()) {
|
||||
//Content-Type is mandatory. We can't say anything about the encoding, but x-www-form-urlencoded is the most likely to work.
|
||||
//This warning indicates a bug in application code not setting a required header.
|
||||
//Note that if using QHttpMultipart, the content-type is set in QNetworkAccessManagerPrivate::prepareMultipart already
|
||||
qWarning("content-type missing in HTTP POST, defaulting to application/x-www-form-urlencoded. Use QNetworkRequest::setHeader() to fix this problem.");
|
||||
ba += "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||
}
|
||||
if (!request.d->uploadByteDevice && request.d->url.hasQuery()) {
|
||||
QByteArray query = request.d->url.encodedQuery();
|
||||
ba += "Content-Length: ";
|
||||
ba += QByteArray::number(query.size());
|
||||
ba += "\r\n\r\n";
|
||||
ba += query;
|
||||
} else {
|
||||
ba += "\r\n";
|
||||
}
|
||||
} else {
|
||||
ba += "\r\n";
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
||||
|
||||
// QHttpNetworkRequest
|
||||
|
||||
QHttpNetworkRequest::QHttpNetworkRequest(const QUrl &url, Operation operation, Priority priority)
|
||||
: d(new QHttpNetworkRequestPrivate(operation, priority, url))
|
||||
{
|
||||
}
|
||||
|
||||
QHttpNetworkRequest::QHttpNetworkRequest(const QHttpNetworkRequest &other)
|
||||
: QHttpNetworkHeader(other), d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpNetworkRequest::~QHttpNetworkRequest()
|
||||
{
|
||||
}
|
||||
|
||||
QUrl QHttpNetworkRequest::url() const
|
||||
{
|
||||
return d->url;
|
||||
}
|
||||
void QHttpNetworkRequest::setUrl(const QUrl &url)
|
||||
{
|
||||
d->url = url;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::isSsl() const
|
||||
{
|
||||
return d->ssl;
|
||||
}
|
||||
void QHttpNetworkRequest::setSsl(bool s)
|
||||
{
|
||||
d->ssl = s;
|
||||
}
|
||||
|
||||
qint64 QHttpNetworkRequest::contentLength() const
|
||||
{
|
||||
return d->contentLength();
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setContentLength(qint64 length)
|
||||
{
|
||||
d->setContentLength(length);
|
||||
}
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
|
||||
{
|
||||
return d->fields;
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
|
||||
{
|
||||
return d->headerField(name, defaultValue);
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setHeaderField(const QByteArray &name, const QByteArray &data)
|
||||
{
|
||||
d->setHeaderField(name, data);
|
||||
}
|
||||
|
||||
QHttpNetworkRequest &QHttpNetworkRequest::operator=(const QHttpNetworkRequest &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::operator==(const QHttpNetworkRequest &other) const
|
||||
{
|
||||
return d->operator==(*other.d);
|
||||
}
|
||||
|
||||
QHttpNetworkRequest::Operation QHttpNetworkRequest::operation() const
|
||||
{
|
||||
return d->operation;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setOperation(Operation operation)
|
||||
{
|
||||
d->operation = operation;
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkRequest::customVerb() const
|
||||
{
|
||||
return d->customVerb;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setCustomVerb(const QByteArray &customVerb)
|
||||
{
|
||||
d->customVerb = customVerb;
|
||||
}
|
||||
|
||||
QHttpNetworkRequest::Priority QHttpNetworkRequest::priority() const
|
||||
{
|
||||
return d->priority;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setPriority(Priority priority)
|
||||
{
|
||||
d->priority = priority;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::isPipeliningAllowed() const
|
||||
{
|
||||
return d->pipeliningAllowed;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setPipeliningAllowed(bool b)
|
||||
{
|
||||
d->pipeliningAllowed = b;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::withCredentials() const
|
||||
{
|
||||
return d->withCredentials;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setWithCredentials(bool b)
|
||||
{
|
||||
d->withCredentials = b;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setUploadByteDevice(QNonContiguousByteDevice *bd)
|
||||
{
|
||||
d->uploadByteDevice = bd;
|
||||
}
|
||||
|
||||
QNonContiguousByteDevice* QHttpNetworkRequest::uploadByteDevice() const
|
||||
{
|
||||
return d->uploadByteDevice;
|
||||
}
|
||||
|
||||
int QHttpNetworkRequest::majorVersion() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int QHttpNetworkRequest::minorVersion() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPNETWORKREQUEST_H
|
||||
#define QHTTPNETWORKREQUEST_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include "qhttpnetworkheader_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNonContiguousByteDevice;
|
||||
|
||||
class QHttpNetworkRequestPrivate;
|
||||
class Q_AUTOTEST_EXPORT QHttpNetworkRequest: public QHttpNetworkHeader
|
||||
{
|
||||
public:
|
||||
enum Operation {
|
||||
Options,
|
||||
Get,
|
||||
Head,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Trace,
|
||||
Connect,
|
||||
Custom
|
||||
};
|
||||
|
||||
enum Priority {
|
||||
HighPriority,
|
||||
NormalPriority,
|
||||
LowPriority
|
||||
};
|
||||
|
||||
QHttpNetworkRequest(const QUrl &url = QUrl(), Operation operation = Get, Priority priority = NormalPriority);
|
||||
QHttpNetworkRequest(const QHttpNetworkRequest &other);
|
||||
virtual ~QHttpNetworkRequest();
|
||||
QHttpNetworkRequest &operator=(const QHttpNetworkRequest &other);
|
||||
bool operator==(const QHttpNetworkRequest &other) const;
|
||||
|
||||
QUrl url() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
int majorVersion() const;
|
||||
int minorVersion() const;
|
||||
|
||||
qint64 contentLength() const;
|
||||
void setContentLength(qint64 length);
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> > header() const;
|
||||
QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
|
||||
void setHeaderField(const QByteArray &name, const QByteArray &data);
|
||||
|
||||
Operation operation() const;
|
||||
void setOperation(Operation operation);
|
||||
|
||||
QByteArray customVerb() const;
|
||||
void setCustomVerb(const QByteArray &customOperation);
|
||||
|
||||
Priority priority() const;
|
||||
void setPriority(Priority priority);
|
||||
|
||||
bool isPipeliningAllowed() const;
|
||||
void setPipeliningAllowed(bool b);
|
||||
|
||||
bool withCredentials() const;
|
||||
void setWithCredentials(bool b);
|
||||
|
||||
bool isSsl() const;
|
||||
void setSsl(bool);
|
||||
|
||||
void setUploadByteDevice(QNonContiguousByteDevice *bd);
|
||||
QNonContiguousByteDevice* uploadByteDevice() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QHttpNetworkRequestPrivate> d;
|
||||
friend class QHttpNetworkRequestPrivate;
|
||||
friend class QHttpNetworkConnectionPrivate;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
};
|
||||
|
||||
class QHttpNetworkRequestPrivate : public QHttpNetworkHeaderPrivate
|
||||
{
|
||||
public:
|
||||
QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
|
||||
QHttpNetworkRequest::Priority pri, const QUrl &newUrl = QUrl());
|
||||
QHttpNetworkRequestPrivate(const QHttpNetworkRequestPrivate &other);
|
||||
~QHttpNetworkRequestPrivate();
|
||||
bool operator==(const QHttpNetworkRequestPrivate &other) const;
|
||||
QByteArray methodName() const;
|
||||
QByteArray uri(bool throughProxy) const;
|
||||
|
||||
static QByteArray header(const QHttpNetworkRequest &request, bool throughProxy);
|
||||
|
||||
QHttpNetworkRequest::Operation operation;
|
||||
QByteArray customVerb;
|
||||
QHttpNetworkRequest::Priority priority;
|
||||
mutable QNonContiguousByteDevice* uploadByteDevice;
|
||||
bool autoDecompress;
|
||||
bool pipeliningAllowed;
|
||||
bool withCredentials;
|
||||
bool ssl;
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
//Q_DECLARE_METATYPE(QHttpNetworkRequest)
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
|
||||
#endif // QHTTPNETWORKREQUEST_H
|
|
@ -1,611 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//#define QHTTPTHREADDELEGATE_DEBUG
|
||||
#include "qhttpthreaddelegate_p.h"
|
||||
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QAuthenticator>
|
||||
#include <QEventLoop>
|
||||
|
||||
#include "qhttpnetworkreply_p.h"
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
#include "qnetworkcommon_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
|
||||
{
|
||||
QNetworkReply::NetworkError code;
|
||||
// we've got an error
|
||||
switch (httpStatusCode) {
|
||||
case 401: // Authorization required
|
||||
code = QNetworkReply::AuthenticationRequiredError;
|
||||
break;
|
||||
|
||||
case 403: // Access denied
|
||||
code = QNetworkReply::ContentOperationNotPermittedError;
|
||||
break;
|
||||
|
||||
case 404: // Not Found
|
||||
code = QNetworkReply::ContentNotFoundError;
|
||||
break;
|
||||
|
||||
case 405: // Method Not Allowed
|
||||
code = QNetworkReply::ContentOperationNotPermittedError;
|
||||
break;
|
||||
|
||||
case 407:
|
||||
code = QNetworkReply::ProxyAuthenticationRequiredError;
|
||||
break;
|
||||
|
||||
case 418: // I'm a teapot
|
||||
code = QNetworkReply::ProtocolInvalidOperationError;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
if (httpStatusCode > 500) {
|
||||
// some kind of server error
|
||||
code = QNetworkReply::ProtocolUnknownError;
|
||||
} else if (httpStatusCode >= 400) {
|
||||
// content error we did not handle above
|
||||
code = QNetworkReply::UnknownContentError;
|
||||
} else {
|
||||
qWarning("QNetworkAccess: got HTTP status code %d which is not expected from url: \"%s\"",
|
||||
httpStatusCode, qPrintable(url.toString()));
|
||||
code = QNetworkReply::ProtocolFailure;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
|
||||
{
|
||||
QByteArray result;
|
||||
QUrl copy = url;
|
||||
bool isEncrypted = copy.scheme().toLower() == QLatin1String("https");
|
||||
copy.setPort(copy.port(isEncrypted ? 443 : 80));
|
||||
result = copy.toEncoded(QUrl::RemoveUserInfo | QUrl::RemovePath |
|
||||
QUrl::RemoveQuery | QUrl::RemoveFragment);
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
if (proxy && proxy->type() != QNetworkProxy::NoProxy) {
|
||||
QUrl key;
|
||||
|
||||
switch (proxy->type()) {
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
key.setScheme(QLatin1String("proxy-socks5"));
|
||||
break;
|
||||
|
||||
case QNetworkProxy::HttpProxy:
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
key.setScheme(QLatin1String("proxy-http"));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key.scheme().isEmpty()) {
|
||||
key.setUserName(proxy->user());
|
||||
key.setHost(proxy->hostName());
|
||||
key.setPort(proxy->port());
|
||||
key.setEncodedQuery(result);
|
||||
result = key.toEncoded();
|
||||
}
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(proxy)
|
||||
#endif
|
||||
|
||||
return "http-connection:" + result;
|
||||
}
|
||||
|
||||
class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
|
||||
public QNetworkAccessCache::CacheableObject
|
||||
{
|
||||
// Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt)
|
||||
: QHttpNetworkConnection(hostName, port, encrypt)
|
||||
{
|
||||
setExpires(true);
|
||||
setShareable(true);
|
||||
}
|
||||
|
||||
virtual void dispose()
|
||||
{
|
||||
#if 0 // sample code; do this right with the API
|
||||
Q_ASSERT(!isWorking());
|
||||
#endif
|
||||
delete this;
|
||||
}
|
||||
};
|
||||
|
||||
thread_local QNetworkAccessCache* QHttpThreadDelegate::connections = nullptr;
|
||||
|
||||
QHttpThreadDelegate::~QHttpThreadDelegate()
|
||||
{
|
||||
// It could be that the main thread has asked us to shut down, so we need to delete the HTTP reply
|
||||
if (httpReply) {
|
||||
delete httpReply;
|
||||
}
|
||||
|
||||
// Get the object cache that stores our QHttpNetworkConnection objects
|
||||
// and release the entry for this QHttpNetworkConnection
|
||||
if (connections && !cacheKey.isEmpty()) {
|
||||
connections->releaseEntry(cacheKey);
|
||||
delete connections;
|
||||
connections = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
|
||||
QObject(parent)
|
||||
, ssl(false)
|
||||
, downloadBufferMaximumSize(0)
|
||||
, readBufferMaxSize(0)
|
||||
, bytesEmitted(0)
|
||||
, pendingDownloadData(0)
|
||||
, pendingDownloadProgress(0)
|
||||
, synchronous(false)
|
||||
, incomingStatusCode(0)
|
||||
, isPipeliningUsed(false)
|
||||
, incomingContentLength(-1)
|
||||
, incomingErrorCode(QNetworkReply::NoError)
|
||||
, downloadBuffer(0)
|
||||
, httpConnection(0)
|
||||
, httpReply(0)
|
||||
, synchronousRequestLoop(0)
|
||||
{
|
||||
}
|
||||
|
||||
// This is invoked as BlockingQueuedConnection from QNetworkAccessHttpBackend in the user thread
|
||||
void QHttpThreadDelegate::startRequestSynchronously()
|
||||
{
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId();
|
||||
#endif
|
||||
synchronous = true;
|
||||
|
||||
QEventLoop synchronousRequestLoop;
|
||||
this->synchronousRequestLoop = &synchronousRequestLoop;
|
||||
|
||||
// Worst case timeout
|
||||
QTimer::singleShot(30*1000, this, SLOT(abortRequest()));
|
||||
|
||||
QMetaObject::invokeMethod(this, "startRequest", Qt::QueuedConnection);
|
||||
synchronousRequestLoop.exec();
|
||||
|
||||
connections->releaseEntry(cacheKey);
|
||||
delete connections;
|
||||
connections = nullptr;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::startRequestSynchronously() thread=" << QThread::currentThreadId() << "finished";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// This is invoked as QueuedConnection from QNetworkAccessHttpBackend in the user thread
|
||||
void QHttpThreadDelegate::startRequest()
|
||||
{
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::startRequest() thread=" << QThread::currentThreadId();
|
||||
#endif
|
||||
// Check for the QNetworkAccessCache
|
||||
// If not there, create this connection cache
|
||||
if (!connections) {
|
||||
connections = new QNetworkAccessCache();
|
||||
}
|
||||
|
||||
// check if we have an open connection to this host
|
||||
QUrl urlCopy = httpRequest.url();
|
||||
urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
if (transparentProxy.type() != QNetworkProxy::NoProxy)
|
||||
cacheKey = makeCacheKey(urlCopy, &transparentProxy);
|
||||
else if (cacheProxy.type() != QNetworkProxy::NoProxy)
|
||||
cacheKey = makeCacheKey(urlCopy, &cacheProxy);
|
||||
else
|
||||
#endif
|
||||
cacheKey = makeCacheKey(urlCopy, 0);
|
||||
|
||||
|
||||
// the http object is actually a QHttpNetworkConnection
|
||||
httpConnection = static_cast<QNetworkAccessCachedHttpConnection *>(connections->requestEntryNow(cacheKey));
|
||||
if (httpConnection == 0) {
|
||||
// no entry in cache; create an object
|
||||
// the http object is actually a QHttpNetworkConnection
|
||||
httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl);
|
||||
// Set the QSslConfiguration from this QNetworkRequest.
|
||||
if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {
|
||||
httpConnection->setSslConfiguration(incomingSslConfiguration);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
httpConnection->setTransparentProxy(transparentProxy);
|
||||
httpConnection->setCacheProxy(cacheProxy);
|
||||
#endif
|
||||
|
||||
// cache the QHttpNetworkConnection corresponding to this cache key
|
||||
connections->addEntry(cacheKey, httpConnection);
|
||||
}
|
||||
|
||||
|
||||
// Send the request to the connection
|
||||
httpReply = httpConnection->sendRequest(httpRequest);
|
||||
httpReply->setParent(this);
|
||||
|
||||
// Connect the reply signals that we need to handle and then forward
|
||||
if (synchronous) {
|
||||
connect(httpReply,SIGNAL(headerChanged()), this, SLOT(synchronousHeaderChangedSlot()));
|
||||
connect(httpReply,SIGNAL(finished()), this, SLOT(synchronousFinishedSlot()));
|
||||
connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
|
||||
this, SLOT(synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
|
||||
|
||||
connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
|
||||
this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
|
||||
connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
|
||||
this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
|
||||
|
||||
// Don't care about ignored SSL errors for now in the synchronous HTTP case.
|
||||
} else if (!synchronous) {
|
||||
connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
|
||||
connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
|
||||
connect(httpReply,SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
|
||||
this, SLOT(finishedWithErrorSlot(QNetworkReply::NetworkError,QString)));
|
||||
// some signals are only interesting when normal asynchronous style is used
|
||||
connect(httpReply,SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
connect(httpReply,SIGNAL(dataReadProgress(int,int)), this, SLOT(dataReadProgressSlot(int,int)));
|
||||
connect(httpReply,SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrorsSlot(QList<QSslError>)));
|
||||
|
||||
// In the asynchronous HTTP case we can just forward those signals
|
||||
// Connect the reply signals that we can directly forward
|
||||
connect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
|
||||
this, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
|
||||
connect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
|
||||
this, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
|
||||
}
|
||||
|
||||
connect(httpReply, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
|
||||
this, SLOT(cacheCredentialsSlot(QHttpNetworkRequest,QAuthenticator*)));
|
||||
}
|
||||
|
||||
// This gets called from the user thread or by the synchronous HTTP timeout timer
|
||||
void QHttpThreadDelegate::abortRequest()
|
||||
{
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::abortRequest() thread=" << QThread::currentThreadId() << "sync=" << synchronous;
|
||||
#endif
|
||||
if (httpReply) {
|
||||
delete httpReply;
|
||||
httpReply = 0;
|
||||
}
|
||||
|
||||
// Got aborted by the timeout timer
|
||||
if (synchronous) {
|
||||
incomingErrorCode = QNetworkReply::TimeoutError;
|
||||
QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
|
||||
} else {
|
||||
//only delete this for asynchronous mode or QNetworkAccessHttpBackend will crash - see QNetworkAccessHttpBackend::postRequest()
|
||||
this->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::readBufferSizeChanged(qint64 size)
|
||||
{
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::readBufferSizeChanged() size " << size;
|
||||
#endif
|
||||
if (httpReply) {
|
||||
httpReply->setDownstreamLimited(true);
|
||||
httpReply->setReadBufferSize(size);
|
||||
readBufferMaxSize = size;
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::readBufferFreed(qint64 size)
|
||||
{
|
||||
if (readBufferMaxSize) {
|
||||
bytesEmitted -= size;
|
||||
|
||||
QMetaObject::invokeMethod(this, "readyReadSlot", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::readyReadSlot()
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
// Don't do in zerocopy case
|
||||
if (!downloadBuffer.isNull())
|
||||
return;
|
||||
|
||||
if (readBufferMaxSize) {
|
||||
if (bytesEmitted < readBufferMaxSize) {
|
||||
qint64 sizeEmitted = 0;
|
||||
while (httpReply->readAnyAvailable() && (sizeEmitted < (readBufferMaxSize-bytesEmitted))) {
|
||||
if (httpReply->sizeNextBlock() > (readBufferMaxSize-bytesEmitted)) {
|
||||
sizeEmitted = readBufferMaxSize-bytesEmitted;
|
||||
bytesEmitted += sizeEmitted;
|
||||
pendingDownloadData->fetchAndAddRelease(1);
|
||||
emit downloadData(httpReply->read(sizeEmitted));
|
||||
} else {
|
||||
sizeEmitted = httpReply->sizeNextBlock();
|
||||
bytesEmitted += sizeEmitted;
|
||||
pendingDownloadData->fetchAndAddRelease(1);
|
||||
emit downloadData(httpReply->readAny());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// We need to wait until we empty data from the read buffer in the reply.
|
||||
}
|
||||
} else {
|
||||
while (httpReply->readAnyAvailable()) {
|
||||
pendingDownloadData->fetchAndAddRelease(1);
|
||||
emit downloadData(httpReply->readAny());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::finishedSlot()
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::finishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
|
||||
#endif
|
||||
|
||||
// If there is still some data left emit that now
|
||||
while (httpReply->readAnyAvailable()) {
|
||||
pendingDownloadData->fetchAndAddRelease(1);
|
||||
emit downloadData(httpReply->readAny());
|
||||
}
|
||||
|
||||
if (ssl)
|
||||
emit sslConfigurationChanged(httpReply->sslConfiguration());
|
||||
|
||||
if (httpReply->statusCode() >= 400) {
|
||||
// it's an error reply
|
||||
QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
|
||||
"Error downloading %1 - server replied: %2"));
|
||||
msg = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase());
|
||||
emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
|
||||
}
|
||||
|
||||
emit downloadFinished();
|
||||
|
||||
QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
|
||||
httpReply = 0;
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::synchronousFinishedSlot()
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::synchronousFinishedSlot() thread=" << QThread::currentThreadId() << "result=" << httpReply->statusCode();
|
||||
#endif
|
||||
if (httpReply->statusCode() >= 400) {
|
||||
// it's an error reply
|
||||
QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
|
||||
"Error downloading %1 - server replied: %2"));
|
||||
incomingErrorDetail = msg.arg(QString::fromAscii(httpRequest.url().toEncoded()), httpReply->reasonPhrase());
|
||||
incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
|
||||
}
|
||||
|
||||
synchronousDownloadData = httpReply->readAll();
|
||||
|
||||
QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
|
||||
httpReply = 0;
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::finishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
|
||||
#endif
|
||||
|
||||
if (ssl)
|
||||
emit sslConfigurationChanged(httpReply->sslConfiguration());
|
||||
emit error(errorCode,detail);
|
||||
emit downloadFinished();
|
||||
|
||||
|
||||
QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "deleteLater", Qt::QueuedConnection);
|
||||
httpReply = 0;
|
||||
}
|
||||
|
||||
|
||||
void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail)
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::synchronousFinishedWithErrorSlot() thread=" << QThread::currentThreadId() << "error=" << errorCode << detail;
|
||||
#endif
|
||||
incomingErrorCode = errorCode;
|
||||
incomingErrorDetail = detail;
|
||||
|
||||
QMetaObject::invokeMethod(httpReply, "deleteLater", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(synchronousRequestLoop, "quit", Qt::QueuedConnection);
|
||||
httpReply = 0;
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::headerChangedSlot()
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::headerChangedSlot() thread=" << QThread::currentThreadId();
|
||||
#endif
|
||||
|
||||
if (ssl)
|
||||
emit sslConfigurationChanged(httpReply->sslConfiguration());
|
||||
|
||||
// Is using a zerocopy buffer allowed by user and possible with this reply?
|
||||
if (httpReply->supportsUserProvidedDownloadBuffer()
|
||||
&& downloadBufferMaximumSize > 0) {
|
||||
Q_ASSERT(httpReply->contentLength() >= 1);
|
||||
char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
|
||||
if (buf) {
|
||||
downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
|
||||
httpReply->setUserProvidedDownloadBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
// We fetch this into our own
|
||||
incomingHeaders = httpReply->header();
|
||||
incomingStatusCode = httpReply->statusCode();
|
||||
incomingReasonPhrase = httpReply->reasonPhrase();
|
||||
isPipeliningUsed = httpReply->isPipeliningUsed();
|
||||
incomingContentLength = httpReply->contentLength();
|
||||
|
||||
emit downloadMetaData(incomingHeaders,
|
||||
incomingStatusCode,
|
||||
incomingReasonPhrase,
|
||||
isPipeliningUsed,
|
||||
downloadBuffer,
|
||||
incomingContentLength);
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::synchronousHeaderChangedSlot()
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::synchronousHeaderChangedSlot() thread=" << QThread::currentThreadId();
|
||||
#endif
|
||||
// Store the information we need in this object, the QNetworkAccessHttpBackend will later read it
|
||||
incomingHeaders = httpReply->header();
|
||||
incomingStatusCode = httpReply->statusCode();
|
||||
incomingReasonPhrase = httpReply->reasonPhrase();
|
||||
isPipeliningUsed = httpReply->isPipeliningUsed();
|
||||
incomingContentLength = httpReply->contentLength();
|
||||
}
|
||||
|
||||
|
||||
void QHttpThreadDelegate::dataReadProgressSlot(int done, int total)
|
||||
{
|
||||
// If we don't have a download buffer don't attempt to go this codepath
|
||||
// It is not used by QNetworkAccessHttpBackend
|
||||
if (downloadBuffer.isNull())
|
||||
return;
|
||||
|
||||
pendingDownloadProgress->fetchAndAddRelease(1);
|
||||
emit downloadProgress(done, total);
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator)
|
||||
{
|
||||
authenticationManager->cacheCredentials(request.url(), authenticator);
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::sslErrorsSlot(const QList<QSslError> &errors)
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
emit sslConfigurationChanged(httpReply->sslConfiguration());
|
||||
|
||||
bool ignoreAll = false;
|
||||
QList<QSslError> specificErrors;
|
||||
emit sslErrors(errors, &ignoreAll, &specificErrors);
|
||||
if (ignoreAll)
|
||||
httpReply->ignoreSslErrors();
|
||||
if (!specificErrors.isEmpty())
|
||||
httpReply->ignoreSslErrors(specificErrors);
|
||||
}
|
||||
|
||||
void QHttpThreadDelegate::synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *a)
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
Q_UNUSED(request);
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::synchronousAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
|
||||
#endif
|
||||
|
||||
// Ask the credential cache
|
||||
QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedCredentials(httpRequest.url(), a);
|
||||
if (!credential.isNull()) {
|
||||
a->setUser(credential.user);
|
||||
a->setPassword(credential.password);
|
||||
}
|
||||
|
||||
// Disconnect this connection now since we only want to ask the authentication cache once.
|
||||
QObject::disconnect(httpReply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
|
||||
this, SLOT(synchronousAuthenticationRequiredSlot(QHttpNetworkRequest,QAuthenticator*)));
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &p, QAuthenticator *a)
|
||||
{
|
||||
if (!httpReply)
|
||||
return;
|
||||
|
||||
#ifdef QHTTPTHREADDELEGATE_DEBUG
|
||||
qDebug() << "QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot() thread=" << QThread::currentThreadId();
|
||||
#endif
|
||||
// Ask the credential cache
|
||||
QNetworkAuthenticationCredential credential = authenticationManager->fetchCachedProxyCredentials(p, a);
|
||||
if (!credential.isNull()) {
|
||||
a->setUser(credential.user);
|
||||
a->setPassword(credential.password);
|
||||
}
|
||||
|
||||
// Disconnect this connection now since we only want to ask the authentication cache once.
|
||||
QObject::disconnect(httpReply, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
|
||||
this, SLOT(synchronousProxyAuthenticationRequiredSlot(QNetworkProxy,QAuthenticator*)));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#include "moc_qhttpthreaddelegate.cpp"
|
||||
|
||||
#include "moc_qhttpthreaddelegate_p.h"
|
|
@ -1,291 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPTHREADDELEGATE_H
|
||||
#define QHTTPTHREADDELEGATE_H
|
||||
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkProxy>
|
||||
#include <QSslConfiguration>
|
||||
#include <QSslError>
|
||||
#include <QList>
|
||||
#include <QNetworkReply>
|
||||
#include "qhttpnetworkrequest_p.h"
|
||||
#include "qhttpnetworkconnection_p.h"
|
||||
#include <QSharedPointer>
|
||||
#include "qsslconfiguration.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
#include "qnetworkaccessauthenticationmanager_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAuthenticator;
|
||||
class QHttpNetworkReply;
|
||||
class QEventLoop;
|
||||
class QNetworkAccessCache;
|
||||
class QNetworkAccessCachedHttpConnection;
|
||||
|
||||
class QHttpThreadDelegate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QHttpThreadDelegate(QObject *parent = nullptr);
|
||||
|
||||
~QHttpThreadDelegate();
|
||||
|
||||
// incoming
|
||||
bool ssl;
|
||||
QSslConfiguration incomingSslConfiguration;
|
||||
QHttpNetworkRequest httpRequest;
|
||||
qint64 downloadBufferMaximumSize;
|
||||
qint64 readBufferMaxSize;
|
||||
qint64 bytesEmitted;
|
||||
// From backend, modified by us for signal compression
|
||||
QSharedPointer<QAtomicInt> pendingDownloadData;
|
||||
QSharedPointer<QAtomicInt> pendingDownloadProgress;
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QNetworkProxy cacheProxy;
|
||||
QNetworkProxy transparentProxy;
|
||||
#endif
|
||||
QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
|
||||
bool synchronous;
|
||||
|
||||
// outgoing, Retrieved in the synchronous HTTP case
|
||||
QByteArray synchronousDownloadData;
|
||||
QList<QPair<QByteArray,QByteArray> > incomingHeaders;
|
||||
int incomingStatusCode;
|
||||
QString incomingReasonPhrase;
|
||||
bool isPipeliningUsed;
|
||||
qint64 incomingContentLength;
|
||||
QNetworkReply::NetworkError incomingErrorCode;
|
||||
QString incomingErrorDetail;
|
||||
|
||||
protected:
|
||||
// The zerocopy download buffer, if used:
|
||||
QSharedPointer<char> downloadBuffer;
|
||||
// The QHttpNetworkConnection that is used
|
||||
QNetworkAccessCachedHttpConnection *httpConnection;
|
||||
QByteArray cacheKey;
|
||||
QHttpNetworkReply *httpReply;
|
||||
|
||||
// Used for implementing the synchronous HTTP, see startRequestSynchronously()
|
||||
QEventLoop *synchronousRequestLoop;
|
||||
|
||||
signals:
|
||||
void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *);
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *);
|
||||
#endif
|
||||
void sslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
|
||||
void sslConfigurationChanged(const QSslConfiguration);
|
||||
void downloadMetaData(QList<QPair<QByteArray,QByteArray> >,int,QString,bool,QSharedPointer<char>,qint64);
|
||||
void downloadProgress(qint64, qint64);
|
||||
void downloadData(QByteArray);
|
||||
void error(QNetworkReply::NetworkError, const QString);
|
||||
void downloadFinished();
|
||||
public slots:
|
||||
// This are called via QueuedConnection from user thread
|
||||
void startRequest();
|
||||
void abortRequest();
|
||||
void readBufferSizeChanged(qint64 size);
|
||||
void readBufferFreed(qint64 size);
|
||||
// This is called with a BlockingQueuedConnection from user thread
|
||||
void startRequestSynchronously();
|
||||
protected slots:
|
||||
// From QHttp*
|
||||
void readyReadSlot();
|
||||
void finishedSlot();
|
||||
void finishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
|
||||
void synchronousFinishedSlot();
|
||||
void synchronousFinishedWithErrorSlot(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
|
||||
void headerChangedSlot();
|
||||
void synchronousHeaderChangedSlot();
|
||||
void dataReadProgressSlot(int done, int total);
|
||||
void cacheCredentialsSlot(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
|
||||
void sslErrorsSlot(const QList<QSslError> &errors);
|
||||
|
||||
void synchronousAuthenticationRequiredSlot(const QHttpNetworkRequest &request, QAuthenticator *);
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void synchronousProxyAuthenticationRequiredSlot(const QNetworkProxy &, QAuthenticator *);
|
||||
#endif
|
||||
|
||||
protected:
|
||||
// Cache for all the QHttpNetworkConnection objects.
|
||||
// This is per thread.
|
||||
static thread_local QNetworkAccessCache* connections;
|
||||
|
||||
};
|
||||
|
||||
// This QNonContiguousByteDevice is connected to the QNetworkAccessHttpBackend
|
||||
// and represents the PUT/POST data.
|
||||
class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevice
|
||||
{
|
||||
Q_OBJECT
|
||||
protected:
|
||||
bool wantDataPending;
|
||||
qint64 m_amount;
|
||||
char *m_data;
|
||||
QByteArray m_dataArray;
|
||||
bool m_atEnd;
|
||||
qint64 m_size;
|
||||
qint64 m_pos; // to match calls of haveDataSlot with the expected position
|
||||
public:
|
||||
QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
|
||||
: QNonContiguousByteDevice(),
|
||||
wantDataPending(false),
|
||||
m_amount(0),
|
||||
m_data(0),
|
||||
m_atEnd(aE),
|
||||
m_size(s),
|
||||
m_pos(0)
|
||||
{
|
||||
}
|
||||
|
||||
~QNonContiguousByteDeviceThreadForwardImpl()
|
||||
{
|
||||
}
|
||||
|
||||
qint64 pos()
|
||||
{
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
const char* readPointer(qint64 maximumLength, qint64 &len)
|
||||
{
|
||||
if (m_amount > 0) {
|
||||
len = m_amount;
|
||||
return m_data;
|
||||
}
|
||||
|
||||
if (m_atEnd) {
|
||||
len = -1;
|
||||
} else if (!wantDataPending) {
|
||||
len = 0;
|
||||
wantDataPending = true;
|
||||
emit wantData(maximumLength);
|
||||
} else {
|
||||
// Do nothing, we already sent a wantData signal and wait for results
|
||||
len = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool advanceReadPointer(qint64 a)
|
||||
{
|
||||
if (m_data == 0)
|
||||
return false;
|
||||
|
||||
m_amount -= a;
|
||||
m_data += a;
|
||||
m_pos += a;
|
||||
|
||||
// To main thread to inform about our state. The m_pos will be sent as a sanity check.
|
||||
emit processedData(m_pos, a);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool atEnd()
|
||||
{
|
||||
if (m_amount > 0)
|
||||
return false;
|
||||
else
|
||||
return m_atEnd;
|
||||
}
|
||||
|
||||
bool reset()
|
||||
{
|
||||
m_amount = 0;
|
||||
m_data = 0;
|
||||
m_dataArray.clear();
|
||||
|
||||
if (wantDataPending) {
|
||||
// had requested the user thread to send some data (only 1 in-flight at any moment)
|
||||
wantDataPending = false;
|
||||
}
|
||||
|
||||
// Communicate as BlockingQueuedConnection
|
||||
bool b = false;
|
||||
emit resetData(&b);
|
||||
if (b) {
|
||||
// the reset succeeded, we're at pos 0 again
|
||||
m_pos = 0;
|
||||
// the HTTP code will anyway abort the request if !b.
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
qint64 size()
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
public slots:
|
||||
// From user thread:
|
||||
void haveDataSlot(qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize)
|
||||
{
|
||||
if (pos != m_pos) {
|
||||
// Sometimes when re-sending a request in the qhttpnetwork* layer there is a pending haveData from the
|
||||
// user thread on the way to us. We need to ignore it since it is the data for the wrong(later) chunk.
|
||||
return;
|
||||
}
|
||||
wantDataPending = false;
|
||||
|
||||
m_dataArray = dataArray;
|
||||
m_data = const_cast<char*>(m_dataArray.constData());
|
||||
m_amount = dataArray.size();
|
||||
|
||||
m_atEnd = dataAtEnd;
|
||||
m_size = dataSize;
|
||||
|
||||
// This will tell the HTTP code (QHttpNetworkConnectionChannel) that we have data available now
|
||||
emit readyRead();
|
||||
}
|
||||
|
||||
signals:
|
||||
// void readyRead(); in parent class
|
||||
// void readProgress(qint64 current, qint64 total); happens in the main thread with the real bytedevice
|
||||
|
||||
// to main thread:
|
||||
void wantData(qint64);
|
||||
void processedData(qint64 pos, qint64 amount);
|
||||
void resetData(bool *b);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#endif // QHTTPTHREADDELEGATE_H
|
|
@ -1,287 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "qnetworkaccessauthenticationmanager_p.h"
|
||||
|
||||
#include "QtCore/qbuffer.h"
|
||||
#include "QtCore/qurl.h"
|
||||
#include "QtCore/qvector.h"
|
||||
#include "QtCore/qmutex.h"
|
||||
#include "QtNetwork/qauthenticator.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
|
||||
|
||||
class QNetworkAuthenticationCache: private QVector<QNetworkAuthenticationCredential>,
|
||||
public QNetworkAccessCache::CacheableObject
|
||||
{
|
||||
public:
|
||||
QNetworkAuthenticationCache()
|
||||
: QVector<QNetworkAuthenticationCredential>(), QNetworkAccessCache::CacheableObject()
|
||||
{
|
||||
setExpires(false);
|
||||
setShareable(true);
|
||||
reserve(1);
|
||||
}
|
||||
|
||||
QNetworkAuthenticationCredential *findClosestMatch(const QString &domain)
|
||||
{
|
||||
iterator it = qLowerBound(begin(), end(), domain);
|
||||
if (it == end() && !isEmpty())
|
||||
--it;
|
||||
if (it == end() || !domain.startsWith(it->domain))
|
||||
return 0;
|
||||
return &*it;
|
||||
}
|
||||
|
||||
void insert(const QString &domain, const QString &user, const QString &password)
|
||||
{
|
||||
QNetworkAuthenticationCredential *closestMatch = findClosestMatch(domain);
|
||||
if (closestMatch && closestMatch->domain == domain) {
|
||||
// we're overriding the current credentials
|
||||
closestMatch->user = user;
|
||||
closestMatch->password = password;
|
||||
} else {
|
||||
QNetworkAuthenticationCredential newCredential;
|
||||
newCredential.domain = domain;
|
||||
newCredential.user = user;
|
||||
newCredential.password = password;
|
||||
|
||||
if (closestMatch)
|
||||
QVector<QNetworkAuthenticationCredential>::insert(++closestMatch, newCredential);
|
||||
else
|
||||
QVector<QNetworkAuthenticationCredential>::insert(end(), newCredential);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void dispose() { delete this; }
|
||||
};
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QString &realm)
|
||||
{
|
||||
QUrl key;
|
||||
|
||||
switch (proxy.type()) {
|
||||
case QNetworkProxy::Socks5Proxy:
|
||||
key.setScheme(QLatin1String("proxy-socks5"));
|
||||
break;
|
||||
|
||||
case QNetworkProxy::HttpProxy:
|
||||
case QNetworkProxy::HttpCachingProxy:
|
||||
key.setScheme(QLatin1String("proxy-http"));
|
||||
break;
|
||||
|
||||
case QNetworkProxy::FtpCachingProxy:
|
||||
key.setScheme(QLatin1String("proxy-ftp"));
|
||||
break;
|
||||
|
||||
case QNetworkProxy::DefaultProxy:
|
||||
case QNetworkProxy::NoProxy:
|
||||
// shouldn't happen
|
||||
return QByteArray();
|
||||
|
||||
// no default:
|
||||
// let there be errors if a new proxy type is added in the future
|
||||
}
|
||||
|
||||
if (key.scheme().isEmpty())
|
||||
// proxy type not handled
|
||||
return QByteArray();
|
||||
|
||||
key.setUserName(proxy.user());
|
||||
key.setHost(proxy.hostName());
|
||||
key.setPort(proxy.port());
|
||||
key.setFragment(realm);
|
||||
return "auth:" + key.toEncoded();
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline QByteArray authenticationKey(const QUrl &url, const QString &realm)
|
||||
{
|
||||
QUrl copy = url;
|
||||
copy.setFragment(realm);
|
||||
return "auth:" + copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery);
|
||||
}
|
||||
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void QNetworkAccessAuthenticationManager::cacheProxyCredentials(const QNetworkProxy &p,
|
||||
const QAuthenticator *authenticator)
|
||||
{
|
||||
Q_ASSERT(authenticator);
|
||||
Q_ASSERT(p.type() != QNetworkProxy::DefaultProxy);
|
||||
Q_ASSERT(p.type() != QNetworkProxy::NoProxy);
|
||||
|
||||
QMutexLocker mutexLocker(&mutex);
|
||||
|
||||
QString realm = authenticator->realm();
|
||||
QNetworkProxy proxy = p;
|
||||
proxy.setUser(authenticator->user());
|
||||
|
||||
// don't cache null passwords, empty password may be valid though
|
||||
if (authenticator->password().isNull())
|
||||
return;
|
||||
|
||||
// Set two credentials: one with the username and one without
|
||||
do {
|
||||
// Set two credentials actually: one with and one without the realm
|
||||
do {
|
||||
QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
|
||||
if (cacheKey.isEmpty())
|
||||
return; // should not happen
|
||||
|
||||
QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
|
||||
auth->insert(QString(), authenticator->user(), authenticator->password());
|
||||
authenticationCache.addEntry(cacheKey, auth); // replace the existing one, if there's any
|
||||
|
||||
if (realm.isEmpty()) {
|
||||
break;
|
||||
} else {
|
||||
realm.clear();
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (proxy.user().isEmpty())
|
||||
break;
|
||||
else
|
||||
proxy.setUser(QString());
|
||||
} while (true);
|
||||
}
|
||||
|
||||
QNetworkAuthenticationCredential
|
||||
QNetworkAccessAuthenticationManager::fetchCachedProxyCredentials(const QNetworkProxy &p,
|
||||
const QAuthenticator *authenticator)
|
||||
{
|
||||
QNetworkProxy proxy = p;
|
||||
if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
||||
proxy = QNetworkProxy::applicationProxy();
|
||||
}
|
||||
if (!proxy.password().isEmpty())
|
||||
return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
|
||||
|
||||
QString realm;
|
||||
if (authenticator)
|
||||
realm = authenticator->realm();
|
||||
|
||||
QMutexLocker mutexLocker(&mutex);
|
||||
QByteArray cacheKey = proxyAuthenticationKey(proxy, realm);
|
||||
if (cacheKey.isEmpty())
|
||||
return QNetworkAuthenticationCredential();
|
||||
if (!authenticationCache.hasEntry(cacheKey))
|
||||
return QNetworkAuthenticationCredential();
|
||||
|
||||
QNetworkAuthenticationCache *auth =
|
||||
static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
|
||||
QNetworkAuthenticationCredential cred = *auth->findClosestMatch(QString());
|
||||
authenticationCache.releaseEntry(cacheKey);
|
||||
|
||||
// proxy cache credentials always have exactly one item
|
||||
Q_ASSERT_X(!cred.isNull(), "QNetworkAccessManager",
|
||||
"Internal inconsistency: found a cache key for a proxy, but it's empty");
|
||||
return cred;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void QNetworkAccessAuthenticationManager::cacheCredentials(const QUrl &url,
|
||||
const QAuthenticator *authenticator)
|
||||
{
|
||||
Q_ASSERT(authenticator);
|
||||
QString domain = QString::fromLatin1("/"); // FIXME: make QAuthenticator return the domain
|
||||
QString realm = authenticator->realm();
|
||||
|
||||
QMutexLocker mutexLocker(&mutex);
|
||||
|
||||
// Set two credentials actually: one with and one without the username in the URL
|
||||
QUrl copy = url;
|
||||
copy.setUserName(authenticator->user());
|
||||
do {
|
||||
QByteArray cacheKey = authenticationKey(copy, realm);
|
||||
if (authenticationCache.hasEntry(cacheKey)) {
|
||||
QNetworkAuthenticationCache *auth =
|
||||
static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
|
||||
auth->insert(domain, authenticator->user(), authenticator->password());
|
||||
authenticationCache.releaseEntry(cacheKey);
|
||||
} else {
|
||||
QNetworkAuthenticationCache *auth = new QNetworkAuthenticationCache;
|
||||
auth->insert(domain, authenticator->user(), authenticator->password());
|
||||
authenticationCache.addEntry(cacheKey, auth);
|
||||
}
|
||||
|
||||
if (copy.userName().isEmpty()) {
|
||||
break;
|
||||
} else {
|
||||
copy.setUserName(QString());
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
/*!
|
||||
Fetch the credential data from the credential cache.
|
||||
|
||||
If auth is 0 (as it is when called from createRequest()), this will try to
|
||||
look up with an empty realm. That fails in most cases for HTTP (because the
|
||||
realm is seldom empty for HTTP challenges). In any case, QHttpNetworkConnection
|
||||
never sends the credentials on the first attempt: it needs to find out what
|
||||
authentication methods the server supports.
|
||||
|
||||
For FTP, realm is always empty.
|
||||
*/
|
||||
QNetworkAuthenticationCredential
|
||||
QNetworkAccessAuthenticationManager::fetchCachedCredentials(const QUrl &url,
|
||||
const QAuthenticator *authentication)
|
||||
{
|
||||
if (!url.password().isEmpty())
|
||||
return QNetworkAuthenticationCredential(); // no need to set credentials if it already has them
|
||||
|
||||
QString realm;
|
||||
if (authentication)
|
||||
realm = authentication->realm();
|
||||
|
||||
QByteArray cacheKey = authenticationKey(url, realm);
|
||||
|
||||
QMutexLocker mutexLocker(&mutex);
|
||||
if (!authenticationCache.hasEntry(cacheKey))
|
||||
return QNetworkAuthenticationCredential();
|
||||
|
||||
QNetworkAuthenticationCache *auth =
|
||||
static_cast<QNetworkAuthenticationCache *>(authenticationCache.requestEntryNow(cacheKey));
|
||||
QNetworkAuthenticationCredential *cred = auth->findClosestMatch(url.path());
|
||||
QNetworkAuthenticationCredential ret;
|
||||
if (cred)
|
||||
ret = *cred;
|
||||
authenticationCache.releaseEntry(cacheKey);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void QNetworkAccessAuthenticationManager::clearCache()
|
||||
{
|
||||
authenticationCache.clear();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
@ -1,86 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
|
||||
#define QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "QtNetwork/qnetworkproxy.h"
|
||||
#include "QtCore/QMutex"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAuthenticator;
|
||||
class QAbstractNetworkCache;
|
||||
class QNetworkCookieJar;
|
||||
|
||||
class QNetworkAuthenticationCredential
|
||||
{
|
||||
public:
|
||||
QString domain;
|
||||
QString user;
|
||||
QString password;
|
||||
bool isNull() {
|
||||
return domain.isNull() && user.isNull() && password.isNull();
|
||||
}
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(QNetworkAuthenticationCredential, Q_MOVABLE_TYPE);
|
||||
inline bool operator<(const QNetworkAuthenticationCredential &t1, const QString &t2)
|
||||
{ return t1.domain < t2; }
|
||||
|
||||
class QNetworkAccessAuthenticationManager
|
||||
{
|
||||
public:
|
||||
QNetworkAccessAuthenticationManager() { };
|
||||
|
||||
void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
|
||||
QNetworkAuthenticationCredential fetchCachedCredentials(const QUrl &url,
|
||||
const QAuthenticator *auth = 0);
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
|
||||
QNetworkAuthenticationCredential fetchCachedProxyCredentials(const QNetworkProxy &proxy,
|
||||
const QAuthenticator *auth = 0);
|
||||
#endif
|
||||
|
||||
void clearCache();
|
||||
|
||||
protected:
|
||||
QNetworkAccessCache authenticationCache;
|
||||
QMutex mutex;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,357 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include "qhash.h"
|
||||
#include "qmutex.h"
|
||||
#include "qnetworkaccesscachebackend_p.h"
|
||||
#include "qabstractnetworkcache.h"
|
||||
#include "qhostinfo.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkAccessBackendFactoryData: public QList<QNetworkAccessBackendFactory *>
|
||||
{
|
||||
public:
|
||||
QNetworkAccessBackendFactoryData() : QList<QNetworkAccessBackendFactory *>()
|
||||
{
|
||||
valid.ref();
|
||||
}
|
||||
~QNetworkAccessBackendFactoryData()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> locker(mutex); // why do we need to lock?
|
||||
valid.deref();
|
||||
}
|
||||
|
||||
std::recursive_mutex mutex;
|
||||
//this is used to avoid (re)constructing factory data from destructors of other global classes
|
||||
static QAtomicInt valid;
|
||||
};
|
||||
Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
|
||||
QAtomicInt QNetworkAccessBackendFactoryData::valid = QAtomicInt(0);
|
||||
|
||||
QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> locker(factoryData()->mutex);
|
||||
factoryData()->append(this);
|
||||
}
|
||||
|
||||
QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
|
||||
{
|
||||
if (QNetworkAccessBackendFactoryData::valid) {
|
||||
std::lock_guard<std::recursive_mutex> locker(factoryData()->mutex);
|
||||
factoryData()->removeAll(this);
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkAccessBackend *QNetworkAccessManagerPrivate::findBackend(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request)
|
||||
{
|
||||
if (QNetworkAccessBackendFactoryData::valid) {
|
||||
std::lock_guard<std::recursive_mutex> locker(factoryData()->mutex);
|
||||
QNetworkAccessBackendFactoryData::ConstIterator it = factoryData()->constBegin(),
|
||||
end = factoryData()->constEnd();
|
||||
while (it != end) {
|
||||
QNetworkAccessBackend *backend = (*it)->create(op, request);
|
||||
if (backend) {
|
||||
backend->manager = this;
|
||||
return backend; // found a factory that handled our request
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
QNonContiguousByteDevice* QNetworkAccessBackend::createUploadByteDevice()
|
||||
{
|
||||
if (reply->outgoingDataBuffer)
|
||||
uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingDataBuffer));
|
||||
else if (reply->outgoingData) {
|
||||
uploadByteDevice = QSharedPointer<QNonContiguousByteDevice>(QNonContiguousByteDeviceFactory::create(reply->outgoingData));
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool bufferDisallowed =
|
||||
reply->request.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
|
||||
QVariant(false)) == QVariant(true);
|
||||
if (bufferDisallowed)
|
||||
uploadByteDevice->disableReset();
|
||||
|
||||
// We want signal emissions only for normal asynchronous uploads
|
||||
if (!isSynchronous())
|
||||
connect(uploadByteDevice.data(), SIGNAL(readProgress(qint64,qint64)), this, SLOT(emitReplyUploadProgress(qint64,qint64)));
|
||||
|
||||
return uploadByteDevice.data();
|
||||
}
|
||||
|
||||
// need to have this function since the reply is a private member variable
|
||||
// and the special backends need to access this.
|
||||
void QNetworkAccessBackend::emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal)
|
||||
{
|
||||
if (reply->isFinished)
|
||||
return;
|
||||
reply->emitUploadProgress(bytesSent, bytesTotal);
|
||||
}
|
||||
|
||||
QNetworkAccessBackend::QNetworkAccessBackend()
|
||||
: manager(0)
|
||||
, reply(0)
|
||||
, synchronous(false)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkAccessBackend::~QNetworkAccessBackend()
|
||||
{
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::downstreamReadyWrite()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setDownstreamLimited(bool b)
|
||||
{
|
||||
Q_UNUSED(b);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setReadBufferSize(qint64 size)
|
||||
{
|
||||
Q_UNUSED(size);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::emitReadBufferFreed(qint64 size)
|
||||
{
|
||||
Q_UNUSED(size);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::copyFinished(QIODevice *)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::ignoreSslErrors()
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_UNUSED(errors);
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::fetchSslConfiguration(QSslConfiguration &) const
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setSslConfiguration(const QSslConfiguration &)
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData QNetworkAccessBackend::fetchCacheMetaData(const QNetworkCacheMetaData &) const
|
||||
{
|
||||
return QNetworkCacheMetaData();
|
||||
}
|
||||
|
||||
QNetworkAccessManager::Operation QNetworkAccessBackend::operation() const
|
||||
{
|
||||
return reply->operation;
|
||||
}
|
||||
|
||||
QNetworkRequest QNetworkAccessBackend::request() const
|
||||
{
|
||||
return reply->request;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QList<QNetworkProxy> QNetworkAccessBackend::proxyList() const
|
||||
{
|
||||
return reply->proxyList;
|
||||
}
|
||||
#endif
|
||||
|
||||
QAbstractNetworkCache *QNetworkAccessBackend::networkCache() const
|
||||
{
|
||||
if (!manager)
|
||||
return 0;
|
||||
return manager->networkCache;
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setCachingEnabled(bool enable)
|
||||
{
|
||||
reply->setCachingEnabled(enable);
|
||||
}
|
||||
|
||||
bool QNetworkAccessBackend::isCachingEnabled() const
|
||||
{
|
||||
return reply->isCachingEnabled();
|
||||
}
|
||||
|
||||
qint64 QNetworkAccessBackend::nextDownstreamBlockSize() const
|
||||
{
|
||||
return reply->nextDownstreamBlockSize();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::writeDownstreamData(QByteDataBuffer &list)
|
||||
{
|
||||
reply->appendDownstreamData(list);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::writeDownstreamData(QIODevice *data)
|
||||
{
|
||||
reply->appendDownstreamData(data);
|
||||
}
|
||||
|
||||
// not actually appending data, it was already written to the user buffer
|
||||
void QNetworkAccessBackend::writeDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
reply->appendDownstreamDataDownloadBuffer(bytesReceived, bytesTotal);
|
||||
}
|
||||
|
||||
char* QNetworkAccessBackend::getDownloadBuffer(qint64 size)
|
||||
{
|
||||
return reply->getDownloadBuffer(size);
|
||||
}
|
||||
|
||||
QVariant QNetworkAccessBackend::header(QNetworkRequest::KnownHeaders header) const
|
||||
{
|
||||
return reply->q_func()->header(header);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
reply->setCookedHeader(header, value);
|
||||
}
|
||||
|
||||
bool QNetworkAccessBackend::hasRawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
return reply->q_func()->hasRawHeader(headerName);
|
||||
}
|
||||
|
||||
QByteArray QNetworkAccessBackend::rawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
return reply->q_func()->rawHeader(headerName);
|
||||
}
|
||||
|
||||
QList<QByteArray> QNetworkAccessBackend::rawHeaderList() const
|
||||
{
|
||||
return reply->q_func()->rawHeaderList();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
|
||||
{
|
||||
reply->setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
QVariant QNetworkAccessBackend::attribute(QNetworkRequest::Attribute code) const
|
||||
{
|
||||
return reply->q_func()->attribute(code);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
|
||||
{
|
||||
if (value.isValid())
|
||||
reply->attributes.insert(code, value);
|
||||
else
|
||||
reply->attributes.remove(code);
|
||||
}
|
||||
QUrl QNetworkAccessBackend::url() const
|
||||
{
|
||||
return reply->url;
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::setUrl(const QUrl &url)
|
||||
{
|
||||
reply->url = url;
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::finished()
|
||||
{
|
||||
reply->finished();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::error(QNetworkReply::NetworkError code, const QString &errorString)
|
||||
{
|
||||
reply->error(code, errorString);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void QNetworkAccessBackend::proxyAuthenticationRequired(const QNetworkProxy &proxy,
|
||||
QAuthenticator *authenticator)
|
||||
{
|
||||
manager->proxyAuthenticationRequired(this, proxy, authenticator);
|
||||
}
|
||||
#endif
|
||||
|
||||
void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator)
|
||||
{
|
||||
manager->authenticationRequired(this, authenticator);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::metaDataChanged()
|
||||
{
|
||||
reply->metaDataChanged();
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::redirectionRequested(const QUrl &target)
|
||||
{
|
||||
reply->redirectionRequested(target);
|
||||
}
|
||||
|
||||
void QNetworkAccessBackend::sslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
reply->sslErrors(errors);
|
||||
}
|
||||
|
||||
/*!
|
||||
Starts the backend. Returns true if the backend is started. Returns false if the backend
|
||||
could not be started due to an unopened or roaming session. The caller should recall this
|
||||
function once the session has been opened or the roaming process has finished.
|
||||
*/
|
||||
bool QNetworkAccessBackend::start()
|
||||
{
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
// the proxy depends only on the url
|
||||
reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
|
||||
#endif
|
||||
|
||||
// now start the request
|
||||
open();
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkaccessbackend_p.h"
|
||||
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSBACKEND_P_H
|
||||
#define QNETWORKACCESSBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkreplyimpl_p.h"
|
||||
#include "QtCore/qobject.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAuthenticator;
|
||||
class QNetworkProxy;
|
||||
class QNetworkProxyQuery;
|
||||
class QNetworkRequest;
|
||||
class QUrl;
|
||||
class QUrlInfo;
|
||||
class QSslConfiguration;
|
||||
|
||||
class QNetworkAccessManagerPrivate;
|
||||
class QNetworkReplyImplPrivate;
|
||||
class QAbstractNetworkCache;
|
||||
class QNetworkCacheMetaData;
|
||||
class QNonContiguousByteDevice;
|
||||
|
||||
// Should support direct file upload from disk or download to disk.
|
||||
//
|
||||
// - The HTTP handler will use two QIODevices for communication (pull mechanism)
|
||||
// - KIO uses a pull mechanism too (data/dataReq signals)
|
||||
class QNetworkAccessBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessBackend();
|
||||
virtual ~QNetworkAccessBackend();
|
||||
|
||||
// To avoid mistaking with QIODevice names, the functions here
|
||||
// have different names. The Connection has two streams:
|
||||
//
|
||||
// - Upstream:
|
||||
// The upstream uses a QNonContiguousByteDevice provided
|
||||
// by the backend. This device emits the usual readyRead()
|
||||
// signal when the backend has data available for the connection
|
||||
// to write. The different backends can listen on this signal
|
||||
// and then pull upload data from the QNonContiguousByteDevice and
|
||||
// deal with it.
|
||||
//
|
||||
//
|
||||
// - Downstream:
|
||||
// Downstream is the data that is being read from this
|
||||
// connection and is given to the user. Downstream operates in a
|
||||
// semi-"push" mechanism: the Connection object pushes the data
|
||||
// it gets from the network, but it should avoid writing too
|
||||
// much if the data isn't being used fast enough. The value
|
||||
// returned by suggestedDownstreamBlockSize() can be used to
|
||||
// determine how much should be written at a time. The
|
||||
// downstreamBytesConsumed() function will be called when the
|
||||
// downstream buffer is consumed by the user -- the Connection
|
||||
// may choose to re-fill it with more data it has ready or get
|
||||
// more data from the network (for instance, by reading from its
|
||||
// socket).
|
||||
|
||||
virtual void open() = 0;
|
||||
virtual bool start();
|
||||
virtual void closeDownstreamChannel() = 0;
|
||||
|
||||
// slot-like:
|
||||
virtual void downstreamReadyWrite();
|
||||
virtual void setDownstreamLimited(bool b);
|
||||
virtual void setReadBufferSize(qint64 size);
|
||||
virtual void emitReadBufferFreed(qint64 size);
|
||||
virtual void copyFinished(QIODevice *);
|
||||
virtual void ignoreSslErrors();
|
||||
virtual void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
|
||||
virtual void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
|
||||
virtual QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
|
||||
|
||||
// information about the request
|
||||
QNetworkAccessManager::Operation operation() const;
|
||||
QNetworkRequest request() const;
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QList<QNetworkProxy> proxyList() const;
|
||||
#endif
|
||||
|
||||
QAbstractNetworkCache *networkCache() const;
|
||||
void setCachingEnabled(bool enable);
|
||||
bool isCachingEnabled() const;
|
||||
|
||||
// information about the reply
|
||||
QUrl url() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
// "cooked" headers
|
||||
QVariant header(QNetworkRequest::KnownHeaders header) const;
|
||||
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
|
||||
|
||||
// raw headers:
|
||||
bool hasRawHeader(const QByteArray &headerName) const;
|
||||
QList<QByteArray> rawHeaderList() const;
|
||||
QByteArray rawHeader(const QByteArray &headerName) const;
|
||||
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
|
||||
|
||||
// attributes:
|
||||
QVariant attribute(QNetworkRequest::Attribute code) const;
|
||||
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
|
||||
|
||||
bool isSynchronous() { return synchronous; }
|
||||
void setSynchronous(bool sync) { synchronous = sync; }
|
||||
|
||||
// return true if the QNonContiguousByteDevice of the upload
|
||||
// data needs to support reset(). Currently needed for HTTP.
|
||||
// This will possibly enable buffering of the upload data.
|
||||
virtual bool needsResetableUploadData() { return false; }
|
||||
|
||||
// Returns true if backend is able to resume downloads.
|
||||
virtual bool canResume() const { return false; }
|
||||
virtual void setResumeOffset(quint64 offset) { Q_UNUSED(offset); }
|
||||
|
||||
protected:
|
||||
// Create the device used for reading the upload data
|
||||
QNonContiguousByteDevice* createUploadByteDevice();
|
||||
|
||||
// these functions control the downstream mechanism
|
||||
// that is, data that has come via the connection and is going out the backend
|
||||
qint64 nextDownstreamBlockSize() const;
|
||||
void writeDownstreamData(QByteDataBuffer &list);
|
||||
|
||||
// not actually appending data, it was already written to the user buffer
|
||||
void writeDownstreamDataDownloadBuffer(qint64, qint64);
|
||||
char* getDownloadBuffer(qint64);
|
||||
|
||||
QSharedPointer<QNonContiguousByteDevice> uploadByteDevice;
|
||||
|
||||
public slots:
|
||||
// for task 251801, needs to be a slot to be called asynchronously
|
||||
void writeDownstreamData(QIODevice *data);
|
||||
|
||||
protected slots:
|
||||
void finished();
|
||||
void error(QNetworkReply::NetworkError code, const QString &errorString);
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
|
||||
#endif
|
||||
void authenticationRequired(QAuthenticator *auth);
|
||||
void metaDataChanged();
|
||||
void redirectionRequested(const QUrl &destination);
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
||||
|
||||
protected:
|
||||
// FIXME In the long run we should get rid of our QNAM architecture
|
||||
// and scrap this ReplyImpl/Backend distinction.
|
||||
QNetworkAccessManagerPrivate *manager;
|
||||
QNetworkReplyImplPrivate *reply;
|
||||
|
||||
private:
|
||||
friend class QNetworkAccessManager;
|
||||
friend class QNetworkAccessManagerPrivate;
|
||||
friend class QNetworkReplyImplPrivate;
|
||||
|
||||
bool synchronous;
|
||||
};
|
||||
|
||||
class QNetworkAccessBackendFactory
|
||||
{
|
||||
public:
|
||||
QNetworkAccessBackendFactory();
|
||||
virtual ~QNetworkAccessBackendFactory();
|
||||
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const = 0;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
|
@ -1,362 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "QtCore/qpointer.h"
|
||||
#include "QtCore/qdatetime.h"
|
||||
#include "QtCore/qqueue.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkcommon_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum ExpiryTimeEnum {
|
||||
ExpiryTime = 120
|
||||
};
|
||||
|
||||
struct Receiver
|
||||
{
|
||||
QPointer<QObject> object;
|
||||
const char *member;
|
||||
};
|
||||
|
||||
// idea copied from qcache.h
|
||||
struct QNetworkAccessCache::Node
|
||||
{
|
||||
QDateTime timestamp;
|
||||
QQueue<Receiver> receiverQueue;
|
||||
QByteArray key;
|
||||
|
||||
Node *older, *newer;
|
||||
CacheableObject *object;
|
||||
|
||||
int useCount;
|
||||
|
||||
Node()
|
||||
: older(0), newer(0), object(0), useCount(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
QNetworkAccessCache::CacheableObject::CacheableObject()
|
||||
{
|
||||
// leave the members uninitialized
|
||||
// they must be initialized by the derived class's constructor
|
||||
}
|
||||
|
||||
QNetworkAccessCache::CacheableObject::~CacheableObject()
|
||||
{
|
||||
#if 0 // ifndef QT_NO_DEBUG
|
||||
if (!key.isEmpty() && Ptr()->hasEntry(key))
|
||||
qWarning() << "QNetworkAccessCache: object" << (void*)this << "key" << key
|
||||
<< "destroyed without being removed from cache first!";
|
||||
#endif
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::CacheableObject::setExpires(bool enable)
|
||||
{
|
||||
expires = enable;
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::CacheableObject::setShareable(bool enable)
|
||||
{
|
||||
shareable = enable;
|
||||
}
|
||||
|
||||
QNetworkAccessCache::QNetworkAccessCache()
|
||||
: oldest(0), newest(0)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkAccessCache::~QNetworkAccessCache()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::clear()
|
||||
{
|
||||
NodeHash hashCopy = hash;
|
||||
hash.clear();
|
||||
|
||||
// remove all entries
|
||||
NodeHash::Iterator it = hashCopy.begin();
|
||||
NodeHash::Iterator end = hashCopy.end();
|
||||
for ( ; it != end; ++it) {
|
||||
it->object->key.clear();
|
||||
it->object->dispose();
|
||||
}
|
||||
|
||||
// now delete:
|
||||
hashCopy.clear();
|
||||
|
||||
timer.stop();
|
||||
|
||||
oldest = newest = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Appends the entry given by @p key to the end of the linked list.
|
||||
(i.e., makes it the newest entry)
|
||||
*/
|
||||
void QNetworkAccessCache::linkEntry(const QByteArray &key)
|
||||
{
|
||||
NodeHash::Iterator it = hash.find(key);
|
||||
if (it == hash.end())
|
||||
return;
|
||||
|
||||
Node *const node = &it.value();
|
||||
Q_ASSERT(node != oldest && node != newest);
|
||||
Q_ASSERT(node->older == 0 && node->newer == 0);
|
||||
Q_ASSERT(node->useCount == 0);
|
||||
|
||||
if (newest) {
|
||||
Q_ASSERT(newest->newer == 0);
|
||||
newest->newer = node;
|
||||
node->older = newest;
|
||||
}
|
||||
if (!oldest) {
|
||||
// there are no entries, so this is the oldest one too
|
||||
oldest = node;
|
||||
}
|
||||
|
||||
node->timestamp = QDateTime::currentDateTime().addSecs(ExpiryTime);
|
||||
newest = node;
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes the entry pointed by @p key from the linked list.
|
||||
Returns true if the entry removed was the oldest one.
|
||||
*/
|
||||
bool QNetworkAccessCache::unlinkEntry(const QByteArray &key)
|
||||
{
|
||||
NodeHash::Iterator it = hash.find(key);
|
||||
if (it == hash.end())
|
||||
return false;
|
||||
|
||||
Node *const node = &it.value();
|
||||
|
||||
bool wasOldest = false;
|
||||
if (node == oldest) {
|
||||
oldest = node->newer;
|
||||
wasOldest = true;
|
||||
}
|
||||
if (node == newest)
|
||||
newest = node->older;
|
||||
if (node->older)
|
||||
node->older->newer = node->newer;
|
||||
if (node->newer)
|
||||
node->newer->older = node->older;
|
||||
|
||||
node->newer = node->older = 0;
|
||||
return wasOldest;
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::updateTimer()
|
||||
{
|
||||
timer.stop();
|
||||
|
||||
if (!oldest)
|
||||
return;
|
||||
|
||||
int interval = QDateTime::currentDateTime().secsTo(oldest->timestamp);
|
||||
if (interval <= 0) {
|
||||
interval = 0;
|
||||
} else {
|
||||
// round up the interval
|
||||
interval = (interval + 15) & ~16;
|
||||
}
|
||||
|
||||
timer.start(interval * 1000, this);
|
||||
}
|
||||
|
||||
bool QNetworkAccessCache::emitEntryReady(Node *node, QObject *target, const char *member)
|
||||
{
|
||||
if (!connect(this, SIGNAL(entryReady(QNetworkAccessCache::CacheableObject*)),
|
||||
target, member, Qt::QueuedConnection))
|
||||
return false;
|
||||
|
||||
emit entryReady(node->object);
|
||||
disconnect(SIGNAL(entryReady(QNetworkAccessCache::CacheableObject*)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::timerEvent(QTimerEvent *)
|
||||
{
|
||||
// expire old items
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
|
||||
while (oldest && oldest->timestamp < now) {
|
||||
Node *next = oldest->newer;
|
||||
oldest->object->dispose();
|
||||
|
||||
hash.remove(oldest->key); // oldest gets deleted
|
||||
oldest = next;
|
||||
}
|
||||
|
||||
// fixup the list
|
||||
if (oldest)
|
||||
oldest->older = 0;
|
||||
else
|
||||
newest = 0;
|
||||
|
||||
updateTimer();
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::addEntry(const QByteArray &key, CacheableObject *entry)
|
||||
{
|
||||
Q_ASSERT(!key.isEmpty());
|
||||
|
||||
if (unlinkEntry(key))
|
||||
updateTimer();
|
||||
|
||||
Node &node = hash[key]; // create the entry in the hash if it didn't exist
|
||||
if (node.useCount)
|
||||
qWarning("QNetworkAccessCache::addEntry: overriding active cache entry '%s'",
|
||||
key.constData());
|
||||
if (node.object)
|
||||
node.object->dispose();
|
||||
node.object = entry;
|
||||
node.object->key = key;
|
||||
node.key = key;
|
||||
node.useCount = 1;
|
||||
}
|
||||
|
||||
bool QNetworkAccessCache::hasEntry(const QByteArray &key) const
|
||||
{
|
||||
return hash.contains(key);
|
||||
}
|
||||
|
||||
bool QNetworkAccessCache::requestEntry(const QByteArray &key, QObject *target, const char *member)
|
||||
{
|
||||
NodeHash::Iterator it = hash.find(key);
|
||||
if (it == hash.end())
|
||||
return false; // no such entry
|
||||
|
||||
Node *node = &it.value();
|
||||
|
||||
if (node->useCount > 0 && !node->object->shareable) {
|
||||
// object is not shareable and is in use
|
||||
// queue for later use
|
||||
Q_ASSERT(node->older == 0 && node->newer == 0);
|
||||
Receiver receiver;
|
||||
receiver.object = target;
|
||||
receiver.member = member;
|
||||
node->receiverQueue.enqueue(receiver);
|
||||
|
||||
// request queued
|
||||
return true;
|
||||
} else {
|
||||
// node not in use or is shareable
|
||||
if (unlinkEntry(key))
|
||||
updateTimer();
|
||||
|
||||
++node->useCount;
|
||||
return emitEntryReady(node, target, member);
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkAccessCache::CacheableObject *QNetworkAccessCache::requestEntryNow(const QByteArray &key)
|
||||
{
|
||||
NodeHash::Iterator it = hash.find(key);
|
||||
if (it == hash.end())
|
||||
return 0;
|
||||
if (it->useCount > 0) {
|
||||
if (it->object->shareable) {
|
||||
++it->useCount;
|
||||
return it->object;
|
||||
}
|
||||
|
||||
// object in use and not shareable
|
||||
return 0;
|
||||
}
|
||||
|
||||
// entry not in use, let the caller have it
|
||||
bool wasOldest = unlinkEntry(key);
|
||||
++it->useCount;
|
||||
|
||||
if (wasOldest)
|
||||
updateTimer();
|
||||
return it->object;
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::releaseEntry(const QByteArray &key)
|
||||
{
|
||||
NodeHash::Iterator it = hash.find(key);
|
||||
if (it == hash.end()) {
|
||||
qWarning("QNetworkAccessCache::releaseEntry: trying to release key '%s' that is not in cache",
|
||||
key.constData());
|
||||
return;
|
||||
}
|
||||
|
||||
Node *node = &it.value();
|
||||
Q_ASSERT(node->useCount > 0);
|
||||
|
||||
// are there other objects waiting?
|
||||
if (!node->receiverQueue.isEmpty()) {
|
||||
// queue another activation
|
||||
Receiver receiver;
|
||||
do {
|
||||
receiver = node->receiverQueue.dequeue();
|
||||
} while (receiver.object.isNull() && !node->receiverQueue.isEmpty());
|
||||
|
||||
if (!receiver.object.isNull()) {
|
||||
emitEntryReady(node, receiver.object, receiver.member);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!--node->useCount) {
|
||||
// no objects waiting; add it back to the expiry list
|
||||
if (node->object->expires)
|
||||
linkEntry(key);
|
||||
|
||||
if (oldest == node)
|
||||
updateTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessCache::removeEntry(const QByteArray &key)
|
||||
{
|
||||
NodeHash::Iterator it = hash.find(key);
|
||||
if (it == hash.end()) {
|
||||
qWarning("QNetworkAccessCache::removeEntry: trying to remove key '%s' that is not in cache",
|
||||
key.constData());
|
||||
return;
|
||||
}
|
||||
|
||||
Node *node = &it.value();
|
||||
if (unlinkEntry(key))
|
||||
updateTimer();
|
||||
if (node->useCount > 1)
|
||||
qWarning("QNetworkAccessCache::removeEntry: removing active cache entry '%s'",
|
||||
key.constData());
|
||||
|
||||
node->object->key.clear();
|
||||
hash.remove(node->key);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkaccesscache_p.h"
|
||||
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSCACHE_P_H
|
||||
#define QNETWORKACCESSCACHE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "QtCore/qobject.h"
|
||||
#include "QtCore/qbasictimer.h"
|
||||
#include "QtCore/qbytearray.h"
|
||||
#include "QtCore/qhash.h"
|
||||
#include "QtCore/qmetatype.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkRequest;
|
||||
class QUrl;
|
||||
|
||||
// this class is not about caching files but about
|
||||
// caching objects used by QNetworkAccessManager, e.g. existing TCP connections
|
||||
// or credentials.
|
||||
class QNetworkAccessCache: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Node;
|
||||
typedef QHash<QByteArray, Node> NodeHash;
|
||||
|
||||
class CacheableObject
|
||||
{
|
||||
friend class QNetworkAccessCache;
|
||||
QByteArray key;
|
||||
bool expires;
|
||||
bool shareable;
|
||||
public:
|
||||
CacheableObject();
|
||||
virtual ~CacheableObject();
|
||||
virtual void dispose() = 0;
|
||||
inline QByteArray cacheKey() const { return key; }
|
||||
|
||||
protected:
|
||||
void setExpires(bool enable);
|
||||
void setShareable(bool enable);
|
||||
};
|
||||
|
||||
QNetworkAccessCache();
|
||||
~QNetworkAccessCache();
|
||||
|
||||
void clear();
|
||||
|
||||
void addEntry(const QByteArray &key, CacheableObject *entry);
|
||||
bool hasEntry(const QByteArray &key) const;
|
||||
bool requestEntry(const QByteArray &key, QObject *target, const char *member);
|
||||
CacheableObject *requestEntryNow(const QByteArray &key);
|
||||
void releaseEntry(const QByteArray &key);
|
||||
void removeEntry(const QByteArray &key);
|
||||
|
||||
signals:
|
||||
void entryReady(QNetworkAccessCache::CacheableObject *);
|
||||
|
||||
protected:
|
||||
void timerEvent(QTimerEvent *);
|
||||
|
||||
private:
|
||||
// idea copied from qcache.h
|
||||
NodeHash hash;
|
||||
Node *oldest;
|
||||
Node *newest;
|
||||
|
||||
QBasicTimer timer;
|
||||
|
||||
void linkEntry(const QByteArray &key);
|
||||
bool unlinkEntry(const QByteArray &key);
|
||||
void updateTimer();
|
||||
bool emitEntryReady(Node *node, QObject *target, const char *member);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,131 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//#define QNETWORKACCESSCACHEBACKEND_DEBUG
|
||||
|
||||
#include "qnetworkaccesscachebackend_p.h"
|
||||
#include "qabstractnetworkcache.h"
|
||||
#include "qurlinfo.h"
|
||||
#include "qdir.h"
|
||||
#include "qcoreapplication.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
|
||||
: QNetworkAccessBackend()
|
||||
, device(0)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkAccessCacheBackend::~QNetworkAccessCacheBackend()
|
||||
{
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::open()
|
||||
{
|
||||
if (operation() != QNetworkAccessManager::GetOperation || !sendCacheContents()) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessCacheBackend", "Error opening %1")
|
||||
.arg(this->url().toString());
|
||||
error(QNetworkReply::ContentNotFoundError, msg);
|
||||
} else {
|
||||
setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
|
||||
}
|
||||
finished();
|
||||
}
|
||||
|
||||
bool QNetworkAccessCacheBackend::sendCacheContents()
|
||||
{
|
||||
setCachingEnabled(false);
|
||||
QAbstractNetworkCache *nc = networkCache();
|
||||
if (!nc)
|
||||
return false;
|
||||
|
||||
QNetworkCacheMetaData item = nc->metaData(url());
|
||||
if (!item.isValid())
|
||||
return false;
|
||||
|
||||
QNetworkCacheMetaData::AttributesMap attributes = item.attributes();
|
||||
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
|
||||
setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
|
||||
|
||||
// set the raw headers
|
||||
QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
|
||||
QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
|
||||
end = rawHeaders.constEnd();
|
||||
for ( ; it != end; ++it) {
|
||||
if (it->first.toLower() == "cache-control" &&
|
||||
it->second.toLower().contains("must-revalidate")) {
|
||||
return false;
|
||||
}
|
||||
setRawHeader(it->first, it->second);
|
||||
}
|
||||
|
||||
// handle a possible redirect
|
||||
QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
|
||||
if (redirectionTarget.isValid()) {
|
||||
setAttribute(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
|
||||
redirectionRequested(redirectionTarget.toUrl());
|
||||
}
|
||||
|
||||
// signal we're open
|
||||
metaDataChanged();
|
||||
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
QIODevice *contents = nc->data(url());
|
||||
if (!contents)
|
||||
return false;
|
||||
contents->setParent(this);
|
||||
writeDownstreamData(contents);
|
||||
}
|
||||
|
||||
#if defined(QNETWORKACCESSCACHEBACKEND_DEBUG)
|
||||
qDebug() << "Successfully sent cache:" << url();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::closeDownstreamChannel()
|
||||
{
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
device->close();
|
||||
delete device;
|
||||
device = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::closeUpstreamChannel()
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::upstreamReadyRead()
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
|
||||
}
|
||||
|
||||
void QNetworkAccessCacheBackend::downstreamReadyWrite()
|
||||
{
|
||||
Q_ASSERT_X(false, Q_FUNC_INFO, "This function show not have been called!");
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSCACHEBACKEND_P_H
|
||||
#define QNETWORKACCESSCACHEBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkAccessCacheBackend : public QNetworkAccessBackend
|
||||
{
|
||||
|
||||
public:
|
||||
QNetworkAccessCacheBackend();
|
||||
~QNetworkAccessCacheBackend();
|
||||
|
||||
void open();
|
||||
void closeDownstreamChannel();
|
||||
void closeUpstreamChannel();
|
||||
|
||||
void upstreamReadyRead();
|
||||
void downstreamReadyWrite();
|
||||
|
||||
private:
|
||||
bool sendCacheContents();
|
||||
QIODevice *device;
|
||||
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QNETWORKACCESSCACHEBACKEND_P_H
|
|
@ -1,263 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessdebugpipebackend_p.h"
|
||||
#include "QtCore/qdatastream.h"
|
||||
#include <QCoreApplication>
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
|
||||
QNetworkAccessBackend *
|
||||
QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const
|
||||
{
|
||||
// is it an operation we know of?
|
||||
switch (op) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
break;
|
||||
|
||||
default:
|
||||
// no, we can't handle this operation
|
||||
return 0;
|
||||
}
|
||||
|
||||
QUrl url = request.url();
|
||||
if (url.scheme() == QLatin1String("debugpipe"))
|
||||
return new QNetworkAccessDebugPipeBackend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
QNetworkAccessDebugPipeBackend::QNetworkAccessDebugPipeBackend()
|
||||
: bareProtocol(false), hasUploadFinished(false), hasDownloadFinished(false),
|
||||
hasEverythingFinished(false), bytesDownloaded(0), bytesUploaded(0)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkAccessDebugPipeBackend::~QNetworkAccessDebugPipeBackend()
|
||||
{
|
||||
// this is signals disconnect, not network!
|
||||
socket.disconnect(this); // we're not interested in the signals at this point
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::open()
|
||||
{
|
||||
socket.connectToHost(url().host(), url().port(12345));
|
||||
socket.setReadBufferSize(QT_BUFFSIZE);
|
||||
|
||||
// socket ready read -> we can push from socket to downstream
|
||||
connect(&socket, SIGNAL(readyRead()), SLOT(socketReadyRead()));
|
||||
connect(&socket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(socketError()));
|
||||
connect(&socket, SIGNAL(disconnected()), SLOT(socketDisconnected()));
|
||||
connect(&socket, SIGNAL(connected()), SLOT(socketConnected()));
|
||||
// socket bytes written -> we can push more from upstream to socket
|
||||
connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
|
||||
|
||||
bareProtocol = url().queryItemValue(QLatin1String("bare")) == QLatin1String("1");
|
||||
|
||||
if (operation() == QNetworkAccessManager::PutOperation) {
|
||||
uploadByteDevice = createUploadByteDevice();
|
||||
QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
|
||||
QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketReadyRead()
|
||||
{
|
||||
pushFromSocketToDownstream();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::downstreamReadyWrite()
|
||||
{
|
||||
pushFromSocketToDownstream();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketBytesWritten(qint64)
|
||||
{
|
||||
pushFromUpstreamToSocket();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::uploadReadyReadSlot()
|
||||
{
|
||||
pushFromUpstreamToSocket();
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::pushFromSocketToDownstream()
|
||||
{
|
||||
QByteArray buffer;
|
||||
|
||||
if (socket.state() == QAbstractSocket::ConnectingState) {
|
||||
return;
|
||||
}
|
||||
|
||||
forever {
|
||||
if (hasDownloadFinished)
|
||||
return;
|
||||
|
||||
buffer.resize(QT_BUFFSIZE);
|
||||
qint64 haveRead = socket.read(buffer.data(), QT_BUFFSIZE);
|
||||
|
||||
if (haveRead == -1) {
|
||||
hasDownloadFinished = true;
|
||||
// this ensures a good last downloadProgress is emitted
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
|
||||
possiblyFinish();
|
||||
break;
|
||||
} else if (haveRead == 0) {
|
||||
break;
|
||||
} else {
|
||||
// have read something
|
||||
buffer.resize(haveRead);
|
||||
bytesDownloaded += haveRead;
|
||||
|
||||
QByteDataBuffer list;
|
||||
list.append(buffer);
|
||||
buffer.clear(); // important because of implicit sharing!
|
||||
writeDownstreamData(list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::pushFromUpstreamToSocket()
|
||||
{
|
||||
// FIXME
|
||||
if (operation() == QNetworkAccessManager::PutOperation) {
|
||||
if (hasUploadFinished)
|
||||
return;
|
||||
|
||||
forever {
|
||||
if (socket.bytesToWrite() >= QT_BUFFSIZE)
|
||||
return;
|
||||
|
||||
qint64 haveRead;
|
||||
const char *readPointer = uploadByteDevice->readPointer(QT_BUFFSIZE, haveRead);
|
||||
if (haveRead == -1) {
|
||||
// EOF
|
||||
hasUploadFinished = true;
|
||||
emitReplyUploadProgress(bytesUploaded, bytesUploaded);
|
||||
possiblyFinish();
|
||||
break;
|
||||
} else if (haveRead == 0 || readPointer == 0) {
|
||||
// nothing to read right now, we will be called again later
|
||||
break;
|
||||
} else {
|
||||
qint64 haveWritten;
|
||||
haveWritten = socket.write(readPointer, haveRead);
|
||||
|
||||
if (haveWritten < 0) {
|
||||
// write error!
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessDebugPipeBackend", "Write error writing to %1: %2")
|
||||
.arg(url().toString(), socket.errorString());
|
||||
error(QNetworkReply::ProtocolFailure, msg);
|
||||
finished();
|
||||
return;
|
||||
} else {
|
||||
uploadByteDevice->advanceReadPointer(haveWritten);
|
||||
bytesUploaded += haveWritten;
|
||||
emitReplyUploadProgress(bytesUploaded, -1);
|
||||
}
|
||||
|
||||
//QCoreApplication::processEvents();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::possiblyFinish()
|
||||
{
|
||||
if (hasEverythingFinished)
|
||||
return;
|
||||
hasEverythingFinished = true;
|
||||
|
||||
if ((operation() == QNetworkAccessManager::GetOperation) && hasDownloadFinished) {
|
||||
socket.close();
|
||||
finished();
|
||||
} else if ((operation() == QNetworkAccessManager::PutOperation) && hasUploadFinished) {
|
||||
socket.close();
|
||||
finished();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::closeDownstreamChannel()
|
||||
{
|
||||
qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
|
||||
//if (operation() == QNetworkAccessManager::GetOperation)
|
||||
// socket.disconnectFromHost();
|
||||
}
|
||||
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketError()
|
||||
{
|
||||
qWarning("QNetworkAccessDebugPipeBackend::socketError() %d",socket.error());
|
||||
QNetworkReply::NetworkError code;
|
||||
switch (socket.error()) {
|
||||
case QAbstractSocket::RemoteHostClosedError:
|
||||
return; // socketDisconnected will be called
|
||||
|
||||
case QAbstractSocket::NetworkError:
|
||||
code = QNetworkReply::UnknownNetworkError;
|
||||
break;
|
||||
|
||||
default:
|
||||
code = QNetworkReply::ProtocolFailure;
|
||||
break;
|
||||
}
|
||||
|
||||
error(code, QNetworkAccessDebugPipeBackend::tr("Socket error on %1: %2")
|
||||
.arg(url().toString(), socket.errorString()));
|
||||
finished();
|
||||
disconnect(&socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
||||
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketDisconnected()
|
||||
{
|
||||
pushFromSocketToDownstream();
|
||||
|
||||
if (socket.bytesToWrite() == 0) {
|
||||
// normal close
|
||||
} else {
|
||||
// abnormal close
|
||||
QString msg = QNetworkAccessDebugPipeBackend::tr("Remote host closed the connection prematurely on %1")
|
||||
.arg(url().toString());
|
||||
error(QNetworkReply::RemoteHostClosedError, msg);
|
||||
finished();
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessDebugPipeBackend::socketConnected()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkaccessdebugpipebackend_p.h"
|
||||
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSDEBUGPIPEBACKEND_P_H
|
||||
#define QNETWORKACCESSDEBUGPIPEBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "qtcpsocket.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
|
||||
class QNetworkAccessDebugPipeBackend: public QNetworkAccessBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessDebugPipeBackend();
|
||||
virtual ~QNetworkAccessDebugPipeBackend();
|
||||
|
||||
virtual void open();
|
||||
virtual void closeDownstreamChannel();
|
||||
|
||||
virtual void downstreamReadyWrite();
|
||||
|
||||
protected:
|
||||
void pushFromSocketToDownstream();
|
||||
void pushFromUpstreamToSocket();
|
||||
void possiblyFinish();
|
||||
QNonContiguousByteDevice *uploadByteDevice;
|
||||
|
||||
private slots:
|
||||
void uploadReadyReadSlot();
|
||||
void socketReadyRead();
|
||||
void socketBytesWritten(qint64 bytes);
|
||||
void socketError();
|
||||
void socketDisconnected();
|
||||
void socketConnected();
|
||||
|
||||
private:
|
||||
QTcpSocket socket;
|
||||
bool bareProtocol;
|
||||
bool hasUploadFinished;
|
||||
bool hasDownloadFinished;
|
||||
bool hasEverythingFinished;
|
||||
|
||||
qint64 bytesDownloaded;
|
||||
qint64 bytesUploaded;
|
||||
};
|
||||
|
||||
class QNetworkAccessDebugPipeBackendFactory: public QNetworkAccessBackendFactory
|
||||
{
|
||||
public:
|
||||
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const;
|
||||
};
|
||||
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,255 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessfilebackend_p.h"
|
||||
#include "qfileinfo.h"
|
||||
#include "qurlinfo.h"
|
||||
#include "qdir.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
#include "qcoreapplication.h"
|
||||
#include "qcore_unix_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QNetworkAccessBackend *
|
||||
QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const
|
||||
{
|
||||
// is it an operation we know of?
|
||||
switch (op) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
break;
|
||||
|
||||
default:
|
||||
// no, we can't handle this operation
|
||||
return 0;
|
||||
}
|
||||
|
||||
QUrl url = request.url();
|
||||
if (url.isLocalFile()) {
|
||||
return new QNetworkAccessFileBackend;
|
||||
} else if (!url.isEmpty() && url.authority().isEmpty()) {
|
||||
// check if QFile could, in theory, open this URL via the file engines
|
||||
// it has to be in the format:
|
||||
// prefix:path/to/file
|
||||
// or prefix:/path/to/file
|
||||
//
|
||||
// this construct here must match the one below in open()
|
||||
QFileInfo fi(url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery));
|
||||
// On Windows and Symbian the drive letter is detected as the scheme.
|
||||
if (fi.exists() && (url.scheme().isEmpty() || (url.scheme().length() == 1)))
|
||||
qWarning("QNetworkAccessFileBackendFactory: URL has no schema set, use file:// for files");
|
||||
if (fi.exists() || (op == QNetworkAccessManager::PutOperation && fi.dir().exists()))
|
||||
return new QNetworkAccessFileBackend;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QNetworkAccessFileBackend::QNetworkAccessFileBackend()
|
||||
: uploadByteDevice(0), totalBytes(0), hasUploadFinished(false)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkAccessFileBackend::~QNetworkAccessFileBackend()
|
||||
{
|
||||
}
|
||||
|
||||
void QNetworkAccessFileBackend::open()
|
||||
{
|
||||
QUrl url = this->url();
|
||||
|
||||
if (url.host() == QLatin1String("localhost"))
|
||||
url.setHost(QString());
|
||||
// do not allow UNC paths on Unix
|
||||
if (!url.host().isEmpty()) {
|
||||
// we handle only local files
|
||||
error(QNetworkReply::ProtocolInvalidOperationError,
|
||||
QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString()));
|
||||
finished();
|
||||
return;
|
||||
}
|
||||
if (url.path().isEmpty())
|
||||
url.setPath(QLatin1String("/"));
|
||||
setUrl(url);
|
||||
|
||||
QString fileName = url.toLocalFile();
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
|
||||
}
|
||||
file.setFileName(fileName);
|
||||
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
if (!loadFileInfo())
|
||||
return;
|
||||
}
|
||||
|
||||
QIODevice::OpenMode mode;
|
||||
switch (operation()) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
mode = QIODevice::ReadOnly;
|
||||
break;
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
mode = QIODevice::WriteOnly | QIODevice::Truncate;
|
||||
uploadByteDevice = createUploadByteDevice();
|
||||
QObject::connect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(uploadReadyReadSlot()));
|
||||
QMetaObject::invokeMethod(this, "uploadReadyReadSlot", Qt::QueuedConnection);
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT_X(false, "QNetworkAccessFileBackend::open",
|
||||
"Got a request operation I cannot handle!!");
|
||||
return;
|
||||
}
|
||||
|
||||
mode |= QIODevice::Unbuffered;
|
||||
bool opened = file.open(mode);
|
||||
|
||||
// could we open the file?
|
||||
if (!opened) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
|
||||
.arg(this->url().toString(), file.errorString());
|
||||
|
||||
// why couldn't we open the file?
|
||||
// if we're opening for reading, either it doesn't exist, or it's access denied
|
||||
// if we're opening for writing, not existing means it's access denied too
|
||||
if (file.exists() || operation() == QNetworkAccessManager::PutOperation)
|
||||
error(QNetworkReply::ContentAccessDenied, msg);
|
||||
else
|
||||
error(QNetworkReply::ContentNotFoundError, msg);
|
||||
finished();
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFileBackend::uploadReadyReadSlot()
|
||||
{
|
||||
if (hasUploadFinished)
|
||||
return;
|
||||
|
||||
forever {
|
||||
qint64 haveRead;
|
||||
const char *readPointer = uploadByteDevice->readPointer(-1, haveRead);
|
||||
if (haveRead == -1) {
|
||||
// EOF
|
||||
hasUploadFinished = true;
|
||||
file.flush();
|
||||
file.close();
|
||||
finished();
|
||||
break;
|
||||
} else if (haveRead == 0 || readPointer == 0) {
|
||||
// nothing to read right now, we will be called again later
|
||||
break;
|
||||
} else {
|
||||
qint64 haveWritten;
|
||||
haveWritten = file.write(readPointer, haveRead);
|
||||
|
||||
if (haveWritten < 0) {
|
||||
// write error!
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Write error writing to %1: %2")
|
||||
.arg(url().toString(), file.errorString());
|
||||
error(QNetworkReply::ProtocolFailure, msg);
|
||||
|
||||
finished();
|
||||
return;
|
||||
} else {
|
||||
uploadByteDevice->advanceReadPointer(haveWritten);
|
||||
}
|
||||
|
||||
|
||||
file.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFileBackend::closeDownstreamChannel()
|
||||
{
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFileBackend::downstreamReadyWrite()
|
||||
{
|
||||
Q_ASSERT_X(operation() == QNetworkAccessManager::GetOperation, "QNetworkAccessFileBackend",
|
||||
"We're being told to download data but operation isn't GET!");
|
||||
|
||||
readMoreFromFile();
|
||||
}
|
||||
|
||||
bool QNetworkAccessFileBackend::loadFileInfo()
|
||||
{
|
||||
QStatInfo si(file.fileName());
|
||||
setHeader(QNetworkRequest::LastModifiedHeader, si.lastModified());
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, qint64(si.size()));
|
||||
|
||||
// signal we're open
|
||||
metaDataChanged();
|
||||
|
||||
if (si.isDir()) {
|
||||
error(QNetworkReply::ContentOperationNotPermittedError,
|
||||
QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url().toString()));
|
||||
finished();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QNetworkAccessFileBackend::readMoreFromFile()
|
||||
{
|
||||
qint64 wantToRead;
|
||||
while ((wantToRead = nextDownstreamBlockSize()) > 0) {
|
||||
// ### FIXME!!
|
||||
// Obtain a pointer from the ringbuffer!
|
||||
// Avoid extra copy
|
||||
QByteArray data(wantToRead, Qt::Uninitialized);
|
||||
qint64 actuallyRead = file.read(data.data(), wantToRead);
|
||||
if (actuallyRead <= 0) {
|
||||
// EOF or error
|
||||
if (file.error() != QFile::NoError) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Read error reading from %1: %2")
|
||||
.arg(url().toString(), file.errorString());
|
||||
error(QNetworkReply::ProtocolFailure, msg);
|
||||
|
||||
finished();
|
||||
return false;
|
||||
}
|
||||
|
||||
finished();
|
||||
return true;
|
||||
}
|
||||
|
||||
data.resize(actuallyRead);
|
||||
totalBytes += actuallyRead;
|
||||
|
||||
QByteDataBuffer list;
|
||||
list.append(data);
|
||||
data.clear(); // important because of implicit sharing!
|
||||
writeDownstreamData(list);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkaccessfilebackend_p.h"
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSFILEBACKEND_P_H
|
||||
#define QNETWORKACCESSFILEBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "QtCore/qfile.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkAccessFileBackend: public QNetworkAccessBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessFileBackend();
|
||||
virtual ~QNetworkAccessFileBackend();
|
||||
|
||||
virtual void open();
|
||||
virtual void closeDownstreamChannel();
|
||||
|
||||
virtual void downstreamReadyWrite();
|
||||
|
||||
public slots:
|
||||
void uploadReadyReadSlot();
|
||||
protected:
|
||||
QNonContiguousByteDevice *uploadByteDevice;
|
||||
private:
|
||||
QFile file;
|
||||
qint64 totalBytes;
|
||||
bool hasUploadFinished;
|
||||
|
||||
bool loadFileInfo();
|
||||
bool readMoreFromFile();
|
||||
};
|
||||
|
||||
class QNetworkAccessFileBackendFactory: public QNetworkAccessBackendFactory
|
||||
{
|
||||
public:
|
||||
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,359 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessftpbackend_p.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "QtNetwork/qauthenticator.h"
|
||||
#include "qnoncontiguousbytedevice_p.h"
|
||||
|
||||
#ifndef QT_NO_FTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
enum {
|
||||
DefaultFtpPort = 21
|
||||
};
|
||||
|
||||
static QByteArray makeCacheKey(const QUrl &url)
|
||||
{
|
||||
QUrl copy = url;
|
||||
copy.setPort(url.port(DefaultFtpPort));
|
||||
return "ftp-connection:" +
|
||||
copy.toEncoded(QUrl::RemovePassword | QUrl::RemovePath | QUrl::RemoveQuery |
|
||||
QUrl::RemoveFragment);
|
||||
}
|
||||
|
||||
QNetworkAccessBackend *
|
||||
QNetworkAccessFtpBackendFactory::create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const
|
||||
{
|
||||
// is it an operation we know of?
|
||||
switch (op) {
|
||||
case QNetworkAccessManager::GetOperation:
|
||||
case QNetworkAccessManager::PutOperation:
|
||||
break;
|
||||
|
||||
default:
|
||||
// no, we can't handle this operation
|
||||
return 0;
|
||||
}
|
||||
|
||||
QUrl url = request.url();
|
||||
if (url.scheme().compare(QLatin1String("ftp"), Qt::CaseInsensitive) == 0)
|
||||
return new QNetworkAccessFtpBackend;
|
||||
return 0;
|
||||
}
|
||||
|
||||
class QNetworkAccessCachedFtpConnection: public QFtp, public QNetworkAccessCache::CacheableObject
|
||||
{
|
||||
// Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessCachedFtpConnection()
|
||||
{
|
||||
setExpires(true);
|
||||
setShareable(false);
|
||||
}
|
||||
|
||||
void dispose()
|
||||
{
|
||||
connect(this, SIGNAL(done(bool)), this, SLOT(deleteLater()));
|
||||
close();
|
||||
}
|
||||
};
|
||||
|
||||
QNetworkAccessFtpBackend::QNetworkAccessFtpBackend()
|
||||
: ftp(0), uploadDevice(0), totalBytes(0), helpId(-1), sizeId(-1), mdtmId(-1),
|
||||
supportsSize(false), supportsMdtm(false), state(Idle)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkAccessFtpBackend::~QNetworkAccessFtpBackend()
|
||||
{
|
||||
//if backend destroyed while in use, then abort (this is the code path from QNetworkReply::abort)
|
||||
if (ftp && state != Disconnecting)
|
||||
ftp->abort();
|
||||
disconnectFromFtp();
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::open()
|
||||
{
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QNetworkProxy proxy;
|
||||
foreach (const QNetworkProxy &p, proxyList()) {
|
||||
// use the first FTP proxy
|
||||
// or no proxy at all
|
||||
if (p.type() == QNetworkProxy::FtpCachingProxy
|
||||
|| p.type() == QNetworkProxy::NoProxy) {
|
||||
proxy = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// did we find an FTP proxy or a NoProxy?
|
||||
if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
||||
// unsuitable proxies
|
||||
error(QNetworkReply::ProxyNotFoundError,
|
||||
tr("No suitable proxy found"));
|
||||
finished();
|
||||
return;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
QUrl url = this->url();
|
||||
if (url.path().isEmpty()) {
|
||||
url.setPath(QLatin1String("/"));
|
||||
setUrl(url);
|
||||
}
|
||||
if (url.path().endsWith(QLatin1Char('/'))) {
|
||||
error(QNetworkReply::ContentOperationNotPermittedError,
|
||||
tr("Cannot open %1: is a directory").arg(url.toString()));
|
||||
finished();
|
||||
return;
|
||||
}
|
||||
state = LoggingIn;
|
||||
|
||||
QNetworkAccessCache* objectCache = QNetworkAccessManagerPrivate::getObjectCache(this);
|
||||
QByteArray cacheKey = makeCacheKey(url);
|
||||
if (!objectCache->requestEntry(cacheKey, this,
|
||||
SLOT(ftpConnectionReady(QNetworkAccessCache::CacheableObject*)))) {
|
||||
ftp = new QNetworkAccessCachedFtpConnection;
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
if (proxy.type() == QNetworkProxy::FtpCachingProxy)
|
||||
ftp->setProxy(proxy.hostName(), proxy.port());
|
||||
#endif
|
||||
ftp->connectToHost(url.host(), url.port(DefaultFtpPort));
|
||||
ftp->login(url.userName(), url.password());
|
||||
|
||||
objectCache->addEntry(cacheKey, ftp);
|
||||
ftpConnectionReady(ftp);
|
||||
}
|
||||
|
||||
// Put operation
|
||||
if (operation() == QNetworkAccessManager::PutOperation) {
|
||||
uploadDevice = QNonContiguousByteDeviceFactory::wrap(createUploadByteDevice());
|
||||
uploadDevice->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::closeDownstreamChannel()
|
||||
{
|
||||
state = Disconnecting;
|
||||
if (operation() == QNetworkAccessManager::GetOperation)
|
||||
ftp->abort();
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::downstreamReadyWrite()
|
||||
{
|
||||
if (state == Transferring && ftp && ftp->bytesAvailable())
|
||||
ftpReadyRead();
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::ftpConnectionReady(QNetworkAccessCache::CacheableObject *o)
|
||||
{
|
||||
ftp = static_cast<QNetworkAccessCachedFtpConnection *>(o);
|
||||
connect(ftp, SIGNAL(done(bool)), SLOT(ftpDone()));
|
||||
connect(ftp, SIGNAL(rawCommandReply(int,QString)), SLOT(ftpRawCommandReply(int,QString)));
|
||||
connect(ftp, SIGNAL(readyRead()), SLOT(ftpReadyRead()));
|
||||
|
||||
// is the login process done already?
|
||||
if (ftp->state() == QFtp::LoggedIn)
|
||||
ftpDone();
|
||||
|
||||
// no, defer the actual operation until after we've logged in
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::disconnectFromFtp(CacheCleanupMode mode)
|
||||
{
|
||||
state = Disconnecting;
|
||||
|
||||
if (ftp) {
|
||||
disconnect(ftp, 0, this, 0);
|
||||
|
||||
QByteArray key = makeCacheKey(url());
|
||||
if (mode == RemoveCachedConnection) {
|
||||
QNetworkAccessManagerPrivate::getObjectCache(this)->removeEntry(key);
|
||||
ftp->dispose();
|
||||
} else {
|
||||
QNetworkAccessManagerPrivate::getObjectCache(this)->releaseEntry(key);
|
||||
}
|
||||
|
||||
ftp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::ftpDone()
|
||||
{
|
||||
// the last command we sent is done
|
||||
if (state == LoggingIn && ftp->state() != QFtp::LoggedIn) {
|
||||
if (ftp->state() == QFtp::Connected) {
|
||||
// the login did not succeed
|
||||
QUrl newUrl = url();
|
||||
newUrl.setUserInfo(QString());
|
||||
setUrl(newUrl);
|
||||
|
||||
QAuthenticator auth;
|
||||
authenticationRequired(&auth);
|
||||
|
||||
if (!auth.isNull()) {
|
||||
// try again:
|
||||
newUrl.setUserName(auth.user());
|
||||
ftp->login(auth.user(), auth.password());
|
||||
return;
|
||||
}
|
||||
|
||||
error(QNetworkReply::AuthenticationRequiredError,
|
||||
tr("Logging in to %1 failed: authentication required")
|
||||
.arg(url().host()));
|
||||
} else {
|
||||
// we did not connect
|
||||
QNetworkReply::NetworkError code;
|
||||
switch (ftp->error()) {
|
||||
case QFtp::HostNotFound:
|
||||
code = QNetworkReply::HostNotFoundError;
|
||||
break;
|
||||
|
||||
case QFtp::ConnectionRefused:
|
||||
code = QNetworkReply::ConnectionRefusedError;
|
||||
break;
|
||||
|
||||
default:
|
||||
code = QNetworkReply::ProtocolFailure;
|
||||
break;
|
||||
}
|
||||
|
||||
error(code, ftp->errorString());
|
||||
}
|
||||
|
||||
// we're not connected, so remove the cache entry:
|
||||
disconnectFromFtp(RemoveCachedConnection);
|
||||
finished();
|
||||
return;
|
||||
}
|
||||
|
||||
// check for errors:
|
||||
if (ftp->error() != QFtp::NoError) {
|
||||
QString msg;
|
||||
if (operation() == QNetworkAccessManager::GetOperation)
|
||||
msg = tr("Error while downloading %1: %2");
|
||||
else
|
||||
msg = tr("Error while uploading %1: %2");
|
||||
msg = msg.arg(url().toString(), ftp->errorString());
|
||||
|
||||
if (state == Statting)
|
||||
// file probably doesn't exist
|
||||
error(QNetworkReply::ContentNotFoundError, msg);
|
||||
else
|
||||
error(QNetworkReply::ContentAccessDenied, msg);
|
||||
|
||||
disconnectFromFtp(RemoveCachedConnection);
|
||||
finished();
|
||||
}
|
||||
|
||||
if (state == LoggingIn) {
|
||||
state = CheckingFeatures;
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
// send help command to find out if server supports "SIZE" and "MDTM"
|
||||
QString command = url().path();
|
||||
command.prepend(QLatin1String("%1 "));
|
||||
helpId = ftp->rawCommand(QLatin1String("HELP")); // get supported commands
|
||||
} else {
|
||||
ftpDone();
|
||||
}
|
||||
} else if (state == CheckingFeatures) {
|
||||
state = Statting;
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
// logged in successfully, send the stat requests (if supported)
|
||||
QString command = url().path();
|
||||
command.prepend(QLatin1String("%1 "));
|
||||
if (supportsSize) {
|
||||
ftp->rawCommand(QLatin1String("TYPE I"));
|
||||
sizeId = ftp->rawCommand(command.arg(QLatin1String("SIZE"))); // get size
|
||||
}
|
||||
if (supportsMdtm)
|
||||
mdtmId = ftp->rawCommand(command.arg(QLatin1String("MDTM"))); // get modified time
|
||||
if (!supportsSize && !supportsMdtm)
|
||||
ftpDone(); // no commands sent, move to the next state
|
||||
} else {
|
||||
ftpDone();
|
||||
}
|
||||
} else if (state == Statting) {
|
||||
// statted successfully, send the actual request
|
||||
emit metaDataChanged();
|
||||
state = Transferring;
|
||||
|
||||
QFtp::TransferType type = QFtp::Binary;
|
||||
if (operation() == QNetworkAccessManager::GetOperation) {
|
||||
setCachingEnabled(true);
|
||||
ftp->get(url().path(), 0, type);
|
||||
} else {
|
||||
ftp->put(uploadDevice, url().path(), type);
|
||||
}
|
||||
|
||||
} else if (state == Transferring) {
|
||||
// upload or download finished
|
||||
disconnectFromFtp();
|
||||
finished();
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::ftpReadyRead()
|
||||
{
|
||||
QByteArray data = ftp->readAll();
|
||||
QByteDataBuffer list;
|
||||
list.append(data);
|
||||
data.clear(); // important because of implicit sharing!
|
||||
writeDownstreamData(list);
|
||||
}
|
||||
|
||||
void QNetworkAccessFtpBackend::ftpRawCommandReply(int code, const QString &text)
|
||||
{
|
||||
//qDebug() << "FTP reply:" << code << text;
|
||||
int id = ftp->currentId();
|
||||
|
||||
if ((id == helpId) && ((code == 200) || (code == 214))) { // supported commands
|
||||
// the "FEAT" ftp command would be nice here, but it is not part of the
|
||||
// initial FTP RFC 959, neither ar "SIZE" nor "MDTM" (they are all specified
|
||||
// in RFC 3659)
|
||||
if (text.contains(QLatin1String("SIZE"), Qt::CaseSensitive))
|
||||
supportsSize = true;
|
||||
if (text.contains(QLatin1String("MDTM"), Qt::CaseSensitive))
|
||||
supportsMdtm = true;
|
||||
} else if (code == 213) { // file status
|
||||
if (id == sizeId) {
|
||||
// reply to the size command
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, text.toLongLong());
|
||||
#ifndef QT_NO_DATESTRING
|
||||
} else if (id == mdtmId) {
|
||||
QDateTime dt = QDateTime::fromString(text, QLatin1String("yyyyMMddHHmmss"));
|
||||
setHeader(QNetworkRequest::LastModifiedHeader, dt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_FTP
|
||||
|
||||
#include "moc_qnetworkaccessftpbackend_p.h"
|
||||
|
||||
#include "moc_qnetworkaccessftpbackend.cpp"
|
|
@ -1,107 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSFTPBACKEND_P_H
|
||||
#define QNETWORKACCESSFTPBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "QtNetwork/qftp.h"
|
||||
|
||||
#include "QtCore/qpointer.h"
|
||||
|
||||
#ifndef QT_NO_FTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkAccessFtpIODevice;
|
||||
class QNetworkAccessCachedFtpConnection;
|
||||
|
||||
class QNetworkAccessFtpBackend: public QNetworkAccessBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State {
|
||||
Idle,
|
||||
//Connecting,
|
||||
LoggingIn,
|
||||
CheckingFeatures,
|
||||
Statting,
|
||||
Transferring,
|
||||
Disconnecting
|
||||
};
|
||||
|
||||
QNetworkAccessFtpBackend();
|
||||
virtual ~QNetworkAccessFtpBackend();
|
||||
|
||||
virtual void open();
|
||||
virtual void closeDownstreamChannel();
|
||||
|
||||
virtual void downstreamReadyWrite();
|
||||
|
||||
enum CacheCleanupMode {
|
||||
ReleaseCachedConnection,
|
||||
RemoveCachedConnection
|
||||
};
|
||||
|
||||
void disconnectFromFtp(CacheCleanupMode mode = ReleaseCachedConnection);
|
||||
|
||||
public slots:
|
||||
void ftpConnectionReady(QNetworkAccessCache::CacheableObject *object);
|
||||
void ftpDone();
|
||||
void ftpReadyRead();
|
||||
void ftpRawCommandReply(int code, const QString &text);
|
||||
|
||||
private:
|
||||
friend class QNetworkAccessFtpIODevice;
|
||||
QPointer<QNetworkAccessCachedFtpConnection> ftp;
|
||||
QIODevice *uploadDevice;
|
||||
qint64 totalBytes;
|
||||
int helpId, sizeId, mdtmId;
|
||||
bool supportsSize, supportsMdtm;
|
||||
State state;
|
||||
};
|
||||
|
||||
class QNetworkAccessFtpBackendFactory: public QNetworkAccessBackendFactory
|
||||
{
|
||||
public:
|
||||
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_FTP
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,148 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSHTTPBACKEND_P_H
|
||||
#define QNETWORKACCESSHTTPBACKEND_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qhttpnetworkconnection_p.h"
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "qabstractsocket.h"
|
||||
|
||||
#include "QtCore/qpointer.h"
|
||||
#include "QtCore/qdatetime.h"
|
||||
#include "QtCore/qsharedpointer.h"
|
||||
#include "qatomic.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkAccessCachedHttpConnection;
|
||||
|
||||
class QNetworkAccessHttpBackendIODevice;
|
||||
|
||||
class QNetworkAccessHttpBackend: public QNetworkAccessBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkAccessHttpBackend();
|
||||
virtual ~QNetworkAccessHttpBackend();
|
||||
|
||||
virtual void open();
|
||||
virtual void closeDownstreamChannel();
|
||||
|
||||
virtual void downstreamReadyWrite();
|
||||
virtual void setDownstreamLimited(bool b);
|
||||
virtual void setReadBufferSize(qint64 size);
|
||||
virtual void emitReadBufferFreed(qint64 size);
|
||||
|
||||
virtual void copyFinished(QIODevice *);
|
||||
virtual void ignoreSslErrors();
|
||||
virtual void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
virtual void fetchSslConfiguration(QSslConfiguration &configuration) const;
|
||||
virtual void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
QNetworkCacheMetaData fetchCacheMetaData(const QNetworkCacheMetaData &metaData) const;
|
||||
|
||||
// we return true since HTTP needs to send PUT/POST data again after having authenticated
|
||||
bool needsResetableUploadData() { return true; }
|
||||
|
||||
bool canResume() const;
|
||||
void setResumeOffset(quint64 offset);
|
||||
|
||||
signals:
|
||||
// To HTTP thread:
|
||||
void startHttpRequest();
|
||||
void abortHttpRequest();
|
||||
void readBufferSizeChanged(qint64 size);
|
||||
void readBufferFreed(qint64 size);
|
||||
|
||||
void startHttpRequestSynchronously();
|
||||
|
||||
void haveUploadData(const qint64 pos, QByteArray dataArray, bool dataAtEnd, qint64 dataSize);
|
||||
private slots:
|
||||
// From HTTP thread:
|
||||
void replyDownloadData(QByteArray);
|
||||
void replyFinished();
|
||||
void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &,int,QString,bool,QSharedPointer<char>,qint64);
|
||||
void replyDownloadProgressSlot(qint64,qint64);
|
||||
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
|
||||
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
|
||||
void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
|
||||
void replySslConfigurationChanged(const QSslConfiguration&);
|
||||
|
||||
// From QNonContiguousByteDeviceThreadForwardImpl in HTTP thread:
|
||||
void resetUploadDataSlot(bool *r);
|
||||
void wantUploadDataSlot(qint64);
|
||||
void sentUploadDataSlot(qint64, qint64);
|
||||
|
||||
bool sendCacheContents(const QNetworkCacheMetaData &metaData);
|
||||
|
||||
private:
|
||||
QHttpNetworkRequest httpRequest; // There is also a copy in the HTTP thread
|
||||
int statusCode;
|
||||
qint64 uploadByteDevicePosition;
|
||||
QString reasonPhrase;
|
||||
// Will be increased by HTTP thread:
|
||||
QSharedPointer<QAtomicInt> pendingDownloadDataEmissions;
|
||||
QSharedPointer<QAtomicInt> pendingDownloadProgressEmissions;
|
||||
bool loadingFromCache;
|
||||
QByteDataBuffer pendingDownloadData;
|
||||
bool usingZerocopyDownloadBuffer;
|
||||
|
||||
QSslConfiguration *pendingSslConfiguration;
|
||||
bool pendingIgnoreAllSslErrors;
|
||||
QList<QSslError> pendingIgnoreSslErrorsList;
|
||||
|
||||
quint64 resumeOffset;
|
||||
|
||||
bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
|
||||
void invalidateCache();
|
||||
void postRequest();
|
||||
void readFromHttp();
|
||||
void checkForRedirect(const int statusCode);
|
||||
};
|
||||
|
||||
class QNetworkAccessHttpBackendFactory : public QNetworkAccessBackendFactory
|
||||
{
|
||||
public:
|
||||
virtual QNetworkAccessBackend *create(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &request) const;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#endif
|
|
@ -1,998 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include "qnetworkcookie.h"
|
||||
#include "qabstractnetworkcache.h"
|
||||
#include "qnetworkcookiejar.h"
|
||||
#include "qnetworkaccesshttpbackend_p.h"
|
||||
#include "qnetworkaccessftpbackend_p.h"
|
||||
#include "qnetworkaccessfilebackend_p.h"
|
||||
#include "qnetworkaccessdebugpipebackend_p.h"
|
||||
#include "qnetworkaccesscachebackend_p.h"
|
||||
#include "qnetworkreplydataimpl_p.h"
|
||||
#include "qnetworkreplyfileimpl_p.h"
|
||||
#include "qbuffer.h"
|
||||
#include "qurl.h"
|
||||
#include "qvector.h"
|
||||
#include "qauthenticator_p.h"
|
||||
#include "qsslconfiguration.h"
|
||||
#include "qhttpmultipart.h"
|
||||
#include "qhttpmultipart_p.h"
|
||||
#include "qthread.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
Q_GLOBAL_STATIC(QNetworkAccessHttpBackendFactory, httpBackend)
|
||||
#endif // QT_NO_HTTP
|
||||
Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
|
||||
#ifndef QT_NO_FTP
|
||||
Q_GLOBAL_STATIC(QNetworkAccessFtpBackendFactory, ftpBackend)
|
||||
#endif // QT_NO_FTP
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
|
||||
#endif
|
||||
|
||||
static void ensureInitialized()
|
||||
{
|
||||
#ifndef QT_NO_HTTP
|
||||
(void) httpBackend();
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
#ifndef QT_NO_FTP
|
||||
(void) ftpBackend();
|
||||
#endif
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
(void) debugpipeBackend();
|
||||
#endif
|
||||
|
||||
// leave this one last since it will query the special QAbstractFileEngines
|
||||
(void) fileBackend();
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QNetworkAccessManager
|
||||
\brief The QNetworkAccessManager class allows the application to
|
||||
send network requests and receive replies
|
||||
\since 4.4
|
||||
|
||||
\ingroup network
|
||||
\inmodule QtNetwork
|
||||
\reentrant
|
||||
|
||||
The Network Access API is constructed around one QNetworkAccessManager
|
||||
object, which holds the common configuration and settings for the requests
|
||||
it sends. It contains the proxy and cache configuration, as well as the
|
||||
signals related to such issues, and reply signals that can be used to
|
||||
monitor the progress of a network operation. One QNetworkAccessManager
|
||||
should be enough for the whole Qt application.
|
||||
|
||||
Once a QNetworkAccessManager object has been created, the application can
|
||||
use it to send requests over the network. A group of standard functions
|
||||
are supplied that take a request and optional data, and each return a
|
||||
QNetworkReply object. The returned object is used to obtain any data
|
||||
returned in response to the corresponding request.
|
||||
|
||||
A simple download off the network could be accomplished with:
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 0
|
||||
|
||||
QNetworkAccessManager has an asynchronous API.
|
||||
When the \tt replyFinished slot above is called, the parameter it
|
||||
takes is the QNetworkReply object containing the downloaded data
|
||||
as well as meta-data (headers, etc.).
|
||||
|
||||
\note After the request has finished, it is the responsibility of the user
|
||||
to delete the QNetworkReply object at an appropriate time. Do not directly
|
||||
delete it inside the slot connected to finished(). You can use the
|
||||
deleteLater() function.
|
||||
|
||||
\note QNetworkAccessManager queues the requests it receives. The number
|
||||
of requests executed in parallel is dependent on the protocol.
|
||||
Currently, for the HTTP protocol on desktop platforms, 6 requests are
|
||||
executed in parallel for one host/port combination.
|
||||
|
||||
A more involved example, assuming the manager is already existent,
|
||||
can be:
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 1
|
||||
|
||||
\sa QNetworkRequest, QNetworkReply, QNetworkProxy
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkAccessManager::Operation
|
||||
|
||||
Indicates the operation this reply is processing.
|
||||
|
||||
\value HeadOperation retrieve headers operation (created
|
||||
with head())
|
||||
|
||||
\value GetOperation retrieve headers and download contents
|
||||
(created with get())
|
||||
|
||||
\value PutOperation upload contents operation (created
|
||||
with put())
|
||||
|
||||
\value PostOperation send the contents of an HTML form for
|
||||
processing via HTTP POST (created with post())
|
||||
|
||||
\value DeleteOperation delete contents operation (created with
|
||||
deleteResource())
|
||||
|
||||
\value CustomOperation custom operation (created with
|
||||
sendCustomRequest()) \since 4.7
|
||||
|
||||
\omitvalue UnknownOperation
|
||||
|
||||
\sa QNetworkReply::operation()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkAccessManager::NetworkAccessibility
|
||||
|
||||
Indicates whether the network is accessible via this network access manager.
|
||||
|
||||
\value UnknownAccessibility The network accessibility cannot be determined.
|
||||
\value NotAccessible The network is not currently accessible, either because there
|
||||
is currently no network coverage or network access has been
|
||||
explicitly disabled by a call to setNetworkAccessible().
|
||||
\value Accessible The network is accessible.
|
||||
|
||||
\sa networkAccessible
|
||||
*/
|
||||
|
||||
/*!
|
||||
\property QNetworkAccessManager::networkAccessible
|
||||
\brief whether the network is currently accessible via this network access manager.
|
||||
|
||||
\since 4.7
|
||||
|
||||
If the network is \l {NotAccessible}{not accessible} the network access manager will not
|
||||
process any new network requests, all such requests will fail with an error. Requests with
|
||||
URLs with the file:// scheme will still be processed.
|
||||
|
||||
By default the value of this property reflects the physical state of the device. Applications
|
||||
may override it to disable all network requests via this network access manager by calling
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 4
|
||||
|
||||
Network requests can be reenabled again by calling
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkaccessmanager.cpp 5
|
||||
|
||||
\note Calling setNetworkAccessible() does not change the network state.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkAccessManager::networkAccessibleChanged(QNetworkAccessManager::NetworkAccessibility accessible)
|
||||
|
||||
This signal is emitted when the value of the \l networkAccessible property changes.
|
||||
\a accessible is the new network accessibility.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkAccessManager::networkSessionConnected()
|
||||
|
||||
\since 4.7
|
||||
|
||||
\internal
|
||||
|
||||
This signal is emitted when the status of the network session changes into a usable (Connected)
|
||||
state. It is used to signal to QNetworkReplys to start or migrate their network operation once
|
||||
the network session has been opened or finished roaming.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkAccessManager::proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator)
|
||||
|
||||
This signal is emitted whenever a proxy requests authentication
|
||||
and QNetworkAccessManager cannot find a valid, cached
|
||||
credential. The slot connected to this signal should fill in the
|
||||
credentials for the proxy \a proxy in the \a authenticator object.
|
||||
|
||||
QNetworkAccessManager will cache the credentials internally. The
|
||||
next time the proxy requests authentication, QNetworkAccessManager
|
||||
will automatically send the same credential without emitting the
|
||||
proxyAuthenticationRequired signal again.
|
||||
|
||||
If the proxy rejects the credentials, QNetworkAccessManager will
|
||||
emit the signal again.
|
||||
|
||||
\sa proxy(), setProxy(), authenticationRequired()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkAccessManager::authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator)
|
||||
|
||||
This signal is emitted whenever a final server requests
|
||||
authentication before it delivers the requested contents. The slot
|
||||
connected to this signal should fill the credentials for the
|
||||
contents (which can be determined by inspecting the \a reply
|
||||
object) in the \a authenticator object.
|
||||
|
||||
QNetworkAccessManager will cache the credentials internally and
|
||||
will send the same values if the server requires authentication
|
||||
again, without emitting the authenticationRequired() signal. If it
|
||||
rejects the credentials, this signal will be emitted again.
|
||||
|
||||
\note It is not possible to use a QueuedConnection to connect to
|
||||
this signal, as the connection will fail if the authenticator has
|
||||
not been filled in with new information when the signal returns.
|
||||
|
||||
\sa proxyAuthenticationRequired()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkAccessManager::finished(QNetworkReply *reply)
|
||||
|
||||
This signal is emitted whenever a pending network reply is
|
||||
finished. The \a reply parameter will contain a pointer to the
|
||||
reply that has just finished. This signal is emitted in tandem
|
||||
with the QNetworkReply::finished() signal.
|
||||
|
||||
See QNetworkReply::finished() for information on the status that
|
||||
the object will be in.
|
||||
|
||||
\note Do not delete the \a reply object in the slot connected to this
|
||||
signal. Use deleteLater().
|
||||
|
||||
\sa QNetworkReply::finished(), QNetworkReply::error()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkAccessManager::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
|
||||
|
||||
This signal is emitted if the SSL/TLS session encountered errors
|
||||
during the set up, including certificate verification errors. The
|
||||
\a errors parameter contains the list of errors and \a reply is
|
||||
the QNetworkReply that is encountering these errors.
|
||||
|
||||
To indicate that the errors are not fatal and that the connection
|
||||
should proceed, the QNetworkReply::ignoreSslErrors() function should be called
|
||||
from the slot connected to this signal. If it is not called, the
|
||||
SSL session will be torn down before any data is exchanged
|
||||
(including the URL).
|
||||
|
||||
This signal can be used to display an error message to the user
|
||||
indicating that security may be compromised and display the
|
||||
SSL settings (see sslConfiguration() to obtain it). If the user
|
||||
decides to proceed after analyzing the remote certificate, the
|
||||
slot should call ignoreSslErrors().
|
||||
|
||||
\sa QSslSocket::sslErrors(), QNetworkReply::sslErrors(),
|
||||
QNetworkReply::sslConfiguration(), QNetworkReply::ignoreSslErrors()
|
||||
*/
|
||||
|
||||
|
||||
/*!
|
||||
Constructs a QNetworkAccessManager object that is the center of
|
||||
the Network Access API and sets \a parent as the parent object.
|
||||
*/
|
||||
QNetworkAccessManager::QNetworkAccessManager(QObject *parent)
|
||||
: QObject(*new QNetworkAccessManagerPrivate, parent)
|
||||
{
|
||||
ensureInitialized();
|
||||
|
||||
qRegisterMetaType<QNetworkReply::NetworkError>("QNetworkReply::NetworkError");
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the QNetworkAccessManager object and frees up any
|
||||
resources. Note that QNetworkReply objects that are returned from
|
||||
this class have this object set as their parents, which means that
|
||||
they will be deleted along with it if you don't call
|
||||
QObject::setParent() on them.
|
||||
*/
|
||||
QNetworkAccessManager::~QNetworkAccessManager()
|
||||
{
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
delete d_func()->proxyFactory;
|
||||
#endif
|
||||
|
||||
// Delete the QNetworkReply children first.
|
||||
// Else a QAbstractNetworkCache might get deleted in ~QObject
|
||||
// before a QNetworkReply that accesses the QAbstractNetworkCache
|
||||
// object in its destructor.
|
||||
qDeleteAll(findChildren<QNetworkReply *>());
|
||||
// The other children will be deleted in this ~QObject
|
||||
// FIXME instead of this "hack" make the QNetworkReplyImpl
|
||||
// properly watch the cache deletion, e.g. via a QWeakPointer.
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
/*!
|
||||
Returns the QNetworkProxy that the requests sent using this
|
||||
QNetworkAccessManager object will use. The default value for the
|
||||
proxy is QNetworkProxy::DefaultProxy.
|
||||
|
||||
\sa setProxy(), setProxyFactory(), proxyAuthenticationRequired()
|
||||
*/
|
||||
QNetworkProxy QNetworkAccessManager::proxy() const
|
||||
{
|
||||
return d_func()->proxy;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the proxy to be used in future requests to be \a proxy. This
|
||||
does not affect requests that have already been sent. The
|
||||
proxyAuthenticationRequired() signal will be emitted if the proxy
|
||||
requests authentication.
|
||||
|
||||
A proxy set with this function will be used for all requests
|
||||
issued by QNetworkAccessManager. In some cases, it might be
|
||||
necessary to select different proxies depending on the type of
|
||||
request being sent or the destination host. If that's the case,
|
||||
you should consider using setProxyFactory().
|
||||
|
||||
\sa proxy(), proxyAuthenticationRequired()
|
||||
*/
|
||||
void QNetworkAccessManager::setProxy(const QNetworkProxy &proxy)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
delete d->proxyFactory;
|
||||
d->proxy = proxy;
|
||||
d->proxyFactory = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
|
||||
\since 4.5
|
||||
|
||||
Returns the proxy factory that this QNetworkAccessManager object
|
||||
is using to determine the proxies to be used for requests.
|
||||
|
||||
Note that the pointer returned by this function is managed by
|
||||
QNetworkAccessManager and could be deleted at any time.
|
||||
|
||||
\sa setProxyFactory(), proxy()
|
||||
*/
|
||||
QNetworkProxyFactory *QNetworkAccessManager::proxyFactory() const
|
||||
{
|
||||
return d_func()->proxyFactory;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.5
|
||||
|
||||
Sets the proxy factory for this class to be \a factory. A proxy
|
||||
factory is used to determine a more specific list of proxies to be
|
||||
used for a given request, instead of trying to use the same proxy
|
||||
value for all requests.
|
||||
|
||||
All queries sent by QNetworkAccessManager will have type
|
||||
QNetworkProxyQuery::UrlRequest.
|
||||
|
||||
For example, a proxy factory could apply the following rules:
|
||||
\list
|
||||
\o if the target address is in the local network (for example,
|
||||
if the hostname contains no dots or if it's an IP address in
|
||||
the organization's range), return QNetworkProxy::NoProxy
|
||||
\o if the request is FTP, return an FTP proxy
|
||||
\o if the request is HTTP or HTTPS, then return an HTTP proxy
|
||||
\o otherwise, return a SOCKSv5 proxy server
|
||||
\endlist
|
||||
|
||||
The lifetime of the object \a factory will be managed by
|
||||
QNetworkAccessManager. It will delete the object when necessary.
|
||||
|
||||
\note If a specific proxy is set with setProxy(), the factory will not
|
||||
be used.
|
||||
|
||||
\sa proxyFactory(), setProxy(), QNetworkProxyQuery
|
||||
*/
|
||||
void QNetworkAccessManager::setProxyFactory(QNetworkProxyFactory *factory)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
delete d->proxyFactory;
|
||||
d->proxyFactory = factory;
|
||||
d->proxy = QNetworkProxy();
|
||||
}
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\since 4.5
|
||||
|
||||
Returns the cache that is used to store data obtained from the network.
|
||||
|
||||
\sa setCache()
|
||||
*/
|
||||
QAbstractNetworkCache *QNetworkAccessManager::cache() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
return d->networkCache;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.5
|
||||
|
||||
Sets the manager's network cache to be the \a cache specified. The cache
|
||||
is used for all requests dispatched by the manager.
|
||||
|
||||
Use this function to set the network cache object to a class that implements
|
||||
additional features, like saving the cookies to permanent storage.
|
||||
|
||||
\note QNetworkAccessManager takes ownership of the \a cache object.
|
||||
|
||||
QNetworkAccessManager by default does not have a set cache.
|
||||
Qt provides a simple disk cache, QNetworkDiskCache, which can be used.
|
||||
|
||||
\sa cache(), QNetworkRequest::CacheLoadControl
|
||||
*/
|
||||
void QNetworkAccessManager::setCache(QAbstractNetworkCache *cache)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
if (d->networkCache != cache) {
|
||||
delete d->networkCache;
|
||||
d->networkCache = cache;
|
||||
if (d->networkCache)
|
||||
d->networkCache->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the QNetworkCookieJar that is used to store cookies
|
||||
obtained from the network as well as cookies that are about to be
|
||||
sent.
|
||||
|
||||
\sa setCookieJar()
|
||||
*/
|
||||
QNetworkCookieJar *QNetworkAccessManager::cookieJar() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
if (!d->cookieJar)
|
||||
d->createCookieJar();
|
||||
return d->cookieJar;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the manager's cookie jar to be the \a cookieJar specified.
|
||||
The cookie jar is used by all requests dispatched by the manager.
|
||||
|
||||
Use this function to set the cookie jar object to a class that
|
||||
implements additional features, like saving the cookies to permanent
|
||||
storage.
|
||||
|
||||
\note QNetworkAccessManager takes ownership of the \a cookieJar object.
|
||||
|
||||
If \a cookieJar is in the same thread as this QNetworkAccessManager,
|
||||
it will set the parent of the \a cookieJar
|
||||
so that the cookie jar is deleted when this
|
||||
object is deleted as well. If you want to share cookie jars
|
||||
between different QNetworkAccessManager objects, you may want to
|
||||
set the cookie jar's parent to 0 after calling this function.
|
||||
|
||||
QNetworkAccessManager by default does not implement any cookie
|
||||
policy of its own: it accepts all cookies sent by the server, as
|
||||
long as they are well formed and meet the minimum security
|
||||
requirements (cookie domain matches the request's and cookie path
|
||||
matches the request's). In order to implement your own security
|
||||
policy, override the QNetworkCookieJar::cookiesForUrl() and
|
||||
QNetworkCookieJar::setCookiesFromUrl() virtual functions. Those
|
||||
functions are called by QNetworkAccessManager when it detects a
|
||||
new cookie.
|
||||
|
||||
\sa cookieJar(), QNetworkCookieJar::cookiesForUrl(), QNetworkCookieJar::setCookiesFromUrl()
|
||||
*/
|
||||
void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
d->cookieJarCreated = true;
|
||||
if (d->cookieJar != cookieJar) {
|
||||
if (d->cookieJar && d->cookieJar->parent() == this)
|
||||
delete d->cookieJar;
|
||||
d->cookieJar = cookieJar;
|
||||
if (thread() == cookieJar->thread())
|
||||
d->cookieJar->setParent(this);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
Posts a request to obtain the network headers for \a request
|
||||
and returns a new QNetworkReply object which will contain such headers.
|
||||
|
||||
The function is named after the HTTP request associated (HEAD).
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::head(const QNetworkRequest &request)
|
||||
{
|
||||
return d_func()->postProcess(createRequest(QNetworkAccessManager::HeadOperation, request));
|
||||
}
|
||||
|
||||
/*!
|
||||
Posts a request to obtain the contents of the target \a request
|
||||
and returns a new QNetworkReply object opened for reading which emits the
|
||||
\l{QIODevice::readyRead()}{readyRead()} signal whenever new data
|
||||
arrives.
|
||||
|
||||
The contents as well as associated headers will be downloaded.
|
||||
|
||||
\sa post(), put(), deleteResource(), sendCustomRequest()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
|
||||
{
|
||||
return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));
|
||||
}
|
||||
|
||||
/*!
|
||||
Sends an HTTP POST request to the destination specified by \a request
|
||||
and returns a new QNetworkReply object opened for reading that will
|
||||
contain the reply sent by the server. The contents of the \a data
|
||||
device will be uploaded to the server.
|
||||
|
||||
\a data must be open for reading and must remain valid until the
|
||||
finished() signal is emitted for this reply.
|
||||
|
||||
\note Sending a POST request on protocols other than HTTP and
|
||||
HTTPS is undefined and will probably fail.
|
||||
|
||||
\sa get(), put(), deleteResource(), sendCustomRequest()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QIODevice *data)
|
||||
{
|
||||
return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation, request, data));
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
Sends the contents of the \a data byte array to the destination
|
||||
specified by \a request.
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data)
|
||||
{
|
||||
QBuffer *buffer = new QBuffer;
|
||||
buffer->setData(data);
|
||||
buffer->open(QIODevice::ReadOnly);
|
||||
|
||||
QNetworkReply *reply = post(request, buffer);
|
||||
buffer->setParent(reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.8
|
||||
|
||||
\overload
|
||||
|
||||
Sends the contents of the \a multiPart message to the destination
|
||||
specified by \a request.
|
||||
|
||||
This can be used for sending MIME multipart messages over HTTP.
|
||||
|
||||
\sa QHttpMultiPart, QHttpPart, put()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, QHttpMultiPart *multiPart)
|
||||
{
|
||||
QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
|
||||
QIODevice *device = multiPart->d_func()->device;
|
||||
QNetworkReply *reply = post(newRequest, device);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.8
|
||||
|
||||
\overload
|
||||
|
||||
Sends the contents of the \a multiPart message to the destination
|
||||
specified by \a request.
|
||||
|
||||
This can be used for sending MIME multipart messages over HTTP.
|
||||
|
||||
\sa QHttpMultiPart, QHttpPart, post()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QHttpMultiPart *multiPart)
|
||||
{
|
||||
QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
|
||||
QIODevice *device = multiPart->d_func()->device;
|
||||
QNetworkReply *reply = put(newRequest, device);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*!
|
||||
Uploads the contents of \a data to the destination \a request and
|
||||
returnes a new QNetworkReply object that will be open for reply.
|
||||
|
||||
\a data must be opened for reading when this function is called
|
||||
and must remain valid until the finished() signal is emitted for
|
||||
this reply.
|
||||
|
||||
Whether anything will be available for reading from the returned
|
||||
object is protocol dependent. For HTTP, the server may send a
|
||||
small HTML page indicating the upload was successful (or not).
|
||||
Other protocols will probably have content in their replies.
|
||||
|
||||
\note For HTTP, this request will send a PUT request, which most servers
|
||||
do not allow. Form upload mechanisms, including that of uploading
|
||||
files through HTML forms, use the POST mechanism.
|
||||
|
||||
\sa get(), post(), deleteResource(), sendCustomRequest()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, QIODevice *data)
|
||||
{
|
||||
return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, data));
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
|
||||
Sends the contents of the \a data byte array to the destination
|
||||
specified by \a request.
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const QByteArray &data)
|
||||
{
|
||||
QBuffer *buffer = new QBuffer;
|
||||
buffer->setData(data);
|
||||
buffer->open(QIODevice::ReadOnly);
|
||||
|
||||
QNetworkReply *reply = put(request, buffer);
|
||||
buffer->setParent(reply);
|
||||
return reply;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Sends a request to delete the resource identified by the URL of \a request.
|
||||
|
||||
\note This feature is currently available for HTTP only, performing an
|
||||
HTTP DELETE request.
|
||||
|
||||
\sa get(), post(), put(), sendCustomRequest()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::deleteResource(const QNetworkRequest &request)
|
||||
{
|
||||
return d_func()->postProcess(createRequest(QNetworkAccessManager::DeleteOperation, request));
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.7
|
||||
|
||||
Sends a custom request to the server identified by the URL of \a request.
|
||||
|
||||
It is the user's responsibility to send a \a verb to the server that is valid
|
||||
according to the HTTP specification.
|
||||
|
||||
This method provides means to send verbs other than the common ones provided
|
||||
via get() or post() etc., for instance sending an HTTP OPTIONS command.
|
||||
|
||||
If \a data is not empty, the contents of the \a data
|
||||
device will be uploaded to the server; in that case, data must be open for
|
||||
reading and must remain valid until the finished() signal is emitted for this reply.
|
||||
|
||||
\note This feature is currently available for HTTP(S) only.
|
||||
|
||||
\sa get(), post(), put(), deleteResource()
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data)
|
||||
{
|
||||
QNetworkRequest newRequest(request);
|
||||
newRequest.setAttribute(QNetworkRequest::CustomVerbAttribute, verb);
|
||||
return d_func()->postProcess(createRequest(QNetworkAccessManager::CustomOperation, newRequest, data));
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new QNetworkReply object to handle the operation \a op
|
||||
and request \a req. The device \a outgoingData is always 0 for Get and
|
||||
Head requests, but is the value passed to post() and put() in
|
||||
those operations (the QByteArray variants will pass a QBuffer
|
||||
object).
|
||||
|
||||
The default implementation calls QNetworkCookieJar::cookiesForUrl()
|
||||
on the cookie jar set with setCookieJar() to obtain the cookies to
|
||||
be sent to the remote server.
|
||||
|
||||
The returned object must be in an open state.
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &req,
|
||||
QIODevice *outgoingData)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
|
||||
bool isLocalFile = req.url().isLocalFile();
|
||||
QString scheme = req.url().scheme().toLower();
|
||||
|
||||
// fast path for GET on file:// URLs
|
||||
// The QNetworkAccessFileBackend will right now only be used for PUT
|
||||
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
|
||||
&& isLocalFile) {
|
||||
return new QNetworkReplyFileImpl(this, req, op);
|
||||
}
|
||||
|
||||
if ((op == QNetworkAccessManager::GetOperation || op == QNetworkAccessManager::HeadOperation)
|
||||
&& scheme == QLatin1String("data")) {
|
||||
return new QNetworkReplyDataImpl(this, req, op);
|
||||
}
|
||||
|
||||
QNetworkRequest::CacheLoadControl mode =
|
||||
static_cast<QNetworkRequest::CacheLoadControl>(
|
||||
req.attribute(QNetworkRequest::CacheLoadControlAttribute,
|
||||
QNetworkRequest::PreferNetwork).toInt());
|
||||
if (mode == QNetworkRequest::AlwaysCache
|
||||
&& (op == QNetworkAccessManager::GetOperation
|
||||
|| op == QNetworkAccessManager::HeadOperation)) {
|
||||
// FIXME Implement a QNetworkReplyCacheImpl instead, see QTBUG-15106
|
||||
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
|
||||
QNetworkReplyImplPrivate *priv = reply->d_func();
|
||||
priv->manager = this;
|
||||
priv->backend = new QNetworkAccessCacheBackend();
|
||||
priv->backend->manager = this->d_func();
|
||||
priv->backend->setParent(reply);
|
||||
priv->backend->reply = priv;
|
||||
priv->setup(op, req, outgoingData);
|
||||
return reply;
|
||||
}
|
||||
|
||||
QNetworkRequest request = req;
|
||||
if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
|
||||
outgoingData && !outgoingData->isSequential()) {
|
||||
// request has no Content-Length
|
||||
// but the data that is outgoing is random-access
|
||||
request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
|
||||
}
|
||||
|
||||
if (static_cast<QNetworkRequest::LoadControl>
|
||||
(request.attribute(QNetworkRequest::CookieLoadControlAttribute,
|
||||
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
|
||||
if (d->cookieJar) {
|
||||
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
|
||||
if (!cookies.isEmpty())
|
||||
request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
|
||||
}
|
||||
}
|
||||
|
||||
// first step: create the reply
|
||||
QNetworkReplyImpl *reply = new QNetworkReplyImpl(this);
|
||||
QNetworkReplyImplPrivate *priv = reply->d_func();
|
||||
priv->manager = this;
|
||||
|
||||
// second step: fetch cached credentials
|
||||
// This is not done for the time being, we should use signal emissions to request
|
||||
// the credentials from cache.
|
||||
|
||||
// third step: find a backend
|
||||
priv->backend = d->findBackend(op, request);
|
||||
|
||||
if (priv->backend) {
|
||||
priv->backend->setParent(reply);
|
||||
priv->backend->reply = priv;
|
||||
}
|
||||
|
||||
reply->setSslConfiguration(request.sslConfiguration());
|
||||
|
||||
// fourth step: setup the reply
|
||||
priv->setup(op, request, outgoingData);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
void QNetworkAccessManagerPrivate::_q_replyFinished()
|
||||
{
|
||||
Q_Q(QNetworkAccessManager);
|
||||
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
|
||||
if (reply)
|
||||
emit q->finished(reply);
|
||||
}
|
||||
|
||||
void QNetworkAccessManagerPrivate::_q_replySslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_Q(QNetworkAccessManager);
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(q->sender());
|
||||
if (reply)
|
||||
emit q->sslErrors(reply, errors);
|
||||
}
|
||||
|
||||
QNetworkReply *QNetworkAccessManagerPrivate::postProcess(QNetworkReply *reply)
|
||||
{
|
||||
Q_Q(QNetworkAccessManager);
|
||||
QNetworkReplyPrivate::setManager(reply, q);
|
||||
q->connect(reply, SIGNAL(finished()), SLOT(_q_replyFinished()));
|
||||
q->connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(_q_replySslErrors(QList<QSslError>)));
|
||||
return reply;
|
||||
}
|
||||
|
||||
void QNetworkAccessManagerPrivate::createCookieJar() const
|
||||
{
|
||||
if (!cookieJarCreated) {
|
||||
// keep the ugly hack in here
|
||||
QNetworkAccessManagerPrivate *that = const_cast<QNetworkAccessManagerPrivate *>(this);
|
||||
that->cookieJar = new QNetworkCookieJar(that->q_func());
|
||||
that->cookieJarCreated = true;
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkAccessManagerPrivate::authenticationRequired(QNetworkAccessBackend *backend,
|
||||
QAuthenticator *authenticator)
|
||||
{
|
||||
Q_Q(QNetworkAccessManager);
|
||||
|
||||
// FIXME: Add support for domains (i.e., the leading path)
|
||||
QUrl url = backend->reply->url;
|
||||
|
||||
// don't try the cache for the same URL twice in a row
|
||||
// being called twice for the same URL means the authentication failed
|
||||
// also called when last URL is empty, e.g. on first call
|
||||
if ((static_cast<QNetworkRequest::LoadControl>
|
||||
(backend->request().attribute(QNetworkRequest::AuthenticationReuseAttribute).toInt()) != QNetworkRequest::Manual)
|
||||
&& (backend->reply->urlForLastAuthentication.isEmpty()
|
||||
|| url != backend->reply->urlForLastAuthentication)) {
|
||||
// if credentials are included in the url, then use them
|
||||
if (!url.userName().isEmpty()
|
||||
&& !url.password().isEmpty()) {
|
||||
authenticator->setUser(url.userName());
|
||||
authenticator->setPassword(url.password());
|
||||
backend->reply->urlForLastAuthentication = url;
|
||||
authenticationManager->cacheCredentials(url, authenticator);
|
||||
return;
|
||||
}
|
||||
|
||||
QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedCredentials(url, authenticator);
|
||||
if (!cred.isNull()) {
|
||||
authenticator->setUser(cred.user);
|
||||
authenticator->setPassword(cred.password);
|
||||
backend->reply->urlForLastAuthentication = url;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we emit a signal here in synchronous mode, the user might spin
|
||||
// an event loop, which might recurse and lead to problems
|
||||
if (backend->isSynchronous())
|
||||
return;
|
||||
|
||||
backend->reply->urlForLastAuthentication = url;
|
||||
emit q->authenticationRequired(backend->reply->q_func(), authenticator);
|
||||
authenticationManager->cacheCredentials(url, authenticator);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void QNetworkAccessManagerPrivate::proxyAuthenticationRequired(QNetworkAccessBackend *backend,
|
||||
const QNetworkProxy &proxy,
|
||||
QAuthenticator *authenticator)
|
||||
{
|
||||
Q_Q(QNetworkAccessManager);
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*authenticator);
|
||||
if (proxy != backend->reply->lastProxyAuthentication && (!priv || !priv->hasFailed)) {
|
||||
QNetworkAuthenticationCredential cred = authenticationManager->fetchCachedProxyCredentials(proxy);
|
||||
if (!cred.isNull()) {
|
||||
authenticator->setUser(cred.user);
|
||||
authenticator->setPassword(cred.password);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// if we emit a signal here in synchronous mode, the user might spin
|
||||
// an event loop, which might recurse and lead to problems
|
||||
if (backend->isSynchronous())
|
||||
return;
|
||||
|
||||
backend->reply->lastProxyAuthentication = proxy;
|
||||
emit q->proxyAuthenticationRequired(proxy, authenticator);
|
||||
authenticationManager->cacheProxyCredentials(proxy, authenticator);
|
||||
}
|
||||
|
||||
QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProxyQuery &query)
|
||||
{
|
||||
QList<QNetworkProxy> proxies;
|
||||
if (proxyFactory) {
|
||||
proxies = proxyFactory->queryProxy(query);
|
||||
if (proxies.isEmpty()) {
|
||||
qWarning("QNetworkAccessManager: factory %p has returned an empty result set",
|
||||
proxyFactory);
|
||||
proxies << QNetworkProxy::NoProxy;
|
||||
}
|
||||
} else if (proxy.type() == QNetworkProxy::DefaultProxy) {
|
||||
// no proxy set, query the application
|
||||
return QNetworkProxyFactory::proxyForQuery(query);
|
||||
} else {
|
||||
proxies << proxy;
|
||||
}
|
||||
|
||||
return proxies;
|
||||
}
|
||||
#endif
|
||||
|
||||
void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
|
||||
{
|
||||
manager->d_func()->objectCache.clear();
|
||||
manager->d_func()->authenticationManager->clearCache();
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
if (manager->d_func()->httpThread) {
|
||||
// The thread will deleteLater() itself from its finished() signal
|
||||
manager->d_func()->httpThread->quit();
|
||||
manager->d_func()->httpThread->wait(5000);
|
||||
manager->d_func()->httpThread = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
|
||||
{
|
||||
#ifndef QT_NO_HTTP
|
||||
if (httpThread) {
|
||||
// The thread will deleteLater() itself from its finished() signal
|
||||
httpThread->quit();
|
||||
httpThread->wait(5000);
|
||||
httpThread = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart)
|
||||
{
|
||||
// copy the request, we probably need to add some headers
|
||||
QNetworkRequest newRequest(request);
|
||||
|
||||
// add Content-Type header if not there already
|
||||
if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
|
||||
QByteArray contentType;
|
||||
contentType.reserve(34 + multiPart->d_func()->boundary.count());
|
||||
contentType += "multipart/";
|
||||
switch (multiPart->d_func()->contentType) {
|
||||
case QHttpMultiPart::RelatedType:
|
||||
contentType += "related";
|
||||
break;
|
||||
case QHttpMultiPart::FormDataType:
|
||||
contentType += "form-data";
|
||||
break;
|
||||
case QHttpMultiPart::AlternativeType:
|
||||
contentType += "alternative";
|
||||
break;
|
||||
default:
|
||||
contentType += "mixed";
|
||||
break;
|
||||
}
|
||||
// putting the boundary into quotes, recommended in RFC 2046 section 5.1.1
|
||||
contentType += QLatin1String("; boundary=\"") + multiPart->d_func()->boundary + QLatin1Char('"');
|
||||
newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
|
||||
}
|
||||
|
||||
// add MIME-Version header if not there already (we must include the header
|
||||
// if the message conforms to RFC 2045, see section 4 of that RFC)
|
||||
QByteArray mimeHeader("MIME-Version");
|
||||
if (!request.hasRawHeader(mimeHeader))
|
||||
newRequest.setRawHeader(mimeHeader, QByteArray("1.0"));
|
||||
|
||||
QIODevice *device = multiPart->d_func()->device;
|
||||
if (!device->isReadable()) {
|
||||
if (!device->isOpen()) {
|
||||
if (!device->open(QIODevice::ReadOnly))
|
||||
qWarning("could not open device for reading");
|
||||
} else {
|
||||
qWarning("device is not readable");
|
||||
}
|
||||
}
|
||||
|
||||
return newRequest;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#include "moc_qnetworkaccessmanager.h"
|
|
@ -1,114 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSMANAGER_H
|
||||
#define QNETWORKACCESSMANAGER_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QIODevice;
|
||||
class QAbstractNetworkCache;
|
||||
class QAuthenticator;
|
||||
class QByteArray;
|
||||
template<typename T> class QList;
|
||||
class QNetworkCookie;
|
||||
class QNetworkCookieJar;
|
||||
class QNetworkRequest;
|
||||
class QNetworkReply;
|
||||
class QNetworkProxy;
|
||||
class QNetworkProxyFactory;
|
||||
class QSslError;
|
||||
class QHttpMultiPart;
|
||||
|
||||
class QNetworkReplyImplPrivate;
|
||||
class QNetworkAccessManagerPrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkAccessManager: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Operation {
|
||||
HeadOperation = 1,
|
||||
GetOperation,
|
||||
PutOperation,
|
||||
PostOperation,
|
||||
DeleteOperation,
|
||||
CustomOperation,
|
||||
|
||||
UnknownOperation = 0
|
||||
};
|
||||
|
||||
explicit QNetworkAccessManager(QObject *parent = nullptr);
|
||||
~QNetworkAccessManager();
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QNetworkProxy proxy() const;
|
||||
void setProxy(const QNetworkProxy &proxy);
|
||||
QNetworkProxyFactory *proxyFactory() const;
|
||||
void setProxyFactory(QNetworkProxyFactory *factory);
|
||||
#endif
|
||||
|
||||
QAbstractNetworkCache *cache() const;
|
||||
void setCache(QAbstractNetworkCache *cache);
|
||||
|
||||
QNetworkCookieJar *cookieJar() const;
|
||||
void setCookieJar(QNetworkCookieJar *cookieJar);
|
||||
|
||||
QNetworkReply *head(const QNetworkRequest &request);
|
||||
QNetworkReply *get(const QNetworkRequest &request);
|
||||
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
|
||||
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
|
||||
QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
|
||||
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
|
||||
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
|
||||
QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
|
||||
QNetworkReply *deleteResource(const QNetworkRequest &request);
|
||||
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = 0);
|
||||
|
||||
Q_SIGNALS:
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
|
||||
#endif
|
||||
void authenticationRequired(QNetworkReply *reply, QAuthenticator *authenticator);
|
||||
void finished(QNetworkReply *reply);
|
||||
void sslErrors(QNetworkReply *reply, const QList<QSslError> &errors);
|
||||
|
||||
protected:
|
||||
virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request,
|
||||
QIODevice *outgoingData = 0);
|
||||
|
||||
private:
|
||||
friend class QNetworkReplyImplPrivate;
|
||||
friend class QNetworkAccessHttpBackend;
|
||||
|
||||
Q_DECLARE_PRIVATE(QNetworkAccessManager)
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_replySslErrors(QList<QSslError>))
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif
|
|
@ -1,120 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKACCESSMANAGER_P_H
|
||||
#define QNETWORKACCESSMANAGER_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qobject_p.h"
|
||||
#include "QtNetwork/qnetworkproxy.h"
|
||||
#include "qnetworkaccessauthenticationmanager_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAuthenticator;
|
||||
class QAbstractNetworkCache;
|
||||
class QNetworkAuthenticationCredential;
|
||||
class QNetworkCookieJar;
|
||||
|
||||
class QNetworkAccessManagerPrivate: public QObjectPrivate
|
||||
{
|
||||
public:
|
||||
QNetworkAccessManagerPrivate()
|
||||
: networkCache(0), cookieJar(0),
|
||||
#ifndef QT_NO_HTTP
|
||||
httpThread(0),
|
||||
#endif
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
proxyFactory(0),
|
||||
#endif
|
||||
cookieJarCreated(false),
|
||||
authenticationManager(new QNetworkAccessAuthenticationManager)
|
||||
{ }
|
||||
~QNetworkAccessManagerPrivate();
|
||||
|
||||
void _q_replyFinished();
|
||||
void _q_replySslErrors(const QList<QSslError> &errors);
|
||||
QNetworkReply *postProcess(QNetworkReply *reply);
|
||||
void createCookieJar() const;
|
||||
|
||||
void authenticationRequired(QNetworkAccessBackend *backend, QAuthenticator *authenticator);
|
||||
void cacheCredentials(const QUrl &url, const QAuthenticator *auth);
|
||||
QNetworkAuthenticationCredential *fetchCachedCredentials(const QUrl &url,
|
||||
const QAuthenticator *auth = 0);
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(QNetworkAccessBackend *backend, const QNetworkProxy &proxy,
|
||||
QAuthenticator *authenticator);
|
||||
void cacheProxyCredentials(const QNetworkProxy &proxy, const QAuthenticator *auth);
|
||||
QNetworkAuthenticationCredential *fetchCachedProxyCredentials(const QNetworkProxy &proxy,
|
||||
const QAuthenticator *auth = 0);
|
||||
QList<QNetworkProxy> queryProxy(const QNetworkProxyQuery &query);
|
||||
#endif
|
||||
|
||||
QNetworkAccessBackend *findBackend(QNetworkAccessManager::Operation op, const QNetworkRequest &request);
|
||||
|
||||
QNetworkRequest prepareMultipart(const QNetworkRequest &request, QHttpMultiPart *multiPart);
|
||||
|
||||
// this is the cache for storing downloaded files
|
||||
QAbstractNetworkCache *networkCache;
|
||||
|
||||
QNetworkCookieJar *cookieJar;
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
QThread *httpThread;
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QNetworkProxy proxy;
|
||||
QNetworkProxyFactory *proxyFactory;
|
||||
#endif
|
||||
|
||||
bool cookieJarCreated;
|
||||
|
||||
// The cache with authorization data:
|
||||
QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
|
||||
|
||||
// this cache can be used by individual backends to cache e.g. their TCP connections to a server
|
||||
// and use the connections for multiple requests.
|
||||
QNetworkAccessCache objectCache;
|
||||
static inline QNetworkAccessCache *getObjectCache(QNetworkAccessBackend *backend)
|
||||
{ return &backend->manager->objectCache; }
|
||||
Q_AUTOTEST_EXPORT static void clearCache(QNetworkAccessManager *manager);
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkAccessManager)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,98 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKCOOKIE_H
|
||||
#define QNETWORKCOOKIE_H
|
||||
|
||||
#include <QtCore/QSharedData>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QMetaType>
|
||||
#include <QtCore/QObject>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QByteArray;
|
||||
class QDateTime;
|
||||
class QString;
|
||||
class QUrl;
|
||||
|
||||
class QNetworkCookiePrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkCookie
|
||||
{
|
||||
public:
|
||||
enum RawForm {
|
||||
NameAndValueOnly,
|
||||
Full
|
||||
};
|
||||
|
||||
QNetworkCookie(const QByteArray &name = QByteArray(), const QByteArray &value = QByteArray());
|
||||
QNetworkCookie(const QNetworkCookie &other);
|
||||
~QNetworkCookie();
|
||||
QNetworkCookie &operator=(const QNetworkCookie &other);
|
||||
bool operator==(const QNetworkCookie &other) const;
|
||||
inline bool operator!=(const QNetworkCookie &other) const
|
||||
{ return !(*this == other); }
|
||||
|
||||
bool isSecure() const;
|
||||
void setSecure(bool enable);
|
||||
bool isHttpOnly() const;
|
||||
void setHttpOnly(bool enable);
|
||||
|
||||
bool isSessionCookie() const;
|
||||
QDateTime expirationDate() const;
|
||||
void setExpirationDate(const QDateTime &date);
|
||||
|
||||
QString domain() const;
|
||||
void setDomain(const QString &domain);
|
||||
|
||||
QString path() const;
|
||||
void setPath(const QString &path);
|
||||
|
||||
QByteArray name() const;
|
||||
void setName(const QByteArray &cookieName);
|
||||
|
||||
QByteArray value() const;
|
||||
void setValue(const QByteArray &value);
|
||||
|
||||
QByteArray toRawForm(RawForm form = Full) const;
|
||||
|
||||
static QList<QNetworkCookie> parseCookies(const QByteArray &cookieString);
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QNetworkCookiePrivate> d;
|
||||
friend class QNetworkCookiePrivate;
|
||||
};
|
||||
Q_DECLARE_TYPEINFO(QNetworkCookie, Q_MOVABLE_TYPE);
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
class QDebug;
|
||||
Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QNetworkCookie &);
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkCookie)
|
||||
Q_DECLARE_METATYPE(QList<QNetworkCookie>)
|
||||
|
||||
|
||||
#endif
|
|
@ -1,80 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKCOOKIE_P_H
|
||||
#define QNETWORKCOOKIE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access framework. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "QtCore/qdatetime.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkCookiePrivate: public QSharedData
|
||||
{
|
||||
public:
|
||||
inline QNetworkCookiePrivate() : secure(false), httpOnly(false) { }
|
||||
static QList<QNetworkCookie> parseSetCookieHeaderLine(const QByteArray &cookieString);
|
||||
|
||||
QDateTime expirationDate;
|
||||
QString domain;
|
||||
QString path;
|
||||
QString comment;
|
||||
QByteArray name;
|
||||
QByteArray value;
|
||||
bool secure;
|
||||
bool httpOnly;
|
||||
};
|
||||
|
||||
static inline bool isLWS(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
|
||||
}
|
||||
|
||||
static int nextNonWhitespace(const QByteArray &text, int from)
|
||||
{
|
||||
// RFC 2616 defines linear whitespace as:
|
||||
// LWS = [CRLF] 1*( SP | HT )
|
||||
// We ignore the fact that CRLF must come as a pair at this point
|
||||
// It's an invalid HTTP header if that happens.
|
||||
while (from < text.length()) {
|
||||
if (isLWS(text.at(from)))
|
||||
++from;
|
||||
else
|
||||
return from; // non-whitespace
|
||||
}
|
||||
|
||||
// reached the end
|
||||
return text.length();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,301 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkcookiejar.h"
|
||||
#include "qnetworkcookiejar_p.h"
|
||||
|
||||
#include "QtNetwork/qnetworkcookie.h"
|
||||
#include "QtCore/qurl.h"
|
||||
#include "QtCore/qdatetime.h"
|
||||
#include "qtldurl_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QNetworkCookieJar
|
||||
\brief The QNetworkCookieJar class implements a simple jar of QNetworkCookie objects
|
||||
\since 4.4
|
||||
|
||||
Cookies are small bits of information that stateless protocols
|
||||
like HTTP use to maintain some persistent information across
|
||||
requests.
|
||||
|
||||
A cookie is set by a remote server when it replies to a request
|
||||
and it expects the same cookie to be sent back when further
|
||||
requests are sent.
|
||||
|
||||
The cookie jar is the object that holds all cookies set in
|
||||
previous requests. Web browsers save their cookie jars to disk in
|
||||
order to conserve permanent cookies across invocations of the
|
||||
application.
|
||||
|
||||
QNetworkCookieJar does not implement permanent storage: it only
|
||||
keeps the cookies in memory. Once the QNetworkCookieJar object is
|
||||
deleted, all cookies it held will be discarded as well. If you
|
||||
want to save the cookies, you should derive from this class and
|
||||
implement the saving to disk to your own storage format.
|
||||
|
||||
This class implements only the basic security recommended by the
|
||||
cookie specifications and does not implement any cookie acceptance
|
||||
policy (it accepts all cookies set by any requests). In order to
|
||||
override those rules, you should reimplement the
|
||||
cookiesForUrl() and setCookiesFromUrl() virtual
|
||||
functions. They are called by QNetworkReply and
|
||||
QNetworkAccessManager when they detect new cookies and when they
|
||||
require cookies.
|
||||
|
||||
\sa QNetworkCookie, QNetworkAccessManager, QNetworkReply,
|
||||
QNetworkRequest, QNetworkAccessManager::setCookieJar()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Creates a QNetworkCookieJar object and sets the parent object to
|
||||
be \a parent.
|
||||
|
||||
The cookie jar is initialized to empty.
|
||||
*/
|
||||
QNetworkCookieJar::QNetworkCookieJar(QObject *parent)
|
||||
: QObject(*new QNetworkCookieJarPrivate, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys this cookie jar object and discards all cookies stored in
|
||||
it. Cookies are not saved to disk in the QNetworkCookieJar default
|
||||
implementation.
|
||||
|
||||
If you need to save the cookies to disk, you have to derive from
|
||||
QNetworkCookieJar and save the cookies to disk yourself.
|
||||
*/
|
||||
QNetworkCookieJar::~QNetworkCookieJar()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns all cookies stored in this cookie jar. This function is
|
||||
suitable for derived classes to save cookies to disk, as well as
|
||||
to implement cookie expiration and other policies.
|
||||
|
||||
\sa setAllCookies(), cookiesForUrl()
|
||||
*/
|
||||
QList<QNetworkCookie> QNetworkCookieJar::allCookies() const
|
||||
{
|
||||
return d_func()->allCookies;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the internal list of cookies held by this cookie jar to be \a
|
||||
cookieList. This function is suitable for derived classes to
|
||||
implement loading cookies from permanent storage, or their own
|
||||
cookie acceptance policies by reimplementing
|
||||
setCookiesFromUrl().
|
||||
|
||||
\sa allCookies(), setCookiesFromUrl()
|
||||
*/
|
||||
void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
|
||||
{
|
||||
Q_D(QNetworkCookieJar);
|
||||
d->allCookies = cookieList;
|
||||
}
|
||||
|
||||
static inline bool isParentPath(QString path, QString reference)
|
||||
{
|
||||
if (!path.endsWith(QLatin1Char('/')))
|
||||
path += QLatin1Char('/');
|
||||
if (!reference.endsWith(QLatin1Char('/')))
|
||||
reference += QLatin1Char('/');
|
||||
return path.startsWith(reference);
|
||||
}
|
||||
|
||||
static inline bool isParentDomain(const QString &domain, const QString &reference)
|
||||
{
|
||||
if (!reference.startsWith(QLatin1Char('.')))
|
||||
return domain == reference;
|
||||
|
||||
return domain.endsWith(reference) || domain == reference.mid(1);
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds the cookies in the list \a cookieList to this cookie
|
||||
jar. Default values for path and domain are taken from the \a
|
||||
url object.
|
||||
|
||||
Returns true if one or more cookies are set for \a url,
|
||||
otherwise false.
|
||||
|
||||
If a cookie already exists in the cookie jar, it will be
|
||||
overridden by those in \a cookieList.
|
||||
|
||||
The default QNetworkCookieJar class implements only a very basic
|
||||
security policy (it makes sure that the cookies' domain and path
|
||||
match the reply's). To enhance the security policy with your own
|
||||
algorithms, override setCookiesFromUrl().
|
||||
|
||||
Also, QNetworkCookieJar does not have a maximum cookie jar
|
||||
size. Reimplement this function to discard older cookies to create
|
||||
room for new ones.
|
||||
|
||||
\sa cookiesForUrl(), QNetworkAccessManager::setCookieJar()
|
||||
*/
|
||||
bool QNetworkCookieJar::setCookiesFromUrl(const QList<QNetworkCookie> &cookieList,
|
||||
const QUrl &url)
|
||||
{
|
||||
Q_D(QNetworkCookieJar);
|
||||
QString defaultDomain = url.host();
|
||||
QString pathAndFileName = url.path();
|
||||
QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
|
||||
if (defaultPath.isEmpty())
|
||||
defaultPath = QLatin1Char('/');
|
||||
|
||||
int added = 0;
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
foreach (QNetworkCookie cookie, cookieList) {
|
||||
bool isDeletion = !cookie.isSessionCookie() &&
|
||||
cookie.expirationDate() < now;
|
||||
|
||||
// validate the cookie & set the defaults if unset
|
||||
if (cookie.path().isEmpty())
|
||||
cookie.setPath(defaultPath);
|
||||
// don't do path checking. See http://bugreports.qt-project.org/browse/QTBUG-5815
|
||||
// else if (!isParentPath(pathAndFileName, cookie.path())) {
|
||||
// continue; // not accepted
|
||||
// }
|
||||
if (cookie.domain().isEmpty()) {
|
||||
cookie.setDomain(defaultDomain);
|
||||
} else {
|
||||
// Ensure the domain starts with a dot if its field was not empty
|
||||
// in the HTTP header. There are some servers that forget the
|
||||
// leading dot and this is actually forbidden according to RFC 2109,
|
||||
// but all browsers accept it anyway so we do that as well.
|
||||
if (!cookie.domain().startsWith(QLatin1Char('.')))
|
||||
cookie.setDomain(QLatin1Char('.') + cookie.domain());
|
||||
|
||||
QString domain = cookie.domain();
|
||||
if (!(isParentDomain(domain, defaultDomain)
|
||||
|| isParentDomain(defaultDomain, domain)))
|
||||
continue; // not accepted
|
||||
|
||||
// the check for effective TLDs makes the "embedded dot" rule from RFC 2109 section 4.3.2
|
||||
// redundant; the "leading dot" rule has been relaxed anyway, see above
|
||||
// we remove the leading dot for this check
|
||||
if (qIsEffectiveTLD(domain.remove(0, 1)))
|
||||
continue; // not accepted
|
||||
}
|
||||
|
||||
for (int i = 0; i < d->allCookies.size(); ++i) {
|
||||
// does this cookie already exist?
|
||||
const QNetworkCookie ¤t = d->allCookies.at(i);
|
||||
if (cookie.name() == current.name() &&
|
||||
cookie.domain() == current.domain() &&
|
||||
cookie.path() == current.path()) {
|
||||
// found a match
|
||||
d->allCookies.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// did not find a match
|
||||
if (!isDeletion) {
|
||||
int countForDomain = 0;
|
||||
for (int i = d->allCookies.size() - 1; i >= 0; --i) {
|
||||
// Start from the end and delete the oldest cookies to keep a maximum count of 50.
|
||||
const QNetworkCookie ¤t = d->allCookies.at(i);
|
||||
if (isParentDomain(cookie.domain(), current.domain())
|
||||
|| isParentDomain(current.domain(), cookie.domain())) {
|
||||
if (countForDomain >= 49)
|
||||
d->allCookies.removeAt(i);
|
||||
else
|
||||
++countForDomain;
|
||||
}
|
||||
}
|
||||
|
||||
d->allCookies += cookie;
|
||||
++added;
|
||||
}
|
||||
}
|
||||
return (added > 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the cookies to be added to when a request is sent to
|
||||
\a url. This function is called by the default
|
||||
QNetworkAccessManager::createRequest(), which adds the
|
||||
cookies returned by this function to the request being sent.
|
||||
|
||||
If more than one cookie with the same name is found, but with
|
||||
differing paths, the one with longer path is returned before the
|
||||
one with shorter path. In other words, this function returns
|
||||
cookies sorted decreasingly by path length.
|
||||
|
||||
The default QNetworkCookieJar class implements only a very basic
|
||||
security policy (it makes sure that the cookies' domain and path
|
||||
match the reply's). To enhance the security policy with your own
|
||||
algorithms, override cookiesForUrl().
|
||||
|
||||
\sa setCookiesFromUrl(), QNetworkAccessManager::setCookieJar()
|
||||
*/
|
||||
QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
|
||||
{
|
||||
// \b Warning! This is only a dumb implementation!
|
||||
// It does NOT follow all of the recommendations from
|
||||
// http://wp.netscape.com/newsref/std/cookie_spec.html
|
||||
// It does not implement a very good cross-domain verification yet.
|
||||
|
||||
Q_D(const QNetworkCookieJar);
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
QList<QNetworkCookie> result;
|
||||
bool isEncrypted = url.scheme().toLower() == QLatin1String("https");
|
||||
|
||||
// scan our cookies for something that matches
|
||||
foreach (const QNetworkCookie &cookie, d->allCookies) {
|
||||
if (!isParentDomain(url.host(), cookie.domain()))
|
||||
continue;
|
||||
if (!isParentPath(url.path(), cookie.path()))
|
||||
continue;
|
||||
if (!cookie.isSessionCookie() && cookie.expirationDate() < now)
|
||||
continue;
|
||||
if (cookie.isSecure() && !isEncrypted)
|
||||
continue;
|
||||
|
||||
// insert this cookie into result, sorted by path
|
||||
QList<QNetworkCookie>::Iterator insertIt = result.begin();
|
||||
while (insertIt != result.end()) {
|
||||
if (insertIt->path().length() < cookie.path().length()) {
|
||||
// insert here
|
||||
insertIt = result.insert(insertIt, cookie);
|
||||
break;
|
||||
} else {
|
||||
++insertIt;
|
||||
}
|
||||
}
|
||||
|
||||
// this is the shortest path yet, just append
|
||||
if (insertIt == result.end())
|
||||
result += cookie;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#include "moc_qnetworkcookiejar.h"
|
|
@ -1,58 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKCOOKIEJAR_H
|
||||
#define QNETWORKCOOKIEJAR_H
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
// ### Qt5 remove this include
|
||||
#include <QtNetwork/QNetworkCookie>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QNetworkCookieJarPrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkCookieJar: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkCookieJar(QObject *parent = nullptr);
|
||||
virtual ~QNetworkCookieJar();
|
||||
|
||||
virtual QList<QNetworkCookie> cookiesForUrl(const QUrl &url) const;
|
||||
virtual bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl &url);
|
||||
|
||||
protected:
|
||||
QList<QNetworkCookie> allCookies() const;
|
||||
void setAllCookies(const QList<QNetworkCookie> &cookieList);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QNetworkCookieJar)
|
||||
Q_DISABLE_COPY(QNetworkCookieJar)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif
|
|
@ -1,51 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKCOOKIEJAR_P_H
|
||||
#define QNETWORKCOOKIEJAR_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access framework. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qobject_p.h"
|
||||
#include "qnetworkcookie.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkCookieJarPrivate: public QObjectPrivate
|
||||
{
|
||||
public:
|
||||
QList<QNetworkCookie> allCookies;
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkCookieJar)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,561 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
// #define QNETWORKDISKCACHE_DEBUG
|
||||
|
||||
#include "qnetworkdiskcache.h"
|
||||
#include "qnetworkdiskcache_p.h"
|
||||
#include "qscopedpointer.h"
|
||||
#include "qfile.h"
|
||||
#include "qdir.h"
|
||||
#include "qdatetime.h"
|
||||
#include "qdiriterator.h"
|
||||
#include "qurl.h"
|
||||
#include "qcryptographichash.h"
|
||||
#include "qbuffer.h"
|
||||
#include "qdebug.h"
|
||||
#include "qcorecommon_p.h"
|
||||
#include "qcore_unix_p.h"
|
||||
|
||||
#ifndef QT_NO_NETWORKDISKCACHE
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#define CACHE_POSTFIX QLatin1String(".cache")
|
||||
|
||||
/*!
|
||||
Given a URL, generates a unique enough filename with suffix
|
||||
*/
|
||||
static QString uniqueFileName(const QUrl &url)
|
||||
{
|
||||
QUrl cleanUrl = url;
|
||||
cleanUrl.setPassword(QString());
|
||||
cleanUrl.setFragment(QString());
|
||||
|
||||
const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
|
||||
return hash.toHex() + CACHE_POSTFIX;
|
||||
}
|
||||
|
||||
enum {
|
||||
CacheMagic = 0xe8,
|
||||
CurrentCacheVersion = 9
|
||||
};
|
||||
|
||||
void QCacheItem::write(QFile *device) const
|
||||
{
|
||||
QDataStream out(device);
|
||||
|
||||
out << qint32(CacheMagic);
|
||||
out << qint32(CurrentCacheVersion);
|
||||
out << metaData;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns false if the file is invalid cache file, true otherwise
|
||||
*/
|
||||
bool QCacheItem::read(QFile *device)
|
||||
{
|
||||
QDataStream in(device);
|
||||
|
||||
qint32 marker;
|
||||
qint32 v;
|
||||
in >> marker;
|
||||
in >> v;
|
||||
if (marker != CacheMagic || v != CurrentCacheVersion) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QCacheItem::read: invalid marker or cache version" << marker << v;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
in >> metaData;
|
||||
|
||||
// quick and dirty check if metadata's URL field and the file's name are in sync
|
||||
QString expectedFilename = uniqueFileName(metaData.url());
|
||||
if (!device->fileName().endsWith(expectedFilename)) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QCacheItem::read: unexpected filename" << device->fileName();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
return metaData.isValid();
|
||||
}
|
||||
|
||||
/*!
|
||||
Generates fully qualified path of cached resource from a URL
|
||||
*/
|
||||
QString QNetworkDiskCachePrivate::cacheFileName(const QUrl &url) const
|
||||
{
|
||||
if (!url.isValid() || cacheDirectory.isEmpty()) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCachePrivate::cacheFileName: invalid URL or cache directory is empty" << url << cacheDirectory;
|
||||
#endif
|
||||
return QString();
|
||||
}
|
||||
return cacheDirectory + uniqueFileName(url);
|
||||
}
|
||||
|
||||
qint64 QNetworkDiskCachePrivate::cacheSize() const
|
||||
{
|
||||
QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
|
||||
QDirIterator it(cacheDirectory, filters, QDirIterator::Subdirectories);
|
||||
|
||||
qint64 total = 0;
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
const QFileInfo info = it.fileInfo();
|
||||
const QString fileName = info.fileName();
|
||||
if (fileName.endsWith(CACHE_POSTFIX)) {
|
||||
total += info.size();
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
/*!
|
||||
\class QNetworkDiskCache
|
||||
\since 4.5
|
||||
\inmodule QtNetwork
|
||||
|
||||
\brief The QNetworkDiskCache class provides a very basic disk cache.
|
||||
|
||||
QNetworkDiskCache stores each url in its own file inside of the
|
||||
cacheDirectory using QDataStream. Each cache file ends with
|
||||
".cache". Data is written to disk only in insert() and
|
||||
updateMetaData().
|
||||
|
||||
Currently you can not share the same cache files with more then
|
||||
one disk cache.
|
||||
|
||||
QNetworkDiskCache by default limits the amount of space that the cache will
|
||||
use on the system to 50MB.
|
||||
|
||||
Note you have to set the cache directory before it will work.
|
||||
|
||||
A network disk cache can be enabled by:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 0
|
||||
|
||||
When sending requests, to control the preference of when to use the cache
|
||||
and when to use the network, consider the following:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 1
|
||||
|
||||
To check whether the response came from the cache or from the network, the
|
||||
following can be applied:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkdiskcache.cpp 2
|
||||
|
||||
\warning No attempt is made to lock the cache files, if two applications
|
||||
or even threads attempt to cache the same URL with the same cache
|
||||
directory at the same time the result is undefined.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Creates a new disk cache. The \a parent argument is passed to
|
||||
QAbstractNetworkCache's constructor.
|
||||
*/
|
||||
QNetworkDiskCache::QNetworkDiskCache(QObject *parent)
|
||||
: QAbstractNetworkCache(*new QNetworkDiskCachePrivate, parent)
|
||||
{
|
||||
Q_D(QNetworkDiskCache);
|
||||
|
||||
d->currentCacheSize = cacheSize();
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache: cache size is" << d->currentCacheSize;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*!
|
||||
Destroys the cache object. This does not clear the disk cache.
|
||||
*/
|
||||
QNetworkDiskCache::~QNetworkDiskCache()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the location where cached files will be stored.
|
||||
*/
|
||||
QString QNetworkDiskCache::cacheDirectory() const
|
||||
{
|
||||
Q_D(const QNetworkDiskCache);
|
||||
return d->cacheDirectory;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the directory where cached files will be stored to \a cacheDir
|
||||
|
||||
QNetworkDiskCache will create this directory if it does not exists.
|
||||
|
||||
Prepared cache items will be stored in the new cache directory when
|
||||
they are inserted.
|
||||
|
||||
\sa QStandardPaths::CacheLocation
|
||||
*/
|
||||
void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::setCacheDirectory()" << cacheDir;
|
||||
#endif
|
||||
Q_D(QNetworkDiskCache);
|
||||
if (cacheDir.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
d->cacheDirectory = cacheDir;
|
||||
QDir dir(d->cacheDirectory);
|
||||
d->cacheDirectory = dir.absolutePath();
|
||||
if (!d->cacheDirectory.endsWith(QLatin1Char('/'))) {
|
||||
d->cacheDirectory += QLatin1Char('/');
|
||||
}
|
||||
QDir().mkpath(d->cacheDirectory);
|
||||
|
||||
d->currentCacheSize = cacheSize();
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
qint64 QNetworkDiskCache::cacheSize() const
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::cacheSize()";
|
||||
#endif
|
||||
Q_D(const QNetworkDiskCache);
|
||||
return d->currentCacheSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::prepare()" << metaData.url();
|
||||
#endif
|
||||
Q_D(QNetworkDiskCache);
|
||||
if (!metaData.isValid() || !metaData.url().isValid() || !metaData.saveToDisk()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (Q_UNLIKELY(d->cacheDirectory.isEmpty())) {
|
||||
qWarning("QNetworkDiskCache::prepare: the cache directory is not set");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
foreach (const QNetworkCacheMetaData::RawHeader &header, metaData.rawHeaders()) {
|
||||
if (header.first.toLower() == "content-length") {
|
||||
const qint64 size = header.second.toInt();
|
||||
if (size > (maximumCacheSize() * 3)/4)
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QFile *file = new QFile(d->cacheFileName(metaData.url()), this);
|
||||
if (Q_UNLIKELY(!file->open(QFile::ReadWrite))) {
|
||||
qWarning("QNetworkDiskCache::prepare: unable to open cache file");
|
||||
delete file;
|
||||
return nullptr;
|
||||
}
|
||||
QCacheItem cacheItem;
|
||||
cacheItem.metaData = metaData;
|
||||
cacheItem.write(file);
|
||||
return file;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
void QNetworkDiskCache::insert(QIODevice *device)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::insert()" << device;
|
||||
#endif
|
||||
Q_D(QNetworkDiskCache);
|
||||
|
||||
QFile *file = qobject_cast<QFile*>(device);
|
||||
if (Q_UNLIKELY(!file || file->fileName().isEmpty())) {
|
||||
qWarning("QNetworkDiskCache::insert: device is not file or filename is empty");
|
||||
delete file;
|
||||
return;
|
||||
}
|
||||
|
||||
d->currentCacheSize = expire();
|
||||
if (file->error() == QFile::NoError) {
|
||||
d->currentCacheSize += file->size();
|
||||
} else {
|
||||
file->remove();
|
||||
}
|
||||
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::insert()" << file->fileName() << file->size() << file->error();
|
||||
#endif
|
||||
delete file;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
bool QNetworkDiskCache::remove(const QUrl &url)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::remove()" << url;
|
||||
#endif
|
||||
Q_D(QNetworkDiskCache);
|
||||
|
||||
const QString fileName = d->cacheFileName(url);
|
||||
if (fileName.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (QFile::remove(fileName)) {
|
||||
const QStatInfo info(fileName);
|
||||
d->currentCacheSize -= info.size();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
QNetworkCacheMetaData QNetworkDiskCache::metaData(const QUrl &url)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::metaData()" << url;
|
||||
#endif
|
||||
Q_D(QNetworkDiskCache);
|
||||
return fileMetaData(d->cacheFileName(url));
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the QNetworkCacheMetaData for the cache file \a fileName.
|
||||
|
||||
If \a fileName is not a cache file QNetworkCacheMetaData will be invalid.
|
||||
*/
|
||||
QNetworkCacheMetaData QNetworkDiskCache::fileMetaData(const QString &fileName) const
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::fileMetaData()" << fileName;
|
||||
#endif
|
||||
|
||||
if (fileName.isEmpty()) {
|
||||
return QNetworkCacheMetaData();
|
||||
}
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::fileMetaData: could not open" << fileName;
|
||||
#endif
|
||||
return QNetworkCacheMetaData();
|
||||
}
|
||||
QCacheItem cacheitem;
|
||||
if (!cacheitem.read(&file)) {
|
||||
// ### this will not update cache size
|
||||
QFile::remove(fileName);
|
||||
return QNetworkCacheMetaData();
|
||||
}
|
||||
return cacheitem.metaData;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
QIODevice *QNetworkDiskCache::data(const QUrl &url)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::data()" << url;
|
||||
#endif
|
||||
Q_D(const QNetworkDiskCache);
|
||||
|
||||
const QString fileName = d->cacheFileName(url);
|
||||
if (fileName.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
QFile file(fileName);
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::data: could not open" << fileName;
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
QCacheItem cacheitem;
|
||||
if (!cacheitem.read(&file)) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::data: could not read cache" << fileName;
|
||||
#endif
|
||||
remove(url);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QScopedPointer<QBuffer> buffer(new QBuffer());
|
||||
buffer->open(QBuffer::ReadWrite);
|
||||
buffer->write(file.readAll());
|
||||
buffer->seek(0);
|
||||
return buffer.take();
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
void QNetworkDiskCache::updateMetaData(const QNetworkCacheMetaData &metaData)
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::updateMetaData()" << metaData.url();
|
||||
#endif
|
||||
QUrl url = metaData.url();
|
||||
QIODevice *oldDevice = data(url);
|
||||
if (!oldDevice) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::updateMetaData(), no device!";
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
QIODevice *newDevice = prepare(metaData);
|
||||
if (!newDevice) {
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::updateMetaData(), no new device!" << url;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
QSTACKARRAY(char, data, QT_BUFFSIZE);
|
||||
while (!oldDevice->atEnd()) {
|
||||
qint64 s = oldDevice->read(data, sizeof(data));
|
||||
newDevice->write(data, s);
|
||||
}
|
||||
delete oldDevice;
|
||||
insert(newDevice);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the current maximum size in bytes for the disk cache.
|
||||
|
||||
\sa setMaximumCacheSize()
|
||||
*/
|
||||
qint64 QNetworkDiskCache::maximumCacheSize() const
|
||||
{
|
||||
Q_D(const QNetworkDiskCache);
|
||||
return d->maximumCacheSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the maximum size of the disk cache to be \a size in bytes.
|
||||
|
||||
If the new size is smaller then the current cache size then the cache will call expire().
|
||||
|
||||
\sa maximumCacheSize()
|
||||
*/
|
||||
void QNetworkDiskCache::setMaximumCacheSize(qint64 size)
|
||||
{
|
||||
Q_D(QNetworkDiskCache);
|
||||
d->maximumCacheSize = size;
|
||||
d->currentCacheSize = expire();
|
||||
}
|
||||
|
||||
/*!
|
||||
Cleans the cache so that its size is under the maximum cache size.
|
||||
Returns the current size of the cache.
|
||||
|
||||
When the current size of the cache is greater than the maximumCacheSize()
|
||||
older cache files are removed until the total size is less then 90% of
|
||||
maximumCacheSize() starting with the oldest ones first using the file
|
||||
creation date to determine how old a cache file is.
|
||||
|
||||
Subclasses can reimplement this function to change the order that cache
|
||||
files are removed taking into account information in the application
|
||||
knows about that QNetworkDiskCache does not, for example the number of times
|
||||
a cache is accessed.
|
||||
|
||||
Note: cacheSize() calls expire if the current cache size is unknown.
|
||||
|
||||
\sa maximumCacheSize(), fileMetaData()
|
||||
*/
|
||||
qint64 QNetworkDiskCache::expire()
|
||||
{
|
||||
Q_D(QNetworkDiskCache);
|
||||
if (d->currentCacheSize < maximumCacheSize()) {
|
||||
return d->currentCacheSize;
|
||||
}
|
||||
|
||||
if (Q_UNLIKELY(cacheDirectory().isEmpty())) {
|
||||
qWarning("QNetworkDiskCache::expire: the cache directory is not set");
|
||||
return 0;
|
||||
}
|
||||
|
||||
QDir::Filters filters = QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot;
|
||||
QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
|
||||
|
||||
QMultiMap<QDateTime, QString> cacheItems;
|
||||
qint64 totalSize = 0;
|
||||
while (it.hasNext()) {
|
||||
QString path = it.next();
|
||||
QFileInfo info = it.fileInfo();
|
||||
QString fileName = info.fileName();
|
||||
if (fileName.endsWith(CACHE_POSTFIX)) {
|
||||
cacheItems.insert(info.created(), path);
|
||||
totalSize += info.size();
|
||||
}
|
||||
}
|
||||
|
||||
int removedFiles = 0;
|
||||
qint64 goal = (maximumCacheSize() * 9) / 10;
|
||||
QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
|
||||
while (i != cacheItems.constEnd()) {
|
||||
if (totalSize < goal)
|
||||
break;
|
||||
QString name = i.value();
|
||||
QFile file(name);
|
||||
qint64 size = file.size();
|
||||
file.remove();
|
||||
totalSize -= size;
|
||||
++removedFiles;
|
||||
++i;
|
||||
}
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
if (removedFiles > 0) {
|
||||
qDebug() << "QNetworkDiskCache::expire()"
|
||||
<< "Removed:" << removedFiles
|
||||
<< "Kept:" << cacheItems.count() - removedFiles;
|
||||
}
|
||||
#endif
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
\reimp
|
||||
*/
|
||||
void QNetworkDiskCache::clear()
|
||||
{
|
||||
#if defined(QNETWORKDISKCACHE_DEBUG)
|
||||
qDebug() << "QNetworkDiskCache::clear()";
|
||||
#endif
|
||||
Q_D(QNetworkDiskCache);
|
||||
qint64 size = d->maximumCacheSize;
|
||||
d->maximumCacheSize = 0;
|
||||
d->currentCacheSize = expire();
|
||||
d->maximumCacheSize = size;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_NETWORKDISKCACHE
|
||||
|
||||
#include "moc_qnetworkdiskcache.h"
|
|
@ -1,74 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKDISKCACHE_H
|
||||
#define QNETWORKDISKCACHE_H
|
||||
|
||||
#include <QtNetwork/qabstractnetworkcache.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
#ifndef QT_NO_NETWORKDISKCACHE
|
||||
|
||||
class QNetworkDiskCachePrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkDiskCache : public QAbstractNetworkCache
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QNetworkDiskCache(QObject *parent = nullptr);
|
||||
~QNetworkDiskCache();
|
||||
|
||||
QString cacheDirectory() const;
|
||||
void setCacheDirectory(const QString &cacheDir);
|
||||
|
||||
qint64 maximumCacheSize() const;
|
||||
void setMaximumCacheSize(qint64 size);
|
||||
|
||||
qint64 cacheSize() const;
|
||||
QNetworkCacheMetaData metaData(const QUrl &url);
|
||||
void updateMetaData(const QNetworkCacheMetaData &metaData);
|
||||
QIODevice *data(const QUrl &url);
|
||||
bool remove(const QUrl &url);
|
||||
QIODevice *prepare(const QNetworkCacheMetaData &metaData);
|
||||
void insert(QIODevice *device);
|
||||
|
||||
QNetworkCacheMetaData fileMetaData(const QString &fileName) const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void clear();
|
||||
|
||||
protected:
|
||||
virtual qint64 expire();
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QNetworkDiskCache)
|
||||
Q_DISABLE_COPY(QNetworkDiskCache)
|
||||
};
|
||||
|
||||
#endif // QT_NO_NETWORKDISKCACHE
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif // QNETWORKDISKCACHE_H
|
|
@ -1,76 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKDISKCACHE_P_H
|
||||
#define QNETWORKDISKCACHE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qabstractnetworkcache_p.h"
|
||||
|
||||
#include "qfile.h"
|
||||
|
||||
#ifndef QT_NO_NETWORKDISKCACHE
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QCacheItem
|
||||
{
|
||||
public:
|
||||
QNetworkCacheMetaData metaData;
|
||||
|
||||
void write(QFile *device) const;
|
||||
bool read(QFile *device);
|
||||
};
|
||||
|
||||
class QNetworkDiskCachePrivate : public QAbstractNetworkCachePrivate
|
||||
{
|
||||
public:
|
||||
QNetworkDiskCachePrivate()
|
||||
: QAbstractNetworkCachePrivate()
|
||||
, maximumCacheSize(1024 * 1024 * 50)
|
||||
, currentCacheSize(0)
|
||||
{}
|
||||
|
||||
QString cacheFileName(const QUrl &url) const;
|
||||
qint64 cacheSize() const;
|
||||
|
||||
QString cacheDirectory;
|
||||
qint64 maximumCacheSize;
|
||||
qint64 currentCacheSize;
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkDiskCache)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_NO_NETWORKDISKCACHE
|
||||
|
||||
#endif // QNETWORKDISKCACHE_P_H
|
|
@ -1,743 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkreply.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include <QtNetwork/qsslconfiguration.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QNetworkReplyPrivate::QNetworkReplyPrivate()
|
||||
: readBufferMaxSize(0),
|
||||
operation(QNetworkAccessManager::UnknownOperation),
|
||||
errorCode(QNetworkReply::NoError)
|
||||
, isFinished(false)
|
||||
{
|
||||
// set the default attribute values
|
||||
attributes.insert(QNetworkRequest::ConnectionEncryptedAttribute, false);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
\class QNetworkReply
|
||||
\since 4.4
|
||||
\brief The QNetworkReply class contains the data and headers for a request
|
||||
sent with QNetworkAccessManager
|
||||
|
||||
\reentrant
|
||||
\ingroup network
|
||||
\inmodule QtNetwork
|
||||
|
||||
The QNetworkReply class contains the data and meta data related to
|
||||
a request posted with QNetworkAccessManager. Like QNetworkRequest,
|
||||
it contains a URL and headers (both in parsed and raw form), some
|
||||
information about the reply's state and the contents of the reply
|
||||
itself.
|
||||
|
||||
QNetworkReply is a sequential-access QIODevice, which means that
|
||||
once data is read from the object, it no longer kept by the
|
||||
device. It is therefore the application's responsibility to keep
|
||||
this data if it needs to. Whenever more data is received from the
|
||||
network and processed, the readyRead() signal is emitted.
|
||||
|
||||
The downloadProgress() signal is also emitted when data is
|
||||
received, but the number of bytes contained in it may not
|
||||
represent the actual bytes received, if any transformation is done
|
||||
to the contents (for example, decompressing and removing the
|
||||
protocol overhead).
|
||||
|
||||
Even though QNetworkReply is a QIODevice connected to the contents
|
||||
of the reply, it also emits the uploadProgress() signal, which
|
||||
indicates the progress of the upload for operations that have such
|
||||
content.
|
||||
|
||||
\note Do not delete the object in the slot connected to the
|
||||
error() or finished() signal. Use deleteLater().
|
||||
|
||||
\sa QNetworkRequest, QNetworkAccessManager
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkReply::NetworkError
|
||||
|
||||
Indicates all possible error conditions found during the
|
||||
processing of the request.
|
||||
|
||||
\value NoError no error condition.
|
||||
\note When the HTTP protocol returns a redirect no error will be
|
||||
reported. You can check if there is a redirect with the
|
||||
QNetworkRequest::RedirectionTargetAttribute attribute.
|
||||
|
||||
\value ConnectionRefusedError the remote server refused the
|
||||
connection (the server is not accepting requests)
|
||||
|
||||
\value RemoteHostClosedError the remote server closed the
|
||||
connection prematurely, before the entire reply was received and
|
||||
processed
|
||||
|
||||
\value HostNotFoundError the remote host name was not found
|
||||
(invalid hostname)
|
||||
|
||||
\value TimeoutError the connection to the remote server
|
||||
timed out
|
||||
|
||||
\value OperationCanceledError the operation was canceled via calls
|
||||
to abort() or close() before it was finished.
|
||||
|
||||
\value SslHandshakeFailedError the SSL/TLS handshake failed and the
|
||||
encrypted channel could not be established. The sslErrors() signal
|
||||
should have been emitted.
|
||||
|
||||
\value TemporaryNetworkFailureError the connection was broken due
|
||||
to disconnection from the network, however the system has initiated
|
||||
roaming to another access point. The request should be resubmitted
|
||||
and will be processed as soon as the connection is re-established.
|
||||
|
||||
\value ProxyConnectionRefusedError the connection to the proxy
|
||||
server was refused (the proxy server is not accepting requests)
|
||||
|
||||
\value ProxyConnectionClosedError the proxy server closed the
|
||||
connection prematurely, before the entire reply was received and
|
||||
processed
|
||||
|
||||
\value ProxyNotFoundError the proxy host name was not
|
||||
found (invalid proxy hostname)
|
||||
|
||||
\value ProxyTimeoutError the connection to the proxy
|
||||
timed out or the proxy did not reply in time to the request sent
|
||||
|
||||
\value ProxyAuthenticationRequiredError the proxy requires
|
||||
authentication in order to honour the request but did not accept
|
||||
any credentials offered (if any)
|
||||
|
||||
\value ContentAccessDenied the access to the remote
|
||||
content was denied (similar to HTTP error 401)
|
||||
|
||||
\value ContentOperationNotPermittedError the operation requested
|
||||
on the remote content is not permitted
|
||||
|
||||
\value ContentNotFoundError the remote content was not
|
||||
found at the server (similar to HTTP error 404)
|
||||
|
||||
\value AuthenticationRequiredError the remote server requires
|
||||
authentication to serve the content but the credentials provided
|
||||
were not accepted (if any)
|
||||
|
||||
\value ContentReSendError the request needed to be sent
|
||||
again, but this failed for example because the upload data
|
||||
could not be read a second time.
|
||||
|
||||
\value ProtocolUnknownError the Network Access API cannot
|
||||
honor the request because the protocol is not known
|
||||
|
||||
\value ProtocolInvalidOperationError the requested operation is
|
||||
invalid for this protocol
|
||||
|
||||
\value UnknownNetworkError an unknown network-related
|
||||
error was detected
|
||||
|
||||
\value UnknownProxyError an unknown proxy-related error
|
||||
was detected
|
||||
|
||||
\value UnknownContentError an unknown error related to
|
||||
the remote content was detected
|
||||
|
||||
\value ProtocolFailure a breakdown in protocol was
|
||||
detected (parsing error, invalid or unexpected responses, etc.)
|
||||
|
||||
\sa error()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::sslErrors(const QList<QSslError> &errors)
|
||||
|
||||
This signal is emitted if the SSL/TLS session encountered errors
|
||||
during the set up, including certificate verification errors. The
|
||||
\a errors parameter contains the list of errors.
|
||||
|
||||
To indicate that the errors are not fatal and that the connection
|
||||
should proceed, the ignoreSslErrors() function should be called
|
||||
from the slot connected to this signal. If it is not called, the
|
||||
SSL session will be torn down before any data is exchanged
|
||||
(including the URL).
|
||||
|
||||
This signal can be used to display an error message to the user
|
||||
indicating that security may be compromised and display the
|
||||
SSL settings (see sslConfiguration() to obtain it). If the user
|
||||
decides to proceed after analyzing the remote certificate, the
|
||||
slot should call ignoreSslErrors().
|
||||
|
||||
\sa QSslSocket::sslErrors(), QNetworkAccessManager::sslErrors(),
|
||||
sslConfiguration(), ignoreSslErrors()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::metaDataChanged()
|
||||
|
||||
\omit FIXME: Update name? \endomit
|
||||
|
||||
This signal is emitted whenever the metadata in this reply
|
||||
changes. metadata is any information that is not the content
|
||||
(data) itself, including the network headers. In the majority of
|
||||
cases, the metadata will be known fully by the time the first
|
||||
byte of data is received. However, it is possible to receive
|
||||
updates of headers or other metadata during the processing of the
|
||||
data.
|
||||
|
||||
\sa header(), rawHeaderList(), rawHeader(), hasRawHeader()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::finished()
|
||||
|
||||
This signal is emitted when the reply has finished
|
||||
processing. After this signal is emitted, there will be no more
|
||||
updates to the reply's data or metadata.
|
||||
|
||||
Unless close() has been called, the reply will be still be opened
|
||||
for reading, so the data can be retrieved by calls to read() or
|
||||
readAll(). In particular, if no calls to read() were made as a
|
||||
result of readyRead(), a call to readAll() will retrieve the full
|
||||
contents in a QByteArray.
|
||||
|
||||
This signal is emitted in tandem with
|
||||
QNetworkAccessManager::finished() where that signal's reply
|
||||
parameter is this object.
|
||||
|
||||
\note Do not delete the object in the slot connected to this
|
||||
signal. Use deleteLater().
|
||||
|
||||
You can also use isFinished() to check if a QNetworkReply
|
||||
has finished even before you receive the finished() signal.
|
||||
|
||||
\sa QNetworkAccessManager::finished(), isFinished()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::error(QNetworkReply::NetworkError code)
|
||||
|
||||
This signal is emitted when the reply detects an error in
|
||||
processing. The finished() signal will probably follow, indicating
|
||||
that the connection is over.
|
||||
|
||||
The \a code parameter contains the code of the error that was
|
||||
detected. Call errorString() to obtain a textual representation of
|
||||
the error condition.
|
||||
|
||||
\note Do not delete the object in the slot connected to this
|
||||
signal. Use deleteLater().
|
||||
|
||||
\sa error(), errorString()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::uploadProgress(qint64 bytesSent, qint64 bytesTotal)
|
||||
|
||||
This signal is emitted to indicate the progress of the upload part
|
||||
of this network request, if there's any. If there's no upload
|
||||
associated with this request, this signal will not be emitted.
|
||||
|
||||
The \a bytesSent
|
||||
parameter indicates the number of bytes uploaded, while \a
|
||||
bytesTotal indicates the total number of bytes to be uploaded. If
|
||||
the number of bytes to be uploaded could not be determined, \a
|
||||
bytesTotal will be -1.
|
||||
|
||||
The upload is finished when \a bytesSent is equal to \a
|
||||
bytesTotal. At that time, \a bytesTotal will not be -1.
|
||||
|
||||
\sa downloadProgress()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::downloadProgress(qint64 bytesReceived, qint64 bytesTotal)
|
||||
|
||||
This signal is emitted to indicate the progress of the download
|
||||
part of this network request, if there's any. If there's no
|
||||
download associated with this request, this signal will be emitted
|
||||
once with 0 as the value of both \a bytesReceived and \a
|
||||
bytesTotal.
|
||||
|
||||
The \a bytesReceived parameter indicates the number of bytes
|
||||
received, while \a bytesTotal indicates the total number of bytes
|
||||
expected to be downloaded. If the number of bytes to be downloaded
|
||||
is not known, \a bytesTotal will be -1.
|
||||
|
||||
The download is finished when \a bytesReceived is equal to \a
|
||||
bytesTotal. At that time, \a bytesTotal will not be -1.
|
||||
|
||||
Note that the values of both \a bytesReceived and \a bytesTotal
|
||||
may be different from size(), the total number of bytes
|
||||
obtained through read() or readAll(), or the value of the
|
||||
header(ContentLengthHeader). The reason for that is that there may
|
||||
be protocol overhead or the data may be compressed during the
|
||||
download.
|
||||
|
||||
\sa uploadProgress(), bytesAvailable()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn void QNetworkReply::abort()
|
||||
|
||||
Aborts the operation immediately and close down any network
|
||||
connections still open. Uploads still in progress are also
|
||||
aborted.
|
||||
|
||||
\sa close()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Creates a QNetworkReply object with parent \a parent.
|
||||
|
||||
You cannot directly instantiate QNetworkReply objects. Use
|
||||
QNetworkAccessManager functions to do that.
|
||||
*/
|
||||
QNetworkReply::QNetworkReply(QObject *parent)
|
||||
: QIODevice(*new QNetworkReplyPrivate, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
QNetworkReply::QNetworkReply(QNetworkReplyPrivate &dd, QObject *parent)
|
||||
: QIODevice(dd, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Disposes of this reply and frees any resources associated with
|
||||
it. If any network connections are still open, they will be
|
||||
closed.
|
||||
|
||||
\sa abort(), close()
|
||||
*/
|
||||
QNetworkReply::~QNetworkReply()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
bool QNetworkReply::isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the size of the read buffer, in bytes.
|
||||
|
||||
\sa setReadBufferSize()
|
||||
*/
|
||||
qint64 QNetworkReply::readBufferSize() const
|
||||
{
|
||||
return d_func()->readBufferMaxSize;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the size of the read buffer to be \a size bytes. The read
|
||||
buffer is the buffer that holds data that is being downloaded off
|
||||
the network, before it is read with QIODevice::read(). Setting the
|
||||
buffer size to 0 will make the buffer unlimited in size.
|
||||
|
||||
QNetworkReply will try to stop reading from the network once this
|
||||
buffer is full (i.e., bytesAvailable() returns \a size or more),
|
||||
thus causing the download to throttle down as well. If the buffer
|
||||
is not limited in size, QNetworkReply will try to download as fast
|
||||
as possible from the network.
|
||||
|
||||
Unlike QAbstractSocket::setReadBufferSize(), QNetworkReply cannot
|
||||
guarantee precision in the read buffer size. That is,
|
||||
bytesAvailable() can return more than \a size.
|
||||
|
||||
\sa readBufferSize()
|
||||
*/
|
||||
void QNetworkReply::setReadBufferSize(qint64 size)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->readBufferMaxSize = size;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the QNetworkAccessManager that was used to create this
|
||||
QNetworkReply object. Initially, it is also the parent object.
|
||||
*/
|
||||
QNetworkAccessManager *QNetworkReply::manager() const
|
||||
{
|
||||
return d_func()->manager;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the request that was posted for this reply. In special,
|
||||
note that the URL for the request may be different than that of
|
||||
the reply.
|
||||
|
||||
\sa QNetworkRequest::url(), url(), setRequest()
|
||||
*/
|
||||
QNetworkRequest QNetworkReply::request() const
|
||||
{
|
||||
return d_func()->request;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the operation that was posted for this reply.
|
||||
|
||||
\sa setOperation()
|
||||
*/
|
||||
QNetworkAccessManager::Operation QNetworkReply::operation() const
|
||||
{
|
||||
return d_func()->operation;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the error that was found during the processing of this
|
||||
request. If no error was found, returns NoError.
|
||||
|
||||
\sa setError()
|
||||
*/
|
||||
QNetworkReply::NetworkError QNetworkReply::error() const
|
||||
{
|
||||
return d_func()->errorCode;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Returns true when the reply has finished or was aborted.
|
||||
|
||||
\sa isRunning()
|
||||
*/
|
||||
bool QNetworkReply::isFinished() const
|
||||
{
|
||||
return d_func()->isFinished;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Returns true when the request is still processing and the
|
||||
reply has not finished or was aborted yet.
|
||||
|
||||
\sa isFinished()
|
||||
*/
|
||||
bool QNetworkReply::isRunning() const
|
||||
{
|
||||
return !isFinished();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the URL of the content downloaded or uploaded. Note that
|
||||
the URL may be different from that of the original request.
|
||||
|
||||
\sa request(), setUrl(), QNetworkRequest::url()
|
||||
*/
|
||||
QUrl QNetworkReply::url() const
|
||||
{
|
||||
return d_func()->url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value of the known header \a header, if that header
|
||||
was sent by the remote server. If the header was not sent, returns
|
||||
an invalid QVariant.
|
||||
|
||||
\sa rawHeader(), setHeader(), QNetworkRequest::header()
|
||||
*/
|
||||
QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const
|
||||
{
|
||||
return d_func()->cookedHeaders.value(header);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the raw header of name \a headerName was sent by
|
||||
the remote server
|
||||
|
||||
\sa rawHeader()
|
||||
*/
|
||||
bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
Q_D(const QNetworkReply);
|
||||
return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the raw contents of the header \a headerName as sent by
|
||||
the remote server. If there is no such header, returns an empty
|
||||
byte array, which may be indistinguishable from an empty
|
||||
header. Use hasRawHeader() to verify if the server sent such
|
||||
header field.
|
||||
|
||||
\sa setRawHeader(), hasRawHeader(), header()
|
||||
*/
|
||||
QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
Q_D(const QNetworkReply);
|
||||
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
|
||||
d->findRawHeader(headerName);
|
||||
if (it != d->rawHeaders.constEnd())
|
||||
return it->second;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
/*! \typedef QNetworkReply::RawHeaderPair
|
||||
|
||||
RawHeaderPair is a QPair<QByteArray, QByteArray> where the first
|
||||
QByteArray is the header name and the second is the header.
|
||||
*/
|
||||
|
||||
/*!
|
||||
Returns a list of raw header pairs.
|
||||
*/
|
||||
const QList<QNetworkReply::RawHeaderPair>& QNetworkReply::rawHeaderPairs() const
|
||||
{
|
||||
Q_D(const QNetworkReply);
|
||||
return d->rawHeaders;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of headers fields that were sent by the remote
|
||||
server, in the order that they were sent. Duplicate headers are
|
||||
merged together and take place of the latter duplicate.
|
||||
*/
|
||||
QList<QByteArray> QNetworkReply::rawHeaderList() const
|
||||
{
|
||||
return d_func()->rawHeadersKeys();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the attribute associated with the code \a code. If the
|
||||
attribute has not been set, it returns an invalid QVariant (type QVariant::Null).
|
||||
|
||||
You can expect the default values listed in
|
||||
QNetworkRequest::Attribute to be applied to the values returned by
|
||||
this function.
|
||||
|
||||
\sa setAttribute(), QNetworkRequest::Attribute
|
||||
*/
|
||||
QVariant QNetworkReply::attribute(QNetworkRequest::Attribute code) const
|
||||
{
|
||||
return d_func()->attributes.value(code);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the SSL configuration and state associated with this
|
||||
reply, if SSL was used. It will contain the remote server's
|
||||
certificate, its certificate chain leading to the Certificate
|
||||
Authority as well as the encryption ciphers in use.
|
||||
|
||||
The peer's certificate and its certificate chain will be known by
|
||||
the time sslErrors() is emitted, if it's emitted.
|
||||
*/
|
||||
QSslConfiguration QNetworkReply::sslConfiguration() const
|
||||
{
|
||||
return QSslConfiguration();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the SSL configuration for the network connection associated
|
||||
with this request, if possible, to be that of \a config.
|
||||
*/
|
||||
void QNetworkReply::setSslConfiguration(const QSslConfiguration &config)
|
||||
{
|
||||
Q_UNUSED(config);
|
||||
}
|
||||
|
||||
/*!
|
||||
\overload
|
||||
\since 4.6
|
||||
|
||||
If this function is called, the SSL errors given in \a errors
|
||||
will be ignored.
|
||||
|
||||
\note Because most SSL errors are associated with a certificate, for most
|
||||
of them you must set the expected certificate this SSL error is related to.
|
||||
If, for instance, you want to issue a request to a server that uses
|
||||
a self-signed certificate, consider the following snippet:
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkreply.cpp 0
|
||||
|
||||
Multiple calls to this function will replace the list of errors that
|
||||
were passed in previous calls.
|
||||
You can clear the list of errors you want to ignore by calling this
|
||||
function with an empty list.
|
||||
|
||||
\sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
|
||||
*/
|
||||
void QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_UNUSED(errors);
|
||||
}
|
||||
|
||||
/*!
|
||||
If this function is called, SSL errors related to network
|
||||
connection will be ignored, including certificate validation
|
||||
errors.
|
||||
|
||||
\warning Be sure to always let the user inspect the errors
|
||||
reported by the sslErrors() signal, and only call this method
|
||||
upon confirmation from the user that proceeding is ok.
|
||||
If there are unexpected errors, the reply should be aborted.
|
||||
Calling this method without inspecting the actual errors will
|
||||
most likely pose a security risk for your application. Use it
|
||||
with great care!
|
||||
|
||||
This function can be called from the slot connected to the
|
||||
sslErrors() signal, which indicates which errors were
|
||||
found.
|
||||
|
||||
\sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
|
||||
*/
|
||||
void QNetworkReply::ignoreSslErrors()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
qint64 QNetworkReply::writeData(const char *, qint64)
|
||||
{
|
||||
return -1; // you can't write
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the associated operation for this object to be \a
|
||||
operation. This value will be returned by operation().
|
||||
|
||||
Note: the operation should be set when this object is created and
|
||||
not changed again.
|
||||
|
||||
\sa operation(), setRequest()
|
||||
*/
|
||||
void QNetworkReply::setOperation(QNetworkAccessManager::Operation operation)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->operation = operation;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the associated request for this object to be \a request. This
|
||||
value will be returned by request().
|
||||
|
||||
Note: the request should be set when this object is created and
|
||||
not changed again.
|
||||
|
||||
\sa request(), setOperation()
|
||||
*/
|
||||
void QNetworkReply::setRequest(const QNetworkRequest &request)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->request = request;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the error condition to be \a errorCode. The human-readable
|
||||
message is set with \a errorString.
|
||||
|
||||
Calling setError() does not emit the error(QNetworkReply::NetworkError)
|
||||
signal.
|
||||
|
||||
\sa error(), errorString()
|
||||
*/
|
||||
void QNetworkReply::setError(NetworkError errorCode, const QString &errorString)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->errorCode = errorCode;
|
||||
setErrorString(errorString); // in QIODevice
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.8
|
||||
Sets the reply as \a finished.
|
||||
|
||||
After having this set the replies data must not change.
|
||||
|
||||
\sa isFinished()
|
||||
*/
|
||||
void QNetworkReply::setFinished(bool finished)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->isFinished = finished;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Sets the URL being processed to be \a url. Normally, the URL
|
||||
matches that of the request that was posted, but for a variety of
|
||||
reasons it can be different (for example, a file path being made
|
||||
absolute or canonical).
|
||||
|
||||
\sa url(), request(), QNetworkRequest::url()
|
||||
*/
|
||||
void QNetworkReply::setUrl(const QUrl &url)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->url = url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the known header \a header to be of value \a value. The
|
||||
corresponding raw form of the header will be set as well.
|
||||
|
||||
\sa header(), setRawHeader(), QNetworkRequest::setHeader()
|
||||
*/
|
||||
void QNetworkReply::setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->setCookedHeader(header, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the raw header \a headerName to be of value \a value. If \a
|
||||
headerName was previously set, it is overridden. Multiple HTTP
|
||||
headers of the same name are functionally equivalent to one single
|
||||
header with the values concatenated, separated by commas.
|
||||
|
||||
If \a headerName matches a known header, the value \a value will
|
||||
be parsed and the corresponding parsed form will also be set.
|
||||
|
||||
\sa rawHeader(), header(), setHeader(), QNetworkRequest::setRawHeader()
|
||||
*/
|
||||
void QNetworkReply::setRawHeader(const QByteArray &headerName, const QByteArray &value)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
d->setRawHeader(headerName, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the attribute \a code to have value \a value. If \a code was
|
||||
previously set, it will be overridden. If \a value is an invalid
|
||||
QVariant, the attribute will be unset.
|
||||
|
||||
\sa attribute(), QNetworkRequest::setAttribute()
|
||||
*/
|
||||
void QNetworkReply::setAttribute(QNetworkRequest::Attribute code, const QVariant &value)
|
||||
{
|
||||
Q_D(QNetworkReply);
|
||||
if (value.isValid())
|
||||
d->attributes.insert(code, value);
|
||||
else
|
||||
d->attributes.remove(code);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#include "moc_qnetworkreply.h"
|
|
@ -1,152 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREPLY_H
|
||||
#define QNETWORKREPLY_H
|
||||
|
||||
#include <QtCore/QIODevice>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QUrl;
|
||||
class QVariant;
|
||||
class QAuthenticator;
|
||||
class QSslConfiguration;
|
||||
class QSslError;
|
||||
|
||||
class QNetworkReplyPrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkReply: public QIODevice
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_ENUMS(NetworkError)
|
||||
public:
|
||||
enum NetworkError {
|
||||
NoError = 0,
|
||||
|
||||
// network layer errors [relating to the destination server] (1-99):
|
||||
ConnectionRefusedError = 1,
|
||||
RemoteHostClosedError,
|
||||
HostNotFoundError,
|
||||
TimeoutError,
|
||||
OperationCanceledError,
|
||||
SslHandshakeFailedError,
|
||||
TemporaryNetworkFailureError,
|
||||
UnknownNetworkError = 99,
|
||||
|
||||
// proxy errors (101-199):
|
||||
ProxyConnectionRefusedError = 101,
|
||||
ProxyConnectionClosedError,
|
||||
ProxyNotFoundError,
|
||||
ProxyTimeoutError,
|
||||
ProxyAuthenticationRequiredError,
|
||||
UnknownProxyError = 199,
|
||||
|
||||
// content errors (201-299):
|
||||
ContentAccessDenied = 201,
|
||||
ContentOperationNotPermittedError,
|
||||
ContentNotFoundError,
|
||||
AuthenticationRequiredError,
|
||||
ContentReSendError,
|
||||
UnknownContentError = 299,
|
||||
|
||||
// protocol errors
|
||||
ProtocolUnknownError = 301,
|
||||
ProtocolInvalidOperationError,
|
||||
ProtocolFailure = 399
|
||||
};
|
||||
|
||||
~QNetworkReply();
|
||||
virtual void abort() = 0;
|
||||
|
||||
// reimplemented from QIODevice
|
||||
virtual bool isSequential() const;
|
||||
|
||||
// like QAbstractSocket:
|
||||
qint64 readBufferSize() const;
|
||||
virtual void setReadBufferSize(qint64 size);
|
||||
|
||||
QNetworkAccessManager *manager() const;
|
||||
QNetworkAccessManager::Operation operation() const;
|
||||
QNetworkRequest request() const;
|
||||
NetworkError error() const;
|
||||
bool isFinished() const;
|
||||
bool isRunning() const;
|
||||
QUrl url() const;
|
||||
|
||||
// "cooked" headers
|
||||
QVariant header(QNetworkRequest::KnownHeaders header) const;
|
||||
|
||||
// raw headers:
|
||||
bool hasRawHeader(const QByteArray &headerName) const;
|
||||
QList<QByteArray> rawHeaderList() const;
|
||||
QByteArray rawHeader(const QByteArray &headerName) const;
|
||||
|
||||
typedef QPair<QByteArray, QByteArray> RawHeaderPair;
|
||||
const QList<RawHeaderPair>& rawHeaderPairs() const;
|
||||
|
||||
// attributes
|
||||
QVariant attribute(QNetworkRequest::Attribute code) const;
|
||||
|
||||
virtual QSslConfiguration sslConfiguration() const;
|
||||
virtual void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
virtual void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
public Q_SLOTS:
|
||||
virtual void ignoreSslErrors();
|
||||
|
||||
Q_SIGNALS:
|
||||
void metaDataChanged();
|
||||
void finished();
|
||||
void error(QNetworkReply::NetworkError);
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
||||
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||
|
||||
protected:
|
||||
QNetworkReply(QObject *parent = nullptr);
|
||||
QNetworkReply(QNetworkReplyPrivate &dd, QObject *parent);
|
||||
virtual qint64 writeData(const char *data, qint64 len);
|
||||
|
||||
void setOperation(QNetworkAccessManager::Operation operation);
|
||||
void setRequest(const QNetworkRequest &request);
|
||||
void setError(NetworkError errorCode, const QString &errorString);
|
||||
void setFinished(bool);
|
||||
void setUrl(const QUrl &url);
|
||||
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
|
||||
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
|
||||
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QNetworkReply)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif
|
|
@ -1,64 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREPLY_P_H
|
||||
#define QNETWORKREPLY_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkrequest_p.h"
|
||||
#include "qnetworkreply.h"
|
||||
#include "QtCore/qpointer.h"
|
||||
#include "qiodevice_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QNetworkReplyPrivate: public QIODevicePrivate, public QNetworkHeadersPrivate
|
||||
{
|
||||
public:
|
||||
QNetworkReplyPrivate();
|
||||
QNetworkRequest request;
|
||||
QUrl url;
|
||||
QPointer<QNetworkAccessManager> manager;
|
||||
qint64 readBufferMaxSize;
|
||||
QNetworkAccessManager::Operation operation;
|
||||
QNetworkReply::NetworkError errorCode;
|
||||
bool isFinished;
|
||||
|
||||
static inline void setManager(QNetworkReply *reply, QNetworkAccessManager *manager)
|
||||
{ reply->d_func()->manager = manager; }
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkReply)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,111 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkreplydataimpl_p.h"
|
||||
#include "qdataurl_p.h"
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QMetaObject>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
|
||||
: QNetworkReply(*new QNetworkReplyDataImplPrivate(), parent)
|
||||
{
|
||||
Q_D(QNetworkReplyDataImpl);
|
||||
setRequest(req);
|
||||
setUrl(req.url());
|
||||
setOperation(op);
|
||||
setFinished(true);
|
||||
QNetworkReply::open(QIODevice::ReadOnly);
|
||||
|
||||
QUrl url = req.url();
|
||||
|
||||
// FIXME qDecodeDataUrl should instead be rewritten to have the QByteArray
|
||||
// and the mime type as an output parameter and return a bool instead
|
||||
QPair<QString, QByteArray> decodeDataUrlResult = qDecodeDataUrl(url);
|
||||
|
||||
if (! decodeDataUrlResult.first.isNull()) {
|
||||
QString &mimeType = decodeDataUrlResult.first;
|
||||
qint64 size = decodeDataUrlResult.second.size();
|
||||
setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, size);
|
||||
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
|
||||
|
||||
d->decodedData.setBuffer(&decodeDataUrlResult.second);
|
||||
d->decodedData.open(QIODevice::ReadOnly);
|
||||
|
||||
QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
|
||||
Q_ARG(qint64,size), Q_ARG(qint64, size));
|
||||
QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
} else {
|
||||
// something wrong with this URI
|
||||
const QString msg = QCoreApplication::translate("QNetworkAccessDataBackend",
|
||||
"Invalid URI: %1").arg(QString::fromLatin1(url.toEncoded()));
|
||||
setError(QNetworkReply::ProtocolFailure, msg);
|
||||
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
|
||||
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolFailure));
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
qint64 QNetworkReplyDataImpl::size() const
|
||||
{
|
||||
Q_D(const QNetworkReplyDataImpl);
|
||||
return d->decodedData.size();
|
||||
}
|
||||
|
||||
qint64 QNetworkReplyDataImpl::bytesAvailable() const
|
||||
{
|
||||
Q_D(const QNetworkReplyDataImpl);
|
||||
return QNetworkReply::bytesAvailable() + d->decodedData.bytesAvailable();
|
||||
}
|
||||
|
||||
void QNetworkReplyDataImpl::abort()
|
||||
{
|
||||
QNetworkReply::close();
|
||||
}
|
||||
|
||||
bool QNetworkReplyDataImpl::isSequential() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
qint64 QNetworkReplyDataImpl::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QNetworkReplyDataImpl);
|
||||
|
||||
// TODO idea:
|
||||
// Instead of decoding the whole data into new memory, we could decode on demand.
|
||||
// Note that this might be tricky to do.
|
||||
|
||||
return d->decodedData.read(data, maxlen);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkreplydataimpl_p.h"
|
||||
|
||||
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREPLYDATAIMPL_H
|
||||
#define QNETWORKREPLYDATAIMPL_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkreply.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include <QBuffer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QNetworkReplyDataImplPrivate;
|
||||
class QNetworkReplyDataImpl: public QNetworkReply
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkReplyDataImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
|
||||
|
||||
// reimplemented from QIODevice
|
||||
qint64 size() const final;
|
||||
qint64 bytesAvailable() const final;
|
||||
|
||||
// reimplemented from QNetworkReply
|
||||
void abort() final;
|
||||
bool isSequential() const final;
|
||||
qint64 readData(char *data, qint64 maxlen) final;
|
||||
|
||||
Q_DECLARE_PRIVATE(QNetworkReplyDataImpl)
|
||||
};
|
||||
|
||||
class QNetworkReplyDataImplPrivate: public QNetworkReplyPrivate
|
||||
{
|
||||
public:
|
||||
QBuffer decodedData;
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkReplyDataImpl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QNETWORKREPLYDATAIMPL_H
|
|
@ -1,168 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkreplyfileimpl_p.h"
|
||||
#include "qdatetime.h"
|
||||
#include "qcoreapplication.h"
|
||||
#include "qdebug.h"
|
||||
#include "qcore_unix_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
|
||||
: QNetworkReplyPrivate(), realFileSize(0)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
|
||||
: QNetworkReply(*new QNetworkReplyFileImplPrivate(), parent)
|
||||
{
|
||||
setRequest(req);
|
||||
setUrl(req.url());
|
||||
setOperation(op);
|
||||
setFinished(true);
|
||||
QNetworkReply::open(QIODevice::ReadOnly);
|
||||
|
||||
QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
|
||||
|
||||
QUrl url = req.url();
|
||||
if (url.host() == QLatin1String("localhost"))
|
||||
url.setHost(QString());
|
||||
|
||||
// do not allow UNC paths on Unix
|
||||
if (!url.host().isEmpty()) {
|
||||
// we handle only local files
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
|
||||
setError(QNetworkReply::ProtocolInvalidOperationError, msg);
|
||||
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
|
||||
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
if (url.path().isEmpty())
|
||||
url.setPath(QLatin1String("/"));
|
||||
setUrl(url);
|
||||
|
||||
|
||||
QString fileName = url.toLocalFile();
|
||||
if (fileName.isEmpty()) {
|
||||
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
|
||||
}
|
||||
|
||||
QStatInfo si(fileName);
|
||||
if (si.isDir()) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
|
||||
setError(QNetworkReply::ContentOperationNotPermittedError, msg);
|
||||
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
|
||||
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
d->realFile.setFileName(fileName);
|
||||
bool opened = d->realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
|
||||
// could we open the file?
|
||||
if (!opened) {
|
||||
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
|
||||
.arg(d->realFile.fileName(), d->realFile.errorString());
|
||||
|
||||
if (d->realFile.exists()) {
|
||||
setError(QNetworkReply::ContentAccessDenied, msg);
|
||||
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
|
||||
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
|
||||
} else {
|
||||
setError(QNetworkReply::ContentNotFoundError, msg);
|
||||
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
|
||||
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
|
||||
}
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
return;
|
||||
}
|
||||
|
||||
setHeader(QNetworkRequest::LastModifiedHeader, si.lastModified());
|
||||
d->realFileSize = si.size();
|
||||
setHeader(QNetworkRequest::ContentLengthHeader, d->realFileSize);
|
||||
|
||||
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
|
||||
Q_ARG(qint64, d->realFileSize), Q_ARG(qint64, d->realFileSize));
|
||||
QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
qint64 QNetworkReplyFileImpl::size() const
|
||||
{
|
||||
Q_D(const QNetworkReplyFileImpl);
|
||||
return d->realFileSize;
|
||||
}
|
||||
|
||||
qint64 QNetworkReplyFileImpl::bytesAvailable() const
|
||||
{
|
||||
Q_D(const QNetworkReplyFileImpl);
|
||||
if (!d->realFile.isOpen())
|
||||
return QNetworkReply::bytesAvailable();
|
||||
return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable();
|
||||
}
|
||||
|
||||
void QNetworkReplyFileImpl::close()
|
||||
{
|
||||
Q_D(QNetworkReplyFileImpl);
|
||||
QNetworkReply::close();
|
||||
d->realFile.close();
|
||||
}
|
||||
|
||||
void QNetworkReplyFileImpl::abort()
|
||||
{
|
||||
Q_D(QNetworkReplyFileImpl);
|
||||
QNetworkReply::close();
|
||||
d->realFile.close();
|
||||
}
|
||||
|
||||
bool QNetworkReplyFileImpl::isSequential () const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QNetworkReplyFileImpl);
|
||||
if (!d->realFile.isOpen())
|
||||
return -1;
|
||||
qint64 ret = d->realFile.read(data, maxlen);
|
||||
if (bytesAvailable() == 0 && d->realFile.isOpen())
|
||||
d->realFile.close();
|
||||
if (ret == 0 && bytesAvailable() == 0)
|
||||
return -1;
|
||||
else
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkreplyfileimpl_p.h"
|
||||
|
||||
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREPLYFILEIMPL_H
|
||||
#define QNETWORKREPLYFILEIMPL_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkreply.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include <QFile>
|
||||
#include <QAbstractFileEngine>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QNetworkReplyFileImplPrivate;
|
||||
class QNetworkReplyFileImpl: public QNetworkReply
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
|
||||
|
||||
// reimplemented from QIODevice
|
||||
qint64 size() const final;
|
||||
qint64 bytesAvailable() const final;
|
||||
void close() final;
|
||||
|
||||
// reimplemented from QNetworkReply
|
||||
bool isSequential() const final;
|
||||
void abort() final;
|
||||
qint64 readData(char *data, qint64 maxlen) final;
|
||||
|
||||
Q_DECLARE_PRIVATE(QNetworkReplyFileImpl)
|
||||
};
|
||||
|
||||
class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate
|
||||
{
|
||||
public:
|
||||
QNetworkReplyFileImplPrivate();
|
||||
|
||||
QFile realFile;
|
||||
qint64 realFileSize;
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkReplyFileImpl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QNETWORKREPLYFILEIMPL_H
|
|
@ -1,983 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qnetworkreplyimpl_p.h"
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkcookie.h"
|
||||
#include "qabstractnetworkcache.h"
|
||||
#include "QtCore/qcoreapplication.h"
|
||||
#include "QtCore/qdatetime.h"
|
||||
#include "QtNetwork/qsslconfiguration.h"
|
||||
#include "QtNetwork/qnetworkcookiejar.h"
|
||||
#include "qnetworkaccesshttpbackend_p.h"
|
||||
#include "qnetworkaccessmanager_p.h"
|
||||
#include "qnetworkcommon_p.h"
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
|
||||
: backend(0), outgoingData(0),
|
||||
copyDevice(0),
|
||||
cacheEnabled(false), cacheSaveDevice(0),
|
||||
notificationHandlingPaused(false),
|
||||
bytesDownloaded(0), lastBytesDownloaded(-1), bytesUploaded(-1), preMigrationDownloaded(-1),
|
||||
httpStatusCode(0),
|
||||
state(Idle)
|
||||
, downloadBufferReadPosition(0)
|
||||
, downloadBufferCurrentSize(0)
|
||||
, downloadBufferMaximumSize(0)
|
||||
, downloadBuffer(0)
|
||||
{
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_startOperation()
|
||||
{
|
||||
// ensure this function is only being called once
|
||||
if (state == Working || state == Finished) {
|
||||
qDebug("QNetworkReplyImpl::_q_startOperation was called more than once");
|
||||
return;
|
||||
}
|
||||
state = Working;
|
||||
|
||||
// note: if that method is called directly, it cannot happen that the backend is 0,
|
||||
// because we just checked via a qobject_cast that we got a http backend (see
|
||||
// QNetworkReplyImplPrivate::setup())
|
||||
if (!backend) {
|
||||
error(QNetworkReplyImpl::ProtocolUnknownError,
|
||||
QCoreApplication::translate("QNetworkReply", "Protocol \"%1\" is unknown").arg(url.scheme())); // not really true!;
|
||||
finished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!backend->start()) {
|
||||
qWarning("Backend start failed");
|
||||
state = Working;
|
||||
error(QNetworkReplyImpl::UnknownNetworkError,
|
||||
QCoreApplication::translate("QNetworkReply", "backend start error."));
|
||||
finished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (backend && backend->isSynchronous()) {
|
||||
state = Finished;
|
||||
q_func()->setFinished(true);
|
||||
} else {
|
||||
if (state != Finished) {
|
||||
if (operation == QNetworkAccessManager::GetOperation)
|
||||
pendingNotifications.append(NotifyDownstreamReadyWrite);
|
||||
|
||||
handleNotifications();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_copyReadyRead()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (state != Working)
|
||||
return;
|
||||
if (!copyDevice || !q->isOpen())
|
||||
return;
|
||||
|
||||
// FIXME Optimize to use download buffer if it is a QBuffer.
|
||||
// Needs to be done where sendCacheContents() (?) of HTTP is emitting
|
||||
// metaDataChanged ?
|
||||
|
||||
forever {
|
||||
qint64 bytesToRead = nextDownstreamBlockSize();
|
||||
if (bytesToRead == 0)
|
||||
// we'll be called again, eventually
|
||||
break;
|
||||
|
||||
bytesToRead = qBound<qint64>(1, bytesToRead, copyDevice->bytesAvailable());
|
||||
QByteArray byteData(bytesToRead, Qt::Uninitialized);
|
||||
qint64 bytesActuallyRead = copyDevice->read(byteData.data(), byteData.size());
|
||||
if (bytesActuallyRead == -1) {
|
||||
byteData.clear();
|
||||
backendNotify(NotifyCopyFinished);
|
||||
break;
|
||||
}
|
||||
|
||||
byteData.resize(bytesActuallyRead);
|
||||
readBuffer.append(byteData);
|
||||
|
||||
if (!copyDevice->isSequential() && copyDevice->atEnd()) {
|
||||
backendNotify(NotifyCopyFinished);
|
||||
bytesDownloaded += bytesActuallyRead;
|
||||
break;
|
||||
}
|
||||
|
||||
bytesDownloaded += bytesActuallyRead;
|
||||
}
|
||||
|
||||
if (bytesDownloaded == lastBytesDownloaded) {
|
||||
// we didn't read anything
|
||||
return;
|
||||
}
|
||||
|
||||
lastBytesDownloaded = bytesDownloaded;
|
||||
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
||||
if (preMigrationDownloaded != Q_INT64_C(-1))
|
||||
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
|
||||
pauseNotificationHandling();
|
||||
// emit readyRead before downloadProgress incase this will cause events to be
|
||||
// processed and we get into a recursive call (as in QProgressDialog).
|
||||
emit q->readyRead();
|
||||
emit q->downloadProgress(bytesDownloaded,
|
||||
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
|
||||
resumeNotificationHandling();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_copyReadChannelFinished()
|
||||
{
|
||||
_q_copyReadyRead();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_bufferOutgoingDataFinished()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
// make sure this is only called once, ever.
|
||||
//_q_bufferOutgoingData may call it or the readChannelFinished emission
|
||||
if (state != Buffering)
|
||||
return;
|
||||
|
||||
// disconnect signals
|
||||
QObject::disconnect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
|
||||
QObject::disconnect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
|
||||
|
||||
// finally, start the request
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_bufferOutgoingData()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
if (!outgoingDataBuffer) {
|
||||
// first call, create our buffer
|
||||
outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
|
||||
|
||||
QObject::connect(outgoingData, SIGNAL(readyRead()), q, SLOT(_q_bufferOutgoingData()));
|
||||
QObject::connect(outgoingData, SIGNAL(readChannelFinished()), q, SLOT(_q_bufferOutgoingDataFinished()));
|
||||
}
|
||||
|
||||
qint64 bytesBuffered = 0;
|
||||
qint64 bytesToBuffer = 0;
|
||||
|
||||
// read data into our buffer
|
||||
forever {
|
||||
bytesToBuffer = outgoingData->bytesAvailable();
|
||||
// unknown? just try 2 kB, this also ensures we always try to read the EOF
|
||||
if (bytesToBuffer <= 0)
|
||||
bytesToBuffer = 2*1024;
|
||||
|
||||
char *dst = outgoingDataBuffer->reserve(bytesToBuffer);
|
||||
bytesBuffered = outgoingData->read(dst, bytesToBuffer);
|
||||
|
||||
if (bytesBuffered == -1) {
|
||||
// EOF has been reached.
|
||||
outgoingDataBuffer->chop(bytesToBuffer);
|
||||
|
||||
_q_bufferOutgoingDataFinished();
|
||||
break;
|
||||
} else if (bytesBuffered == 0) {
|
||||
// nothing read right now, just wait until we get called again
|
||||
outgoingDataBuffer->chop(bytesToBuffer);
|
||||
|
||||
break;
|
||||
} else {
|
||||
// don't break, try to read() again
|
||||
outgoingDataBuffer->chop(bytesToBuffer - bytesBuffered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const QNetworkRequest &req,
|
||||
QIODevice *data)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
outgoingData = data;
|
||||
request = req;
|
||||
url = request.url();
|
||||
operation = op;
|
||||
|
||||
q->QIODevice::open(QIODevice::ReadOnly);
|
||||
// Internal code that does a HTTP reply for the synchronous Ajax
|
||||
// in QtWebKit.
|
||||
QVariant synchronousHttpAttribute = req.attribute(
|
||||
static_cast<QNetworkRequest::Attribute>(QNetworkRequest::SynchronousRequestAttribute));
|
||||
// The synchronous HTTP is a corner case, we will put all upload data in one big QByteArray in the outgoingDataBuffer.
|
||||
// Yes, this is not the most efficient thing to do, but on the other hand synchronous XHR needs to die anyway.
|
||||
if (synchronousHttpAttribute.toBool() && outgoingData) {
|
||||
outgoingDataBuffer = QSharedPointer<QRingBuffer>(new QRingBuffer());
|
||||
qint64 previousDataSize = 0;
|
||||
do {
|
||||
previousDataSize = outgoingDataBuffer->size();
|
||||
outgoingDataBuffer->append(outgoingData->readAll());
|
||||
} while (outgoingDataBuffer->size() != previousDataSize);
|
||||
}
|
||||
|
||||
if (backend)
|
||||
backend->setSynchronous(synchronousHttpAttribute.toBool());
|
||||
|
||||
|
||||
if (outgoingData && backend && !backend->isSynchronous()) {
|
||||
// there is data to be uploaded, e.g. HTTP POST.
|
||||
|
||||
if (!backend->needsResetableUploadData() || !outgoingData->isSequential()) {
|
||||
// backend does not need upload buffering or
|
||||
// fixed size non-sequential
|
||||
// just start the operation
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
} else {
|
||||
bool bufferingDisallowed =
|
||||
req.attribute(QNetworkRequest::DoNotBufferUploadDataAttribute,
|
||||
false).toBool();
|
||||
|
||||
if (bufferingDisallowed) {
|
||||
// if a valid content-length header for the request was supplied, we can disable buffering
|
||||
// if not, we will buffer anyway
|
||||
if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
} else {
|
||||
state = Buffering;
|
||||
QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
|
||||
}
|
||||
} else {
|
||||
// _q_startOperation will be called when the buffering has finished.
|
||||
state = Buffering;
|
||||
QMetaObject::invokeMethod(q, "_q_bufferOutgoingData", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// for HTTP, we want to send out the request as fast as possible to the network, without
|
||||
// invoking methods in a QueuedConnection
|
||||
#ifndef QT_NO_HTTP
|
||||
if (qobject_cast<QNetworkAccessHttpBackend *>(backend) || (backend && backend->isSynchronous())) {
|
||||
_q_startOperation();
|
||||
} else {
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
}
|
||||
#else
|
||||
if (backend && backend->isSynchronous())
|
||||
_q_startOperation();
|
||||
else
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
#endif // QT_NO_HTTP
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::backendNotify(InternalNotifications notification)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (!pendingNotifications.contains(notification))
|
||||
pendingNotifications.enqueue(notification);
|
||||
|
||||
if (pendingNotifications.size() == 1)
|
||||
QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::handleNotifications()
|
||||
{
|
||||
if (notificationHandlingPaused)
|
||||
return;
|
||||
|
||||
NotificationQueue current = pendingNotifications;
|
||||
pendingNotifications.clear();
|
||||
|
||||
if (state != Working)
|
||||
return;
|
||||
|
||||
while (state == Working && !current.isEmpty()) {
|
||||
InternalNotifications notification = current.dequeue();
|
||||
switch (notification) {
|
||||
case NotifyDownstreamReadyWrite:
|
||||
if (copyDevice)
|
||||
_q_copyReadyRead();
|
||||
else
|
||||
backend->downstreamReadyWrite();
|
||||
break;
|
||||
|
||||
case NotifyCloseDownstreamChannel:
|
||||
backend->closeDownstreamChannel();
|
||||
break;
|
||||
|
||||
case NotifyCopyFinished: {
|
||||
QIODevice *dev = copyDevice;
|
||||
copyDevice = 0;
|
||||
backend->copyFinished(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not handle the notifications while we are emitting downloadProgress
|
||||
// or readyRead
|
||||
void QNetworkReplyImplPrivate::pauseNotificationHandling()
|
||||
{
|
||||
notificationHandlingPaused = true;
|
||||
}
|
||||
|
||||
// Resume notification handling
|
||||
void QNetworkReplyImplPrivate::resumeNotificationHandling()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
notificationHandlingPaused = false;
|
||||
if (pendingNotifications.size() >= 1)
|
||||
QCoreApplication::postEvent(q, new QEvent(QEvent::NetworkReplyUpdated));
|
||||
}
|
||||
|
||||
QAbstractNetworkCache *QNetworkReplyImplPrivate::networkCache() const
|
||||
{
|
||||
if (!backend)
|
||||
return 0;
|
||||
return backend->networkCache();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::createCache()
|
||||
{
|
||||
// check if we can save and if we're allowed to
|
||||
if (!networkCache()
|
||||
|| !request.attribute(QNetworkRequest::CacheSaveControlAttribute, true).toBool()
|
||||
|| request.attribute(QNetworkRequest::CacheLoadControlAttribute,
|
||||
QNetworkRequest::PreferNetwork).toInt()
|
||||
== QNetworkRequest::AlwaysNetwork)
|
||||
return;
|
||||
cacheEnabled = true;
|
||||
}
|
||||
|
||||
bool QNetworkReplyImplPrivate::isCachingEnabled() const
|
||||
{
|
||||
return (cacheEnabled && networkCache() != 0);
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::setCachingEnabled(bool enable)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (!enable && !cacheEnabled)
|
||||
return; // nothing to do
|
||||
if (enable && cacheEnabled)
|
||||
return; // nothing to do either!
|
||||
|
||||
if (enable) {
|
||||
if (bytesDownloaded) {
|
||||
// refuse to enable in this case
|
||||
qCritical("QNetworkReplyImpl: backend error: caching was enabled after some bytes had been written");
|
||||
return;
|
||||
}
|
||||
|
||||
createCache();
|
||||
} else {
|
||||
// someone told us to turn on, then back off?
|
||||
// ok... but you should make up your mind
|
||||
qDebug("QNetworkReplyImpl: setCachingEnabled(true) called after setCachingEnabled(false) -- "
|
||||
"backend %s probably needs to be fixed",
|
||||
backend->metaObject()->className());
|
||||
networkCache()->remove(url);
|
||||
cacheSaveDevice = 0;
|
||||
cacheEnabled = false;
|
||||
QObject::disconnect(networkCache(), SIGNAL(destroyed()), q, SLOT(_q_cacheDestroyed()));
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_cacheDestroyed()
|
||||
{
|
||||
//destruction of cache invalidates cacheSaveDevice
|
||||
cacheSaveDevice = 0;
|
||||
cacheEnabled = false;
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::_q_cacheSaveDeviceAboutToClose()
|
||||
{
|
||||
// do not keep a dangling pointer to the device around (device
|
||||
// is closing because e.g. QAbstractNetworkCache::remove() was called).
|
||||
cacheSaveDevice = 0;
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::completeCacheSave()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (cacheEnabled) {
|
||||
if (errorCode != QNetworkReplyImpl::NoError) {
|
||||
networkCache()->remove(url);
|
||||
} else if (cacheSaveDevice) {
|
||||
networkCache()->insert(cacheSaveDevice);
|
||||
}
|
||||
QObject::disconnect(networkCache(), SIGNAL(destroyed()), q, SLOT(_q_cacheDestroyed()));
|
||||
}
|
||||
cacheSaveDevice = 0;
|
||||
cacheEnabled = false;
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::emitUploadProgress(qint64 bytesSent, qint64 bytesTotal)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
bytesUploaded = bytesSent;
|
||||
pauseNotificationHandling();
|
||||
emit q->uploadProgress(bytesSent, bytesTotal);
|
||||
resumeNotificationHandling();
|
||||
}
|
||||
|
||||
|
||||
qint64 QNetworkReplyImplPrivate::nextDownstreamBlockSize() const
|
||||
{
|
||||
enum { DesiredBufferSize = 32 * 1024 };
|
||||
if (readBufferMaxSize == 0)
|
||||
return DesiredBufferSize;
|
||||
|
||||
return qMax<qint64>(0, readBufferMaxSize - readBuffer.byteAmount());
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::initCacheSaveDevice()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
// The disk cache does not support partial content, so don't even try to
|
||||
// save any such content into the cache.
|
||||
if (q->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 206) {
|
||||
cacheEnabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// save the meta data
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
metaData = backend->fetchCacheMetaData(metaData);
|
||||
|
||||
// save the redirect request also in the cache
|
||||
QVariant redirectionTarget = q->attribute(QNetworkRequest::RedirectionTargetAttribute);
|
||||
if (redirectionTarget.isValid()) {
|
||||
QNetworkCacheMetaData::AttributesMap attributes = metaData.attributes();
|
||||
attributes.insert(QNetworkRequest::RedirectionTargetAttribute, redirectionTarget);
|
||||
metaData.setAttributes(attributes);
|
||||
}
|
||||
|
||||
cacheSaveDevice = networkCache()->prepare(metaData);
|
||||
|
||||
if (cacheSaveDevice)
|
||||
q->connect(cacheSaveDevice, SIGNAL(aboutToClose()), SLOT(_q_cacheSaveDeviceAboutToClose()));
|
||||
|
||||
if (!cacheSaveDevice || (cacheSaveDevice && !cacheSaveDevice->isOpen())) {
|
||||
if (cacheSaveDevice && !cacheSaveDevice->isOpen())
|
||||
qCritical("QNetworkReplyImpl: network cache returned a device that is not open -- "
|
||||
"class %s probably needs to be fixed",
|
||||
networkCache()->metaObject()->className());
|
||||
|
||||
networkCache()->remove(url);
|
||||
cacheSaveDevice = 0;
|
||||
cacheEnabled = false;
|
||||
} else {
|
||||
q->connect(networkCache(), SIGNAL(destroyed()), SLOT(_q_cacheDestroyed()));
|
||||
}
|
||||
}
|
||||
|
||||
// we received downstream data and send this to the cache
|
||||
// and to our readBuffer (which in turn gets read by the user of QNetworkReply)
|
||||
void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (!q->isOpen())
|
||||
return;
|
||||
|
||||
if (cacheEnabled && !cacheSaveDevice) {
|
||||
initCacheSaveDevice();
|
||||
}
|
||||
|
||||
qint64 bytesWritten = 0;
|
||||
for (int i = 0; i < data.bufferCount(); i++) {
|
||||
QByteArray const &item = data[i];
|
||||
|
||||
if (cacheSaveDevice)
|
||||
cacheSaveDevice->write(item.constData(), item.size());
|
||||
readBuffer.append(item);
|
||||
|
||||
bytesWritten += item.size();
|
||||
}
|
||||
data.clear();
|
||||
|
||||
bytesDownloaded += bytesWritten;
|
||||
lastBytesDownloaded = bytesDownloaded;
|
||||
|
||||
appendDownstreamDataSignalEmissions();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
||||
if (preMigrationDownloaded != Q_INT64_C(-1))
|
||||
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
|
||||
pauseNotificationHandling();
|
||||
// important: At the point of this readyRead(), the data parameter list must be empty,
|
||||
// else implicit sharing will trigger memcpy when the user is reading data!
|
||||
emit q->readyRead();
|
||||
// emit readyRead before downloadProgress incase this will cause events to be
|
||||
// processed and we get into a recursive call (as in QProgressDialog).
|
||||
emit q->downloadProgress(bytesDownloaded,
|
||||
totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
|
||||
|
||||
resumeNotificationHandling();
|
||||
// do we still have room in the buffer?
|
||||
if (nextDownstreamBlockSize() > 0)
|
||||
backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
||||
}
|
||||
|
||||
// this is used when it was fetched from the cache, right?
|
||||
void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (!q->isOpen())
|
||||
return;
|
||||
|
||||
// read until EOF from data
|
||||
if (copyDevice) {
|
||||
qCritical("QNetworkReplyImpl: copy from QIODevice already in progress -- "
|
||||
"backend probly needs to be fixed");
|
||||
return;
|
||||
}
|
||||
|
||||
copyDevice = data;
|
||||
q->connect(copyDevice, SIGNAL(readyRead()), SLOT(_q_copyReadyRead()));
|
||||
q->connect(copyDevice, SIGNAL(readChannelFinished()), SLOT(_q_copyReadChannelFinished()));
|
||||
|
||||
// start the copy:
|
||||
_q_copyReadyRead();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::appendDownstreamData(const QByteArray &data)
|
||||
{
|
||||
Q_UNUSED(data)
|
||||
// TODO implement
|
||||
|
||||
// TODO call
|
||||
|
||||
qFatal("QNetworkReplyImplPrivate::appendDownstreamData not implemented");
|
||||
}
|
||||
|
||||
char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
if (!downloadBuffer) {
|
||||
// We are requested to create it
|
||||
// Check attribute() if allocating a buffer of that size can be allowed
|
||||
QVariant bufferAllocationPolicy = request.attribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute);
|
||||
if (bufferAllocationPolicy.isValid() && bufferAllocationPolicy.toLongLong() >= size) {
|
||||
downloadBufferCurrentSize = 0;
|
||||
downloadBufferMaximumSize = size;
|
||||
Q_ASSERT(downloadBufferMaximumSize >= 1);
|
||||
downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
|
||||
downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
|
||||
|
||||
q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
|
||||
}
|
||||
}
|
||||
|
||||
return downloadBuffer;
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::setDownloadBuffer(QSharedPointer<char> sp, qint64 size)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
downloadBufferPointer = sp;
|
||||
downloadBuffer = downloadBufferPointer.data();
|
||||
downloadBufferCurrentSize = 0;
|
||||
downloadBufferMaximumSize = size;
|
||||
q->setAttribute(QNetworkRequest::DownloadBufferAttribute, qVariantFromValue<QSharedPointer<char> > (downloadBufferPointer));
|
||||
}
|
||||
|
||||
|
||||
void QNetworkReplyImplPrivate::appendDownstreamDataDownloadBuffer(qint64 bytesReceived, qint64 bytesTotal)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
if (!q->isOpen())
|
||||
return;
|
||||
|
||||
if (cacheEnabled && !cacheSaveDevice)
|
||||
initCacheSaveDevice();
|
||||
|
||||
if (cacheSaveDevice && bytesReceived == bytesTotal) {
|
||||
// if (lastBytesDownloaded == -1)
|
||||
// lastBytesDownloaded = 0;
|
||||
// cacheSaveDevice->write(downloadBuffer + lastBytesDownloaded, bytesReceived - lastBytesDownloaded);
|
||||
|
||||
// Write everything in one go if we use a download buffer. might be more performant.
|
||||
cacheSaveDevice->write(downloadBuffer, bytesTotal);
|
||||
}
|
||||
|
||||
bytesDownloaded = bytesReceived;
|
||||
lastBytesDownloaded = bytesReceived;
|
||||
|
||||
downloadBufferCurrentSize = bytesReceived;
|
||||
|
||||
// Only emit readyRead when actual data is there
|
||||
// emit readyRead before downloadProgress incase this will cause events to be
|
||||
// processed and we get into a recursive call (as in QProgressDialog).
|
||||
if (bytesDownloaded > 0)
|
||||
emit q->readyRead();
|
||||
emit q->downloadProgress(bytesDownloaded, bytesTotal);
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::finished()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
if (state == Finished || state == Aborted || state == WaitingForSession)
|
||||
return;
|
||||
|
||||
pauseNotificationHandling();
|
||||
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
|
||||
if (preMigrationDownloaded != Q_INT64_C(-1))
|
||||
totalSize = totalSize.toLongLong() + preMigrationDownloaded;
|
||||
|
||||
resumeNotificationHandling();
|
||||
|
||||
state = Finished;
|
||||
q->setFinished(true);
|
||||
|
||||
pendingNotifications.clear();
|
||||
|
||||
pauseNotificationHandling();
|
||||
if (totalSize.isNull() || totalSize == -1) {
|
||||
emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
|
||||
}
|
||||
|
||||
if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
|
||||
emit q->uploadProgress(0, 0);
|
||||
resumeNotificationHandling();
|
||||
|
||||
// if we don't know the total size of or we received everything save the cache
|
||||
if (totalSize.isNull() || totalSize == -1 || QVariant(bytesDownloaded) == totalSize)
|
||||
completeCacheSave();
|
||||
|
||||
// note: might not be a good idea, since users could decide to delete us
|
||||
// which would delete the backend too...
|
||||
// maybe we should protect the backend
|
||||
pauseNotificationHandling();
|
||||
emit q->readChannelFinished();
|
||||
emit q->finished();
|
||||
resumeNotificationHandling();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const QString &errorMessage)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
// Can't set and emit multiple errors.
|
||||
if (errorCode != QNetworkReply::NoError) {
|
||||
qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
|
||||
return;
|
||||
}
|
||||
|
||||
errorCode = code;
|
||||
q->setErrorString(errorMessage);
|
||||
|
||||
// note: might not be a good idea, since users could decide to delete us
|
||||
// which would delete the backend too...
|
||||
// maybe we should protect the backend
|
||||
emit q->error(code);
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::metaDataChanged()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
// 1. do we have cookies?
|
||||
// 2. are we allowed to set them?
|
||||
if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()
|
||||
&& (static_cast<QNetworkRequest::LoadControl>
|
||||
(request.attribute(QNetworkRequest::CookieSaveControlAttribute,
|
||||
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic)) {
|
||||
QList<QNetworkCookie> cookies =
|
||||
qvariant_cast<QList<QNetworkCookie> >(cookedHeaders.value(QNetworkRequest::SetCookieHeader));
|
||||
QNetworkCookieJar *jar = manager->cookieJar();
|
||||
if (jar)
|
||||
jar->setCookiesFromUrl(cookies, url);
|
||||
}
|
||||
emit q->metaDataChanged();
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::redirectionRequested(const QUrl &target)
|
||||
{
|
||||
attributes.insert(QNetworkRequest::RedirectionTargetAttribute, target);
|
||||
}
|
||||
|
||||
void QNetworkReplyImplPrivate::sslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
emit q->sslErrors(errors);
|
||||
}
|
||||
|
||||
QNetworkReplyImpl::QNetworkReplyImpl(QObject *parent)
|
||||
: QNetworkReply(*new QNetworkReplyImplPrivate, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QNetworkReplyImpl::~QNetworkReplyImpl()
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
|
||||
// This code removes the data from the cache if it was prematurely aborted.
|
||||
// See QNetworkReplyImplPrivate::completeCacheSave(), we disable caching there after the cache
|
||||
// save had been properly finished. So if it is still enabled it means we got deleted/aborted.
|
||||
if (d->isCachingEnabled())
|
||||
d->networkCache()->remove(url());
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::abort()
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (d->state == QNetworkReplyImplPrivate::Finished || d->state == QNetworkReplyImplPrivate::Aborted)
|
||||
return;
|
||||
|
||||
// stop both upload and download
|
||||
if (d->outgoingData)
|
||||
disconnect(d->outgoingData, 0, this, 0);
|
||||
if (d->copyDevice)
|
||||
disconnect(d->copyDevice, 0, this, 0);
|
||||
|
||||
QNetworkReply::close();
|
||||
|
||||
if (d->state != QNetworkReplyImplPrivate::Finished) {
|
||||
// call finished which will emit signals
|
||||
d->error(OperationCanceledError, tr("Operation canceled"));
|
||||
if (d->state == QNetworkReplyImplPrivate::WaitingForSession)
|
||||
d->state = QNetworkReplyImplPrivate::Working;
|
||||
d->finished();
|
||||
}
|
||||
d->state = QNetworkReplyImplPrivate::Aborted;
|
||||
|
||||
// finished may access the backend
|
||||
if (d->backend) {
|
||||
d->backend->deleteLater();
|
||||
d->backend = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::close()
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (d->state == QNetworkReplyImplPrivate::Aborted ||
|
||||
d->state == QNetworkReplyImplPrivate::Finished)
|
||||
return;
|
||||
|
||||
// stop the download
|
||||
if (d->backend)
|
||||
d->backend->closeDownstreamChannel();
|
||||
if (d->copyDevice)
|
||||
disconnect(d->copyDevice, 0, this, 0);
|
||||
|
||||
QNetworkReply::close();
|
||||
|
||||
// call finished which will emit signals
|
||||
d->error(OperationCanceledError, tr("Operation canceled"));
|
||||
d->finished();
|
||||
}
|
||||
|
||||
bool QNetworkReplyImpl::canReadLine () const
|
||||
{
|
||||
Q_D(const QNetworkReplyImpl);
|
||||
return QNetworkReply::canReadLine() || d->readBuffer.canReadLine();
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Returns the number of bytes available for reading with
|
||||
QIODevice::read(). The number of bytes available may grow until
|
||||
the finished() signal is emitted.
|
||||
*/
|
||||
qint64 QNetworkReplyImpl::bytesAvailable() const
|
||||
{
|
||||
// Special case for the "zero copy" download buffer
|
||||
Q_D(const QNetworkReplyImpl);
|
||||
if (d->downloadBuffer) {
|
||||
qint64 maxAvail = d->downloadBufferCurrentSize - d->downloadBufferReadPosition;
|
||||
return QNetworkReply::bytesAvailable() + maxAvail;
|
||||
}
|
||||
|
||||
return QNetworkReply::bytesAvailable() + d_func()->readBuffer.byteAmount();
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::setReadBufferSize(qint64 size)
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (size > d->readBufferMaxSize &&
|
||||
size > d->readBuffer.byteAmount())
|
||||
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
||||
|
||||
QNetworkReply::setReadBufferSize(size);
|
||||
|
||||
if (d->backend) {
|
||||
d->backend->setDownstreamLimited(d->readBufferMaxSize > 0);
|
||||
d->backend->setReadBufferSize(size);
|
||||
}
|
||||
}
|
||||
|
||||
QSslConfiguration QNetworkReplyImpl::sslConfiguration() const
|
||||
{
|
||||
Q_D(const QNetworkReplyImpl);
|
||||
QSslConfiguration config;
|
||||
if (d->backend)
|
||||
d->backend->fetchSslConfiguration(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::setSslConfiguration(const QSslConfiguration &config)
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (d->backend && !config.isNull())
|
||||
d->backend->setSslConfiguration(config);
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::ignoreSslErrors()
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (d->backend)
|
||||
d->backend->ignoreSslErrors();
|
||||
}
|
||||
|
||||
void QNetworkReplyImpl::ignoreSslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
if (d->backend)
|
||||
d->backend->ignoreSslErrors(errors);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QNetworkReplyImpl);
|
||||
|
||||
// Special case code if we have the "zero copy" download buffer
|
||||
if (d->downloadBuffer) {
|
||||
qint64 maxAvail = qMin<qint64>(d->downloadBufferCurrentSize - d->downloadBufferReadPosition, maxlen);
|
||||
if (maxAvail == 0)
|
||||
return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
|
||||
// FIXME what about "Aborted" state?
|
||||
memcpy(data, d->downloadBuffer + d->downloadBufferReadPosition, maxAvail);
|
||||
d->downloadBufferReadPosition += maxAvail;
|
||||
return maxAvail;
|
||||
}
|
||||
|
||||
|
||||
if (d->readBuffer.isEmpty())
|
||||
return d->state == QNetworkReplyImplPrivate::Finished ? -1 : 0;
|
||||
// FIXME what about "Aborted" state?
|
||||
|
||||
d->backendNotify(QNetworkReplyImplPrivate::NotifyDownstreamReadyWrite);
|
||||
if (maxlen == 1) {
|
||||
// optimization for getChar()
|
||||
*data = d->readBuffer.getChar();
|
||||
if (d->backend && readBufferSize())
|
||||
d->backend->emitReadBufferFreed(1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
maxlen = qMin<qint64>(maxlen, d->readBuffer.byteAmount());
|
||||
qint64 bytesRead = d->readBuffer.read(data, maxlen);
|
||||
if (d->backend && readBufferSize())
|
||||
d->backend->emitReadBufferFreed(bytesRead);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal Reimplemented for internal purposes
|
||||
*/
|
||||
bool QNetworkReplyImpl::event(QEvent *e)
|
||||
{
|
||||
if (e->type() == QEvent::NetworkReplyUpdated) {
|
||||
d_func()->handleNotifications();
|
||||
return true;
|
||||
}
|
||||
|
||||
return QObject::event(e);
|
||||
}
|
||||
|
||||
/*
|
||||
Migrates the backend of the QNetworkReply to a new network connection if required. Returns
|
||||
true if the reply is migrated or it is not required; otherwise returns false.
|
||||
*/
|
||||
bool QNetworkReplyImplPrivate::migrateBackend()
|
||||
{
|
||||
Q_Q(QNetworkReplyImpl);
|
||||
|
||||
// Network reply is already finished or aborted, don't need to migrate.
|
||||
if (state == Finished || state == Aborted)
|
||||
return true;
|
||||
|
||||
// Request has outgoing data, not migrating.
|
||||
if (outgoingData)
|
||||
return false;
|
||||
|
||||
// Request is serviced from the cache, don't need to migrate.
|
||||
if (copyDevice)
|
||||
return true;
|
||||
|
||||
// Backend does not support resuming download.
|
||||
if (backend && !backend->canResume())
|
||||
return false;
|
||||
|
||||
state = QNetworkReplyImplPrivate::Reconnecting;
|
||||
|
||||
if (backend) {
|
||||
delete backend;
|
||||
backend = 0;
|
||||
}
|
||||
|
||||
cookedHeaders.clear();
|
||||
rawHeaders.clear();
|
||||
|
||||
preMigrationDownloaded = bytesDownloaded;
|
||||
|
||||
backend = manager->d_func()->findBackend(operation, request);
|
||||
|
||||
if (backend) {
|
||||
backend->setParent(q);
|
||||
backend->reply = this;
|
||||
backend->setResumeOffset(bytesDownloaded);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
if (qobject_cast<QNetworkAccessHttpBackend *>(backend)) {
|
||||
_q_startOperation();
|
||||
} else {
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
}
|
||||
#else
|
||||
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
|
||||
#endif // QT_NO_HTTP
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qnetworkreplyimpl_p.h"
|
||||
|
||||
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREPLYIMPL_P_H
|
||||
#define QNETWORKREPLYIMPL_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkreply.h"
|
||||
#include "qnetworkreply_p.h"
|
||||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkproxy.h"
|
||||
#include "QtCore/qmap.h"
|
||||
#include "QtCore/qqueue.h"
|
||||
#include "QtCore/qbuffer.h"
|
||||
#include "qringbuffer_p.h"
|
||||
#include "qbytedata_p.h"
|
||||
#include <QSharedPointer>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAbstractNetworkCache;
|
||||
class QNetworkAccessBackend;
|
||||
|
||||
class QNetworkReplyImplPrivate;
|
||||
class QNetworkReplyImpl: public QNetworkReply
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QNetworkReplyImpl(QObject *parent = nullptr);
|
||||
~QNetworkReplyImpl();
|
||||
virtual void abort();
|
||||
|
||||
// reimplemented from QNetworkReply / QIODevice
|
||||
virtual void close();
|
||||
virtual qint64 bytesAvailable() const;
|
||||
virtual void setReadBufferSize(qint64 size);
|
||||
virtual bool canReadLine () const;
|
||||
|
||||
virtual qint64 readData(char *data, qint64 maxlen);
|
||||
virtual bool event(QEvent *);
|
||||
|
||||
virtual QSslConfiguration sslConfiguration() const;
|
||||
virtual void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
virtual void ignoreSslErrors();
|
||||
virtual void ignoreSslErrors(const QList<QSslError> &errors);
|
||||
|
||||
Q_DECLARE_PRIVATE(QNetworkReplyImpl)
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_startOperation())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_copyReadyRead())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_copyReadChannelFinished())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingData())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_bufferOutgoingDataFinished())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_cacheDestroyed())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose())
|
||||
};
|
||||
|
||||
class QNetworkReplyImplPrivate: public QNetworkReplyPrivate
|
||||
{
|
||||
public:
|
||||
enum InternalNotifications {
|
||||
NotifyDownstreamReadyWrite,
|
||||
NotifyCloseDownstreamChannel,
|
||||
NotifyCopyFinished
|
||||
};
|
||||
|
||||
enum State {
|
||||
Idle, // The reply is idle.
|
||||
Buffering, // The reply is buffering outgoing data.
|
||||
Working, // The reply is uploading/downloading data.
|
||||
Finished, // The reply has finished.
|
||||
Aborted, // The reply has been aborted.
|
||||
WaitingForSession, // The reply is waiting for the session to open before connecting.
|
||||
Reconnecting // The reply will reconnect to once roaming has completed.
|
||||
};
|
||||
|
||||
typedef QQueue<InternalNotifications> NotificationQueue;
|
||||
|
||||
QNetworkReplyImplPrivate();
|
||||
|
||||
void _q_startOperation();
|
||||
void _q_sourceReadyRead();
|
||||
void _q_sourceReadChannelFinished();
|
||||
void _q_copyReadyRead();
|
||||
void _q_copyReadChannelFinished();
|
||||
void _q_bufferOutgoingData();
|
||||
void _q_bufferOutgoingDataFinished();
|
||||
void _q_cacheDestroyed();
|
||||
void _q_cacheSaveDeviceAboutToClose();
|
||||
|
||||
void setup(QNetworkAccessManager::Operation op, const QNetworkRequest &request,
|
||||
QIODevice *outgoingData);
|
||||
|
||||
void pauseNotificationHandling();
|
||||
void resumeNotificationHandling();
|
||||
void backendNotify(InternalNotifications notification);
|
||||
void handleNotifications();
|
||||
void createCache();
|
||||
void completeCacheSave();
|
||||
|
||||
// callbacks from the backend (through the manager):
|
||||
void setCachingEnabled(bool enable);
|
||||
bool isCachingEnabled() const;
|
||||
void consume(qint64 count);
|
||||
void emitUploadProgress(qint64 bytesSent, qint64 bytesTotal);
|
||||
qint64 nextDownstreamBlockSize() const;
|
||||
|
||||
void initCacheSaveDevice();
|
||||
void appendDownstreamDataSignalEmissions();
|
||||
void appendDownstreamData(QByteDataBuffer &data);
|
||||
void appendDownstreamData(QIODevice *data);
|
||||
void appendDownstreamData(const QByteArray &data);
|
||||
|
||||
void setDownloadBuffer(QSharedPointer<char> sp, qint64 size);
|
||||
char* getDownloadBuffer(qint64 size);
|
||||
void appendDownstreamDataDownloadBuffer(qint64, qint64);
|
||||
|
||||
void finished();
|
||||
void error(QNetworkReply::NetworkError code, const QString &errorString);
|
||||
void metaDataChanged();
|
||||
void redirectionRequested(const QUrl &target);
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
|
||||
QNetworkAccessBackend *backend;
|
||||
QIODevice *outgoingData;
|
||||
QSharedPointer<QRingBuffer> outgoingDataBuffer;
|
||||
QIODevice *copyDevice;
|
||||
QAbstractNetworkCache *networkCache() const;
|
||||
|
||||
bool migrateBackend();
|
||||
|
||||
bool cacheEnabled;
|
||||
QIODevice *cacheSaveDevice;
|
||||
|
||||
NotificationQueue pendingNotifications;
|
||||
bool notificationHandlingPaused;
|
||||
|
||||
QUrl urlForLastAuthentication;
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
QNetworkProxy lastProxyAuthentication;
|
||||
QList<QNetworkProxy> proxyList;
|
||||
#endif
|
||||
|
||||
// Used for normal downloading. For "zero copy" the downloadBuffer is used
|
||||
QByteDataBuffer readBuffer;
|
||||
qint64 bytesDownloaded;
|
||||
qint64 lastBytesDownloaded;
|
||||
qint64 bytesUploaded;
|
||||
qint64 preMigrationDownloaded;
|
||||
|
||||
QString httpReasonPhrase;
|
||||
int httpStatusCode;
|
||||
|
||||
State state;
|
||||
|
||||
// only used when the "zero copy" style is used. Else readBuffer is used.
|
||||
// Please note that the whole "zero copy" download buffer API is private right now. Do not use it.
|
||||
qint64 downloadBufferReadPosition;
|
||||
qint64 downloadBufferCurrentSize;
|
||||
qint64 downloadBufferMaximumSize;
|
||||
QSharedPointer<char> downloadBufferPointer;
|
||||
char* downloadBuffer;
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkReplyImpl)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
|
@ -1,994 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qplatformdefs.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "qnetworkcookie.h"
|
||||
#include "qnetworkrequest_p.h"
|
||||
#include "qsslconfiguration.h"
|
||||
#include "qnetworkcommon_p.h"
|
||||
#include "QtCore/qshareddata.h"
|
||||
#include "QtCore/qlocale.h"
|
||||
#include "QtCore/qdatetime.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#ifndef QT_NO_DATESTRING
|
||||
# include <stdio.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
/*!
|
||||
\class QNetworkRequest
|
||||
\brief The QNetworkRequest class holds a request to be sent with QNetworkAccessManager.
|
||||
\since 4.4
|
||||
|
||||
\ingroup network
|
||||
\inmodule QtNetwork
|
||||
|
||||
QNetworkRequest is part of the Network Access API and is the class
|
||||
holding the information necessary to send a request over the
|
||||
network. It contains a URL and some ancillary information that can
|
||||
be used to modify the request.
|
||||
|
||||
\sa QNetworkReply, QNetworkAccessManager
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkRequest::KnownHeaders
|
||||
|
||||
List of known header types that QNetworkRequest parses. Each known
|
||||
header is also represented in raw form with its full HTTP name.
|
||||
|
||||
\value ContentTypeHeader corresponds to the HTTP Content-Type
|
||||
header and contains a string containing the media (MIME) type and
|
||||
any auxiliary data (for instance, charset)
|
||||
|
||||
\value ContentLengthHeader corresponds to the HTTP Content-Length
|
||||
header and contains the length in bytes of the data transmitted.
|
||||
|
||||
\value LocationHeader corresponds to the HTTP Location
|
||||
header and contains a URL representing the actual location of the
|
||||
data, including the destination URL in case of redirections.
|
||||
|
||||
\value LastModifiedHeader corresponds to the HTTP Last-Modified
|
||||
header and contains a QDateTime representing the last modification
|
||||
date of the contents
|
||||
|
||||
\value CookieHeader corresponds to the HTTP Cookie header
|
||||
and contains a QList<QNetworkCookie> representing the cookies to
|
||||
be sent back to the server
|
||||
|
||||
\value SetCookieHeader corresponds to the HTTP Set-Cookie
|
||||
header and contains a QList<QNetworkCookie> representing the
|
||||
cookies sent by the server to be stored locally
|
||||
|
||||
\sa header(), setHeader(), rawHeader(), setRawHeader()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkRequest::Attribute
|
||||
\since 4.7
|
||||
|
||||
Attribute codes for the QNetworkRequest and QNetworkReply.
|
||||
|
||||
Attributes are extra meta-data that are used to control the
|
||||
behavior of the request and to pass further information from the
|
||||
reply back to the application. Attributes are also extensible,
|
||||
allowing custom implementations to pass custom values.
|
||||
|
||||
The following table explains what the default attribute codes are,
|
||||
the QVariant types associated, the default value if said attribute
|
||||
is missing and whether it's used in requests or replies.
|
||||
|
||||
\value HttpStatusCodeAttribute
|
||||
Replies only, type: QVariant::Int (no default)
|
||||
Indicates the HTTP status code received from the HTTP server
|
||||
(like 200, 304, 404, 401, etc.). If the connection was not
|
||||
HTTP-based, this attribute will not be present.
|
||||
|
||||
\value HttpReasonPhraseAttribute
|
||||
Replies only, type: QVariant::ByteArray (no default)
|
||||
Indicates the HTTP reason phrase as received from the HTTP
|
||||
server (like "Ok", "Found", "Not Found", "Access Denied",
|
||||
etc.) This is the human-readable representation of the status
|
||||
code (see above). If the connection was not HTTP-based, this
|
||||
attribute will not be present.
|
||||
|
||||
\value RedirectionTargetAttribute
|
||||
Replies only, type: QVariant::Url (no default)
|
||||
If present, it indicates that the server is redirecting the
|
||||
request to a different URL. The Network Access API does not by
|
||||
default follow redirections: it's up to the application to
|
||||
determine if the requested redirection should be allowed,
|
||||
according to its security policies.
|
||||
The returned URL might be relative. Use QUrl::resolved()
|
||||
to create an absolute URL out of it.
|
||||
|
||||
\value ConnectionEncryptedAttribute
|
||||
Replies only, type: QVariant::Bool (default: false)
|
||||
Indicates whether the data was obtained through an encrypted
|
||||
(secure) connection.
|
||||
|
||||
\value CacheLoadControlAttribute
|
||||
Requests only, type: QVariant::Int (default: QNetworkRequest::PreferNetwork)
|
||||
Controls how the cache should be accessed. The possible values
|
||||
are those of QNetworkRequest::CacheLoadControl. Note that the
|
||||
default QNetworkAccessManager implementation does not support
|
||||
caching. However, this attribute may be used by certain
|
||||
backends to modify their requests (for example, for caching proxies).
|
||||
|
||||
\value CacheSaveControlAttribute
|
||||
Requests only, type: QVariant::Bool (default: true)
|
||||
Controls if the data obtained should be saved to cache for
|
||||
future uses. If the value is false, the data obtained will not
|
||||
be automatically cached. If true, data may be cached, provided
|
||||
it is cacheable (what is cacheable depends on the protocol
|
||||
being used).
|
||||
|
||||
\value SourceIsFromCacheAttribute
|
||||
Replies only, type: QVariant::Bool (default: false)
|
||||
Indicates whether the data was obtained from cache
|
||||
or not.
|
||||
|
||||
\value DoNotBufferUploadDataAttribute
|
||||
Requests only, type: QVariant::Bool (default: false)
|
||||
Indicates whether the QNetworkAccessManager code is
|
||||
allowed to buffer the upload data, e.g. when doing a HTTP POST.
|
||||
When using this flag with sequential upload data, the ContentLengthHeader
|
||||
header must be set.
|
||||
|
||||
\value HttpPipeliningAllowedAttribute
|
||||
Requests only, type: QVariant::Bool (default: false)
|
||||
Indicates whether the QNetworkAccessManager code is
|
||||
allowed to use HTTP pipelining with this request.
|
||||
|
||||
\value HttpPipeliningWasUsedAttribute
|
||||
Replies only, type: QVariant::Bool
|
||||
Indicates whether the HTTP pipelining was used for receiving
|
||||
this reply.
|
||||
|
||||
\value CustomVerbAttribute
|
||||
Requests only, type: QVariant::ByteArray
|
||||
Holds the value for the custom HTTP verb to send (destined for usage
|
||||
of other verbs than GET, POST, PUT and DELETE). This verb is set
|
||||
when calling QNetworkAccessManager::sendCustomRequest().
|
||||
|
||||
\value CookieLoadControlAttribute
|
||||
Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic)
|
||||
Indicates whether to send 'Cookie' headers in the request.
|
||||
This attribute is set to false by QtWebKit when creating a cross-origin
|
||||
XMLHttpRequest where withCredentials has not been set explicitly to true by the
|
||||
Javascript that created the request.
|
||||
See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag}{here} for more information.
|
||||
(This value was introduced in 4.7.)
|
||||
|
||||
\value CookieSaveControlAttribute
|
||||
Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic)
|
||||
Indicates whether to save 'Cookie' headers received from the server in reply
|
||||
to the request.
|
||||
This attribute is set to false by QtWebKit when creating a cross-origin
|
||||
XMLHttpRequest where withCredentials has not been set explicitly to true by the
|
||||
Javascript that created the request.
|
||||
See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
|
||||
(This value was introduced in 4.7.)
|
||||
|
||||
\value AuthenticationReuseAttribute
|
||||
Requests only, type: QVariant::Int (default: QNetworkRequest::Automatic)
|
||||
Indicates whether to use cached authorization credentials in the request,
|
||||
if available. If this is set to QNetworkRequest::Manual and the authentication
|
||||
mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP
|
||||
header with any cached credentials it may have for the request's URL.
|
||||
This attribute is set to QNetworkRequest::Manual by QtWebKit when creating a cross-origin
|
||||
XMLHttpRequest where withCredentials has not been set explicitly to true by the
|
||||
Javascript that created the request.
|
||||
See \l{http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag} {here} for more information.
|
||||
(This value was introduced in 4.7.)
|
||||
|
||||
\omitvalue MaximumDownloadBufferSizeAttribute
|
||||
|
||||
\omitvalue DownloadBufferAttribute
|
||||
|
||||
\omitvalue SynchronousRequestAttribute
|
||||
|
||||
\value User
|
||||
Special type. Additional information can be passed in
|
||||
QVariants with types ranging from User to UserMax. The default
|
||||
implementation of Network Access will ignore any request
|
||||
attributes in this range and it will not produce any
|
||||
attributes in this range in replies. The range is reserved for
|
||||
extensions of QNetworkAccessManager.
|
||||
|
||||
\value UserMax
|
||||
Special type. See User.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkRequest::CacheLoadControl
|
||||
|
||||
Controls the caching mechanism of QNetworkAccessManager.
|
||||
|
||||
\value AlwaysNetwork always load from network and do not
|
||||
check if the cache has a valid entry (similar to the
|
||||
"Reload" feature in browsers)
|
||||
|
||||
\value PreferNetwork default value; load from the network
|
||||
if the cached entry is older than the network entry
|
||||
|
||||
\value PreferCache load from cache if available,
|
||||
otherwise load from network. Note that this can return possibly
|
||||
stale (but not expired) items from cache.
|
||||
|
||||
\value AlwaysCache only load from cache, indicating error
|
||||
if the item was not cached (i.e., off-line mode)
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkRequest::LoadControl
|
||||
\since 4.7
|
||||
|
||||
Indicates if an aspect of the request's loading mechanism has been
|
||||
manually overridden, e.g. by QtWebKit.
|
||||
|
||||
\value Automatic default value: indicates default behaviour.
|
||||
|
||||
\value Manual indicates behaviour has been manually overridden.
|
||||
*/
|
||||
|
||||
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
|
||||
{
|
||||
public:
|
||||
inline QNetworkRequestPrivate()
|
||||
: priority(QNetworkRequest::NormalPriority)
|
||||
, sslConfiguration(0)
|
||||
{ qRegisterMetaType<QNetworkRequest>(); }
|
||||
~QNetworkRequestPrivate()
|
||||
{
|
||||
delete sslConfiguration;
|
||||
}
|
||||
|
||||
|
||||
QNetworkRequestPrivate(const QNetworkRequestPrivate &other)
|
||||
: QSharedData(other), QNetworkHeadersPrivate(other)
|
||||
{
|
||||
url = other.url;
|
||||
priority = other.priority;
|
||||
|
||||
sslConfiguration = 0;
|
||||
if (other.sslConfiguration)
|
||||
sslConfiguration = new QSslConfiguration(*other.sslConfiguration);
|
||||
}
|
||||
|
||||
inline bool operator==(const QNetworkRequestPrivate &other) const
|
||||
{
|
||||
return url == other.url &&
|
||||
priority == other.priority &&
|
||||
rawHeaders == other.rawHeaders &&
|
||||
attributes == other.attributes;
|
||||
// don't compare cookedHeaders
|
||||
}
|
||||
|
||||
QUrl url;
|
||||
QNetworkRequest::Priority priority;
|
||||
mutable QSslConfiguration *sslConfiguration;
|
||||
};
|
||||
|
||||
/*!
|
||||
Constructs a QNetworkRequest object with \a url as the URL to be
|
||||
requested.
|
||||
|
||||
\sa url(), setUrl()
|
||||
*/
|
||||
QNetworkRequest::QNetworkRequest(const QUrl &url)
|
||||
: d(new QNetworkRequestPrivate)
|
||||
{
|
||||
d->url = url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Creates a copy of \a other.
|
||||
*/
|
||||
QNetworkRequest::QNetworkRequest(const QNetworkRequest &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Disposes of the QNetworkRequest object.
|
||||
*/
|
||||
QNetworkRequest::~QNetworkRequest()
|
||||
{
|
||||
// QSharedDataPointer auto deletes
|
||||
d = 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if this object is the same as \a other (i.e., if they
|
||||
have the same URL, same headers and same meta-data settings).
|
||||
|
||||
\sa operator!=()
|
||||
*/
|
||||
bool QNetworkRequest::operator==(const QNetworkRequest &other) const
|
||||
{
|
||||
return d == other.d || *d == *other.d;
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn bool QNetworkRequest::operator!=(const QNetworkRequest &other) const
|
||||
|
||||
Returns false if this object is not the same as \a other.
|
||||
|
||||
\sa operator==()
|
||||
*/
|
||||
|
||||
/*!
|
||||
Creates a copy of \a other
|
||||
*/
|
||||
QNetworkRequest &QNetworkRequest::operator=(const QNetworkRequest &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the URL this network request is referring to.
|
||||
|
||||
\sa setUrl()
|
||||
*/
|
||||
QUrl QNetworkRequest::url() const
|
||||
{
|
||||
return d->url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the URL this network request is referring to to be \a url.
|
||||
|
||||
\sa url()
|
||||
*/
|
||||
void QNetworkRequest::setUrl(const QUrl &url)
|
||||
{
|
||||
d->url = url;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the value of the known network header \a header if it is
|
||||
present in this request. If it is not present, returns QVariant()
|
||||
(i.e., an invalid variant).
|
||||
|
||||
\sa KnownHeaders, rawHeader(), setHeader()
|
||||
*/
|
||||
QVariant QNetworkRequest::header(KnownHeaders header) const
|
||||
{
|
||||
return d->cookedHeaders.value(header);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the value of the known header \a header to be \a value,
|
||||
overriding any previously set headers. This operation also sets
|
||||
the equivalent raw HTTP header.
|
||||
|
||||
\sa KnownHeaders, setRawHeader(), header()
|
||||
*/
|
||||
void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
d->setCookedHeader(header, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the raw header \a headerName is present in this
|
||||
network request.
|
||||
|
||||
\sa rawHeader(), setRawHeader()
|
||||
*/
|
||||
bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the raw form of header \a headerName. If no such header is
|
||||
present, an empty QByteArray is returned, which may be
|
||||
indistinguishable from a header that is present but has no content
|
||||
(use hasRawHeader() to find out if the header exists or not).
|
||||
|
||||
Raw headers can be set with setRawHeader() or with setHeader().
|
||||
|
||||
\sa header(), setRawHeader()
|
||||
*/
|
||||
QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
|
||||
{
|
||||
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
|
||||
d->findRawHeader(headerName);
|
||||
if (it != d->rawHeaders.constEnd())
|
||||
return it->second;
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a list of all raw headers that are set in this network
|
||||
request. The list is in the order that the headers were set.
|
||||
|
||||
\sa hasRawHeader(), rawHeader()
|
||||
*/
|
||||
QList<QByteArray> QNetworkRequest::rawHeaderList() const
|
||||
{
|
||||
return d->rawHeadersKeys();
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the header \a headerName to be of value \a headerValue. If \a
|
||||
headerName corresponds to a known header (see
|
||||
QNetworkRequest::KnownHeaders), the raw format will be parsed and
|
||||
the corresponding "cooked" header will be set as well.
|
||||
|
||||
For example:
|
||||
\snippet doc/src/snippets/code/src_network_access_qnetworkrequest.cpp 0
|
||||
|
||||
will also set the known header LastModifiedHeader to be the
|
||||
QDateTime object of the parsed date.
|
||||
|
||||
Note: setting the same header twice overrides the previous
|
||||
setting. To accomplish the behaviour of multiple HTTP headers of
|
||||
the same name, you should concatenate the two values, separating
|
||||
them with a comma (",") and set one single raw header.
|
||||
|
||||
\sa KnownHeaders, setHeader(), hasRawHeader(), rawHeader()
|
||||
*/
|
||||
void QNetworkRequest::setRawHeader(const QByteArray &headerName, const QByteArray &headerValue)
|
||||
{
|
||||
d->setRawHeader(headerName, headerValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the attribute associated with the code \a code. If the
|
||||
attribute has not been set, it returns \a defaultValue.
|
||||
|
||||
Note: this function does not apply the defaults listed in
|
||||
QNetworkRequest::Attribute.
|
||||
|
||||
\sa setAttribute(), QNetworkRequest::Attribute
|
||||
*/
|
||||
QVariant QNetworkRequest::attribute(Attribute code, const QVariant &defaultValue) const
|
||||
{
|
||||
return d->attributes.value(code, defaultValue);
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the attribute associated with code \a code to be value \a
|
||||
value. If the attribute is already set, the previous value is
|
||||
discarded. In special, if \a value is an invalid QVariant, the
|
||||
attribute is unset.
|
||||
|
||||
\sa attribute(), QNetworkRequest::Attribute
|
||||
*/
|
||||
void QNetworkRequest::setAttribute(Attribute code, const QVariant &value)
|
||||
{
|
||||
if (value.isValid())
|
||||
d->attributes.insert(code, value);
|
||||
else
|
||||
d->attributes.remove(code);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns this network request's SSL configuration. By default, no
|
||||
SSL settings are specified.
|
||||
|
||||
\sa setSslConfiguration()
|
||||
*/
|
||||
QSslConfiguration QNetworkRequest::sslConfiguration() const
|
||||
{
|
||||
if (!d->sslConfiguration)
|
||||
d->sslConfiguration = new QSslConfiguration(QSslConfiguration::defaultConfiguration());
|
||||
return *d->sslConfiguration;
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets this network request's SSL configuration to be \a config. The
|
||||
settings that apply are the private key, the local certificate,
|
||||
the SSL protocol (SSLv2, SSLv3, TLSv1 where applicable), the CA
|
||||
certificates and the ciphers that the SSL backend is allowed to
|
||||
use.
|
||||
|
||||
By default, no SSL configuration is set, which allows the backends
|
||||
to choose freely what configuration is best for them.
|
||||
|
||||
\sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
|
||||
*/
|
||||
void QNetworkRequest::setSslConfiguration(const QSslConfiguration &config)
|
||||
{
|
||||
if (!d->sslConfiguration)
|
||||
d->sslConfiguration = new QSslConfiguration(config);
|
||||
else
|
||||
*d->sslConfiguration = config;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Allows setting a reference to the \a object initiating
|
||||
the request.
|
||||
|
||||
For example QtWebKit sets the originating object to the
|
||||
QWebFrame that initiated the request.
|
||||
|
||||
\sa originatingObject()
|
||||
*/
|
||||
void QNetworkRequest::setOriginatingObject(QObject *object)
|
||||
{
|
||||
d->originatingObject = object;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.6
|
||||
|
||||
Returns a reference to the object that initiated this
|
||||
network request; returns 0 if not set or the object has
|
||||
been destroyed.
|
||||
|
||||
\sa setOriginatingObject()
|
||||
*/
|
||||
QObject *QNetworkRequest::originatingObject() const
|
||||
{
|
||||
return d->originatingObject.data();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.7
|
||||
|
||||
Return the priority of this request.
|
||||
|
||||
\sa setPriority()
|
||||
*/
|
||||
QNetworkRequest::Priority QNetworkRequest::priority() const
|
||||
{
|
||||
return d->priority;
|
||||
}
|
||||
|
||||
/*! \enum QNetworkRequest::Priority
|
||||
|
||||
\since 4.7
|
||||
|
||||
This enum lists the possible network request priorities.
|
||||
|
||||
\value HighPriority High priority
|
||||
\value NormalPriority Normal priority
|
||||
\value LowPriority Low priority
|
||||
*/
|
||||
|
||||
/*!
|
||||
\since 4.7
|
||||
|
||||
Set the priority of this request to \a priority.
|
||||
|
||||
\note The \a priority is only a hint to the network access
|
||||
manager. It can use it or not. Currently it is used for HTTP to
|
||||
decide which request should be sent first to a server.
|
||||
|
||||
\sa priority()
|
||||
*/
|
||||
void QNetworkRequest::setPriority(Priority priority)
|
||||
{
|
||||
d->priority = priority;
|
||||
}
|
||||
|
||||
static QByteArray headerName(QNetworkRequest::KnownHeaders header)
|
||||
{
|
||||
switch (header) {
|
||||
case QNetworkRequest::ContentTypeHeader:
|
||||
return "Content-Type";
|
||||
|
||||
case QNetworkRequest::ContentLengthHeader:
|
||||
return "Content-Length";
|
||||
|
||||
case QNetworkRequest::LocationHeader:
|
||||
return "Location";
|
||||
|
||||
case QNetworkRequest::LastModifiedHeader:
|
||||
return "Last-Modified";
|
||||
|
||||
case QNetworkRequest::CookieHeader:
|
||||
return "Cookie";
|
||||
|
||||
case QNetworkRequest::SetCookieHeader:
|
||||
return "Set-Cookie";
|
||||
|
||||
case QNetworkRequest::ContentDispositionHeader:
|
||||
return "Content-Disposition";
|
||||
|
||||
// no default:
|
||||
// if new values are added, this will generate a compiler warning
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
|
||||
{
|
||||
switch (header) {
|
||||
case QNetworkRequest::ContentTypeHeader:
|
||||
case QNetworkRequest::ContentLengthHeader:
|
||||
case QNetworkRequest::ContentDispositionHeader:
|
||||
return value.toByteArray();
|
||||
|
||||
case QNetworkRequest::LocationHeader:
|
||||
switch (value.type()) {
|
||||
case QVariant::Url:
|
||||
return value.toUrl().toEncoded();
|
||||
|
||||
default:
|
||||
return value.toByteArray();
|
||||
}
|
||||
|
||||
case QNetworkRequest::LastModifiedHeader:
|
||||
switch (value.type()) {
|
||||
case QVariant::Date:
|
||||
case QVariant::DateTime:
|
||||
// generate RFC 1123/822 dates:
|
||||
return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
|
||||
|
||||
default:
|
||||
return value.toByteArray();
|
||||
}
|
||||
|
||||
case QNetworkRequest::CookieHeader: {
|
||||
QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
|
||||
if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
|
||||
cookies << qvariant_cast<QNetworkCookie>(value);
|
||||
|
||||
QByteArray result;
|
||||
bool first = true;
|
||||
foreach (const QNetworkCookie &cookie, cookies) {
|
||||
if (!first)
|
||||
result += "; ";
|
||||
first = false;
|
||||
result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
case QNetworkRequest::SetCookieHeader: {
|
||||
QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
|
||||
if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
|
||||
cookies << qvariant_cast<QNetworkCookie>(value);
|
||||
|
||||
QByteArray result;
|
||||
bool first = true;
|
||||
foreach (const QNetworkCookie &cookie, cookies) {
|
||||
if (!first)
|
||||
result += ", ";
|
||||
first = false;
|
||||
result += cookie.toRawForm(QNetworkCookie::Full);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
static QNetworkRequest::KnownHeaders parseHeaderName(const QByteArray &headerName)
|
||||
{
|
||||
if (headerName.isEmpty())
|
||||
return QNetworkRequest::KnownHeaders(-1);
|
||||
|
||||
switch (tolower(headerName.at(0))) {
|
||||
case 'c':
|
||||
if (qstricmp(headerName.constData(), "content-type") == 0)
|
||||
return QNetworkRequest::ContentTypeHeader;
|
||||
else if (qstricmp(headerName.constData(), "content-length") == 0)
|
||||
return QNetworkRequest::ContentLengthHeader;
|
||||
else if (qstricmp(headerName.constData(), "cookie") == 0)
|
||||
return QNetworkRequest::CookieHeader;
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
if (qstricmp(headerName.constData(), "location") == 0)
|
||||
return QNetworkRequest::LocationHeader;
|
||||
else if (qstricmp(headerName.constData(), "last-modified") == 0)
|
||||
return QNetworkRequest::LastModifiedHeader;
|
||||
break;
|
||||
|
||||
case 's':
|
||||
if (qstricmp(headerName.constData(), "set-cookie") == 0)
|
||||
return QNetworkRequest::SetCookieHeader;
|
||||
break;
|
||||
}
|
||||
|
||||
return QNetworkRequest::KnownHeaders(-1); // nothing found
|
||||
}
|
||||
|
||||
static QVariant parseHttpDate(const QByteArray &raw)
|
||||
{
|
||||
QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
|
||||
if (dt.isValid())
|
||||
return dt;
|
||||
return QVariant(); // transform an invalid QDateTime into a null QVariant
|
||||
}
|
||||
|
||||
static QVariant parseCookieHeader(const QByteArray &raw)
|
||||
{
|
||||
QList<QNetworkCookie> result;
|
||||
QList<QByteArray> cookieList = raw.split(';');
|
||||
foreach (const QByteArray &cookie, cookieList) {
|
||||
QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
|
||||
if (parsed.count() != 1)
|
||||
return QVariant(); // invalid Cookie: header
|
||||
|
||||
result += parsed;
|
||||
}
|
||||
|
||||
return QVariant::fromValue(result);
|
||||
}
|
||||
|
||||
static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
|
||||
{
|
||||
// header is always a valid value
|
||||
switch (header) {
|
||||
case QNetworkRequest::ContentTypeHeader:
|
||||
// copy exactly, convert to QString
|
||||
return QString::fromLatin1(value);
|
||||
|
||||
case QNetworkRequest::ContentLengthHeader: {
|
||||
bool ok;
|
||||
qint64 result = value.trimmed().toLongLong(&ok);
|
||||
if (ok)
|
||||
return result;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case QNetworkRequest::LocationHeader: {
|
||||
QUrl result = QUrl::fromEncoded(value, QUrl::StrictMode);
|
||||
if (result.isValid() && !result.scheme().isEmpty())
|
||||
return result;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
case QNetworkRequest::LastModifiedHeader:
|
||||
return parseHttpDate(value);
|
||||
|
||||
case QNetworkRequest::CookieHeader:
|
||||
return parseCookieHeader(value);
|
||||
|
||||
case QNetworkRequest::SetCookieHeader:
|
||||
return QVariant::fromValue(QNetworkCookie::parseCookies(value));
|
||||
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QNetworkHeadersPrivate::RawHeadersList::ConstIterator
|
||||
QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
|
||||
{
|
||||
RawHeadersList::ConstIterator it = rawHeaders.constBegin();
|
||||
RawHeadersList::ConstIterator end = rawHeaders.constEnd();
|
||||
for ( ; it != end; ++it)
|
||||
if (qstricmp(it->first.constData(), key.constData()) == 0)
|
||||
return it;
|
||||
|
||||
return end; // not found
|
||||
}
|
||||
|
||||
QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
|
||||
{
|
||||
return rawHeaders;
|
||||
}
|
||||
|
||||
QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
|
||||
{
|
||||
QList<QByteArray> result;
|
||||
RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
|
||||
end = rawHeaders.constEnd();
|
||||
for ( ; it != end; ++it)
|
||||
result << it->first;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArray &value)
|
||||
{
|
||||
if (key.isEmpty())
|
||||
// refuse to accept an empty raw header
|
||||
return;
|
||||
|
||||
setRawHeaderInternal(key, value);
|
||||
parseAndSetHeader(key, value);
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
Sets the internal raw headers list to match \a list. The cooked headers
|
||||
will also be updated.
|
||||
|
||||
If \a list contains duplicates, they will be stored, but only the first one
|
||||
is usually accessed.
|
||||
*/
|
||||
void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
|
||||
{
|
||||
cookedHeaders.clear();
|
||||
rawHeaders = list;
|
||||
|
||||
RawHeadersList::ConstIterator it = rawHeaders.constBegin();
|
||||
RawHeadersList::ConstIterator end = rawHeaders.constEnd();
|
||||
for ( ; it != end; ++it)
|
||||
parseAndSetHeader(it->first, it->second);
|
||||
}
|
||||
|
||||
void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
|
||||
const QVariant &value)
|
||||
{
|
||||
QByteArray name = headerName(header);
|
||||
if (name.isEmpty()) {
|
||||
// headerName verifies that \a header is a known value
|
||||
qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value.isNull()) {
|
||||
setRawHeaderInternal(name, QByteArray());
|
||||
cookedHeaders.remove(header);
|
||||
} else {
|
||||
QByteArray rawValue = headerValue(header, value);
|
||||
if (rawValue.isEmpty()) {
|
||||
qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
|
||||
value.typeName(), name.constData());
|
||||
return;
|
||||
}
|
||||
|
||||
setRawHeaderInternal(name, rawValue);
|
||||
cookedHeaders.insert(header, value);
|
||||
}
|
||||
}
|
||||
|
||||
void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
|
||||
{
|
||||
RawHeadersList::Iterator it = rawHeaders.begin();
|
||||
while (it != rawHeaders.end()) {
|
||||
if (qstricmp(it->first.constData(), key.constData()) == 0)
|
||||
it = rawHeaders.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
if (value.isNull())
|
||||
return; // only wanted to erase key
|
||||
|
||||
RawHeaderPair pair;
|
||||
pair.first = key;
|
||||
pair.second = value;
|
||||
rawHeaders.append(pair);
|
||||
}
|
||||
|
||||
void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value)
|
||||
{
|
||||
// is it a known header?
|
||||
QNetworkRequest::KnownHeaders parsedKey = parseHeaderName(key);
|
||||
if (parsedKey != QNetworkRequest::KnownHeaders(-1)) {
|
||||
if (value.isNull()) {
|
||||
cookedHeaders.remove(parsedKey);
|
||||
} else if (parsedKey == QNetworkRequest::ContentLengthHeader
|
||||
&& cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
|
||||
// Only set the cooked header "Content-Length" once.
|
||||
// See bug QTBUG-15311
|
||||
} else {
|
||||
cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Fast month string to int conversion. This code
|
||||
// assumes that the Month name is correct and that
|
||||
// the string is at least three chars long.
|
||||
static int name_to_month(const char* month_str)
|
||||
{
|
||||
switch (month_str[0]) {
|
||||
case 'J':
|
||||
switch (month_str[1]) {
|
||||
case 'a':
|
||||
return 1;
|
||||
case 'u':
|
||||
switch (month_str[2] ) {
|
||||
case 'n':
|
||||
return 6;
|
||||
case 'l':
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
return 2;
|
||||
case 'M':
|
||||
switch (month_str[2] ) {
|
||||
case 'r':
|
||||
return 3;
|
||||
case 'y':
|
||||
return 5;
|
||||
}
|
||||
break;
|
||||
case 'A':
|
||||
switch (month_str[1]) {
|
||||
case 'p':
|
||||
return 4;
|
||||
case 'u':
|
||||
return 8;
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
return 10;
|
||||
case 'S':
|
||||
return 9;
|
||||
case 'N':
|
||||
return 11;
|
||||
case 'D':
|
||||
return 12;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
|
||||
{
|
||||
// HTTP dates have three possible formats:
|
||||
// RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
|
||||
// RFC 850 - dddd, dd-MMM-yy hh:mm:ss "GMT"
|
||||
// ANSI C's asctime - ddd MMM d hh:mm:ss yyyy
|
||||
// We only handle them exactly. If they deviate, we bail out.
|
||||
|
||||
int pos = value.indexOf(',');
|
||||
QDateTime dt;
|
||||
#ifndef QT_NO_DATESTRING
|
||||
if (pos == -1) {
|
||||
// no comma -> asctime(3) format
|
||||
dt = QDateTime::fromString(QString::fromLatin1(value), Qt::TextDate);
|
||||
} else {
|
||||
// Use sscanf over QLocal/QDateTimeParser for speed reasons. See the
|
||||
// QtWebKit performance benchmarks to get an idea.
|
||||
if (pos == 3) {
|
||||
char month_name[4];
|
||||
int day, year, hour, minute, second;
|
||||
if (sscanf(value.constData(), "%*3s, %d %3s %d %d:%d:%d 'GMT'", &day, month_name, &year, &hour, &minute, &second) == 6)
|
||||
dt = QDateTime(QDate(year, name_to_month(month_name), day), QTime(hour, minute, second));
|
||||
} else {
|
||||
QLocale c = QLocale::c();
|
||||
// eat the weekday, the comma and the space following it
|
||||
QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
|
||||
// must be RFC 850 date
|
||||
dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
|
||||
}
|
||||
}
|
||||
#endif // QT_NO_DATESTRING
|
||||
|
||||
if (dt.isValid())
|
||||
dt.setTimeSpec(Qt::UTC);
|
||||
return dt;
|
||||
}
|
||||
|
||||
QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
|
||||
{
|
||||
return QLocale::c().toString(dt, QLatin1String("ddd, dd MMM yyyy hh:mm:ss 'GMT'"))
|
||||
.toLatin1();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREQUEST_H
|
||||
#define QNETWORKREQUEST_H
|
||||
|
||||
#include <QtCore/QSharedData>
|
||||
#include <QtCore/QString>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QVariant>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QSslConfiguration;
|
||||
|
||||
class QNetworkRequestPrivate;
|
||||
class Q_NETWORK_EXPORT QNetworkRequest
|
||||
{
|
||||
public:
|
||||
enum KnownHeaders {
|
||||
ContentTypeHeader,
|
||||
ContentLengthHeader,
|
||||
LocationHeader,
|
||||
LastModifiedHeader,
|
||||
CookieHeader,
|
||||
SetCookieHeader,
|
||||
ContentDispositionHeader // added for QMultipartMessage
|
||||
};
|
||||
enum Attribute {
|
||||
HttpStatusCodeAttribute,
|
||||
HttpReasonPhraseAttribute,
|
||||
RedirectionTargetAttribute,
|
||||
ConnectionEncryptedAttribute,
|
||||
CacheLoadControlAttribute,
|
||||
CacheSaveControlAttribute,
|
||||
SourceIsFromCacheAttribute,
|
||||
DoNotBufferUploadDataAttribute,
|
||||
HttpPipeliningAllowedAttribute,
|
||||
HttpPipeliningWasUsedAttribute,
|
||||
CustomVerbAttribute,
|
||||
CookieLoadControlAttribute,
|
||||
AuthenticationReuseAttribute,
|
||||
CookieSaveControlAttribute,
|
||||
MaximumDownloadBufferSizeAttribute, // internal
|
||||
DownloadBufferAttribute, // internal
|
||||
SynchronousRequestAttribute, // internal
|
||||
|
||||
User = 1000,
|
||||
UserMax = 32767
|
||||
};
|
||||
enum CacheLoadControl {
|
||||
AlwaysNetwork,
|
||||
PreferNetwork,
|
||||
PreferCache,
|
||||
AlwaysCache
|
||||
};
|
||||
enum LoadControl {
|
||||
Automatic = 0,
|
||||
Manual
|
||||
};
|
||||
|
||||
enum Priority {
|
||||
HighPriority = 1,
|
||||
NormalPriority = 3,
|
||||
LowPriority = 5
|
||||
};
|
||||
|
||||
explicit QNetworkRequest(const QUrl &url = QUrl());
|
||||
QNetworkRequest(const QNetworkRequest &other);
|
||||
~QNetworkRequest();
|
||||
QNetworkRequest &operator=(const QNetworkRequest &other);
|
||||
|
||||
bool operator==(const QNetworkRequest &other) const;
|
||||
inline bool operator!=(const QNetworkRequest &other) const
|
||||
{ return !operator==(other); }
|
||||
|
||||
QUrl url() const;
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
// "cooked" headers
|
||||
QVariant header(KnownHeaders header) const;
|
||||
void setHeader(KnownHeaders header, const QVariant &value);
|
||||
|
||||
// raw headers:
|
||||
bool hasRawHeader(const QByteArray &headerName) const;
|
||||
QList<QByteArray> rawHeaderList() const;
|
||||
QByteArray rawHeader(const QByteArray &headerName) const;
|
||||
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
|
||||
|
||||
// attributes
|
||||
QVariant attribute(Attribute code, const QVariant &defaultValue = QVariant()) const;
|
||||
void setAttribute(Attribute code, const QVariant &value);
|
||||
|
||||
QSslConfiguration sslConfiguration() const;
|
||||
void setSslConfiguration(const QSslConfiguration &configuration);
|
||||
|
||||
void setOriginatingObject(QObject *object);
|
||||
QObject *originatingObject() const;
|
||||
|
||||
Priority priority() const;
|
||||
void setPriority(Priority priority);
|
||||
|
||||
private:
|
||||
QSharedDataPointer<QNetworkRequestPrivate> d;
|
||||
friend class QNetworkRequestPrivate;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkRequest)
|
||||
|
||||
|
||||
#endif
|
|
@ -1,79 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QNETWORKREQUEST_P_H
|
||||
#define QNETWORKREQUEST_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists for the convenience
|
||||
// of the Network Access API. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qnetworkrequest.h"
|
||||
#include "QtCore/qbytearray.h"
|
||||
#include "QtCore/qlist.h"
|
||||
#include "QtCore/qhash.h"
|
||||
#include "QtCore/qshareddata.h"
|
||||
#include "QtCore/qsharedpointer.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// this is the common part between QNetworkRequestPrivate, QNetworkReplyPrivate and QHttpPartPrivate
|
||||
class QNetworkHeadersPrivate
|
||||
{
|
||||
public:
|
||||
typedef QPair<QByteArray, QByteArray> RawHeaderPair;
|
||||
typedef QList<RawHeaderPair> RawHeadersList;
|
||||
typedef QHash<QNetworkRequest::KnownHeaders, QVariant> CookedHeadersMap;
|
||||
typedef QHash<QNetworkRequest::Attribute, QVariant> AttributesMap;
|
||||
|
||||
RawHeadersList rawHeaders;
|
||||
CookedHeadersMap cookedHeaders;
|
||||
AttributesMap attributes;
|
||||
QWeakPointer<QObject> originatingObject;
|
||||
|
||||
RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
|
||||
RawHeadersList allRawHeaders() const;
|
||||
QList<QByteArray> rawHeadersKeys() const;
|
||||
void setRawHeader(const QByteArray &key, const QByteArray &value);
|
||||
void setAllRawHeaders(const RawHeadersList &list);
|
||||
void setCookedHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
|
||||
|
||||
static QDateTime fromHttpDate(const QByteArray &value);
|
||||
static QByteArray toHttpDate(const QDateTime &dt);
|
||||
|
||||
private:
|
||||
void setRawHeaderInternal(const QByteArray &key, const QByteArray &value);
|
||||
void parseAndSetHeader(const QByteArray &key, const QByteArray &value);
|
||||
};
|
||||
|
||||
Q_DECLARE_TYPEINFO(QNetworkHeadersPrivate::RawHeaderPair, Q_MOVABLE_TYPE);
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
#endif
|
|
@ -25,7 +25,6 @@
|
|||
#include "qhash.h"
|
||||
#include "qbytearray.h"
|
||||
#include "qcryptographichash.h"
|
||||
#include "qhttp.h"
|
||||
#include "qiodevice.h"
|
||||
#include "qdatastream.h"
|
||||
#include "qendian.h"
|
||||
|
@ -46,12 +45,10 @@ static QByteArray qNtlmPhase3(QAuthenticatorPrivate *ctx, const QByteArray& phas
|
|||
\ingroup network
|
||||
\inmodule QtNetwork
|
||||
|
||||
The QAuthenticator class is usually used in the
|
||||
\l{QNetworkAccessManager::}{authenticationRequired()} and
|
||||
\l{QNetworkAccessManager::}{proxyAuthenticationRequired()} signals of QNetworkAccessManager and
|
||||
QAbstractSocket. The class provides a way to pass back the required
|
||||
authentication information to the socket when accessing services that
|
||||
require authentication.
|
||||
The QAuthenticator class is usually used in QAbstractSocket. The
|
||||
class provides a way to pass back the required authentication
|
||||
information to the socket when accessing services that require
|
||||
authentication.
|
||||
|
||||
QAuthenticator supports the following authentication methods:
|
||||
\list
|
||||
|
@ -296,21 +293,6 @@ QAuthenticatorPrivate::QAuthenticatorPrivate()
|
|||
nonceCount = 0;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
void QAuthenticatorPrivate::parseHttpResponse(const QHttpResponseHeader &header, bool isProxy)
|
||||
{
|
||||
const QList<QPair<QString, QString> > values = header.values();
|
||||
QList<QPair<QByteArray, QByteArray> > rawValues;
|
||||
|
||||
QList<QPair<QString, QString> >::const_iterator it, end;
|
||||
for (it = values.constBegin(), end = values.constEnd(); it != end; ++it)
|
||||
rawValues.append(qMakePair(it->first.toLatin1(), it->second.toUtf8()));
|
||||
|
||||
// continue in byte array form
|
||||
parseHttpResponse(rawValues, isProxy);
|
||||
}
|
||||
#endif
|
||||
|
||||
QAuthenticatorPrivate::~QAuthenticatorPrivate()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -63,10 +63,6 @@ private:
|
|||
QAuthenticatorPrivate *d;
|
||||
|
||||
friend class QAuthenticatorPrivate;
|
||||
friend class QHttpPrivate;
|
||||
friend class QHttpNetworkConnectionPrivate;
|
||||
friend class QHttpSocketEngine;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
@ -41,8 +41,6 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttpResponseHeader;
|
||||
|
||||
class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
|
||||
{
|
||||
public:
|
||||
|
@ -83,9 +81,6 @@ public:
|
|||
QByteArray digestMd5Response(const QByteArray &challenge, const QByteArray &method, const QByteArray &path);
|
||||
static QHash<QByteArray, QByteArray> parseDigestAuthenticationChallenge(const QByteArray &challenge);
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
void parseHttpResponse(const QHttpResponseHeader &, bool isProxy);
|
||||
#endif
|
||||
void parseHttpResponse(const QList<QPair<QByteArray, QByteArray> >&, bool isProxy);
|
||||
void updateCredentials();
|
||||
};
|
||||
|
|
|
@ -33,11 +33,11 @@
|
|||
|
||||
QNetworkProxy provides the method for configuring network layer
|
||||
proxy support to the Qt network classes. The currently supported
|
||||
classes are QAbstractSocket, QTcpSocket, QUdpSocket, QTcpServer,
|
||||
QNetworkAccessManager and QFtp. The proxy support is designed to
|
||||
be as transparent as possible. This means that existing
|
||||
network-enabled applications that you have written should
|
||||
automatically support network proxy using the following code.
|
||||
classes are QAbstractSocket, QTcpSocket, QUdpSocket and
|
||||
QTcpServer. The proxy support is designed to be as transparent
|
||||
as possible. This means that existing network-enabled
|
||||
applications that you have written should automatically support
|
||||
network proxy using the following code.
|
||||
|
||||
\snippet doc/src/snippets/code/src_network_kernel_qnetworkproxy.cpp 0
|
||||
|
||||
|
@ -131,25 +131,6 @@
|
|||
\o TunnelingCapability, ListeningCapability,
|
||||
UdpTunnelingCapability, HostNameLookupCapability
|
||||
|
||||
\row
|
||||
\o HTTP
|
||||
\o Implemented using the "CONNECT" command, supports only
|
||||
outgoing TCP connections; supports authentication.
|
||||
\o TunnelingCapability, CachingCapability, HostNameLookupCapability
|
||||
|
||||
\row
|
||||
\o Caching-only HTTP
|
||||
\o Implemented using normal HTTP commands, it is useful only
|
||||
in the context of HTTP requests (see QNetworkAccessManager)
|
||||
\o CachingCapability, HostNameLookupCapability
|
||||
|
||||
\row
|
||||
\o Caching FTP
|
||||
\o Implemented using an FTP proxy, it is useful only in the
|
||||
context of FTP requests (see QFtp,
|
||||
QNetworkAccessManager)
|
||||
\o CachingCapability, HostNameLookupCapability
|
||||
|
||||
\endtable
|
||||
|
||||
Also note that you shouldn't set the application default proxy
|
||||
|
@ -209,7 +190,6 @@
|
|||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QSocks5SocketEngineHandler;
|
||||
class QHttpSocketEngineHandler;
|
||||
|
||||
class QGlobalNetworkProxy
|
||||
{
|
||||
|
@ -727,8 +707,7 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
|
|||
|
||||
The destination host name is the host in the connection in the
|
||||
case of outgoing connection sockets. It is the \c hostName
|
||||
parameter passed to QTcpSocket::connectToHost() or the host
|
||||
component of a URL requested with QNetworkRequest.
|
||||
parameter passed to QTcpSocket::connectToHost().
|
||||
|
||||
The destination port number is the requested port to connect to in
|
||||
the case of outgoing sockets, while the local port number is the
|
||||
|
@ -777,14 +756,6 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
|
|||
specific circumstances, for example to indicate which remote
|
||||
host a connection is expected from. The URL component is not used.
|
||||
|
||||
\row
|
||||
\o UrlRequest
|
||||
\o A more high-level request, such as those coming from
|
||||
QNetworkAccessManager. These requests will inevitably use an
|
||||
outgoing TCP socket, but the this query type is provided to
|
||||
indicate that more detailed information is present in the URL
|
||||
component. For ease of implementation, the URL's host and
|
||||
port are set as the destination address.
|
||||
\endtable
|
||||
|
||||
It should be noted that any of the criteria may be missing or
|
||||
|
@ -793,8 +764,7 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
|
|||
the query should make their best guess or apply some
|
||||
implementation-defined default values.
|
||||
|
||||
\sa QNetworkProxy, QNetworkProxyFactory, QNetworkAccessManager,
|
||||
QAbstractSocket::setProxy()
|
||||
\sa QNetworkProxy, QNetworkProxyFactory, QAbstractSocket::setProxy()
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
@ -1104,11 +1074,6 @@ void QNetworkProxyQuery::setUrl(const QUrl &url)
|
|||
created with Qt will query the factory to determine the proxy to
|
||||
be used.
|
||||
|
||||
A factory can also be set in certain frameworks that support
|
||||
multiple connections, such as QNetworkAccessManager. When set on
|
||||
such object, the factory will be queried for sockets created by
|
||||
that framework only.
|
||||
|
||||
\section1 System Proxies
|
||||
|
||||
You can configure a factory to use the system proxy's settings.
|
||||
|
@ -1204,9 +1169,7 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
|
|||
|
||||
If you cannot determine a better proxy alternative, use
|
||||
QNetworkProxy::DefaultProxy, which tells the code querying for a
|
||||
proxy to use a higher alternative. For example, if this factory is
|
||||
set to a QNetworkAccessManager object, DefaultProxy will tell it
|
||||
to query the application-level proxy settings.
|
||||
proxy to use a higher alternative.
|
||||
|
||||
If this factory is set as the application proxy factory,
|
||||
DefaultProxy and NoProxy will have the same meaning.
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
#ifndef QNETWORKCOMMON_H
|
||||
#define QNETWORKCOMMON_H
|
||||
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qsharedpointer.h"
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkAccessCache::CacheableObject*)
|
||||
Q_DECLARE_METATYPE(QSharedPointer<char>)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
|
@ -143,7 +143,7 @@
|
|||
issue to be aware of, though: You must make sure that enough data
|
||||
is available before attempting to read it using operator>>().
|
||||
|
||||
\sa QFtp, QNetworkAccessManager, QTcpServer
|
||||
\sa QTcpServer
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
|
|
@ -1,814 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qhttpsocketengine_p.h"
|
||||
#include "qtcpsocket.h"
|
||||
#include "qhostaddress.h"
|
||||
#include "qurl.h"
|
||||
#include "qhttp.h"
|
||||
#include "qelapsedtimer.h"
|
||||
#include "qnetworkinterface.h"
|
||||
#include "qcore_unix_p.h"
|
||||
#include "qnetworkcommon_p.h"
|
||||
#include "qcorecommon_p.h"
|
||||
|
||||
#if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP)
|
||||
#include "qdebug.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#define DEBUG
|
||||
|
||||
QHttpSocketEngine::QHttpSocketEngine(QObject *parent)
|
||||
: QAbstractSocketEngine(*new QHttpSocketEnginePrivate, parent)
|
||||
{
|
||||
}
|
||||
|
||||
QHttpSocketEngine::~QHttpSocketEngine()
|
||||
{
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (type != QAbstractSocket::TcpSocket)
|
||||
return false;
|
||||
|
||||
setProtocol(protocol);
|
||||
setSocketType(type);
|
||||
d->socket = new QTcpSocket(this);
|
||||
|
||||
// Explicitly disable proxying on the proxy socket itself to avoid
|
||||
// unwanted recursion.
|
||||
d->socket->setProxy(QNetworkProxy::NoProxy);
|
||||
|
||||
// Intercept all the signals.
|
||||
connect(d->socket, SIGNAL(connected()),
|
||||
this, SLOT(slotSocketConnected()),
|
||||
Qt::DirectConnection);
|
||||
connect(d->socket, SIGNAL(disconnected()),
|
||||
this, SLOT(slotSocketDisconnected()),
|
||||
Qt::DirectConnection);
|
||||
connect(d->socket, SIGNAL(readyRead()),
|
||||
this, SLOT(slotSocketReadNotification()),
|
||||
Qt::DirectConnection);
|
||||
connect(d->socket, SIGNAL(bytesWritten(qint64)),
|
||||
this, SLOT(slotSocketBytesWritten()),
|
||||
Qt::DirectConnection);
|
||||
connect(d->socket, SIGNAL(error(QAbstractSocket::SocketError)),
|
||||
this, SLOT(slotSocketError(QAbstractSocket::SocketError)),
|
||||
Qt::DirectConnection);
|
||||
connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
|
||||
this, SLOT(slotSocketStateChanged(QAbstractSocket::SocketState)),
|
||||
Qt::DirectConnection);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::initialize(int, QAbstractSocket::SocketState)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::setProxy(const QNetworkProxy &proxy)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->proxy = proxy;
|
||||
QString user = proxy.user();
|
||||
if (!user.isEmpty())
|
||||
d->authenticator.setUser(user);
|
||||
QString password = proxy.password();
|
||||
if (!password.isEmpty())
|
||||
d->authenticator.setPassword(password);
|
||||
}
|
||||
|
||||
int QHttpSocketEngine::socketDescriptor() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
return d->socket ? d->socket->socketDescriptor() : 0;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::isValid() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
return d->socket;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::connectInternal()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
|
||||
d->credentialsSent = false;
|
||||
|
||||
// If the handshake is done, enter ConnectedState state and return true.
|
||||
if (d->state == Connected) {
|
||||
qWarning("QHttpSocketEngine::connectToHost: called when already connected");
|
||||
setState(QAbstractSocket::ConnectedState);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (d->state == ConnectSent && d->socketState != QAbstractSocket::ConnectedState)
|
||||
setState(QAbstractSocket::UnconnectedState);
|
||||
|
||||
// Handshake isn't done. If unconnected, start connecting.
|
||||
if (d->state == None && d->socket->state() == QAbstractSocket::UnconnectedState) {
|
||||
setState(QAbstractSocket::ConnectingState);
|
||||
//limit buffer in internal socket, data is buffered in the external socket under application control
|
||||
d->socket->setReadBufferSize(65536);
|
||||
d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
|
||||
}
|
||||
|
||||
// If connected (might happen right away, at least for localhost services
|
||||
// on some BSD systems), there might already be bytes available.
|
||||
if (bytesAvailable())
|
||||
slotSocketReadNotification();
|
||||
|
||||
return d->socketState == QAbstractSocket::ConnectedState;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
|
||||
setPeerAddress(address);
|
||||
setPeerPort(port);
|
||||
d->peerName.clear();
|
||||
|
||||
return connectInternal();
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::connectToHostByName(const QString &hostname, quint16 port)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
|
||||
setPeerAddress(QHostAddress());
|
||||
setPeerPort(port);
|
||||
d->peerName = hostname;
|
||||
|
||||
return connectInternal();
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::bind(const QHostAddress &, quint16)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::listen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int QHttpSocketEngine::accept()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::close()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (d->socket) {
|
||||
d->socket->close();
|
||||
delete d->socket;
|
||||
d->socket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
qint64 QHttpSocketEngine::bytesAvailable() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
return d->readBuffer.size() + (d->socket ? d->socket->bytesAvailable() : 0);
|
||||
}
|
||||
|
||||
qint64 QHttpSocketEngine::read(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
qint64 bytesRead = d->socket->read(data, maxlen);
|
||||
|
||||
if (d->socket->state() == QAbstractSocket::UnconnectedState
|
||||
&& d->socket->bytesAvailable() == 0) {
|
||||
emitReadNotification();
|
||||
}
|
||||
|
||||
if (bytesRead == -1) {
|
||||
// If nothing has been read so far, and the direct socket read
|
||||
// failed, return the socket's error. Otherwise, fall through and
|
||||
// return as much as we read so far.
|
||||
close();
|
||||
setError(QAbstractSocket::RemoteHostClosedError,
|
||||
QLatin1String("Remote host closed"));
|
||||
setState(QAbstractSocket::UnconnectedState);
|
||||
return -1;
|
||||
}
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
qint64 QHttpSocketEngine::write(const char *data, qint64 len)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
return d->socket->write(data, len);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_UDPSOCKET
|
||||
#ifndef QT_NO_NETWORKINTERFACE
|
||||
bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &,
|
||||
const QNetworkInterface &)
|
||||
{
|
||||
setError(QAbstractSocket::UnsupportedSocketOperationError,
|
||||
QLatin1String("Operation on socket is not supported"));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &,
|
||||
const QNetworkInterface &)
|
||||
{
|
||||
setError(QAbstractSocket::UnsupportedSocketOperationError,
|
||||
QLatin1String("Operation on socket is not supported"));
|
||||
return false;
|
||||
}
|
||||
|
||||
QNetworkInterface QHttpSocketEngine::multicastInterface() const
|
||||
{
|
||||
return QNetworkInterface();
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &)
|
||||
{
|
||||
setError(QAbstractSocket::UnsupportedSocketOperationError,
|
||||
QLatin1String("Operation on socket is not supported"));
|
||||
return false;
|
||||
}
|
||||
#endif // QT_NO_NETWORKINTERFACE
|
||||
|
||||
qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *,
|
||||
quint16 *)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QHostAddress &,
|
||||
quint16)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::hasPendingDatagrams() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
qint64 QHttpSocketEngine::pendingDatagramSize() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif // QT_NO_UDPSOCKET
|
||||
|
||||
qint64 QHttpSocketEngine::bytesToWrite() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
if (d->socket) {
|
||||
return d->socket->bytesToWrite();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int QHttpSocketEngine::option(SocketOption option) const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
if (d->socket) {
|
||||
// convert the enum and call the real socket
|
||||
if (option == QAbstractSocketEngine::LowDelayOption)
|
||||
return d->socket->socketOption(QAbstractSocket::LowDelayOption).toInt();
|
||||
if (option == QAbstractSocketEngine::KeepAliveOption)
|
||||
return d->socket->socketOption(QAbstractSocket::KeepAliveOption).toInt();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::setOption(SocketOption option, int value)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (d->socket) {
|
||||
// convert the enum and call the real socket
|
||||
if (option == QAbstractSocketEngine::LowDelayOption)
|
||||
d->socket->setSocketOption(QAbstractSocket::LowDelayOption, value);
|
||||
if (option == QAbstractSocketEngine::KeepAliveOption)
|
||||
d->socket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
|
||||
if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
|
||||
return false;
|
||||
|
||||
// Wait for more data if nothing is available.
|
||||
if (!d->socket->bytesAvailable()) {
|
||||
if (!d->socket->waitForReadyRead(msecs)) {
|
||||
if (d->socket->state() == QAbstractSocket::UnconnectedState)
|
||||
return true;
|
||||
setError(d->socket->error(), d->socket->errorString());
|
||||
if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
|
||||
*timedOut = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're not connected yet, wait until we are, or until an error
|
||||
// occurs.
|
||||
while (d->state != Connected && d->socket->waitForReadyRead(msecs)) {
|
||||
// Loop while the protocol handshake is taking place.
|
||||
}
|
||||
|
||||
// Report any error that may occur.
|
||||
if (d->state != Connected) {
|
||||
setError(d->socket->error(), d->socket->errorString());
|
||||
if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
|
||||
*timedOut = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
|
||||
// If we're connected, just forward the call.
|
||||
if (d->state == Connected) {
|
||||
if (d->socket->bytesToWrite()) {
|
||||
if (!d->socket->waitForBytesWritten(msecs)) {
|
||||
if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
|
||||
*timedOut = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we're not connected yet, wait until we are, and until bytes have
|
||||
// been received (i.e., the socket has connected, we have sent the
|
||||
// greeting, and then received the response).
|
||||
while (d->state != Connected && d->socket->waitForReadyRead(msecs)) {
|
||||
// Loop while the protocol handshake is taking place.
|
||||
}
|
||||
|
||||
// Report any error that may occur.
|
||||
if (d->state != Connected) {
|
||||
// setError(d->socket->error(), d->socket->errorString());
|
||||
if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
|
||||
*timedOut = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
|
||||
bool checkRead, bool checkWrite,
|
||||
int msecs, bool *timedOut)
|
||||
{
|
||||
Q_UNUSED(checkRead);
|
||||
|
||||
if (!checkWrite) {
|
||||
// Not interested in writing? Then we wait for read notifications.
|
||||
bool canRead = waitForRead(msecs, timedOut);
|
||||
if (readyToRead)
|
||||
*readyToRead = canRead;
|
||||
return canRead;
|
||||
}
|
||||
|
||||
// Interested in writing? Then we wait for write notifications.
|
||||
bool canWrite = waitForWrite(msecs, timedOut);
|
||||
if (readyToWrite)
|
||||
*readyToWrite = canWrite;
|
||||
return canWrite;
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::isReadNotificationEnabled() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
return d->readNotificationEnabled;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (d->readNotificationEnabled == enable)
|
||||
return;
|
||||
|
||||
d->readNotificationEnabled = enable;
|
||||
if (enable) {
|
||||
// Enabling read notification can trigger a notification.
|
||||
if (bytesAvailable())
|
||||
slotSocketReadNotification();
|
||||
}
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::isWriteNotificationEnabled() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
return d->writeNotificationEnabled;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->writeNotificationEnabled = enable;
|
||||
if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
|
||||
QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
bool QHttpSocketEngine::isExceptionNotificationEnabled() const
|
||||
{
|
||||
Q_D(const QHttpSocketEngine);
|
||||
return d->exceptNotificationEnabled;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->exceptNotificationEnabled = enable;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::slotSocketConnected()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
|
||||
// Send the greeting.
|
||||
const char method[] = "CONNECT";
|
||||
QByteArray peerAddress = d->peerName.isEmpty() ?
|
||||
d->peerAddress.toString().toLatin1() :
|
||||
QUrl::toAce(d->peerName);
|
||||
QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
|
||||
QByteArray data = method;
|
||||
data += " ";
|
||||
data += path;
|
||||
data += " HTTP/1.1\r\n";
|
||||
data += "Proxy-Connection: keep-alive\r\n"
|
||||
"User-Agent: ";
|
||||
QVariant v = property("_q_user-agent");
|
||||
if (v.isValid())
|
||||
data += v.toByteArray();
|
||||
else
|
||||
data += "Mozilla/5.0";
|
||||
data += QLatin1String("\r\nHost: ") + peerAddress + QLatin1String("\r\n");
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
|
||||
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
|
||||
if (priv && priv->method != QAuthenticatorPrivate::None) {
|
||||
d->credentialsSent = true;
|
||||
data += QLatin1String("Proxy-Authorization: ") + priv->calculateResponse(method, path) + QLatin1String("\r\n");
|
||||
}
|
||||
data += "\r\n";
|
||||
// qDebug() << ">>>>>>>> sending request" << this;
|
||||
// qDebug() << data;
|
||||
// qDebug() << ">>>>>>>";
|
||||
d->socket->write(data);
|
||||
d->state = ConnectSent;
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::slotSocketDisconnected()
|
||||
{
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::slotSocketReadNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (d->state != Connected && d->socket->bytesAvailable() == 0)
|
||||
return;
|
||||
|
||||
if (d->state == Connected) {
|
||||
// Forward as a read notification.
|
||||
if (d->readNotificationEnabled)
|
||||
emitReadNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
readResponseContent:
|
||||
if (d->state == ReadResponseContent) {
|
||||
QSTACKARRAY(char, dummybuffer, 4096);
|
||||
while (d->pendingResponseData) {
|
||||
int read = d->socket->read(dummybuffer, qMin(sizeof(dummybuffer), (size_t)d->pendingResponseData));
|
||||
if (read >= 0)
|
||||
dummybuffer[read] = 0;
|
||||
|
||||
if (read == 0)
|
||||
return;
|
||||
if (read == -1) {
|
||||
d->socket->disconnectFromHost();
|
||||
emitWriteNotification();
|
||||
return;
|
||||
}
|
||||
d->pendingResponseData -= read;
|
||||
}
|
||||
if (d->pendingResponseData > 0)
|
||||
return;
|
||||
d->state = SendAuthentication;
|
||||
slotSocketConnected();
|
||||
return;
|
||||
}
|
||||
|
||||
// Still in handshake mode. Wait until we've got a full response.
|
||||
bool done = false;
|
||||
do {
|
||||
d->readBuffer += d->socket->readLine();
|
||||
} while (!(done = d->readBuffer.endsWith("\r\n\r\n")) && d->socket->canReadLine());
|
||||
|
||||
if (!done) {
|
||||
// Wait for more.
|
||||
return;
|
||||
}
|
||||
|
||||
if (!d->readBuffer.startsWith("HTTP/1.")) {
|
||||
// protocol error, this isn't HTTP
|
||||
d->readBuffer.clear();
|
||||
d->socket->close();
|
||||
setState(QAbstractSocket::UnconnectedState);
|
||||
setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy"));
|
||||
emitConnectionNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
QHttpResponseHeader responseHeader(QString::fromLatin1(d->readBuffer));
|
||||
d->readBuffer.clear(); // we parsed the proxy protocol response. from now on direct socket reading will be done
|
||||
|
||||
int statusCode = responseHeader.statusCode();
|
||||
QAuthenticatorPrivate *priv = 0;
|
||||
if (statusCode == 200) {
|
||||
d->state = Connected;
|
||||
setLocalAddress(d->socket->localAddress());
|
||||
setLocalPort(d->socket->localPort());
|
||||
setState(QAbstractSocket::ConnectedState);
|
||||
d->authenticator.detach();
|
||||
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
|
||||
priv->hasFailed = false;
|
||||
} else if (statusCode == 407) {
|
||||
if (d->credentialsSent) {
|
||||
//407 response again means the provided username/password were invalid.
|
||||
d->authenticator = QAuthenticator(); //this is needed otherwise parseHttpResponse won't set the state, and then signal isn't emitted.
|
||||
d->authenticator.detach();
|
||||
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
|
||||
priv->hasFailed = true;
|
||||
}
|
||||
else if (d->authenticator.isNull())
|
||||
d->authenticator.detach();
|
||||
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
|
||||
|
||||
priv->parseHttpResponse(responseHeader, true);
|
||||
|
||||
if (priv->phase == QAuthenticatorPrivate::Invalid) {
|
||||
// problem parsing the reply
|
||||
d->socket->close();
|
||||
setState(QAbstractSocket::UnconnectedState);
|
||||
setError(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy"));
|
||||
emitConnectionNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
bool willClose;
|
||||
QString proxyConnectionHeader = responseHeader.value(QLatin1String("Proxy-Connection"));
|
||||
// Although most proxies use the unofficial Proxy-Connection header, the Connection header
|
||||
// from http spec is also allowed.
|
||||
if (proxyConnectionHeader.isEmpty())
|
||||
proxyConnectionHeader = responseHeader.value(QLatin1String("Connection"));
|
||||
proxyConnectionHeader = proxyConnectionHeader.toLower();
|
||||
if (proxyConnectionHeader == QLatin1String("close")) {
|
||||
willClose = true;
|
||||
} else if (proxyConnectionHeader == QLatin1String("keep-alive")) {
|
||||
willClose = false;
|
||||
} else {
|
||||
// no Proxy-Connection header, so use the default
|
||||
// HTTP 1.1's default behaviour is to keep persistent connections
|
||||
// HTTP 1.0 or earlier, so we expect the server to close
|
||||
willClose = (responseHeader.majorVersion() * 0x100 + responseHeader.minorVersion()) <= 0x0100;
|
||||
}
|
||||
|
||||
if (willClose) {
|
||||
// the server will disconnect, so let's avoid receiving an error
|
||||
// especially since the signal below may trigger a new event loop
|
||||
d->socket->disconnectFromHost();
|
||||
d->socket->readAll();
|
||||
}
|
||||
|
||||
if (priv->phase == QAuthenticatorPrivate::Done)
|
||||
emit proxyAuthenticationRequired(d->proxy, &d->authenticator);
|
||||
// priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
|
||||
if (priv->phase == QAuthenticatorPrivate::Done) {
|
||||
setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
|
||||
d->socket->disconnectFromHost();
|
||||
} else {
|
||||
// close the connection if it isn't already and reconnect using the chosen authentication method
|
||||
d->state = SendAuthentication;
|
||||
if (willClose) {
|
||||
d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
|
||||
} else {
|
||||
bool ok;
|
||||
int contentLength = responseHeader.value(QLatin1String("Content-Length")).toInt(&ok);
|
||||
if (ok && contentLength > 0) {
|
||||
d->state = ReadResponseContent;
|
||||
d->pendingResponseData = contentLength;
|
||||
goto readResponseContent;
|
||||
} else {
|
||||
d->state = SendAuthentication;
|
||||
slotSocketConnected();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
d->socket->close();
|
||||
setState(QAbstractSocket::UnconnectedState);
|
||||
if (statusCode == 403 || statusCode == 405) {
|
||||
// 403 Forbidden
|
||||
// 405 Method Not Allowed
|
||||
setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection"));
|
||||
} else if (statusCode == 404) {
|
||||
// 404 Not Found: host lookup error
|
||||
setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
|
||||
} else if (statusCode == 503) {
|
||||
// 503 Service Unavailable: Connection Refused
|
||||
setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused"));
|
||||
} else {
|
||||
// Some other reply
|
||||
//qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
|
||||
setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy"));
|
||||
}
|
||||
}
|
||||
|
||||
// The handshake is done; notify that we're connected (or failed to connect)
|
||||
emitConnectionNotification();
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::slotSocketBytesWritten()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (d->state == Connected && d->writeNotificationEnabled)
|
||||
emitWriteNotification();
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->readBuffer.clear();
|
||||
|
||||
if (d->state != Connected) {
|
||||
// we are in proxy handshaking stages
|
||||
if (error == QAbstractSocket::HostNotFoundError)
|
||||
setError(QAbstractSocket::ProxyNotFoundError, tr("Proxy server not found"));
|
||||
else if (error == QAbstractSocket::ConnectionRefusedError)
|
||||
setError(QAbstractSocket::ProxyConnectionRefusedError, tr("Proxy connection refused"));
|
||||
else if (error == QAbstractSocket::SocketTimeoutError)
|
||||
setError(QAbstractSocket::ProxyConnectionTimeoutError, tr("Proxy server connection timed out"));
|
||||
else if (error == QAbstractSocket::RemoteHostClosedError)
|
||||
setError(QAbstractSocket::ProxyConnectionClosedError, tr("Proxy connection closed prematurely"));
|
||||
else
|
||||
setError(error, d->socket->errorString());
|
||||
emitConnectionNotification();
|
||||
return;
|
||||
}
|
||||
|
||||
// We're connected
|
||||
if (error == QAbstractSocket::SocketTimeoutError)
|
||||
return; // ignore this error
|
||||
|
||||
d->state = None;
|
||||
setError(error, d->socket->errorString());
|
||||
if (error != QAbstractSocket::RemoteHostClosedError)
|
||||
qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
|
||||
//read notification needs to always be emitted, otherwise the higher layer doesn't get the disconnected signal
|
||||
emitReadNotification();
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
|
||||
{
|
||||
Q_UNUSED(state);
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::emitPendingReadNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->readNotificationPending = false;
|
||||
if (d->readNotificationEnabled)
|
||||
emit readNotification();
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::emitPendingWriteNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->writeNotificationPending = false;
|
||||
if (d->writeNotificationEnabled)
|
||||
emit writeNotification();
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::emitPendingConnectionNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->connectionNotificationPending = false;
|
||||
emit connectionNotification();
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::emitReadNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->readNotificationActivated = true;
|
||||
// if there is a connection notification pending we have to emit the readNotification
|
||||
// incase there is connection error. This is only needed for Windows, but it does not
|
||||
// hurt in other cases.
|
||||
if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) {
|
||||
d->readNotificationPending = true;
|
||||
QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::emitWriteNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
d->writeNotificationActivated = true;
|
||||
if (d->writeNotificationEnabled && !d->writeNotificationPending) {
|
||||
d->writeNotificationPending = true;
|
||||
QMetaObject::invokeMethod(this, "emitPendingWriteNotification", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void QHttpSocketEngine::emitConnectionNotification()
|
||||
{
|
||||
Q_D(QHttpSocketEngine);
|
||||
if (!d->connectionNotificationPending) {
|
||||
d->connectionNotificationPending = true;
|
||||
QMetaObject::invokeMethod(this, "emitPendingConnectionNotification", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
|
||||
: readNotificationEnabled(false)
|
||||
, writeNotificationEnabled(false)
|
||||
, exceptNotificationEnabled(false)
|
||||
, readNotificationActivated(false)
|
||||
, writeNotificationActivated(false)
|
||||
, readNotificationPending(false)
|
||||
, writeNotificationPending(false)
|
||||
, connectionNotificationPending(false)
|
||||
, credentialsSent(false)
|
||||
, pendingResponseData(0)
|
||||
{
|
||||
socket = 0;
|
||||
state = QHttpSocketEngine::None;
|
||||
}
|
||||
|
||||
QHttpSocketEnginePrivate::~QHttpSocketEnginePrivate()
|
||||
{
|
||||
}
|
||||
|
||||
QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
|
||||
const QNetworkProxy &proxy,
|
||||
QObject *parent)
|
||||
{
|
||||
if (socketType != QAbstractSocket::TcpSocket)
|
||||
return 0;
|
||||
|
||||
// proxy type must have been resolved by now
|
||||
if (proxy.type() != QNetworkProxy::HttpProxy)
|
||||
return 0;
|
||||
|
||||
// we only accept active sockets
|
||||
if (!qobject_cast<QAbstractSocket *>(parent))
|
||||
return 0;
|
||||
|
||||
QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
|
||||
engine->setProxy(proxy);
|
||||
return engine;
|
||||
}
|
||||
|
||||
QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(int, QObject *)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#include "moc_qhttpsocketengine_p.h"
|
|
@ -1,180 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTPSOCKETENGINE_P_H
|
||||
#define QHTTPSOCKETENGINE_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Katie API. It exists purely as an
|
||||
// implementation detail. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qabstractsocketengine_p.h"
|
||||
#include "qabstractsocket.h"
|
||||
#include "qnetworkproxy.h"
|
||||
#include "qauthenticator_p.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP)
|
||||
|
||||
class QTcpSocket;
|
||||
class QHttpSocketEnginePrivate;
|
||||
|
||||
class Q_AUTOTEST_EXPORT QHttpSocketEngine : public QAbstractSocketEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum HttpState {
|
||||
None,
|
||||
ConnectSent,
|
||||
Connected,
|
||||
SendAuthentication,
|
||||
ReadResponseContent
|
||||
};
|
||||
QHttpSocketEngine(QObject *parent = nullptr);
|
||||
~QHttpSocketEngine();
|
||||
|
||||
bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol);
|
||||
bool initialize(int socketDescriptor, QAbstractSocket::SocketState socketState = QAbstractSocket::ConnectedState);
|
||||
|
||||
void setProxy(const QNetworkProxy &networkProxy);
|
||||
|
||||
int socketDescriptor() const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
bool connectInternal();
|
||||
bool connectToHost(const QHostAddress &address, quint16 port);
|
||||
bool connectToHostByName(const QString &name, quint16 port);
|
||||
bool bind(const QHostAddress &address, quint16 port);
|
||||
bool listen();
|
||||
int accept();
|
||||
void close();
|
||||
|
||||
qint64 bytesAvailable() const;
|
||||
|
||||
qint64 read(char *data, qint64 maxlen);
|
||||
qint64 write(const char *data, qint64 len);
|
||||
|
||||
#ifndef QT_NO_UDPSOCKET
|
||||
#ifndef QT_NO_NETWORKINTERFACE
|
||||
bool joinMulticastGroup(const QHostAddress &groupAddress,
|
||||
const QNetworkInterface &interface);
|
||||
bool leaveMulticastGroup(const QHostAddress &groupAddress,
|
||||
const QNetworkInterface &interface);
|
||||
QNetworkInterface multicastInterface() const;
|
||||
bool setMulticastInterface(const QNetworkInterface &iface);
|
||||
#endif // QT_NO_NETWORKINTERFACE
|
||||
|
||||
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0,
|
||||
quint16 *port = 0);
|
||||
qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr,
|
||||
quint16 port);
|
||||
bool hasPendingDatagrams() const;
|
||||
qint64 pendingDatagramSize() const;
|
||||
#endif // QT_NO_UDPSOCKET
|
||||
|
||||
qint64 bytesToWrite() const;
|
||||
|
||||
int option(SocketOption option) const;
|
||||
bool setOption(SocketOption option, int value);
|
||||
|
||||
bool waitForRead(int msecs = 30000, bool *timedOut = 0);
|
||||
bool waitForWrite(int msecs = 30000, bool *timedOut = 0);
|
||||
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
|
||||
bool checkRead, bool checkWrite,
|
||||
int msecs = 30000, bool *timedOut = 0);
|
||||
|
||||
bool isReadNotificationEnabled() const;
|
||||
void setReadNotificationEnabled(bool enable);
|
||||
bool isWriteNotificationEnabled() const;
|
||||
void setWriteNotificationEnabled(bool enable);
|
||||
bool isExceptionNotificationEnabled() const;
|
||||
void setExceptionNotificationEnabled(bool enable);
|
||||
|
||||
public slots:
|
||||
void slotSocketConnected();
|
||||
void slotSocketDisconnected();
|
||||
void slotSocketReadNotification();
|
||||
void slotSocketBytesWritten();
|
||||
void slotSocketError(QAbstractSocket::SocketError error);
|
||||
void slotSocketStateChanged(QAbstractSocket::SocketState state);
|
||||
|
||||
private slots:
|
||||
void emitPendingReadNotification();
|
||||
void emitPendingWriteNotification();
|
||||
void emitPendingConnectionNotification();
|
||||
|
||||
private:
|
||||
void emitReadNotification();
|
||||
void emitWriteNotification();
|
||||
void emitConnectionNotification();
|
||||
|
||||
Q_DECLARE_PRIVATE(QHttpSocketEngine)
|
||||
Q_DISABLE_COPY(QHttpSocketEngine)
|
||||
|
||||
};
|
||||
|
||||
|
||||
class QHttpSocketEnginePrivate : public QAbstractSocketEnginePrivate
|
||||
{
|
||||
Q_DECLARE_PUBLIC(QHttpSocketEngine)
|
||||
public:
|
||||
QHttpSocketEnginePrivate();
|
||||
~QHttpSocketEnginePrivate();
|
||||
|
||||
QNetworkProxy proxy;
|
||||
QString peerName;
|
||||
QTcpSocket *socket;
|
||||
QByteArray readBuffer; // only used for parsing the proxy response
|
||||
QHttpSocketEngine::HttpState state;
|
||||
QAuthenticator authenticator;
|
||||
bool readNotificationEnabled;
|
||||
bool writeNotificationEnabled;
|
||||
bool exceptNotificationEnabled;
|
||||
bool readNotificationActivated;
|
||||
bool writeNotificationActivated;
|
||||
bool readNotificationPending;
|
||||
bool writeNotificationPending;
|
||||
bool connectionNotificationPending;
|
||||
bool credentialsSent;
|
||||
uint pendingResponseData;
|
||||
};
|
||||
|
||||
class Q_AUTOTEST_EXPORT QHttpSocketEngineHandler : public QSocketEngineHandler
|
||||
{
|
||||
public:
|
||||
virtual QAbstractSocketEngine *createSocketEngine(QAbstractSocket::SocketType socketType,
|
||||
const QNetworkProxy &, QObject *parent);
|
||||
virtual QAbstractSocketEngine *createSocketEngine(int socketDescripter, QObject *parent);
|
||||
};
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QHTTPSOCKETENGINE_H
|
|
@ -372,9 +372,6 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb
|
|||
// If it introduces any performance regressions for Qt 4.6.x (x > 0) then
|
||||
// it will be put back in.
|
||||
//
|
||||
// You can use tests/manual/qhttpnetworkconnection to test HTTP download speed
|
||||
// with this.
|
||||
//
|
||||
// pre-4.6:
|
||||
// setReceiveBufferSize(49152);
|
||||
// setSendBufferSize(49152);
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
\bold{Note:} TCP sockets cannot be opened in QIODevice::Unbuffered mode.
|
||||
|
||||
|
||||
\sa QTcpServer, QUdpSocket, QFtp, QNetworkAccessManager,
|
||||
\sa QTcpServer, QUdpSocket,
|
||||
{Fortune Server Example}, {Fortune Client Example},
|
||||
{Threaded Fortune Server Example}, {Blocking Fortune Client Example},
|
||||
{Loopback Example}, {Torrent Example}
|
||||
|
|
|
@ -81,7 +81,7 @@ QT_BEGIN_NAMESPACE
|
|||
\snippet doc/src/snippets/code/src_network_ssl_qsslconfiguration.cpp 0
|
||||
|
||||
\sa QSsl::SslProtocol, QSslCertificate, QSslCipher, QSslKey
|
||||
QSslSocket, QNetworkAccessManager,
|
||||
QSslSocket,
|
||||
QSslSocket::sslConfiguration(), QSslSocket::setSslConfiguration()
|
||||
*/
|
||||
|
||||
|
@ -326,8 +326,8 @@ void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate)
|
|||
|
||||
Because the peer certificate is set during the handshake phase, it
|
||||
is safe to access the peer certificate from a slot connected to
|
||||
the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors()
|
||||
signal, or the QSslSocket::encrypted() signal.
|
||||
the QSslSocket::sslErrors() signal, or the QSslSocket::encrypted()
|
||||
signal.
|
||||
|
||||
If a null certificate is returned, it can mean the SSL handshake
|
||||
failed, or it can mean the host you are connected to doesn't have
|
||||
|
@ -337,8 +337,7 @@ void QSslConfiguration::setLocalCertificate(const QSslCertificate &certificate)
|
|||
use peerCertificateChain() to get them all at once.
|
||||
|
||||
\sa peerCertificateChain(),
|
||||
QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(),
|
||||
QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors()
|
||||
QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(
|
||||
*/
|
||||
QSslCertificate QSslConfiguration::peerCertificate() const
|
||||
{
|
||||
|
@ -358,8 +357,8 @@ QSslCertificate QSslConfiguration::peerCertificate() const
|
|||
|
||||
Because the peer certificate is set during the handshake phase, it
|
||||
is safe to access the peer certificate from a slot connected to
|
||||
the QSslSocket::sslErrors() signal, QNetworkReply::sslErrors()
|
||||
signal, or the QSslSocket::encrypted() signal.
|
||||
the QSslSocket::sslErrors() signal, or the QSslSocket::encrypted()
|
||||
signal.
|
||||
|
||||
If an empty list is returned, it can mean the SSL handshake
|
||||
failed, or it can mean the host you are connected to doesn't have
|
||||
|
@ -369,8 +368,7 @@ QSslCertificate QSslConfiguration::peerCertificate() const
|
|||
peerCertificate().
|
||||
|
||||
\sa peerCertificate(),
|
||||
QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors(),
|
||||
QNetworkReply::sslErrors(), QNetworkReply::ignoreSslErrors()
|
||||
QSslSocket::sslErrors(), QSslSocket::ignoreSslErrors()
|
||||
*/
|
||||
QList<QSslCertificate> QSslConfiguration::peerCertificateChain() const
|
||||
{
|
||||
|
|
|
@ -347,8 +347,8 @@ void QSslSocketBackendPrivate::destroySslContext()
|
|||
{
|
||||
if (ssl) {
|
||||
// We do not send a shutdown alert here. Just mark the session as
|
||||
// resumable for qhttpnetworkconnection's "optimization", otherwise
|
||||
// OpenSSL won't start a session resumption.
|
||||
// resumable for "optimization", otherwise OpenSSL won't start a
|
||||
// session resumption.
|
||||
SSL_shutdown(ssl);
|
||||
SSL_free(ssl);
|
||||
ssl = 0;
|
||||
|
|
|
@ -71,7 +71,6 @@ static const struct ClassTblData {
|
|||
{ QLatin1String("QAbstractItemModel"), QLatin1String("QtCore/qabstractitemmodel.h") },
|
||||
{ QLatin1String("QAbstractItemView"), QLatin1String("QtGui/qabstractitemview.h") },
|
||||
{ QLatin1String("QAbstractListModel"), QLatin1String("QtCore/qabstractitemmodel.h") },
|
||||
{ QLatin1String("QAbstractNetworkCache"), QLatin1String("QtNetwork/qabstractnetworkcache.h") },
|
||||
{ QLatin1String("QAbstractPageSetupDialog"), QLatin1String("QtGui/qabstractpagesetupdialog.h") },
|
||||
{ QLatin1String("QAbstractPrintDialog"), QLatin1String("QtGui/qabstractprintdialog.h") },
|
||||
{ QLatin1String("QAbstractProxyModel"), QLatin1String("QtGui/qabstractproxymodel.h") },
|
||||
|
@ -254,7 +253,6 @@ static const struct ClassTblData {
|
|||
{ QLatin1String("QFormBuilder"), QLatin1String("QtUiTools/formbuilder.h") },
|
||||
{ QLatin1String("QFormLayout"), QLatin1String("QtGui/qformlayout.h") },
|
||||
{ QLatin1String("QFrame"), QLatin1String("QtGui/qframe.h") },
|
||||
{ QLatin1String("QFtp"), QLatin1String("QtNetwork/qftp.h") },
|
||||
{ QLatin1String("QFutureInterfaceBase"), QLatin1String("QtCore/qfutureinterface.h") },
|
||||
{ QLatin1String("QFutureWatcherBase"), QLatin1String("QtCore/qfuturewatcher.h") },
|
||||
{ QLatin1String("QGenericArgument"), QLatin1String("QtCore/qobjectdefs.h") },
|
||||
|
@ -312,12 +310,6 @@ static const struct ClassTblData {
|
|||
{ QLatin1String("QHostAddress"), QLatin1String("QtNetwork/qhostaddress.h") },
|
||||
{ QLatin1String("QHostInfo"), QLatin1String("QtNetwork/qhostinfo.h") },
|
||||
{ QLatin1String("QHoverEvent"), QLatin1String("QtGui/qevent.h") },
|
||||
{ QLatin1String("QHttp"), QLatin1String("QtNetwork/qhttp.h") },
|
||||
{ QLatin1String("QHttpHeader"), QLatin1String("QtNetwork/qhttp.h") },
|
||||
{ QLatin1String("QHttpMultiPart"), QLatin1String("QtNetwork/qhttpmultipart.h") },
|
||||
{ QLatin1String("QHttpPart"), QLatin1String("QtNetwork/qhttpmultipart.h") },
|
||||
{ QLatin1String("QHttpRequestHeader"), QLatin1String("QtNetwork/qhttp.h") },
|
||||
{ QLatin1String("QHttpResponseHeader"), QLatin1String("QtNetwork/qhttp.h") },
|
||||
{ QLatin1String("QIODevice"), QLatin1String("QtCore/qiodevice.h") },
|
||||
{ QLatin1String("QIPv6Address"), QLatin1String("QtNetwork/qhostaddress.h") },
|
||||
{ QLatin1String("QIcon"), QLatin1String("QtGui/qicon.h") },
|
||||
|
@ -390,18 +382,11 @@ static const struct ClassTblData {
|
|||
{ QLatin1String("QMultiHash"), QLatin1String("QtCore/qhash.h") },
|
||||
{ QLatin1String("QMutex"), QLatin1String("QtCore/qmutex.h") },
|
||||
{ QLatin1String("QMutexLocker"), QLatin1String("QtCore/qmutex.h") },
|
||||
{ QLatin1String("QNetworkAccessManager"), QLatin1String("QtNetwork/qnetworkaccessmanager.h") },
|
||||
{ QLatin1String("QNetworkAddressEntry"), QLatin1String("QtNetwork/qnetworkinterface.h") },
|
||||
{ QLatin1String("QNetworkCacheMetaData"), QLatin1String("QtNetwork/qabstractnetworkcache.h") },
|
||||
{ QLatin1String("QNetworkCookie"), QLatin1String("QtNetwork/qnetworkcookie.h") },
|
||||
{ QLatin1String("QNetworkCookieJar"), QLatin1String("QtNetwork/qnetworkcookiejar.h") },
|
||||
{ QLatin1String("QNetworkDiskCache"), QLatin1String("QtNetwork/qnetworkdiskcache.h") },
|
||||
{ QLatin1String("QNetworkInterface"), QLatin1String("QtNetwork/qnetworkinterface.h") },
|
||||
{ QLatin1String("QNetworkProxy"), QLatin1String("QtNetwork/qnetworkproxy.h") },
|
||||
{ QLatin1String("QNetworkProxyFactory"), QLatin1String("QtNetwork/qnetworkproxy.h") },
|
||||
{ QLatin1String("QNetworkProxyQuery"), QLatin1String("QtNetwork/qnetworkproxy.h") },
|
||||
{ QLatin1String("QNetworkReply"), QLatin1String("QtNetwork/qnetworkreply.h") },
|
||||
{ QLatin1String("QNetworkRequest"), QLatin1String("QtNetwork/qnetworkrequest.h") },
|
||||
{ QLatin1String("QObject"), QLatin1String("QtCore/qobject.h") },
|
||||
{ QLatin1String("QObjectCleanupHandler"), QLatin1String("QtCore/qobjectcleanuphandler.h") },
|
||||
{ QLatin1String("QPageSetupDialog"), QLatin1String("QtGui/qpagesetupdialog.h") },
|
||||
|
|
|
@ -307,18 +307,6 @@ QXmlStreamEntityResolver *QXmlStreamReader::entityResolver() const
|
|||
from the PrematureEndOfDocumentError error and continues parsing the
|
||||
new data with the next call to readNext().
|
||||
|
||||
For example, if your application reads data from the network using a
|
||||
\l{QNetworkAccessManager} {network access manager}, you would issue
|
||||
a \l{QNetworkRequest} {network request} to the manager and receive a
|
||||
\l{QNetworkReply} {network reply} in return. Since a QNetworkReply
|
||||
is a QIODevice, you connect its \l{QNetworkReply::readyRead()}
|
||||
{readyRead()} signal to a custom slot, e.g. \c{slotReadyRead()} in
|
||||
the code snippet shown in the discussion for QNetworkAccessManager.
|
||||
In this slot, you read all available data with
|
||||
\l{QNetworkReply::readAll()} {readAll()} and pass it to the XML
|
||||
stream reader using addData(). Then you call your custom parsing
|
||||
function that reads the XML events from the reader.
|
||||
|
||||
\section1 Performance and memory consumption
|
||||
|
||||
QXmlStreamReader is memory-conservative by design, since it doesn't
|
||||
|
|
|
@ -2727,10 +2727,6 @@ void QXmlSimpleReaderPrivate::initIncrementalParsing()
|
|||
parse() to work incrementally, and making subsequent calls to the
|
||||
parseContinue() function, until all the data has been processed.
|
||||
|
||||
A common way to perform incremental parsing is to connect the \c
|
||||
readyRead() signal of a \l{QNetworkReply} {network reply} a slot,
|
||||
and handle the incoming data there. See QNetworkAccessManager.
|
||||
|
||||
Aspects of the parsing behavior can be adapted using setFeature()
|
||||
and setProperty().
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
katie_test(tst_qhttpnetworkreply
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_qhttpnetworkreply.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_qhttpnetworkreply KtNetwork)
|
|
@ -1,123 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the test suite of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "qhttpnetworkconnection_p.h"
|
||||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
class tst_QHttpNetworkReply: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void init();
|
||||
void cleanup();
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void parseHeader_data();
|
||||
void parseHeader();
|
||||
};
|
||||
|
||||
|
||||
void tst_QHttpNetworkReply::initTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::init()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::parseHeader_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("headers");
|
||||
QTest::addColumn<QStringList>("fields");
|
||||
QTest::addColumn<QStringList>("values");
|
||||
|
||||
QTest::newRow("empty-field") << QByteArray("Set-Cookie: \r\n")
|
||||
<< (QStringList() << "Set-Cookie")
|
||||
<< (QStringList() << "");
|
||||
QTest::newRow("single-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n")
|
||||
<< (QStringList() << "Content-Type")
|
||||
<< (QStringList() << "text/html; charset=utf-8");
|
||||
QTest::newRow("single-field-continued") << QByteArray("Content-Type: text/html;\r\n"
|
||||
" charset=utf-8\r\n")
|
||||
<< (QStringList() << "Content-Type")
|
||||
<< (QStringList() << "text/html; charset=utf-8");
|
||||
|
||||
QTest::newRow("multi-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length: 1024\r\n"
|
||||
"Content-Encoding: gzip\r\n")
|
||||
<< (QStringList() << "Content-Type" << "Content-Length" << "Content-Encoding")
|
||||
<< (QStringList() << "text/html; charset=utf-8" << "1024" << "gzip");
|
||||
QTest::newRow("multi-field-with-emtpy") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length: 1024\r\n"
|
||||
"Set-Cookie: \r\n"
|
||||
"Content-Encoding: gzip\r\n")
|
||||
<< (QStringList() << "Content-Type" << "Content-Length" << "Set-Cookie" << "Content-Encoding")
|
||||
<< (QStringList() << "text/html; charset=utf-8" << "1024" << "" << "gzip");
|
||||
|
||||
QTest::newRow("lws-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length:\r\n 1024\r\n"
|
||||
"Content-Encoding: gzip\r\n")
|
||||
<< (QStringList() << "Content-Type" << "Content-Length" << "Content-Encoding")
|
||||
<< (QStringList() << "text/html; charset=utf-8" << "1024" << "gzip");
|
||||
|
||||
QTest::newRow("duplicated-field") << QByteArray("Vary: Accept-Language\r\n"
|
||||
"Vary: Cookie\r\n"
|
||||
"Vary: User-Agent\r\n")
|
||||
<< (QStringList() << "Vary")
|
||||
<< (QStringList() << "Accept-Language, Cookie, User-Agent");
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::parseHeader()
|
||||
{
|
||||
QFETCH(QByteArray, headers);
|
||||
QFETCH(QStringList, fields);
|
||||
QFETCH(QStringList, values);
|
||||
|
||||
QHttpNetworkReply reply;
|
||||
reply.parseHeader(headers);
|
||||
for (int i = 0; i < fields.count(); ++i) {
|
||||
//qDebug() << "field" << fields.at(i) << "value" << reply.headerField(fields.at(i)) << "expected" << values.at(i);
|
||||
QString field = reply.headerField(fields.at(i).toLatin1());
|
||||
QCOMPARE(field, values.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QHttpNetworkReply)
|
||||
|
||||
#include "moc_tst_qhttpnetworkreply.cpp"
|
||||
|
||||
#else
|
||||
|
||||
QTEST_NOOP_MAIN
|
||||
|
||||
#endif // QT_NO_HTTP
|
|
@ -1,5 +0,0 @@
|
|||
katie_test(tst_qnetworkdiskcache
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_qnetworkdiskcache.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_qnetworkdiskcache KtNetwork)
|
|
@ -1,699 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the test suite of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/QDirIterator>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtCore/QThread>
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <qnetworkdiskcache.h>
|
||||
#include "../../shared/util.h"
|
||||
|
||||
#define EXAMPLE_URL "http://user:pass@www.example.com/#foo"
|
||||
|
||||
class tst_QNetworkDiskCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void init();
|
||||
void cleanup();
|
||||
void accessAfterRemoveReadyReadSlot();
|
||||
|
||||
private slots:
|
||||
void qnetworkdiskcache_data();
|
||||
void qnetworkdiskcache();
|
||||
|
||||
void prepare();
|
||||
void cacheSize();
|
||||
void clear();
|
||||
void data_data();
|
||||
void data();
|
||||
void metaData();
|
||||
void remove();
|
||||
void accessAfterRemove(); // QTBUG-17400
|
||||
void setCacheDirectory_data();
|
||||
void setCacheDirectory();
|
||||
void updateMetaData();
|
||||
void fileMetaData();
|
||||
void expire();
|
||||
|
||||
void oldCacheVersionFile_data();
|
||||
void oldCacheVersionFile();
|
||||
|
||||
void sync();
|
||||
|
||||
void crashWhenParentingCache();
|
||||
|
||||
private:
|
||||
QUrl url; // used by accessAfterRemove()
|
||||
QNetworkDiskCache *diskCache; // used by accessAfterRemove()
|
||||
};
|
||||
|
||||
// FIXME same as in tst_qnetworkreply.cpp .. could be unified
|
||||
// Does not work for POST/PUT!
|
||||
class MiniHttpServer: public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QTcpSocket *client; // always the last one that was received
|
||||
QByteArray dataToTransmit;
|
||||
QByteArray receivedData;
|
||||
bool doClose;
|
||||
bool multiple;
|
||||
int totalConnections;
|
||||
|
||||
MiniHttpServer(const QByteArray &data) : client(0), dataToTransmit(data), doClose(true), multiple(false), totalConnections(0)
|
||||
{
|
||||
listen();
|
||||
connect(this, SIGNAL(newConnection()), this, SLOT(doAccept()));
|
||||
}
|
||||
|
||||
public slots:
|
||||
void doAccept()
|
||||
{
|
||||
client = nextPendingConnection();
|
||||
client->setParent(this);
|
||||
++totalConnections;
|
||||
connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
}
|
||||
|
||||
void readyReadSlot()
|
||||
{
|
||||
receivedData += client->readAll();
|
||||
int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
|
||||
|
||||
if (doubleEndlPos != -1) {
|
||||
// multiple requests incoming. remove the bytes of the current one
|
||||
if (multiple)
|
||||
receivedData.remove(0, doubleEndlPos+4);
|
||||
|
||||
client->write(dataToTransmit);
|
||||
if (doClose) {
|
||||
client->disconnectFromHost();
|
||||
disconnect(client, 0, this, 0);
|
||||
client = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Subclass that exposes the protected functions.
|
||||
class SubQNetworkDiskCache : public QNetworkDiskCache
|
||||
{
|
||||
public:
|
||||
~SubQNetworkDiskCache()
|
||||
{
|
||||
if (!cacheDirectory().isEmpty())
|
||||
clear();
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData call_fileMetaData(QString const &fileName)
|
||||
{ return SubQNetworkDiskCache::fileMetaData(fileName); }
|
||||
|
||||
qint64 call_expire()
|
||||
{ return SubQNetworkDiskCache::expire(); }
|
||||
|
||||
void setupWithOne(const QUrl &url, const QNetworkCacheMetaData &metaData = QNetworkCacheMetaData())
|
||||
{
|
||||
setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
|
||||
QIODevice *d = 0;
|
||||
if (metaData.isValid()) {
|
||||
d = prepare(metaData);
|
||||
} else {
|
||||
QNetworkCacheMetaData m;
|
||||
m.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeader header("content-type", "text/html");
|
||||
QNetworkCacheMetaData::RawHeaderList list;
|
||||
list.append(header);
|
||||
m.setRawHeaders(list);
|
||||
d = prepare(m);
|
||||
}
|
||||
d->write("Hello World!");
|
||||
insert(d);
|
||||
}
|
||||
};
|
||||
|
||||
// This will be called before the first test function is executed.
|
||||
// It is only called once.
|
||||
void tst_QNetworkDiskCache::initTestCase()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
cache.clear();
|
||||
QString s = QDir::tempPath() + "/diskCache/";
|
||||
QDir dir;
|
||||
dir.rmdir(s + "data7"); // the number is the internal cache version
|
||||
dir.rmdir(s + "prepared");
|
||||
dir.rmdir(s);
|
||||
dir.rmdir(s + "http"); // delete directory used by 4.7 and earlier (would make the tests fail)
|
||||
}
|
||||
|
||||
// This will be called after the last test function is executed.
|
||||
// It is only called once.
|
||||
void tst_QNetworkDiskCache::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
// This will be called before each test function is executed.
|
||||
void tst_QNetworkDiskCache::init()
|
||||
{
|
||||
}
|
||||
|
||||
// This will be called after every test function.
|
||||
void tst_QNetworkDiskCache::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::qnetworkdiskcache_data()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::qnetworkdiskcache()
|
||||
{
|
||||
QUrl url(EXAMPLE_URL);
|
||||
SubQNetworkDiskCache cache;
|
||||
QCOMPARE(cache.cacheDirectory(), QString());
|
||||
QCOMPARE(cache.cacheSize(), qint64(0));
|
||||
cache.clear();
|
||||
QCOMPARE(cache.metaData(QUrl()), QNetworkCacheMetaData());
|
||||
QCOMPARE(cache.remove(QUrl()), false);
|
||||
QCOMPARE(cache.remove(url), false);
|
||||
cache.insert((QIODevice*)0);
|
||||
cache.setCacheDirectory(QString());
|
||||
cache.updateMetaData(QNetworkCacheMetaData());
|
||||
cache.prepare(QNetworkCacheMetaData());
|
||||
QCOMPARE(cache.call_fileMetaData(QString()), QNetworkCacheMetaData());
|
||||
|
||||
// leave one hanging around...
|
||||
QNetworkDiskCache badCache;
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
badCache.prepare(metaData);
|
||||
badCache.setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
badCache.prepare(metaData);
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::prepare()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
|
||||
cache.prepare(metaData);
|
||||
cache.remove(url);
|
||||
}
|
||||
|
||||
// public qint64 cacheSize() const
|
||||
void tst_QNetworkDiskCache::cacheSize()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
QCOMPARE(cache.cacheSize(), qint64(0));
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QIODevice *d = cache.prepare(metaData);
|
||||
cache.insert(d);
|
||||
QVERIFY(cache.cacheSize() > qint64(0));
|
||||
|
||||
cache.clear();
|
||||
QCOMPARE(cache.cacheSize(), qint64(0));
|
||||
}
|
||||
|
||||
static QStringList countFiles(const QString dir)
|
||||
{
|
||||
QStringList list;
|
||||
QDir::Filters filter(QDir::AllEntries | QDir::NoDotAndDotDot);
|
||||
QDirIterator it(dir, filter, QDirIterator::Subdirectories);
|
||||
while (it.hasNext())
|
||||
list.append(it.next());
|
||||
return list;
|
||||
}
|
||||
|
||||
// public void clear()
|
||||
void tst_QNetworkDiskCache::clear()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(url);
|
||||
QVERIFY(cache.cacheSize() > qint64(0));
|
||||
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QCOMPARE(countFiles(cacheDirectory).count(), 1);
|
||||
cache.clear();
|
||||
QCOMPARE(countFiles(cacheDirectory).count(), 0);
|
||||
|
||||
// don't delete files that it didn't create
|
||||
QTemporaryFile file(cacheDirectory + "/XXXXXXXXXX");
|
||||
if (file.open()) {
|
||||
QCOMPARE(countFiles(cacheDirectory).count(), 1);
|
||||
cache.clear();
|
||||
QCOMPARE(countFiles(cacheDirectory).count(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkCacheMetaData)
|
||||
void tst_QNetworkDiskCache::data_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkCacheMetaData>("data");
|
||||
|
||||
QTest::newRow("null") << QNetworkCacheMetaData();
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("type", "bin"));
|
||||
metaData.setRawHeaders(headers);
|
||||
QTest::newRow("null") << metaData;
|
||||
}
|
||||
|
||||
// public QIODevice* data(QUrl const& url)
|
||||
void tst_QNetworkDiskCache::data()
|
||||
{
|
||||
QFETCH(QNetworkCacheMetaData, data);
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(url, data);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
QIODevice *d = cache.data(url);
|
||||
QVERIFY(d);
|
||||
QCOMPARE(d->readAll(), QByteArray("Hello World!"));
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
// public QNetworkCacheMetaData metaData(QUrl const& url)
|
||||
void tst_QNetworkDiskCache::metaData()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("type", "bin"));
|
||||
metaData.setRawHeaders(headers);
|
||||
metaData.setLastModified(QDateTime::currentDateTime());
|
||||
metaData.setExpirationDate(QDateTime::currentDateTime());
|
||||
metaData.setSaveToDisk(true);
|
||||
|
||||
cache.setupWithOne(url, metaData);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
QNetworkCacheMetaData cacheMetaData = cache.metaData(url);
|
||||
QVERIFY(cacheMetaData.isValid());
|
||||
QCOMPARE(metaData, cacheMetaData);
|
||||
}
|
||||
}
|
||||
|
||||
// public bool remove(QUrl const& url)
|
||||
void tst_QNetworkDiskCache::remove()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(url);
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QCOMPARE(countFiles(cacheDirectory).count(), 1);
|
||||
cache.remove(url);
|
||||
QCOMPARE(countFiles(cacheDirectory).count(), 0);
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::accessAfterRemove() // QTBUG-17400
|
||||
{
|
||||
QByteArray data("HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"a");
|
||||
|
||||
MiniHttpServer server(data);
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
SubQNetworkDiskCache subCache;
|
||||
subCache.setCacheDirectory(QLatin1String("cacheDir"));
|
||||
diskCache = &subCache;
|
||||
manager->setCache(&subCache);
|
||||
|
||||
url = QUrl("http://127.0.0.1:" + QString::number(server.serverPort()));
|
||||
QNetworkRequest request(url);
|
||||
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
connect(reply, SIGNAL(readyRead()), this, SLOT(accessAfterRemoveReadyReadSlot()));
|
||||
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::accessAfterRemoveReadyReadSlot()
|
||||
{
|
||||
diskCache->remove(url); // this used to cause a crash later on
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::setCacheDirectory_data()
|
||||
{
|
||||
QTest::addColumn<QString>("cacheDir");
|
||||
QTest::newRow("null") << QString();
|
||||
QDir dir("foo");
|
||||
QTest::newRow("foo") << dir.absolutePath() + QString("/");
|
||||
}
|
||||
|
||||
// public void setCacheDirectory(QString const& cacheDir)
|
||||
void tst_QNetworkDiskCache::setCacheDirectory()
|
||||
{
|
||||
QFETCH(QString, cacheDir);
|
||||
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(cacheDir);
|
||||
QCOMPARE(cache.cacheDirectory(), cacheDir);
|
||||
}
|
||||
|
||||
// public void updateMetaData(QNetworkCacheMetaData const& metaData)
|
||||
void tst_QNetworkDiskCache::updateMetaData()
|
||||
{
|
||||
QUrl url(EXAMPLE_URL);
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setupWithOne(url);
|
||||
|
||||
QNetworkCacheMetaData metaData = cache.metaData(url);
|
||||
metaData.setLastModified(QDateTime::currentDateTime());
|
||||
cache.updateMetaData(metaData);
|
||||
QNetworkCacheMetaData newMetaData = cache.metaData(url);
|
||||
QCOMPARE(newMetaData, metaData);
|
||||
}
|
||||
|
||||
// protected QNetworkCacheMetaData fileMetaData(QString const& fileName)
|
||||
void tst_QNetworkDiskCache::fileMetaData()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(url);
|
||||
|
||||
url.setPassword(QString());
|
||||
url.setFragment(QString());
|
||||
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QStringList list = countFiles(cacheDirectory);
|
||||
QCOMPARE(list.count(), 1);
|
||||
foreach(QString fileName, list) {
|
||||
QFileInfo info(fileName);
|
||||
if (info.isFile()) {
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName);
|
||||
QCOMPARE(metaData.url(), url);
|
||||
}
|
||||
}
|
||||
|
||||
QTemporaryFile file(cacheDirectory + "/qt_temp.XXXXXXXXXX");
|
||||
if (file.open()) {
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(file.fileName());
|
||||
QVERIFY(!metaData.isValid());
|
||||
}
|
||||
}
|
||||
|
||||
// protected qint64 expire()
|
||||
void tst_QNetworkDiskCache::expire()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
QCOMPARE(cache.call_expire(), (qint64)0);
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(url);
|
||||
QVERIFY(cache.call_expire() > (qint64)0);
|
||||
qint64 limit = (1024 * 1024 / 4) * 5;
|
||||
cache.setMaximumCacheSize(limit);
|
||||
|
||||
qint64 max = cache.maximumCacheSize();
|
||||
QCOMPARE(max, limit);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (i % 3 == 0)
|
||||
QTest::qWait(2000);
|
||||
QNetworkCacheMetaData m;
|
||||
m.setUrl(QUrl("http://www.foo.com/" + QString::number(i)));
|
||||
QIODevice *d = cache.prepare(m);
|
||||
QString bigString;
|
||||
bigString.fill(QLatin1Char('Z'), (1024 * 1024 / 4));
|
||||
d->write(bigString.toLatin1().data());
|
||||
cache.insert(d);
|
||||
QVERIFY(cache.call_expire() < max);
|
||||
}
|
||||
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QStringList list = countFiles(cacheDirectory);
|
||||
QStringList cacheList;
|
||||
foreach(QString fileName, list) {
|
||||
QFileInfo info(fileName);
|
||||
if (info.isFile()) {
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName);
|
||||
cacheList.append(metaData.url().toString());
|
||||
}
|
||||
}
|
||||
qSort(cacheList);
|
||||
for (int i = 0; i < cacheList.count(); ++i) {
|
||||
QString fileName = cacheList[i];
|
||||
QCOMPARE(fileName, QString("http://www.foo.com/%1").arg(i + 6));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::oldCacheVersionFile_data()
|
||||
{
|
||||
QTest::addColumn<int>("pass");
|
||||
QTest::newRow("0") << 0;
|
||||
QTest::newRow("1") << 1;
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::oldCacheVersionFile()
|
||||
{
|
||||
QFETCH(int, pass);
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(url);
|
||||
|
||||
if (pass == 0) {
|
||||
QString name;
|
||||
{
|
||||
QTemporaryFile file(cache.cacheDirectory() + "/XXXXXXXXXX.cache");
|
||||
file.setAutoRemove(false);
|
||||
QVERIFY(file.open());
|
||||
QDataStream out(&file);
|
||||
out << qint32(0xe8);
|
||||
out << qint32(2);
|
||||
name = file.fileName();
|
||||
file.close();
|
||||
}
|
||||
|
||||
QVERIFY(QFile::exists(name));
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(name);
|
||||
QVERIFY(!metaData.isValid());
|
||||
QVERIFY(!QFile::exists(name));
|
||||
} else {
|
||||
QStringList files = countFiles(cache.cacheDirectory());
|
||||
QCOMPARE(files.count(), 1);
|
||||
// find the file
|
||||
QString cacheFile;
|
||||
foreach (QString file, files) {
|
||||
QFileInfo info(file);
|
||||
if (info.isFile())
|
||||
cacheFile = file;
|
||||
}
|
||||
QVERIFY(QFile::exists(cacheFile));
|
||||
|
||||
QFile file(cacheFile);
|
||||
QVERIFY(file.open(QFile::ReadWrite));
|
||||
QDataStream out(&file);
|
||||
out << qint32(0xe8);
|
||||
out << qint32(2);
|
||||
file.close();
|
||||
|
||||
QIODevice *device = cache.data(url);
|
||||
QVERIFY(!device);
|
||||
QVERIFY(!QFile::exists(cacheFile));
|
||||
}
|
||||
}
|
||||
|
||||
class Runner : public QThread
|
||||
{
|
||||
|
||||
public:
|
||||
Runner()
|
||||
: QThread()
|
||||
, other(0)
|
||||
{}
|
||||
|
||||
void run()
|
||||
{
|
||||
QByteArray longString = "Hello World, this is some long string, well not really that long";
|
||||
for (int j = 0; j < 10; ++j)
|
||||
longString += longString;
|
||||
QByteArray longString2 = "Help, I am stuck in an autotest!";
|
||||
QUrl url(EXAMPLE_URL);
|
||||
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("type", "bin"));
|
||||
metaData.setRawHeaders(headers);
|
||||
metaData.setLastModified(dt);
|
||||
metaData.setSaveToDisk(true);
|
||||
|
||||
QNetworkCacheMetaData metaData2 = metaData;
|
||||
metaData2.setExpirationDate(dt);
|
||||
|
||||
QNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(QDir::tempPath() + "/diskCache");
|
||||
|
||||
int read = 0;
|
||||
|
||||
int i = 0;
|
||||
for (; i < 5000; ++i) {
|
||||
if (other && other->isFinished())
|
||||
break;
|
||||
|
||||
if (write) {
|
||||
QNetworkCacheMetaData m;
|
||||
if (qrand() % 2 == 0)
|
||||
m = metaData;
|
||||
else
|
||||
m = metaData2;
|
||||
|
||||
if (qrand() % 20 == 1) {
|
||||
//qDebug() << "write update";
|
||||
cache.updateMetaData(m);
|
||||
continue;
|
||||
}
|
||||
|
||||
QIODevice *device = cache.prepare(m);
|
||||
if (qrand() % 20 == 1) {
|
||||
//qDebug() << "write remove";
|
||||
cache.remove(url);
|
||||
continue;
|
||||
}
|
||||
QVERIFY(device);
|
||||
if (qrand() % 2 == 0)
|
||||
device->write(longString);
|
||||
else
|
||||
device->write(longString2);
|
||||
//qDebug() << "write write" << device->size();
|
||||
cache.insert(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData gotMetaData = cache.metaData(url);
|
||||
if (gotMetaData.isValid()) {
|
||||
QVERIFY(gotMetaData == metaData || gotMetaData == metaData2);
|
||||
QIODevice *d = cache.data(url);
|
||||
if (d) {
|
||||
QByteArray x = d->readAll();
|
||||
if (x != longString && x != longString2) {
|
||||
qDebug() << x.length() << QString(x);
|
||||
gotMetaData = cache.metaData(url);
|
||||
qDebug() << (gotMetaData.url().toString())
|
||||
<< gotMetaData.lastModified()
|
||||
<< gotMetaData.expirationDate()
|
||||
<< gotMetaData.saveToDisk();
|
||||
}
|
||||
if (gotMetaData.isValid())
|
||||
QVERIFY(x == longString || x == longString2);
|
||||
read++;
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
if (qrand() % 5 == 1)
|
||||
cache.remove(url);
|
||||
if (qrand() % 5 == 1)
|
||||
cache.clear();
|
||||
QThread::sleep(0);
|
||||
}
|
||||
//qDebug() << "read!" << read << i;
|
||||
}
|
||||
|
||||
QDateTime dt;
|
||||
bool write;
|
||||
Runner *other;
|
||||
};
|
||||
|
||||
void tst_QNetworkDiskCache::crashWhenParentingCache()
|
||||
{
|
||||
// the trick here is to not send the complete response
|
||||
// but some data. So we get a readyRead() and it gets tried
|
||||
// to be saved to the cache
|
||||
QByteArray data("HTTP/1.0 200 OK\r\nCache-Control: max-age=300\r\nAge: 1\r\nContent-Length: 5\r\n\r\n123");
|
||||
MiniHttpServer server(data);
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
QNetworkDiskCache *diskCache = new QNetworkDiskCache(manager); // parent to qnam!
|
||||
// we expect the temp dir to be cleaned at some point anyway
|
||||
diskCache->setCacheDirectory(QString("%1/cacheDir_%2").arg(QDir::tempPath()).arg(QCoreApplication::applicationPid()));
|
||||
manager->setCache(diskCache);
|
||||
|
||||
QUrl url("http://127.0.0.1:" + QString::number(server.serverPort()));
|
||||
QNetworkRequest request(url);
|
||||
// request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
QNetworkReply *reply = manager->get(request); // new reply is parented to qnam
|
||||
|
||||
// wait for readyRead of reply!
|
||||
connect(reply, SIGNAL(readyRead()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
delete manager; // crashed before..
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::sync()
|
||||
{
|
||||
// This tests would be a nice to have but is currently not supported.
|
||||
return;
|
||||
|
||||
QTime midnight(0, 0, 0);
|
||||
qsrand(midnight.secsTo(QTime::currentTime()));
|
||||
Runner reader;
|
||||
reader.dt = QDateTime::currentDateTime();
|
||||
reader.write = false;
|
||||
|
||||
Runner writer;
|
||||
writer.dt = reader.dt;
|
||||
writer.write = true;
|
||||
|
||||
writer.other = &reader;
|
||||
reader.other = &writer;
|
||||
|
||||
writer.start();
|
||||
reader.start();
|
||||
writer.wait();
|
||||
reader.wait();
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkDiskCache)
|
||||
|
||||
#include "moc_tst_qnetworkdiskcache.cpp"
|
||||
|
|
@ -26,9 +26,6 @@
|
|||
#include <qcoreapplication.h>
|
||||
#include <qdebug.h>
|
||||
#include <qnetworkproxy.h>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkInterface>
|
||||
#include <QList>
|
||||
#include <QThread>
|
||||
|
|
|
@ -2,4 +2,4 @@ katie_test(tst_qxmlinputsource
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/tst_qxmlinputsource.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_qxmlinputsource KtXml KtNetwork)
|
||||
target_link_libraries(tst_qxmlinputsource KtXml)
|
||||
|
|
|
@ -21,9 +21,6 @@
|
|||
|
||||
|
||||
#include <QDomDocument>
|
||||
#include <QHttp>
|
||||
#include <QTcpServer>
|
||||
#include <QTcpSocket>
|
||||
#include <QTimer>
|
||||
#include <QtDebug>
|
||||
#include <QtTest/QtTest>
|
||||
|
@ -41,7 +38,6 @@ class tst_QXmlInputSource : public QObject
|
|||
private slots:
|
||||
void reset() const;
|
||||
void resetSimplified() const;
|
||||
void waitForReadyIODevice() const;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -92,100 +88,6 @@ void tst_QXmlInputSource::resetSimplified() const
|
|||
QCOMPARE(source.data(), input);
|
||||
}
|
||||
|
||||
class ServerAndClient : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ServerAndClient(QEventLoop &ev) : success(false)
|
||||
, eventLoop(ev)
|
||||
{
|
||||
setObjectName("serverAndClient");
|
||||
tcpServer = new QTcpServer(this);
|
||||
connect(tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||
tcpServer->listen(QHostAddress::LocalHost, 1088);
|
||||
httpClient = new QHttp(this);
|
||||
connect(httpClient, SIGNAL(requestFinished(int, bool)), SLOT(requestFinished(int, bool)));
|
||||
}
|
||||
|
||||
bool success;
|
||||
QEventLoop &eventLoop;
|
||||
|
||||
public slots:
|
||||
void doIt()
|
||||
{
|
||||
QUrl url("http://127.0.0.1:1088");
|
||||
httpClient->setHost( url.host(), 1088);
|
||||
QHttpRequestHeader req_head("POST", url.path());
|
||||
req_head.setValue("host", url.host());
|
||||
req_head.setValue("user-agent", "xml-test");
|
||||
req_head.setValue("keep-alive", "false");
|
||||
|
||||
QByteArray xmlrpc("<methodCall>\r\n\
|
||||
<methodName>SFD.GetVersion</methodName>\r\n\
|
||||
<params/>\r\n\
|
||||
</methodCall>");
|
||||
req_head.setContentLength(xmlrpc.size());
|
||||
req_head.setContentType("text/xml");
|
||||
|
||||
httpClient->request(req_head, xmlrpc);
|
||||
}
|
||||
|
||||
void requestFinished(int, bool isError)
|
||||
{
|
||||
QVERIFY(!isError);
|
||||
}
|
||||
|
||||
private slots:
|
||||
void newConnection()
|
||||
{
|
||||
QTcpSocket *const s = tcpServer->nextPendingConnection();
|
||||
|
||||
if(s)
|
||||
connect(s, SIGNAL(readyRead()), this, SLOT(readyRead()));
|
||||
}
|
||||
|
||||
void readyRead()
|
||||
{
|
||||
QTcpSocket *const s = static_cast<QTcpSocket *>(sender());
|
||||
int bodyLength = -1;
|
||||
|
||||
while(s->canReadLine())
|
||||
{
|
||||
const QString line(s->readLine());
|
||||
|
||||
if(line.startsWith("content-length:"))
|
||||
bodyLength = line.mid(15).toInt();
|
||||
|
||||
if(line == "\r\n")
|
||||
{
|
||||
if(bodyLength == -1)
|
||||
{
|
||||
qFatal("No length was specified in the header.");
|
||||
}
|
||||
|
||||
QDomDocument domDoc;
|
||||
success = domDoc.setContent(s->read(bodyLength));
|
||||
eventLoop.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QTcpServer *tcpServer;
|
||||
QHttp* httpClient;
|
||||
};
|
||||
|
||||
void tst_QXmlInputSource::waitForReadyIODevice() const
|
||||
{
|
||||
QEventLoop el;
|
||||
ServerAndClient sv(el);
|
||||
QTimer::singleShot(1, &sv, SLOT(doIt()));
|
||||
|
||||
el.exec();
|
||||
QVERIFY(sv.success);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QXmlInputSource)
|
||||
|
||||
#include "moc_tst_qxmlinputsource.cpp"
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
katie_test(tst_bench_qfile_vs_qnetworkaccessmanager
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_bench_qfile_vs_qnetworkaccessmanager KtNetwork)
|
|
@ -1,172 +0,0 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2015 The Qt Company Ltd.
|
||||
** Copyright (C) 2016 Ivailo Monev
|
||||
**
|
||||
** This file is part of the test suite of the Katie Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
#include <QDebug>
|
||||
#include <qtest.h>
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtNetwork/qnetworkreply.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtNetwork/qnetworkaccessmanager.h>
|
||||
#include <QtCore/QTemporaryFile>
|
||||
#include <QtCore/QFile>
|
||||
|
||||
class qfile_vs_qnetworkaccessmanager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
// do not use on symbian.. 100 MB is too large..
|
||||
// but.. this is a manual test anyway, so :)
|
||||
protected:
|
||||
void qnamFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request);
|
||||
void qnamImmediateFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request);
|
||||
void qfileFileRead_iteration();
|
||||
static const int iterations = 10;
|
||||
|
||||
private slots:
|
||||
void qnamFileRead();
|
||||
void qnamImmediateFileRead();
|
||||
void qfileFileRead();
|
||||
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
public:
|
||||
qint64 size;
|
||||
QTemporaryFile testFile;
|
||||
|
||||
qfile_vs_qnetworkaccessmanager() : QObject(), size(0) {};
|
||||
};
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::initTestCase()
|
||||
{
|
||||
testFile.open();
|
||||
QByteArray qba(1*1024*1024, 'x'); // 1 MB
|
||||
for (int i = 0; i < 100; i++) {
|
||||
testFile.write(qba);
|
||||
testFile.flush();
|
||||
size += qba.size();
|
||||
} // 100 MB or 10 MB
|
||||
testFile.reset();
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::cleanupTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::qnamFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request)
|
||||
{
|
||||
QNetworkReply* reply = manager.get(request);
|
||||
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()), Qt::QueuedConnection);
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QByteArray qba = reply->readAll();
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::qnamFileRead()
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
QTime t;
|
||||
QNetworkRequest request(QUrl::fromLocalFile(testFile.fileName()));
|
||||
|
||||
// do 3 dry runs for cache warmup
|
||||
qnamFileRead_iteration(manager, request);
|
||||
qnamFileRead_iteration(manager, request);
|
||||
qnamFileRead_iteration(manager, request);
|
||||
|
||||
t.start();
|
||||
// 10 real runs
|
||||
QBENCHMARK_ONCE {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
qnamFileRead_iteration(manager, request);
|
||||
}
|
||||
}
|
||||
|
||||
qint64 elapsed = t.elapsed();
|
||||
qDebug() << endl << "Finished!";
|
||||
qDebug() << "Bytes:" << size;
|
||||
qDebug() << "Speed:" << (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::qnamImmediateFileRead_iteration(QNetworkAccessManager &manager, QNetworkRequest &request)
|
||||
{
|
||||
QNetworkReply* reply = manager.get(request);
|
||||
QVERIFY(reply->isFinished()); // should be like that!
|
||||
QByteArray qba = reply->readAll();
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::qnamImmediateFileRead()
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
QTime t;
|
||||
QNetworkRequest request(QUrl::fromLocalFile(testFile.fileName()));
|
||||
|
||||
// do 3 dry runs for cache warmup
|
||||
qnamImmediateFileRead_iteration(manager, request);
|
||||
qnamImmediateFileRead_iteration(manager, request);
|
||||
qnamImmediateFileRead_iteration(manager, request);
|
||||
|
||||
t.start();
|
||||
// 10 real runs
|
||||
QBENCHMARK_ONCE {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
qnamImmediateFileRead_iteration(manager, request);
|
||||
}
|
||||
}
|
||||
|
||||
qint64 elapsed = t.elapsed();
|
||||
qDebug() << endl << "Finished!";
|
||||
qDebug() << "Bytes:" << size;
|
||||
qDebug() << "Speed:" << (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::qfileFileRead_iteration()
|
||||
{
|
||||
testFile.reset();
|
||||
QByteArray qba = testFile.readAll();
|
||||
}
|
||||
|
||||
void qfile_vs_qnetworkaccessmanager::qfileFileRead()
|
||||
{
|
||||
QTime t;
|
||||
|
||||
// do 3 dry runs for cache warmup
|
||||
qfileFileRead_iteration();
|
||||
qfileFileRead_iteration();
|
||||
qfileFileRead_iteration();
|
||||
|
||||
t.start();
|
||||
// 10 real runs
|
||||
QBENCHMARK_ONCE {
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
qfileFileRead_iteration();
|
||||
}
|
||||
}
|
||||
|
||||
qint64 elapsed = t.elapsed();
|
||||
qDebug() << endl << "Finished!";
|
||||
qDebug() << "Bytes:" << size;
|
||||
qDebug() << "Speed:" << (qreal(size*iterations) / 1024.0) / (qreal(elapsed) / 1000.0) << "KB/sec";
|
||||
}
|
||||
|
||||
QTEST_MAIN(qfile_vs_qnetworkaccessmanager)
|
||||
|
||||
#include "moc_main.cpp"
|
|
@ -1,5 +0,0 @@
|
|||
katie_test(tst_bench_qnetworkdiskcache
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tst_qnetworkdiskcache.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(tst_bench_qnetworkdiskcache KtNetwork KtGui)
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue