diff --git a/src/base/http/requestparser.cpp b/src/base/http/requestparser.cpp index e8a02dd09..be6aba319 100644 --- a/src/base/http/requestparser.cpp +++ b/src/base/http/requestparser.cpp @@ -31,12 +31,13 @@ #include "requestparser.h" #include +#include #include +#include #include #include #include -#include #include #include @@ -59,21 +60,19 @@ namespace return in; } - bool parseHeaderLine(const QStringView line, HeaderMap &out) + std::optional parseHeaderLine(const QByteArrayView line) { // [rfc7230] 3.2. Header Fields const int i = line.indexOf(u':'); if (i <= 0) { qWarning() << Q_FUNC_INFO << "invalid http header:" << line; - return false; + return std::nullopt; } - const QString name = line.first(i).trimmed().toString().toLower(); - const QString value = line.sliced(i + 1).trimmed().toString(); - out[name] = value; - - return true; + const QString name = QString::fromLatin1(line.first(i).trimmed()).toLower(); + const QString value = QString::fromLatin1(line.sliced(i + 1).trimmed()); + return {{name, value}}; } } @@ -93,7 +92,7 @@ RequestParser::ParseResult RequestParser::doParse(const QByteArrayView data) return {ParseStatus::Incomplete, Request(), 0}; } - const QString httpHeaders = QString::fromLatin1(data.constData(), headerEnd); + const QByteArrayView httpHeaders = data.first(headerEnd); if (!parseStartLines(httpHeaders)) { qWarning() << Q_FUNC_INFO << "header parsing error"; @@ -152,36 +151,40 @@ RequestParser::ParseResult RequestParser::doParse(const QByteArrayView data) return {ParseStatus::BadMethod, m_request, 0}; } -bool RequestParser::parseStartLines(const QStringView data) +bool RequestParser::parseStartLines(const QByteArrayView data) { // we don't handle malformed request which uses `LF` for newline - const QList lines = data.split(QString::fromLatin1(CRLF), Qt::SkipEmptyParts); + const QList lines = splitToViews(data, CRLF, Qt::SkipEmptyParts); // [rfc7230] 3.2.2. Field Order - QStringList requestLines; + QByteArrayList requestLines; for (const auto &line : lines) { - if (line.at(0).isSpace() && !requestLines.isEmpty()) + if (QChar::fromLatin1(line.at(0)).isSpace() && !requestLines.isEmpty()) { // continuation of previous line requestLines.last() += line; } else { - requestLines += line.toString(); + requestLines += line.toByteArray(); } } if (requestLines.isEmpty()) return false; - if (!parseRequestLine(requestLines[0])) + if (!parseRequestLine(QString::fromLatin1(requestLines[0]))) return false; for (auto i = ++(requestLines.begin()); i != requestLines.end(); ++i) { - if (!parseHeaderLine(*i, m_request.headers)) + const std::optional header = parseHeaderLine(*i); + if (!header.has_value()) return false; + + const auto [name, value] = header.value(); + m_request.headers[name] = value; } return true; @@ -310,17 +313,23 @@ bool RequestParser::parseFormData(const QByteArrayView data) return false; } - const QString headers = QString::fromLatin1(data.first(eohPos)); + const QByteArrayView headers = data.first(eohPos); const QByteArrayView payload = viewWithoutEndingWith(data.sliced((eohPos + EOH.size())), CRLF); HeaderMap headersMap; - const QList headerLines = QStringView(headers).split(QString::fromLatin1(CRLF), Qt::SkipEmptyParts); + const QList headerLines = splitToViews(headers, CRLF, Qt::SkipEmptyParts); for (const auto &line : headerLines) { - if (line.trimmed().startsWith(HEADER_CONTENT_DISPOSITION, Qt::CaseInsensitive)) + const std::optional header = parseHeaderLine(line); + if (!header.has_value()) + return false; + + const auto [name, value] = header.value(); + + if (name == HEADER_CONTENT_DISPOSITION) { // extract out filename & name - const QList directives = line.split(u';', Qt::SkipEmptyParts); + const QList directives = splitToViews(line, ";", Qt::SkipEmptyParts); for (const auto &directive : directives) { @@ -328,15 +337,14 @@ bool RequestParser::parseFormData(const QByteArrayView data) if (idx < 0) continue; - const QString name = directive.first(idx).trimmed().toString().toLower(); - const QString value = Utils::String::unquote(directive.sliced(idx + 1).trimmed()).toString(); + const QString name = QString::fromLatin1(directive.first(idx).trimmed()).toLower(); + const QString value = QString::fromLatin1(unquote(directive.sliced(idx + 1).trimmed())); headersMap[name] = value; } } else { - if (!parseHeaderLine(line, headersMap)) - return false; + headersMap[name] = value; } } diff --git a/src/base/http/requestparser.h b/src/base/http/requestparser.h index 13b19f3ba..337583701 100644 --- a/src/base/http/requestparser.h +++ b/src/base/http/requestparser.h @@ -61,7 +61,7 @@ namespace Http RequestParser() = default; ParseResult doParse(QByteArrayView data); - bool parseStartLines(QStringView data); + bool parseStartLines(QByteArrayView data); bool parseRequestLine(const QString &line); bool parsePostMessage(QByteArrayView data); diff --git a/src/base/utils/bytearray.cpp b/src/base/utils/bytearray.cpp index d372f507f..61c581008 100644 --- a/src/base/utils/bytearray.cpp +++ b/src/base/utils/bytearray.cpp @@ -31,7 +31,6 @@ #include #include -#include #include QList Utils::ByteArray::splitToViews(const QByteArrayView in, const QByteArrayView sep, const Qt::SplitBehavior behavior) diff --git a/src/base/utils/bytearray.h b/src/base/utils/bytearray.h index 03f0f06d0..602953ae2 100644 --- a/src/base/utils/bytearray.h +++ b/src/base/utils/bytearray.h @@ -31,9 +31,9 @@ #include #include +#include class QByteArray; -class QByteArrayView; namespace Utils::ByteArray { @@ -42,4 +42,19 @@ namespace Utils::ByteArray QByteArray asQByteArray(QByteArrayView view); QByteArray toBase32(const QByteArray &in); + + template + T unquote(const T &arr, const QByteArrayView quotes = "\"") + { + if (arr.length() < 2) + return arr; + + for (const char quote : quotes) + { + if (arr.startsWith(quote) && arr.endsWith(quote)) + return arr.sliced(1, (arr.length() - 2)); + } + + return arr; + } } diff --git a/test/testutilsbytearray.cpp b/test/testutilsbytearray.cpp index f00d43c09..99b26ad84 100644 --- a/test/testutilsbytearray.cpp +++ b/test/testutilsbytearray.cpp @@ -28,6 +28,7 @@ */ #include +#include #include #include #include @@ -123,6 +124,33 @@ private slots: QCOMPARE(Utils::ByteArray::toBase32("0000000000"), "GAYDAMBQGAYDAMBQ"); QCOMPARE(Utils::ByteArray::toBase32("1"), "GE======"); } + + void testUnquote() const + { + const auto test = []() + { + QCOMPARE(Utils::ByteArray::unquote({}), {}); + QCOMPARE(Utils::ByteArray::unquote("abc"), "abc"); + QCOMPARE(Utils::ByteArray::unquote("\"abc\""), "abc"); + QCOMPARE(Utils::ByteArray::unquote("\"a b c\""), "a b c"); + QCOMPARE(Utils::ByteArray::unquote("\"abc"), "\"abc"); + QCOMPARE(Utils::ByteArray::unquote("abc\""), "abc\""); + QCOMPARE(Utils::ByteArray::unquote(" \"abc\" "), " \"abc\" "); + QCOMPARE(Utils::ByteArray::unquote("\"a\"bc\""), "a\"bc"); + QCOMPARE(Utils::ByteArray::unquote("'abc'", "'"), "abc"); + QCOMPARE(Utils::ByteArray::unquote("'abc'", "\"'"), "abc"); + QCOMPARE(Utils::ByteArray::unquote("\"'abc'\"", "\"'"), "'abc'"); + QCOMPARE(Utils::ByteArray::unquote("\"'abc'\"", "'\""), "'abc'"); + QCOMPARE(Utils::ByteArray::unquote("\"'abc'\"", "'"), "\"'abc'\""); + QCOMPARE(Utils::ByteArray::unquote("\"abc'", "'"), "\"abc'"); + QCOMPARE(Utils::ByteArray::unquote("'abc\"", "'"), "'abc\""); + QCOMPARE(Utils::ByteArray::unquote("\"\""), ""); + QCOMPARE(Utils::ByteArray::unquote("\""), "\""); + }; + + test.template operator()(); + test.template operator()(); + } }; QTEST_APPLESS_MAIN(TestUtilsByteArray)