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:
parent
aefa427459
commit
70f38f48c1
@ -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);
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user