Add redirects policy to QNetworkAccessManager
This patch makes it possible to enable/disable redirects on QNAM level (before it was per-request only). This policy would be applied to all subsequent requests* created by QNAM. The policies we support at the moment: a. Manual - that's what we always had - it's up to a user to handle redirects. b. NoLessSafeRedirectsPolicy - we allow http->http, http->https and https->https redirects, but no protocol 'downgrade' (no https->http redirects). c. SameOriginPolicy - we check that protocol/host/port are the same. Updated tst_qnetworkreply. *We previously were enabling redirect for each request, by setting FollowRedirectsAttribute on QNetworkRequest object. For backward compatibility this attribute has a higher priority (if set) than QNAM's policy (and it will work as NoLessSafeRedirectsPolicy). [ChangeLog][QtNetwork] Added redirects policy to QNAM Task-number: QTPM-239 Task-number: QTPM-237 Change-Id: I493d1728254b71b61b5504937e8e01dca5953527 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>bb10
parent
27e27966bf
commit
cebf1fea4a
|
|
@ -522,17 +522,17 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
|
|||
if (!reply->request().isFollowRedirects())
|
||||
return QUrl();
|
||||
|
||||
QUrl rUrl;
|
||||
QUrl redirectUrl;
|
||||
const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
|
||||
for (const QNetworkReply::RawHeaderPair &header : fields) {
|
||||
if (header.first.toLower() == "location") {
|
||||
rUrl = QUrl::fromEncoded(header.second);
|
||||
redirectUrl = QUrl::fromEncoded(header.second);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the location url is invalid/empty, we emit ProtocolUnknownError
|
||||
if (!rUrl.isValid()) {
|
||||
if (!redirectUrl.isValid()) {
|
||||
emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
|
||||
return QUrl();
|
||||
}
|
||||
|
|
@ -544,24 +544,37 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
|
|||
}
|
||||
|
||||
// Resolve the URL if it's relative
|
||||
if (rUrl.isRelative())
|
||||
rUrl = reply->request().url().resolved(rUrl);
|
||||
if (redirectUrl.isRelative())
|
||||
redirectUrl = reply->request().url().resolved(redirectUrl);
|
||||
|
||||
// Check redirect url protocol
|
||||
QString scheme = rUrl.scheme();
|
||||
if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
|
||||
QString previousUrlScheme = reply->request().url().scheme();
|
||||
// Check if we're doing an unsecure redirect (https -> http)
|
||||
if (previousUrlScheme == QLatin1String("https")
|
||||
&& scheme == QLatin1String("http")) {
|
||||
emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
|
||||
return QUrl();
|
||||
const QUrl priorUrl(reply->request().url());
|
||||
if (redirectUrl.scheme() == QLatin1String("http") || redirectUrl.scheme() == QLatin1String("https")) {
|
||||
switch (reply->request().redirectsPolicy()) {
|
||||
case QNetworkRequest::NoLessSafeRedirectsPolicy:
|
||||
// Check if we're doing an unsecure redirect (https -> http)
|
||||
if (priorUrl.scheme() == QLatin1String("https")
|
||||
&& redirectUrl.scheme() == QLatin1String("http")) {
|
||||
emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
|
||||
return QUrl();
|
||||
}
|
||||
break;
|
||||
case QNetworkRequest::SameOriginRedirectsPolicy:
|
||||
if (priorUrl.host() != redirectUrl.host()
|
||||
|| priorUrl.scheme() != redirectUrl.scheme()
|
||||
|| priorUrl.port() != redirectUrl.port()) {
|
||||
emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
|
||||
return QUrl();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(!"Unexpected redirect policy");
|
||||
}
|
||||
} else {
|
||||
emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
|
||||
return QUrl();
|
||||
}
|
||||
return rUrl;
|
||||
return redirectUrl;
|
||||
}
|
||||
|
||||
void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
|
||||
|
|
|
|||
|
|
@ -48,7 +48,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Oper
|
|||
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
|
||||
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
|
||||
autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false), http2Allowed(false),
|
||||
withCredentials(true), preConnect(false), followRedirect(false), redirectCount(0)
|
||||
withCredentials(true), preConnect(false), redirectCount(0),
|
||||
redirectsPolicy(QNetworkRequest::ManualRedirectsPolicy)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -65,8 +66,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
|
|||
withCredentials(other.withCredentials),
|
||||
ssl(other.ssl),
|
||||
preConnect(other.preConnect),
|
||||
followRedirect(other.followRedirect),
|
||||
redirectCount(other.redirectCount)
|
||||
redirectCount(other.redirectCount),
|
||||
redirectsPolicy(other.redirectsPolicy)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +89,8 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
|
|||
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
|
||||
&& (withCredentials == other.withCredentials)
|
||||
&& (ssl == other.ssl)
|
||||
&& (preConnect == other.preConnect);
|
||||
&& (preConnect == other.preConnect)
|
||||
&& (redirectsPolicy == other.redirectsPolicy);
|
||||
}
|
||||
|
||||
QByteArray QHttpNetworkRequest::methodName() const
|
||||
|
|
@ -229,12 +231,17 @@ void QHttpNetworkRequest::setPreConnect(bool preConnect)
|
|||
|
||||
bool QHttpNetworkRequest::isFollowRedirects() const
|
||||
{
|
||||
return d->followRedirect;
|
||||
return d->redirectsPolicy != QNetworkRequest::ManualRedirectsPolicy;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setFollowRedirects(bool followRedirect)
|
||||
void QHttpNetworkRequest::setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy)
|
||||
{
|
||||
d->followRedirect = followRedirect;
|
||||
d->redirectsPolicy = policy;
|
||||
}
|
||||
|
||||
QNetworkRequest::RedirectsPolicy QHttpNetworkRequest::redirectsPolicy() const
|
||||
{
|
||||
return d->redirectsPolicy;
|
||||
}
|
||||
|
||||
int QHttpNetworkRequest::redirectCount() const
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include <private/qhttpnetworkheader_p.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <qmetatype.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
|
@ -130,7 +131,8 @@ public:
|
|||
void setPreConnect(bool preConnect);
|
||||
|
||||
bool isFollowRedirects() const;
|
||||
void setFollowRedirects(bool followRedirect);
|
||||
void setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy);
|
||||
QNetworkRequest::RedirectsPolicy redirectsPolicy() const;
|
||||
|
||||
int redirectCount() const;
|
||||
void setRedirectCount(int count);
|
||||
|
|
@ -173,8 +175,8 @@ public:
|
|||
bool withCredentials;
|
||||
bool ssl;
|
||||
bool preConnect;
|
||||
bool followRedirect;
|
||||
int redirectCount;
|
||||
QNetworkRequest::RedirectsPolicy redirectsPolicy;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1078,6 +1078,45 @@ void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
|
|||
get(request);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.9
|
||||
|
||||
Sets the manager's redirects policy to be the \a policy specified. This policy
|
||||
will affect all subsequent requests created by the manager.
|
||||
|
||||
Use this function to enable or disable HTTP redirects on the manager's level.
|
||||
|
||||
\note When creating a request QNetworkRequest::RedirectAttributePolicy has
|
||||
the highest priority, next by priority is QNetworkRequest::FollowRedirectsAttribute.
|
||||
Finally, the manager's policy has the lowest priority.
|
||||
|
||||
For backwards compatibility the default value is QNetworkRequest::ManualRedirectsPolicy.
|
||||
This may change in the future and some type of auto-redirect policy will become
|
||||
the default; clients relying on manual redirect handling are encouraged to set
|
||||
this policy explicitly in their code.
|
||||
|
||||
\sa redirectsPolicy(), QNetworkRequest::RedirectsPolicy,
|
||||
QNetworkRequest::FollowRedirectsAttribute
|
||||
*/
|
||||
void QNetworkAccessManager::setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
d->redirectsPolicy = policy;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.9
|
||||
|
||||
Returns the redirect policy that is used when creating new requests.
|
||||
|
||||
\sa setRedirectsPolicy(), QNetworkRequest::RedirectsPolicy
|
||||
*/
|
||||
QNetworkRequest::RedirectsPolicy QNetworkAccessManager::redirectsPolicy() const
|
||||
{
|
||||
Q_D(const QNetworkAccessManager);
|
||||
return d->redirectsPolicy;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.7
|
||||
|
||||
|
|
@ -1147,9 +1186,9 @@ QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &r
|
|||
|
||||
/*!
|
||||
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
|
||||
and request \a originalReq. 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()
|
||||
|
|
@ -1159,11 +1198,20 @@ QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &r
|
|||
The returned object must be in an open state.
|
||||
*/
|
||||
QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &req,
|
||||
const QNetworkRequest &originalReq,
|
||||
QIODevice *outgoingData)
|
||||
{
|
||||
Q_D(QNetworkAccessManager);
|
||||
|
||||
QNetworkRequest req(originalReq);
|
||||
if (req.attribute(QNetworkRequest::RedirectsPolicyAttribute).isNull()
|
||||
&& req.attribute(QNetworkRequest::FollowRedirectsAttribute).isNull()) {
|
||||
// We only apply the general manager's policy if:
|
||||
// - RedirectsPolicyAttribute is not set already on request and
|
||||
// - no FollowRedirectsAttribute is set.
|
||||
req.setAttribute(QNetworkRequest::RedirectsPolicyAttribute, redirectsPolicy());
|
||||
}
|
||||
|
||||
bool isLocalFile = req.url().isLocalFile();
|
||||
QString scheme = req.url().scheme();
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
#define QNETWORKACCESSMANAGER_H
|
||||
|
||||
#include <QtNetwork/qtnetworkglobal.h>
|
||||
#include <QtNetwork/qnetworkrequest.h>
|
||||
#include <QtCore/QObject>
|
||||
#ifndef QT_NO_SSL
|
||||
#include <QtNetwork/QSslConfiguration>
|
||||
|
|
@ -49,7 +50,6 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QIODevice;
|
||||
class QAbstractNetworkCache;
|
||||
class QAuthenticator;
|
||||
|
|
@ -57,7 +57,6 @@ class QByteArray;
|
|||
template<typename T> class QList;
|
||||
class QNetworkCookie;
|
||||
class QNetworkCookieJar;
|
||||
class QNetworkRequest;
|
||||
class QNetworkReply;
|
||||
class QNetworkProxy;
|
||||
class QNetworkProxyFactory;
|
||||
|
|
@ -149,6 +148,9 @@ public:
|
|||
#endif
|
||||
void connectToHost(const QString &hostName, quint16 port = 80);
|
||||
|
||||
void setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy);
|
||||
QNetworkRequest::RedirectsPolicy redirectsPolicy() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
#ifndef QT_NO_NETWORKPROXY
|
||||
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
#include "qnetworkaccessmanager.h"
|
||||
#include "qnetworkaccesscache_p.h"
|
||||
#include "qnetworkaccessbackend_p.h"
|
||||
#include "qnetworkrequest.h"
|
||||
#include "private/qobject_p.h"
|
||||
#include "QtNetwork/qnetworkproxy.h"
|
||||
#include "QtNetwork/qnetworksession.h"
|
||||
|
|
@ -91,6 +92,7 @@ public:
|
|||
#endif
|
||||
cookieJarCreated(false),
|
||||
defaultAccessControl(true),
|
||||
redirectsPolicy(QNetworkRequest::ManualRedirectsPolicy),
|
||||
authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
|
||||
{
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
|
|
@ -193,6 +195,7 @@ public:
|
|||
|
||||
bool cookieJarCreated;
|
||||
bool defaultAccessControl;
|
||||
QNetworkRequest::RedirectsPolicy redirectsPolicy;
|
||||
|
||||
// The cache with authorization data:
|
||||
QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
|
||||
|
|
|
|||
|
|
@ -655,8 +655,14 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
|
|||
}
|
||||
#endif
|
||||
|
||||
if (newHttpRequest.attribute(QNetworkRequest::FollowRedirectsAttribute).toBool())
|
||||
httpRequest.setFollowRedirects(true);
|
||||
auto redirectsPolicy = QNetworkRequest::ManualRedirectsPolicy;
|
||||
const QVariant value = newHttpRequest.attribute(QNetworkRequest::RedirectsPolicyAttribute);
|
||||
if (value.isValid())
|
||||
redirectsPolicy = value.value<QNetworkRequest::RedirectsPolicy>();
|
||||
else if (newHttpRequest.attribute(QNetworkRequest::FollowRedirectsAttribute).toBool())
|
||||
redirectsPolicy = QNetworkRequest::NoLessSafeRedirectsPolicy;
|
||||
|
||||
httpRequest.setRedirectsPolicy(redirectsPolicy);
|
||||
|
||||
httpRequest.setPriority(convert(newHttpRequest.priority()));
|
||||
|
||||
|
|
|
|||
|
|
@ -289,6 +289,12 @@ QT_BEGIN_NAMESPACE
|
|||
marked to be decompressed automatically.
|
||||
(This value was introduced in 5.9.)
|
||||
|
||||
\value RedirectsPolicyAttribute
|
||||
Requests only, type: QMetaType::Int, should be one of the
|
||||
QNetworkRequest::RedirectsPolicy values (default: ManualRedirectsPolicy).
|
||||
This attribute obsoletes FollowRedirectsAttribute.
|
||||
(This value was introduced in 5.9.)
|
||||
|
||||
\value User
|
||||
Special type. Additional information can be passed in
|
||||
QVariants with types ranging from User to UserMax. The default
|
||||
|
|
@ -336,6 +342,26 @@ QT_BEGIN_NAMESPACE
|
|||
\value Manual indicates behaviour has been manually overridden.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\enum QNetworkRequest::RedirectsPolicy
|
||||
\since 5.9
|
||||
|
||||
Indicates whether the Network Access API should automatically follow a
|
||||
HTTP redirect response or not.
|
||||
|
||||
\value ManualRedirectsPolicy Default value: not following any redirects.
|
||||
|
||||
\value NoLessSafeRedirectsPolicy Only "http"->"http", "http" -> "https"
|
||||
or "https" -> "https" redirects are allowed.
|
||||
Equivalent to setting the old FollowRedirectsAttribute
|
||||
to true
|
||||
|
||||
\value SameOriginRedirectsPolicy Require the same protocol, host and port.
|
||||
Note, http://example.com and http://example.com:80
|
||||
will fail with this policy (implicit/explicit ports
|
||||
are considered to be a mismatch).
|
||||
*/
|
||||
|
||||
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
||||
class QSslConfiguration;
|
||||
|
||||
class QNetworkRequestPrivate;
|
||||
|
|
@ -92,6 +91,7 @@ public:
|
|||
HTTP2AllowedAttribute,
|
||||
HTTP2WasUsedAttribute,
|
||||
OriginalContentLengthAttribute,
|
||||
RedirectsPolicyAttribute,
|
||||
|
||||
User = 1000,
|
||||
UserMax = 32767
|
||||
|
|
@ -113,6 +113,13 @@ public:
|
|||
LowPriority = 5
|
||||
};
|
||||
|
||||
enum RedirectsPolicy {
|
||||
ManualRedirectsPolicy,
|
||||
NoLessSafeRedirectsPolicy,
|
||||
SameOriginRedirectsPolicy
|
||||
};
|
||||
|
||||
|
||||
explicit QNetworkRequest(const QUrl &url = QUrl());
|
||||
QNetworkRequest(const QNetworkRequest &other);
|
||||
~QNetworkRequest();
|
||||
|
|
@ -169,5 +176,6 @@ Q_DECLARE_SHARED(QNetworkRequest)
|
|||
QT_END_NAMESPACE
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkRequest)
|
||||
Q_DECLARE_METATYPE(QNetworkRequest::RedirectsPolicy)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -476,6 +476,10 @@ private Q_SLOTS:
|
|||
void ioHttpChangeMaxRedirects();
|
||||
void ioHttpRedirectErrors_data();
|
||||
void ioHttpRedirectErrors();
|
||||
void ioHttpRedirectPolicy_data();
|
||||
void ioHttpRedirectPolicy();
|
||||
void ioHttpRedirectPolicyErrors_data();
|
||||
void ioHttpRedirectPolicyErrors();
|
||||
#ifndef QT_NO_SSL
|
||||
void putWithServerClosingConnectionImmediately();
|
||||
#endif
|
||||
|
|
@ -8163,6 +8167,171 @@ void tst_QNetworkReply::ioHttpRedirectErrors()
|
|||
QCOMPARE(spy.count(), 1);
|
||||
QCOMPARE(reply->error(), error);
|
||||
}
|
||||
|
||||
struct SameOriginRedirector : MiniHttpServer
|
||||
{
|
||||
SameOriginRedirector(const QByteArray &data, bool ssl = false)
|
||||
: MiniHttpServer(data, ssl)
|
||||
{ }
|
||||
|
||||
std::vector<QByteArray> responses;
|
||||
|
||||
void reply() override
|
||||
{
|
||||
if (responses.empty()) {
|
||||
dataToTransmit.clear();
|
||||
} else {
|
||||
dataToTransmit = responses.back();
|
||||
responses.pop_back();
|
||||
}
|
||||
|
||||
MiniHttpServer::reply();
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QNetworkReply::ioHttpRedirectPolicy_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::RedirectsPolicy>("policy");
|
||||
QTest::addColumn<bool>("ssl");
|
||||
QTest::addColumn<int>("redirectCount");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
|
||||
QTest::newRow("manual-nossl") << QNetworkRequest::ManualRedirectsPolicy << false << 0 << 307;
|
||||
QTest::newRow("manual-ssl") << QNetworkRequest::ManualRedirectsPolicy << true << 0 << 307;
|
||||
QTest::newRow("nolesssafe-nossl") << QNetworkRequest::NoLessSafeRedirectsPolicy << false << 1 << 200;
|
||||
QTest::newRow("nolesssafe-ssl") << QNetworkRequest::NoLessSafeRedirectsPolicy << true << 1 << 200;
|
||||
QTest::newRow("same-origin-nossl") << QNetworkRequest::SameOriginRedirectsPolicy << false << 1 << 200;
|
||||
QTest::newRow("same-origin-ssl") << QNetworkRequest::SameOriginRedirectsPolicy << true << 1 << 200;
|
||||
}
|
||||
|
||||
void tst_QNetworkReply::ioHttpRedirectPolicy()
|
||||
{
|
||||
QFETCH(const QNetworkRequest::RedirectsPolicy, policy);
|
||||
|
||||
QFETCH(const bool, ssl);
|
||||
#ifdef QT_NO_SSL
|
||||
if (ssl)
|
||||
QSKIP("SSL is not supported");
|
||||
#endif
|
||||
|
||||
QFETCH(const int, redirectCount);
|
||||
QFETCH(const int, statusCode);
|
||||
|
||||
// Setup HTTP server.
|
||||
SameOriginRedirector redirectServer("", ssl);
|
||||
|
||||
QUrl url(QLatin1String(
|
||||
#ifndef QT_NO_SSL
|
||||
ssl ? "https://localhost" :
|
||||
#endif
|
||||
"http://localhost"));
|
||||
|
||||
url.setPort(redirectServer.serverPort());
|
||||
redirectServer.responses.push_back("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
|
||||
redirectServer.responses.push_back(tempRedirectReplyStr().arg(QString(url.toEncoded())).toLatin1());
|
||||
|
||||
// This is the default one we preserve between tests.
|
||||
QCOMPARE(manager.redirectsPolicy(), QNetworkRequest::ManualRedirectsPolicy);
|
||||
|
||||
manager.setRedirectsPolicy(policy);
|
||||
QCOMPARE(manager.redirectsPolicy(), policy);
|
||||
QNetworkReplyPtr reply(manager.get(QNetworkRequest(url)));
|
||||
if (ssl)
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
// Restore default:
|
||||
manager.setRedirectsPolicy(QNetworkRequest::ManualRedirectsPolicy);
|
||||
QSignalSpy redirectSpy(reply.data(), SIGNAL(redirected(QUrl)));
|
||||
QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
|
||||
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
|
||||
QCOMPARE(finishedSpy.count(), 1);
|
||||
QCOMPARE(redirectSpy.count(), redirectCount);
|
||||
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
|
||||
}
|
||||
|
||||
void tst_QNetworkReply::ioHttpRedirectPolicyErrors_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::RedirectsPolicy>("policy");
|
||||
QTest::addColumn<bool>("ssl");
|
||||
QTest::addColumn<QString>("location");
|
||||
QTest::addColumn<int>("maxRedirects");
|
||||
QTest::addColumn<QNetworkReply::NetworkError>("expectedError");
|
||||
|
||||
// 1. NoLessSafeRedirectsPolicy
|
||||
QTest::newRow("nolesssafe-nossl-nossl-too-many") << QNetworkRequest::NoLessSafeRedirectsPolicy
|
||||
<< false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
|
||||
QTest::newRow("nolesssafe-ssl-ssl-too-many") << QNetworkRequest::NoLessSafeRedirectsPolicy
|
||||
<< true << QString("https:/localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
|
||||
QTest::newRow("nolesssafe-ssl-nossl-insecure-redirect") << QNetworkRequest::NoLessSafeRedirectsPolicy
|
||||
<< true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
// 2. SameOriginRedirectsPolicy
|
||||
QTest::newRow("same-origin-nossl-nossl-too-many") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< false << QString("http://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
|
||||
QTest::newRow("same-origin-ssl-ssl-too-many") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< true << QString("https://localhost:%1") << 0 << QNetworkReply::TooManyRedirectsError;
|
||||
QTest::newRow("same-origin-https-http-wrong-protocol") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< true << QString("http://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
QTest::newRow("same-origin-http-https-wrong-protocol") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< false << QString("https://localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
QTest::newRow("same-origin-http-http-wrong-host") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< false << QString("http://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
QTest::newRow("same-origin-https-https-wrong-host") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< true << QString("https://not-so-localhost:%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
QTest::newRow("same-origin-http-http-wrong-port") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< false << QString("http://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
QTest::newRow("same-origin-https-https-wrong-port") << QNetworkRequest::SameOriginRedirectsPolicy
|
||||
<< true << QString("https://localhost/%1") << 50 << QNetworkReply::InsecureRedirectError;
|
||||
}
|
||||
|
||||
void tst_QNetworkReply::ioHttpRedirectPolicyErrors()
|
||||
{
|
||||
QFETCH(const QNetworkRequest::RedirectsPolicy, policy);
|
||||
// This should never happen:
|
||||
QVERIFY(policy != QNetworkRequest::ManualRedirectsPolicy);
|
||||
|
||||
QFETCH(const bool, ssl);
|
||||
QFETCH(const QString, location);
|
||||
QFETCH(const int, maxRedirects);
|
||||
QFETCH(const QNetworkReply::NetworkError, expectedError);
|
||||
|
||||
#ifdef QT_NO_SSL
|
||||
if (ssl || location.contains("https"))
|
||||
QSKIP("SSL required to run this test");
|
||||
#endif
|
||||
|
||||
// Setup the server.
|
||||
MiniHttpServer server("", ssl);
|
||||
server.setDataToTransmit(tempRedirectReplyStr().arg(location.arg(server.serverPort())).toLatin1());
|
||||
|
||||
QUrl url(QLatin1String(
|
||||
#ifndef QT_NO_SSL
|
||||
ssl ? "https://localhost" :
|
||||
#endif
|
||||
"http://localhost"));
|
||||
url.setPort(server.serverPort());
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setMaximumRedirectsAllowed(maxRedirects);
|
||||
// We always reset the policy to the default one ('Manual') after any related
|
||||
// test is finished:
|
||||
QCOMPARE(manager.redirectsPolicy(), QNetworkRequest::ManualRedirectsPolicy);
|
||||
manager.setRedirectsPolicy(policy);
|
||||
QCOMPARE(manager.redirectsPolicy(), policy);
|
||||
|
||||
QNetworkReplyPtr reply(manager.get(request));
|
||||
// Set it back to default:
|
||||
manager.setRedirectsPolicy(QNetworkRequest::ManualRedirectsPolicy);
|
||||
|
||||
if (ssl)
|
||||
reply->ignoreSslErrors();
|
||||
|
||||
QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError)));
|
||||
|
||||
QCOMPARE(waitForFinish(reply), int(Failure));
|
||||
QCOMPARE(spy.count(), 1);
|
||||
QCOMPARE(reply->error(), expectedError);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
|
||||
class PutWithServerClosingConnectionImmediatelyHandler: public QObject
|
||||
|
|
|
|||
Loading…
Reference in New Issue