diff options
author | Marc Mutz <[email protected]> | 2024-06-19 10:16:02 +0200 |
---|---|---|
committer | Marc Mutz <[email protected]> | 2024-06-27 17:23:24 +0200 |
commit | 9503e2c92c2d3b8f5bb9c86274d2430eb0a57880 (patch) | |
tree | 745ec53d843fd205ab9b4e43b374d184acee38f3 | |
parent | 663d992672852741e10d83e6c5184f348ada78bf (diff) |
QFormDataPartBuilder: delay encoding of the part's name to build() time
This is a prerequisite for making QFormDataBuilder::part() return an
existing part instead of creating new parts with the same name
(idempotence), which was pointed out in API review.
Pick-to: 6.8
Task-number: QTBUG-125985
Change-Id: I642e56cc7cb3562304573930a01aa1aafa817f36
Reviewed-by: Mate Barany <[email protected]>
-rw-r--r-- | src/network/access/qformdatabuilder.cpp | 58 | ||||
-rw-r--r-- | src/network/access/qformdatabuilder.h | 6 |
2 files changed, 32 insertions, 32 deletions
diff --git a/src/network/access/qformdatabuilder.cpp b/src/network/access/qformdatabuilder.cpp index 0428f09a813..72ef96c253a 100644 --- a/src/network/access/qformdatabuilder.cpp +++ b/src/network/access/qformdatabuilder.cpp @@ -30,24 +30,6 @@ QT_BEGIN_NAMESPACE \sa QHttpPart, QHttpMultiPart, QFormDataBuilder */ -static QByteArray nameToByteArray(QStringView view) -{ - return view.toUtf8(); -} - -static QByteArray nameToByteArray(QLatin1StringView view) -{ - if (!QtPrivate::isAscii(view)) - return view.toString().toUtf8(); // ### optimize - - return QByteArray::fromRawData(view.data(), view.size()); -} - -static QByteArray nameToByteArray(QUtf8StringView view) -{ - return QByteArray::fromRawData(view.data(), view.size()); -} - static void escapeNameAndAppend(QByteArray &dst, QByteArrayView src) { for (auto c : src) { @@ -57,20 +39,32 @@ static void escapeNameAndAppend(QByteArray &dst, QByteArrayView src) } } +static void escapeNameAndAppend(QByteArray &dst, QStringView src) +{ + qsizetype last = 0; + + // equivalent to for (auto chunk : qTokenize(src, any_of("\\\""))), if there was such a thing + for (qsizetype i = 0, end = src.size(); i != end; ++i) { + const auto c = src[i]; + if (c == u'"' || c == u'\\') { + const auto chunk = src.sliced(last, i - last); + dst += QUtf8::convertFromUnicode(chunk); // ### optimize + dst += '\\'; + last = i; + } + } + dst += QUtf8::convertFromUnicode(src.sliced(last)); +} + /*! Constructs a QFormDataPartBuilder object and sets \a name as the name parameter of the form-data. */ QFormDataPartBuilder::QFormDataPartBuilder(QAnyStringView name, PrivateConstructor /*unused*/) + : m_name{name.toString()} { static_assert(std::is_nothrow_move_constructible_v<decltype(m_body)>); static_assert(std::is_nothrow_move_assignable_v<decltype(m_body)>); - - const auto enc = name.visit([](auto name) { return nameToByteArray(name); }); - - m_headerValue += "form-data; name=\""; - escapeNameAndAppend(m_headerValue, enc); - m_headerValue += "\""; } /*! @@ -210,19 +204,25 @@ QHttpPart QFormDataPartBuilder::build() { QHttpPart httpPart; + QByteArray headerValue; + + headerValue += "form-data; name=\""; + escapeNameAndAppend(headerValue, m_name); + headerValue += "\""; + if (!m_originalBodyName.isNull()) { const bool utf8 = !QtPrivate::isAscii(m_originalBodyName); const auto enc = utf8 ? m_originalBodyName.toUtf8() : m_originalBodyName.toLatin1(); - m_headerValue += "; filename=\""; - escapeNameAndAppend(m_headerValue, enc); - m_headerValue += "\""; + headerValue += "; filename=\""; + escapeNameAndAppend(headerValue, enc); + headerValue += "\""; if (utf8) { // For 'filename*' production see // https://datatracker.ietf.org/doc/html/rfc5987#section-3.2.1 // For providing both filename and filename* parameters see // https://datatracker.ietf.org/doc/html/rfc6266#section-4.3 and // https://datatracker.ietf.org/doc/html/rfc8187#section-4.2 - m_headerValue += "; filename*=UTF-8''" + enc.toPercentEncoding(); + headerValue += "; filename*=UTF-8''" + enc.toPercentEncoding(); } } @@ -244,7 +244,7 @@ QHttpPart QFormDataPartBuilder::build() if (!m_mimeType.isEmpty()) httpPart.setHeader(QNetworkRequest::ContentTypeHeader, m_mimeType); - httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, m_headerValue); + httpPart.setHeader(QNetworkRequest::ContentDispositionHeader, std::move(headerValue)); if (auto d = std::get_if<QIODevice*>(&m_body)) httpPart.setBodyDevice(*d); diff --git a/src/network/access/qformdatabuilder.h b/src/network/access/qformdatabuilder.h index 4305ff8cb76..ddbb8ee0fa2 100644 --- a/src/network/access/qformdatabuilder.h +++ b/src/network/access/qformdatabuilder.h @@ -34,7 +34,7 @@ public: Q_NETWORK_EXPORT explicit QFormDataPartBuilder(QAnyStringView name, PrivateConstructor); QFormDataPartBuilder(QFormDataPartBuilder &&other) noexcept - : m_headerValue(std::move(other.m_headerValue)), + : m_name(std::move(other.m_name)), m_originalBodyName(std::move(other.m_originalBodyName)), m_httpHeaders(std::move(other.m_httpHeaders)), m_body(std::move(other.m_body)), @@ -46,7 +46,7 @@ public: QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QFormDataPartBuilder) void swap(QFormDataPartBuilder &other) noexcept { - m_headerValue.swap(other.m_headerValue); + m_name.swap(other.m_name); m_originalBodyName.swap(other.m_originalBodyName); m_httpHeaders.swap(other.m_httpHeaders); m_body.swap(other.m_body); @@ -75,7 +75,7 @@ private: QAnyStringView mimeType); QHttpPart build(); - QByteArray m_headerValue; + QString m_name; QByteArray m_mimeType; QString m_originalBodyName; QHttpHeaders m_httpHeaders; |