summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2016-12-28 15:27:57 +0100
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2017-01-17 18:51:32 +0000
commitcebf1fea4a6802b8999469f647f52171e87d02b6 (patch)
treeca858bb7eb3b482d1e555c285b08c0e627497256 /src/network
parent27e27966bf01b4c42343100a49b1dd6b014025fe (diff)
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>
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp41
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp21
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h6
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp56
-rw-r--r--src/network/access/qnetworkaccessmanager.h6
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h3
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp10
-rw-r--r--src/network/access/qnetworkrequest.cpp26
-rw-r--r--src/network/access/qnetworkrequest.h10
9 files changed, 147 insertions, 32 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 128f75f93b..e5c6c2f81c 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -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)
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 802043d847..7862d464e1 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -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
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index d1abb76e28..3900e9080e 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -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;
};
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index b763547a15..949b5563d9 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1079,6 +1079,45 @@ void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
}
/*!
+ \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
Sends a custom request to the server identified by the URL of \a request.
@@ -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();
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 649013cced..c0a27100e5 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -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);
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index b4b5e6a789..b996e3d58d 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -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;
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 00f3468ebd..03ed596586 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -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()));
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index bc2507ca51..abc924d0e2 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -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:
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 9a3a7f0fb5..52c398663a 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -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