drop network access classes [ci reset]

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-02-19 17:38:49 +02:00
parent 5e74cf5e11
commit 59da27ca99
105 changed files with 78 additions and 27890 deletions

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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',

View file

@ -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",

View file

@ -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

View file

@ -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
*/

View file

@ -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().

View file

@ -204,7 +204,6 @@ QT_BEGIN_NAMESPACE
\omitvalue ShowWindowRequest
\omitvalue Style
\omitvalue ThreadChange
\omitvalue NetworkReplyUpdated
\omitvalue FutureCallOut
*/

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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, &currentChunkSize);
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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 &current = 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 &current = 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"

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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()
{
}

View file

@ -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

View file

@ -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();
};

View file

@ -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.

View file

@ -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

View file

@ -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
*/
/*!

View file

@ -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"

View file

@ -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

View file

@ -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);

View file

@ -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}

View file

@ -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
{

View file

@ -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;

View file

@ -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") },

View file

@ -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

View file

@ -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().

View file

@ -1,5 +0,0 @@
katie_test(tst_qhttpnetworkreply
${CMAKE_CURRENT_SOURCE_DIR}/tst_qhttpnetworkreply.cpp
)
target_link_libraries(tst_qhttpnetworkreply KtNetwork)

View file

@ -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

View file

@ -1,5 +0,0 @@
katie_test(tst_qnetworkdiskcache
${CMAKE_CURRENT_SOURCE_DIR}/tst_qnetworkdiskcache.cpp
)
target_link_libraries(tst_qnetworkdiskcache KtNetwork)

View file

@ -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"

View file

@ -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>

View file

@ -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)

View file

@ -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"

View file

@ -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)

View file

@ -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"

View file

@ -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