From 142dc7e75faaf76894633851c25a907bd7e8b9b8 Mon Sep 17 00:00:00 2001 From: Aaron Kennedy Date: Wed, 3 Feb 2010 16:45:41 +1000 Subject: XMLHttpRequest redirection --- src/declarative/qml/qmlxmlhttprequest.cpp | 44 ++++++++++---- tests/auto/declarative/shared/testhttpserver.cpp | 11 ++++ tests/auto/declarative/shared/testhttpserver.h | 2 + .../xmlhttprequest/data/redirectError.qml | 23 ++++++++ .../xmlhttprequest/data/redirectRecur.qml | 23 ++++++++ .../declarative/xmlhttprequest/data/redirects.qml | 22 +++++++ .../xmlhttprequest/data/redirecttarget.html | 1 + .../xmlhttprequest/tst_xmlhttprequest.cpp | 67 ++++++++++++++++++++++ 8 files changed, 181 insertions(+), 12 deletions(-) create mode 100644 tests/auto/declarative/xmlhttprequest/data/redirectError.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/redirectRecur.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/redirects.qml create mode 100644 tests/auto/declarative/xmlhttprequest/data/redirecttarget.html diff --git a/src/declarative/qml/qmlxmlhttprequest.cpp b/src/declarative/qml/qmlxmlhttprequest.cpp index f69f254..2c35ebf 100644 --- a/src/declarative/qml/qmlxmlhttprequest.cpp +++ b/src/declarative/qml/qmlxmlhttprequest.cpp @@ -966,12 +966,16 @@ private slots: void finished(); private: + void requestFromUrl(const QUrl &url); + State m_state; bool m_errorFlag; bool m_sendFlag; QString m_method; QUrl m_url; QByteArray m_responseEntityBody; + QByteArray m_data; + int m_redirectCount; typedef QPair HeaderPair; typedef QList HeadersList; @@ -1001,7 +1005,7 @@ private: QmlXMLHttpRequest::QmlXMLHttpRequest() : m_state(Unsent), m_errorFlag(false), m_sendFlag(false), - m_network(0), m_nam(0) + m_redirectCount(0), m_network(0), m_nam(0) { } @@ -1109,16 +1113,10 @@ void QmlXMLHttpRequest::fillHeadersList() } } -QScriptValue QmlXMLHttpRequest::send(const QByteArray &data) +void QmlXMLHttpRequest::requestFromUrl(const QUrl &url) { - m_errorFlag = false; - m_sendFlag = true; - - QScriptValue cbv = dispatchCallback(); - if (cbv.isError()) return cbv; - - m_request.setUrl(m_url); QNetworkRequest request = m_request; + request.setUrl(url); if(m_method == QLatin1String("POST") || m_method == QLatin1String("PUT")) { QVariant var = request.header(QNetworkRequest::ContentTypeHeader); @@ -1153,9 +1151,9 @@ QScriptValue QmlXMLHttpRequest::send(const QByteArray &data) else if (m_method == QLatin1String("HEAD")) m_network = networkAccessManager()->head(request); else if(m_method == QLatin1String("POST")) - m_network = networkAccessManager()->post(request, data); + m_network = networkAccessManager()->post(request, m_data); else if(m_method == QLatin1String("PUT")) - m_network = networkAccessManager()->put(request, data); + m_network = networkAccessManager()->put(request, m_data); QObject::connect(m_network, SIGNAL(downloadProgress(qint64,qint64)), this, SLOT(downloadProgress(qint64))); @@ -1163,6 +1161,16 @@ QScriptValue QmlXMLHttpRequest::send(const QByteArray &data) this, SLOT(error(QNetworkReply::NetworkError))); QObject::connect(m_network, SIGNAL(finished()), this, SLOT(finished())); +} + +QScriptValue QmlXMLHttpRequest::send(const QByteArray &data) +{ + m_errorFlag = false; + m_sendFlag = true; + m_redirectCount = 0; + m_data = data; + + requestFromUrl(m_url); return QScriptValue(); } @@ -1224,6 +1232,7 @@ void QmlXMLHttpRequest::error(QNetworkReply::NetworkError error) m_responseEntityBody = QByteArray(); m_request = QNetworkRequest(); + m_data.clear(); destroyNetwork(); if (error == QNetworkReply::ContentAccessDenied || @@ -1243,9 +1252,19 @@ void QmlXMLHttpRequest::error(QNetworkReply::NetworkError error) if (cbv.isError()) printError(cbv); } +#define XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION 15 void QmlXMLHttpRequest::finished() { - // ### We need to transparently redirect as dictated by the spec + m_redirectCount++; + if (m_redirectCount < XMLHTTPREQUEST_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_network->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + QUrl url = redirect.toUrl(); + destroyNetwork(); + requestFromUrl(url); + return; + } + } m_status = m_network->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); @@ -1259,6 +1278,7 @@ void QmlXMLHttpRequest::finished() if (cbv.isError()) printError(cbv); } m_responseEntityBody.append(m_network->readAll()); + m_data.clear(); destroyNetwork(); if (m_state < Loading) { m_state = Loading; diff --git a/tests/auto/declarative/shared/testhttpserver.cpp b/tests/auto/declarative/shared/testhttpserver.cpp index 6c9d849..490fc95 100644 --- a/tests/auto/declarative/shared/testhttpserver.cpp +++ b/tests/auto/declarative/shared/testhttpserver.cpp @@ -115,6 +115,11 @@ void TestHTTPServer::addAlias(const QString &filename, const QString &alias) aliases.insert(filename, alias); } +void TestHTTPServer::addRedirect(const QString &filename, const QString &redirectName) +{ + redirects.insert(filename, redirectName); +} + bool TestHTTPServer::wait(const QUrl &expect, const QUrl &reply, const QUrl &body) { m_hasFailed = false; @@ -230,6 +235,12 @@ void TestHTTPServer::readyRead() bool TestHTTPServer::reply(QTcpSocket *socket, const QByteArray &fileName) { + if (redirects.contains(fileName)) { + QByteArray response = "HTTP/1.1 302 Found\r\nContent-length: 0\r\nContent-type: text/html; charset=UTF-8\r\nLocation: " + redirects[fileName].toUtf8() + "\r\n\r\n"; + socket->write(response); + return true; + } + for (int ii = 0; ii < dirs.count(); ++ii) { QString dir = dirs.at(ii).first; Mode mode = dirs.at(ii).second; diff --git a/tests/auto/declarative/shared/testhttpserver.h b/tests/auto/declarative/shared/testhttpserver.h index 2a8709f..178122d 100644 --- a/tests/auto/declarative/shared/testhttpserver.h +++ b/tests/auto/declarative/shared/testhttpserver.h @@ -62,6 +62,7 @@ public: bool hasFailed() const; void addAlias(const QString &filename, const QString &aliasName); + void addRedirect(const QString &filename, const QString &redirectName); private slots: void newConnection(); @@ -83,6 +84,7 @@ private: bool m_hasFailed; QHash aliases; + QHash redirects; QTcpServer server; }; diff --git a/tests/auto/declarative/xmlhttprequest/data/redirectError.qml b/tests/auto/declarative/xmlhttprequest/data/redirectError.qml new file mode 100644 index 0000000..6b345cc --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/redirectError.qml @@ -0,0 +1,23 @@ +import Qt 4.6 + +QtObject { + property string url + + property bool dataOK: false + property bool done: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("GET", url); + + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + done = true; + dataOK = x.status == 404; + } + } + + x.send(); + } +} + diff --git a/tests/auto/declarative/xmlhttprequest/data/redirectRecur.qml b/tests/auto/declarative/xmlhttprequest/data/redirectRecur.qml new file mode 100644 index 0000000..c0321dc --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/redirectRecur.qml @@ -0,0 +1,23 @@ +import Qt 4.6 + +QtObject { + property string url + + property bool dataOK: false + property bool done: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("GET", url); + + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + done = true; + dataOK = x.status == 302; + } + } + + x.send(); + } +} + diff --git a/tests/auto/declarative/xmlhttprequest/data/redirects.qml b/tests/auto/declarative/xmlhttprequest/data/redirects.qml new file mode 100644 index 0000000..f6fabdb --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/redirects.qml @@ -0,0 +1,22 @@ +import Qt 4.6 + +QtObject { + property string url + + property bool dataOK: false + property bool done: false + + Component.onCompleted: { + var x = new XMLHttpRequest; + x.open("GET", url); + + x.onreadystatechange = function() { + if (x.readyState == XMLHttpRequest.DONE) { + done = true; + dataOK = x.responseText == "Redirected\n"; + } + } + + x.send(); + } +} diff --git a/tests/auto/declarative/xmlhttprequest/data/redirecttarget.html b/tests/auto/declarative/xmlhttprequest/data/redirecttarget.html new file mode 100644 index 0000000..95f35e0 --- /dev/null +++ b/tests/auto/declarative/xmlhttprequest/data/redirecttarget.html @@ -0,0 +1 @@ +Redirected diff --git a/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp b/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp index d3201e2..253e041 100644 --- a/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp +++ b/tests/auto/declarative/xmlhttprequest/tst_xmlhttprequest.cpp @@ -98,6 +98,7 @@ private slots: void responseText(); void responseXML_invalid(); void invalidMethodUsage(); + void redirects(); // Attributes void document(); @@ -1160,6 +1161,72 @@ void tst_xmlhttprequest::invalidMethodUsage() delete object; } +// Test that XMLHttpRequest transparently redirects +void tst_xmlhttprequest::redirects() +{ + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirecttarget.html"); + server.serveDirectory("data"); + + QmlComponent component(&engine, TEST_FILE("redirects.qml")); + QObject *object = component.beginCreate(engine.rootContext()); + QVERIFY(object != 0); + object->setProperty("url", "http://127.0.0.1:14445/redirect.html"); + object->setProperty("expectedText", ""); + component.completeCreate(); + + TRY_WAIT(object->property("done").toBool() == true); + QCOMPARE(object->property("dataOK").toBool(), true); + + delete object; + } + + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirectmissing.html"); + server.serveDirectory("data"); + + QmlComponent component(&engine, TEST_FILE("redirectError.qml")); + QObject *object = component.beginCreate(engine.rootContext()); + QVERIFY(object != 0); + object->setProperty("url", "http://127.0.0.1:14445/redirect.html"); + object->setProperty("expectedText", ""); + component.completeCreate(); + + TRY_WAIT(object->property("done").toBool() == true); + QCOMPARE(object->property("dataOK").toBool(), true); + + delete object; + } + + { + TestHTTPServer server(SERVER_PORT); + QVERIFY(server.isValid()); + server.addRedirect("redirect.html", "http://127.0.0.1:14445/redirect.html"); + server.serveDirectory("data"); + + QmlComponent component(&engine, TEST_FILE("redirectRecur.qml")); + QObject *object = component.beginCreate(engine.rootContext()); + QVERIFY(object != 0); + object->setProperty("url", "http://127.0.0.1:14445/redirect.html"); + object->setProperty("expectedText", ""); + component.completeCreate(); + + for (int ii = 0; ii < 60; ++ii) { + if (object->property("done").toBool()) break; + QTest::qWait(50); + } + QVERIFY(object->property("done").toBool() == true); + + QCOMPARE(object->property("dataOK").toBool(), true); + + delete object; + } +} + void tst_xmlhttprequest::responseXML_invalid() { QmlComponent component(&engine, TEST_FILE("responseXML_invalid.qml")); -- cgit v0.12