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.

Task-number: QTBUG-125985
Change-Id: I642e56cc7cb3562304573930a01aa1aafa817f36
Reviewed-by: Mate Barany <mate.barany@qt.io>
(cherry picked from commit 9503e2c92c2d3b8f5bb9c86274d2430eb0a57880)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2024-06-19 10:16:02 +02:00 committed by Qt Cherry-pick Bot
parent aefa427459
commit 70f38f48c1
2 changed files with 32 additions and 32 deletions

View File

@ -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);

View File

@ -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;