diff options
author | Mandeep Sandhu <mandeepsandhu.chd@gmail.com> | 2014-03-17 19:21:25 +0530 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-03-09 08:32:14 +0000 |
commit | d815de8c26ccee33713e37a0fec2982755dcfe1f (patch) | |
tree | 9bfae5a77f94368c139e5290399a9f7ec6471367 /tests/auto/network | |
parent | f7118e7f1b495fc0a0b8d83dff1a207dfddb09aa (diff) |
QNetworkAccessManager: Support HTTP redirection
This commit adds support for following HTTP redirect responses on a per
request basis.
This behavior is disabled by default. It can be switched on by
setting the QNetworkRequest::FollowRedirectAttribute to true.
2 new error codes have been added to QNetworkReply:
* TooManyRedirectsError: Set when the number of redirects exceed a
given value set by the user (defaults to 50 if not set)
* UnsecureRedirectError: Set when we are redirecting from a 'https'
to 'http' protocol.
Test cases for the following scenarios:
* Single HTTP redirect using local test server
* Changing max-redirects
* Testing all redirect related error scenarios
The next commit will extend this feature at a QNAM level.
Task-number: QTBUG-8232
Change-Id: If9e28ad12bad08bcdc5bc511b1cd59dc9d8150f0
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@theqtcompany.com>
Diffstat (limited to 'tests/auto/network')
-rw-r--r-- | tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp | 158 |
1 files changed, 156 insertions, 2 deletions
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp index 3c3c5755eb..cd14e42f8f 100644 --- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp +++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp @@ -121,6 +121,14 @@ class tst_QNetworkReply: public QObject return s; }; + static QString tempRedirectReplyStr() { + QString s = "HTTP/1.1 307 Temporary Redirect\r\n" + "Content-Type: text/plain\r\n" + "location: %1\r\n" + "\r\n"; + return s; + }; + QEventLoop *loop; enum RunSimpleRequestReturn { Timeout = 0, Success, Failure }; int returnCode; @@ -458,6 +466,11 @@ private Q_SLOTS: void putWithRateLimiting(); + void ioHttpSingleRedirect(); + void ioHttpChangeMaxRedirects(); + void ioHttpRedirectErrors_data(); + void ioHttpRedirectErrors(); + // NOTE: This test must be last! void parentingRepliesToTheApp(); private: @@ -595,10 +608,16 @@ protected: virtual void reply() { Q_ASSERT(!client.isNull()); // we need to emulate the bytesWrittenSlot call if the data is empty. - if (dataToTransmit.size() == 0) + if (dataToTransmit.size() == 0) { QMetaObject::invokeMethod(this, "bytesWrittenSlot", Qt::QueuedConnection); - else + } else { client->write(dataToTransmit); + // FIXME: For SSL connections, if we don't flush the socket, the + // client never receives the data and since we're doing a disconnect + // immediately afterwards, it causes a RemoteHostClosedError for the + // client + client->flush(); + } } private: void connectSocketSignals() @@ -7958,7 +7977,142 @@ void tst_QNetworkReply::putWithRateLimiting() QCOMPARE(uploadedData, data); } +void tst_QNetworkReply::ioHttpSingleRedirect() +{ + QUrl localhost = QUrl("http://localhost"); + QByteArray http200Reply = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + + // Setup server to which the second server will redirect to + MiniHttpServer server2(http200Reply); + + QUrl redirectUrl = QUrl(localhost); + redirectUrl.setPort(server2.serverPort()); + + QByteArray tempRedirectReply = + tempRedirectReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); + + + // Setup redirect server + MiniHttpServer server(tempRedirectReply); + + localhost.setPort(server.serverPort()); + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl))); + QSignalSpy finSpy(reply.data(), SIGNAL(finished())); + + QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply)); + + // Redirected and finished should be emitted exactly once + QCOMPARE(redSpy.count(), 1); + QCOMPARE(finSpy.count(), 1); + + // Original URL should not be changed after redirect + QCOMPARE(request.url(), localhost); + + // Verify Redirect url + QList<QVariant> args = redSpy.takeFirst(); + QCOMPARE(args.at(0).toUrl(), redirectUrl); + + // Reply url is set to the redirect url + QCOMPARE(reply->url(), redirectUrl); + QCOMPARE(reply->error(), QNetworkReply::NoError); +} + +void tst_QNetworkReply::ioHttpChangeMaxRedirects() +{ + QUrl localhost = QUrl("http://localhost"); + QByteArray http200Reply = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; + + MiniHttpServer server1(""); + MiniHttpServer server2(""); + MiniHttpServer server3(http200Reply); + + QUrl server2Url(localhost); + server2Url.setPort(server2.serverPort()); + server1.setDataToTransmit(tempRedirectReplyStr().arg( + QString(server2Url.toEncoded())).toLatin1()); + + QUrl server3Url(localhost); + server3Url.setPort(server3.serverPort()); + server2.setDataToTransmit(tempRedirectReplyStr().arg( + QString(server3Url.toEncoded())).toLatin1()); + + localhost.setPort(server1.serverPort()); + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + + // Set Max redirects to 1. This will cause TooManyRedirectsError + request.setMaximumRedirectsAllowed(1); + + QNetworkReplyPtr reply(manager.get(request)); + QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl))); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QVERIFY(waitForFinish(reply) == Failure); + + QCOMPARE(redSpy.count(), request.maximumRedirectsAllowed()); + QCOMPARE(spy.count(), 1); + QVERIFY(reply->error() == QNetworkReply::TooManyRedirectsError); + + // Increase max redirects to allow successful completion + request.setMaximumRedirectsAllowed(3); + + QNetworkReplyPtr reply2(manager.get(request)); + QSignalSpy redSpy2(reply2.data(), SIGNAL(redirected(QUrl))); + + QVERIFY2(waitForFinish(reply2) == Success, msgWaitForFinished(reply2)); + + QCOMPARE(redSpy2.count(), 2); + QCOMPARE(reply2->url(), server3Url); + QVERIFY(reply2->error() == QNetworkReply::NoError); +} + +void tst_QNetworkReply::ioHttpRedirectErrors_data() +{ + QTest::addColumn<QString>("url"); + QTest::addColumn<QString>("dataToSend"); + QTest::addColumn<QNetworkReply::NetworkError>("error"); + + QString tempRedirectReply = QString("HTTP/1.1 307 Temporary Redirect\r\n" + "Content-Type: text/plain\r\n" + "location: http://localhost:%1\r\n\r\n"); + + QTest::newRow("too-many-redirects") << "http://localhost" << tempRedirectReply << QNetworkReply::TooManyRedirectsError; + QTest::newRow("insecure-redirect") << "https://localhost" << tempRedirectReply << QNetworkReply::InsecureRedirectError; + QTest::newRow("unknown-redirect") << "http://localhost"<< tempRedirectReply.replace("http", "bad_protocol") << QNetworkReply::ProtocolUnknownError; +} + +void tst_QNetworkReply::ioHttpRedirectErrors() +{ + QFETCH(QString, url); + QFETCH(QString, dataToSend); + QFETCH(QNetworkReply::NetworkError, error); + + QUrl localhost(url); + MiniHttpServer server("", localhost.scheme() == "https"); + + localhost.setPort(server.serverPort()); + + QByteArray d2s = dataToSend.arg( + QString::number(server.serverPort())).toLatin1(); + server.setDataToTransmit(d2s); + + QNetworkRequest request(localhost); + request.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QNetworkReplyPtr reply(manager.get(request)); + if (localhost.scheme() == "https") + reply.data()->ignoreSslErrors(); + QSignalSpy spy(reply.data(), SIGNAL(error(QNetworkReply::NetworkError))); + + QVERIFY(waitForFinish(reply) == Failure); + + QCOMPARE(spy.count(), 1); + QVERIFY(reply->error() == error); +} // NOTE: This test must be last testcase in tst_qnetworkreply! void tst_QNetworkReply::parentingRepliesToTheApp() |