summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorMårten Nordheim <marten.nordheim@qt.io>2017-10-09 14:57:46 +0200
committerMårten Nordheim <marten.nordheim@qt.io>2017-10-17 18:50:14 +0000
commitc4cf90b1f739c47383672de3d66b1d9d5427f5db (patch)
tree6dbbfb0138043672bf3689ccb1e6bca4e0965ab2 /tests
parent8a39384e907e830c907f73009f498c486b22bd20 (diff)
Fix redirecting POST for HTTP 307 and 308
All POST requests that were redirected would previously turn into GET requests. This does not follow the standard for HTTP codes 307 and 308. Task-number: QTBUG-63142 Change-Id: Ibd25a9566066e589670a9bc34e5dc5111f8139d5 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp152
1 files changed, 149 insertions, 3 deletions
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index d236ef0490..d22850ba4e 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -491,6 +491,10 @@ private Q_SLOTS:
void ioHttpRedirect_data();
void ioHttpRedirect();
void ioHttpRedirectFromLocalToRemote();
+ void ioHttpRedirectPost_data();
+ void ioHttpRedirectPost();
+ void ioHttpRedirectMultipartPost_data();
+ void ioHttpRedirectMultipartPost();
#ifndef QT_NO_SSL
void putWithServerClosingConnectionImmediately();
#endif
@@ -542,7 +546,7 @@ static void setupSslServer(QSslSocket* serverSocket)
}
#endif
-// Does not work for POST/PUT!
+// Does not work for PUT! Limited support for POST.
class MiniHttpServer: public QTcpServer
{
Q_OBJECT
@@ -557,6 +561,10 @@ public:
bool multiple;
int totalConnections;
+ bool hasContent = false;
+ int contentRead = 0;
+ int contentLength = 0;
+
MiniHttpServer(const QByteArray &data, bool ssl = false, QThread *thread = 0, bool useipv6 = false)
: dataToTransmit(data), doClose(true), doSsl(ssl), ipv6(useipv6),
multiple(false), totalConnections(0)
@@ -633,6 +641,18 @@ private:
this, SLOT(slotError(QAbstractSocket::SocketError)));
}
+ void parseContentLength()
+ {
+ int index = receivedData.indexOf("Content-Length:");
+ index += sizeof("Content-Length:") - 1;
+ const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r');
+ auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end));
+ bool ok;
+ contentLength = num.toInt(&ok);
+ if (!ok)
+ contentLength = -1;
+ }
+
private slots:
#ifndef QT_NO_SSL
void slotSslErrors(const QList<QSslError>& errors)
@@ -654,12 +674,20 @@ public slots:
{
Q_ASSERT(!client.isNull());
receivedData += client->readAll();
- int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
+ const int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
if (doubleEndlPos != -1) {
+ const int endOfHeader = doubleEndlPos + 4;
+ hasContent = receivedData.startsWith("POST");
+ if (hasContent && contentLength == 0)
+ parseContentLength();
+ contentRead = receivedData.length() - endOfHeader;
+ if (hasContent && contentRead < contentLength)
+ return;
+
// multiple requests incoming. remove the bytes of the current one
if (multiple)
- receivedData.remove(0, doubleEndlPos+4);
+ receivedData.remove(0, endOfHeader);
reply();
}
@@ -8518,6 +8546,124 @@ void tst_QNetworkReply::ioHttpRedirectFromLocalToRemote()
QCOMPARE(reply->readAll(), reference.readAll());
}
+void tst_QNetworkReply::ioHttpRedirectPost_data()
+{
+ QTest::addColumn<QString>("status");
+ QTest::addColumn<QByteArray>("data");
+ QTest::addColumn<QString>("contentType");
+
+ QByteArray data;
+ data = "hello world";
+ QTest::addRow("307") << "307 Temporary Redirect" << data << "text/plain";
+ QString permanentRedirect = "308 Permanent Redirect";
+ QTest::addRow("308") << permanentRedirect << data << "text/plain";
+
+ // Some data from ::putToFile_data
+ data = "";
+ QTest::newRow("empty") << permanentRedirect << data << "application/octet-stream";
+
+ data = QByteArray("abcd\0\1\2\abcd",12);
+ QTest::newRow("with-nul") << permanentRedirect << data << "application/octet-stream";
+
+ data = QByteArray(4097, '\4');
+ QTest::newRow("4k+1") << permanentRedirect << data << "application/octet-stream";
+
+ data = QByteArray(128*1024+1, '\177');
+ QTest::newRow("128k+1") << permanentRedirect << data << "application/octet-stream";
+
+ data = QByteArray(2*1024*1024+1, '\177');
+ QTest::newRow("2MB+1") << permanentRedirect << data << "application/octet-stream";
+}
+
+void tst_QNetworkReply::ioHttpRedirectPost()
+{
+ QFETCH(QString, status);
+ QFETCH(QByteArray, data);
+ QFETCH(QString, contentType);
+
+ QUrl targetUrl("http://" + QtNetworkSettings::serverName() + "/qtest/cgi-bin/md5sum.cgi");
+
+ QString redirectReply = QStringLiteral("HTTP/1.1 %1\r\n"
+ "Content-Type: text/plain\r\n"
+ "location: %2\r\n"
+ "\r\n").arg(status, targetUrl.toString());
+ MiniHttpServer redirectServer(redirectReply.toLatin1());
+ QUrl url("http://localhost/");
+ url.setPort(redirectServer.serverPort());
+ QNetworkRequest request(url);
+ request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, contentType);
+ auto oldRedirectPolicy = manager.redirectPolicy();
+ manager.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy);
+
+ QNetworkReplyPtr reply(manager.post(request, data));
+ // Restore previous policy:
+ manager.setRedirectPolicy(oldRedirectPolicy);
+
+ QCOMPARE(waitForFinish(reply), int(Success));
+ QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
+}
+
+void tst_QNetworkReply::ioHttpRedirectMultipartPost_data()
+{
+ postToHttpMultipart_data();
+}
+
+void tst_QNetworkReply::ioHttpRedirectMultipartPost()
+{
+ // Note: This code is heavily based on postToHttpMultipart
+ QFETCH(QUrl, url);
+
+ static QSet<QByteArray> boundaries;
+
+ QNetworkReplyPtr reply;
+
+ QFETCH(QHttpMultiPart *, multiPart);
+ QFETCH(QByteArray, expectedReplyData);
+ QFETCH(QByteArray, contentType);
+
+ QString redirectReply = tempRedirectReplyStr().arg(url.toString());
+ MiniHttpServer redirectServer(redirectReply.toLatin1());
+ QUrl redirectUrl("http://localhost/");
+ redirectUrl.setPort(redirectServer.serverPort());
+ QNetworkRequest request(redirectUrl);
+
+ // Restore policy when we leave this scope:
+ struct PolicyRestorer
+ {
+ QNetworkAccessManager &qnam;
+ QNetworkRequest::RedirectPolicy policy;
+ PolicyRestorer(QNetworkAccessManager &qnam)
+ : qnam(qnam), policy(qnam.redirectPolicy())
+ { qnam.setRedirectPolicy(QNetworkRequest::RedirectPolicy::NoLessSafeRedirectPolicy); }
+ ~PolicyRestorer() { qnam.setRedirectPolicy(policy); }
+ } policyRestorer(manager);
+
+ // hack for testing the setting of the content-type header by hand:
+ if (contentType == "custom") {
+ QByteArray contentType("multipart/custom; boundary=\"" + multiPart->boundary() + "\"");
+ request.setHeader(QNetworkRequest::ContentTypeHeader, contentType);
+ }
+
+ QVERIFY2(! boundaries.contains(multiPart->boundary()), "boundary '" + multiPart->boundary() + "' has been created twice");
+ boundaries.insert(multiPart->boundary());
+
+ RUN_REQUEST(runMultipartRequest(request, reply, multiPart, "POST"));
+ multiPart->deleteLater();
+
+ QCOMPARE(reply->url(), url);
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 OK
+
+ QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
+ QVERIFY(multiPart->boundary().count() < 70);
+ QByteArray replyData = reply->readAll();
+
+ expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
+// QEXPECT_FAIL("nested", "the server does not understand nested multipart messages", Continue); // see above
+ QCOMPARE(replyData, expectedReplyData);
+}
+
#ifndef QT_NO_SSL
class PutWithServerClosingConnectionImmediatelyHandler: public QObject