summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Hogan <robert@webkit.org>2010-04-26 18:46:28 (GMT)
committerMarkus Goetz <Markus.Goetz@nokia.com>2010-05-04 08:52:21 (GMT)
commitf3066a7f3dc3c3a69344193b0fb50504c5760086 (patch)
treefa02ca7074a996cb30e0980ba7120f7d3a6eef27
parentbff118db81c617fb2460c8341f779adb4d4ec28d (diff)
downloadQt-f3066a7f3dc3c3a69344193b0fb50504c5760086.zip
Qt-f3066a7f3dc3c3a69344193b0fb50504c5760086.tar.gz
Qt-f3066a7f3dc3c3a69344193b0fb50504c5760086.tar.bz2
QNAM HTTP: Introduce attributes for controlling cookies and auth
Introduced QNetworkRequest::CookieLoadControlAttribute, QNetworkRequest::CookieSaveControlAttribute and QNetworkRequest::AuthenticationReuseControlAttribute These are true by default. They only come into play when QtWebKit processes a cross-origin XMLHttpRequest. In such cases QtWebKit sets each of the attributes to false when it creates a QNetworkRequest where the XMLHttpRequest is cross-origin and has withCredentials set to false: var req = new XMLHttpRequest; req.open("GET", "http://host/resource.php", false); req.withCredentials = false; // actually false by default For more information: http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag The QtWebKit counterpart of this patch is tracked at: https://bugs.webkit.org/show_bug.cgi?id=32967 Merge-Request: 592 Reviewed-by: Markus Goetz
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp11
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h2
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp7
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp13
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp5
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccesshttpbackend.cpp13
-rw-r--r--src/network/access/qnetworkaccesshttpbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp27
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp8
-rw-r--r--src/network/access/qnetworkrequest.cpp45
-rw-r--r--src/network/access/qnetworkrequest.h7
13 files changed, 128 insertions, 16 deletions
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 559124f..31c64f0 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -343,9 +343,16 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
copyCredentials(i, auth, isProxy);
QMetaObject::invokeMethod(q, "_q_restartAuthPendingRequests", Qt::QueuedConnection);
}
+ } 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 q->cacheCredentials(reply->request(), auth, q);
}
- // changing values in QAuthenticator will reset the 'phase'
- if (priv->phase == QAuthenticatorPrivate::Done) {
+ // - Changing values in QAuthenticator will reset the 'phase'.
+ // - 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()) {
// authentication is cancelled, send the current contents to the user.
emit channels[i].reply->headerChanged();
emit channels[i].reply->readyRead();
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index b5bd300..51666d6 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -133,6 +133,8 @@ Q_SIGNALS:
#endif
void authenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *authenticator,
const QHttpNetworkConnection *connection = 0);
+ void cacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *authenticator,
+ const QHttpNetworkConnection *connection = 0);
void error(QNetworkReply::NetworkError errorCode, const QString &detail = QString());
private:
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 3b7bc9e..d24eb1f 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -173,7 +173,7 @@ bool QHttpNetworkConnectionChannel::sendRequest()
pendingEncrypt = false;
// if the url contains authentication parameters, use the new ones
// both channels will use the new authentication parameters
- if (!request.url().userInfo().isEmpty()) {
+ if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
QUrl url = request.url();
QAuthenticator &auth = authenticator;
if (url.userName() != auth.user()
@@ -187,7 +187,10 @@ bool QHttpNetworkConnectionChannel::sendRequest()
url.setUserInfo(QString());
request.setUrl(url);
}
- connection->d_func()->createAuthorization(socket, request);
+ // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
+ // and withCredentials has not been set to true.
+ if (request.withCredentials())
+ connection->d_func()->createAuthorization(socket, request);
#ifndef QT_NO_NETWORKPROXY
QByteArray header = QHttpNetworkRequestPrivate::header(request,
(connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 9eb2399..639025e 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -49,7 +49,7 @@ 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)
+ autoDecompress(false), pipeliningAllowed(false), withCredentials(true)
{
}
@@ -62,6 +62,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
autoDecompress = other.autoDecompress;
pipeliningAllowed = other.pipeliningAllowed;
customVerb = other.customVerb;
+ withCredentials = other.withCredentials;
}
QHttpNetworkRequestPrivate::~QHttpNetworkRequestPrivate()
@@ -274,6 +275,16 @@ 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;
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index 1b35a84..15cab73 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -113,6 +113,9 @@ public:
bool isPipeliningAllowed() const;
void setPipeliningAllowed(bool b);
+ bool withCredentials() const;
+ void setWithCredentials(bool b);
+
void setUploadByteDevice(QNonContiguousByteDevice *bd);
QNonContiguousByteDevice* uploadByteDevice() const;
@@ -142,6 +145,7 @@ public:
mutable QNonContiguousByteDevice* uploadByteDevice;
bool autoDecompress;
bool pipeliningAllowed;
+ bool withCredentials;
};
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index f188bd5..2a02c99 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -327,6 +327,11 @@ void QNetworkAccessBackend::authenticationRequired(QAuthenticator *authenticator
manager->authenticationRequired(this, authenticator);
}
+void QNetworkAccessBackend::cacheCredentials(QAuthenticator *authenticator)
+{
+ manager->addCredentials(this->reply->url, authenticator);
+}
+
void QNetworkAccessBackend::metaDataChanged()
{
reply->metaDataChanged();
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 4ce37a6..4fe6de6 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -188,6 +188,7 @@ protected slots:
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *auth);
#endif
void authenticationRequired(QAuthenticator *auth);
+ void cacheCredentials(QAuthenticator *auth);
void metaDataChanged();
void redirectionRequested(const QUrl &destination);
void sslErrors(const QList<QSslError> &errors);
diff --git a/src/network/access/qnetworkaccesshttpbackend.cpp b/src/network/access/qnetworkaccesshttpbackend.cpp
index 3154ed6..a6c5c02 100644
--- a/src/network/access/qnetworkaccesshttpbackend.cpp
+++ b/src/network/access/qnetworkaccesshttpbackend.cpp
@@ -346,6 +346,8 @@ void QNetworkAccessHttpBackend::setupConnection()
#endif
connect(http, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)));
+ connect(http, SIGNAL(cacheCredentials(QHttpNetworkRequest,QAuthenticator*)),
+ SLOT(httpCacheCredentials(QHttpNetworkRequest,QAuthenticator*)));
connect(http, SIGNAL(error(QNetworkReply::NetworkError,QString)),
SLOT(httpError(QNetworkReply::NetworkError,QString)));
#ifndef QT_NO_OPENSSL
@@ -578,6 +580,11 @@ void QNetworkAccessHttpBackend::postRequest()
if (request().attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool() == true)
httpRequest.setPipeliningAllowed(true);
+ if (static_cast<QNetworkRequest::LoadControl>
+ (request().attribute(QNetworkRequest::AuthenticationReuseAttribute,
+ QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
+ httpRequest.setWithCredentials(false);
+
httpReply = http->sendRequest(httpRequest);
httpReply->setParent(this);
#ifndef QT_NO_OPENSSL
@@ -861,6 +868,12 @@ void QNetworkAccessHttpBackend::httpAuthenticationRequired(const QHttpNetworkReq
authenticationRequired(auth);
}
+void QNetworkAccessHttpBackend::httpCacheCredentials(const QHttpNetworkRequest &,
+ QAuthenticator *auth)
+{
+ cacheCredentials(auth);
+}
+
void QNetworkAccessHttpBackend::httpError(QNetworkReply::NetworkError errorCode,
const QString &errorString)
{
diff --git a/src/network/access/qnetworkaccesshttpbackend_p.h b/src/network/access/qnetworkaccesshttpbackend_p.h
index e5cc0ab..254907f 100644
--- a/src/network/access/qnetworkaccesshttpbackend_p.h
+++ b/src/network/access/qnetworkaccesshttpbackend_p.h
@@ -107,6 +107,7 @@ private slots:
void replyFinished();
void replyHeaderChanged();
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
+ void httpCacheCredentials(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
bool sendCacheContents(const QNetworkCacheMetaData &metaData);
void finished(); // override
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index feb9d99..1c7661d 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -948,10 +948,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
// but the data that is outgoing is random-access
request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
}
- if (d->cookieJar) {
- QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
- if (!cookies.isEmpty())
- request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies));
+
+ 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, qVariantFromValue(cookies));
+ }
}
// first step: create the reply
@@ -967,11 +972,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
priv->manager = this;
// second step: fetch cached credentials
- QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url);
- if (cred) {
- url.setUserName(cred->user);
- url.setPassword(cred->password);
- priv->urlForLastAuthentication = url;
+ if (static_cast<QNetworkRequest::LoadControl>
+ (request.attribute(QNetworkRequest::AuthenticationReuseAttribute,
+ QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Automatic) {
+ QNetworkAuthenticationCredential *cred = d->fetchCachedCredentials(url);
+ if (cred) {
+ url.setUserName(cred->user);
+ url.setPassword(cred->password);
+ priv->urlForLastAuthentication = url;
+ }
}
// third step: find a backend
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index 128d18f..31ee2a4 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -673,8 +673,12 @@ void QNetworkReplyImplPrivate::error(QNetworkReplyImpl::NetworkError code, const
void QNetworkReplyImplPrivate::metaDataChanged()
{
Q_Q(QNetworkReplyImpl);
- // do we have cookies?
- if (cookedHeaders.contains(QNetworkRequest::SetCookieHeader) && !manager.isNull()) {
+ // 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();
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 61c116d..79f169f 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -190,6 +190,40 @@ QT_BEGIN_NAMESPACE
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 http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information.
+
+ \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 http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information.
+
+ \value AuthenticationReuseControlAttribute
+ 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 http://www.w3.org/TR/XMLHttpRequest2/#credentials-flag for more information.
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -222,6 +256,17 @@ QT_BEGIN_NAMESPACE
if the item was not cached (i.e., off-line mode)
*/
+/*!
+ \enum QNetworkRequest::LoadControl
+
+ 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:
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index a0ef1a6..d2945c4 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -79,6 +79,9 @@ public:
HttpPipeliningAllowedAttribute,
HttpPipeliningWasUsedAttribute,
CustomVerbAttribute,
+ CookieLoadControlAttribute,
+ AuthenticationReuseAttribute,
+ CookieSaveControlAttribute,
User = 1000,
UserMax = 32767
@@ -89,6 +92,10 @@ public:
PreferCache,
AlwaysCache
};
+ enum LoadControl {
+ Automatic = 0,
+ Manual
+ };
enum Priority {
HighPriority = 1,