Accept LFCRLF to mark end of HTTP Headers

Some embedded servers use LF to mark the end of an individual header,
but use CRLF to mark the end of all the headers. The GoPro WiFi
interface does this, as an example.

Change-Id: I227ab73622c84f439a6cf8703d020393c4d8bf69
Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
Reviewed-by: Markus Goetz (Woboq GmbH) <markus@woboq.com>
bb10
Dyami Caliri 2016-03-07 20:27:09 -08:00
parent d7db6c6c19
commit f8f1bac3f0
3 changed files with 70 additions and 4 deletions

View File

@ -554,9 +554,8 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
if (c == '\n') {
// check for possible header endings. As per HTTP rfc,
// the header endings will be marked by CRLFCRLF. But
// we will allow CRLFCRLF, CRLFLF, LFLF
if (fragment.endsWith("\r\n\r\n")
|| fragment.endsWith("\r\n\n")
// we will allow CRLFCRLF, CRLFLF, LFCRLF, LFLF
if (fragment.endsWith("\n\r\n")
|| fragment.endsWith("\n\n"))
allHeaders = true;

View File

@ -175,7 +175,7 @@ private:
};
class QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
class Q_AUTOTEST_EXPORT QHttpNetworkReplyPrivate : public QObjectPrivate, public QHttpNetworkHeaderPrivate
{
public:
QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());

View File

@ -33,6 +33,9 @@
#include <QtTest/QtTest>
#include <QtCore/QBuffer>
#include <QtCore/QByteArray>
#include "private/qhttpnetworkconnection_p.h"
class tst_QHttpNetworkReply: public QObject
@ -46,6 +49,9 @@ private Q_SLOTS:
void parseHeader_data();
void parseHeader();
void parseEndOfHeader_data();
void parseEndOfHeader();
};
@ -122,5 +128,66 @@ void tst_QHttpNetworkReply::parseHeader()
}
}
class TestHeaderSocket : public QAbstractSocket
{
public:
explicit TestHeaderSocket(const QByteArray &input) : QAbstractSocket(QAbstractSocket::TcpSocket, Q_NULLPTR)
{
inputBuffer.setData(input);
inputBuffer.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
open(QIODevice::ReadOnly | QIODevice::Unbuffered);
}
qint64 readData(char *data, qint64 maxlen) { return inputBuffer.read(data, maxlen); }
QBuffer inputBuffer;
};
class TestHeaderReply : public QHttpNetworkReply
{
public:
QHttpNetworkReplyPrivate *replyPrivate() { return static_cast<QHttpNetworkReplyPrivate *>(d_ptr.data()); }
};
void tst_QHttpNetworkReply::parseEndOfHeader_data()
{
QTest::addColumn<QByteArray>("headers");
QTest::addColumn<qint64>("lengths");
QTest::newRow("CRLFCRLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
"Content-Length:\r\n 1024\r\n"
"Content-Encoding: gzip\r\n\r\nHTTPBODY")
<< qint64(90);
QTest::newRow("CRLFLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
"Content-Length:\r\n 1024\r\n"
"Content-Encoding: gzip\r\n\nHTTPBODY")
<< qint64(89);
QTest::newRow("LFCRLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
"Content-Length:\r\n 1024\r\n"
"Content-Encoding: gzip\n\r\nHTTPBODY")
<< qint64(89);
QTest::newRow("LFLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
"Content-Length:\r\n 1024\r\n"
"Content-Encoding: gzip\n\nHTTPBODY")
<< qint64(88);
}
void tst_QHttpNetworkReply::parseEndOfHeader()
{
QFETCH(QByteArray, headers);
QFETCH(qint64, lengths);
TestHeaderSocket socket(headers);
TestHeaderReply reply;
QHttpNetworkReplyPrivate *replyPrivate = reply.replyPrivate();
qint64 headerBytes = replyPrivate->readHeader(&socket);
QCOMPARE(headerBytes, lengths);
}
QTEST_MAIN(tst_QHttpNetworkReply)
#include "tst_qhttpnetworkreply.moc"