QUrl: Support IPv6 addresses with zone id
Task-number: QTBUG-25550 Change-Id: I37ec02b655abe2779aa11945e20550ce00e43723 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
3f80783b11
commit
8a450f570b
@ -1203,16 +1203,18 @@ inline void QUrlPrivate::setQuery(const QString &value, int from, int iend)
|
||||
|
||||
inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions options) const
|
||||
{
|
||||
// EncodeUnicode is the only flag that matters
|
||||
if ((options & QUrl::FullyDecoded) == QUrl::FullyDecoded)
|
||||
options = 0;
|
||||
else
|
||||
options &= QUrl::EncodeUnicode;
|
||||
if (host.isEmpty())
|
||||
return;
|
||||
if (host.at(0).unicode() == '[') {
|
||||
// IPv6Address and IPvFuture address never require any transformation
|
||||
appendTo += host;
|
||||
// IPv6 addresses might contain a zone-id which needs to be recoded
|
||||
QString hostInCorrectFormat;
|
||||
if (options != 0)
|
||||
qt_urlRecode(hostInCorrectFormat, host.constBegin(), host.constEnd(), options, 0);
|
||||
|
||||
if (hostInCorrectFormat.isEmpty())
|
||||
hostInCorrectFormat = host;
|
||||
|
||||
appendTo += hostInCorrectFormat;
|
||||
} else {
|
||||
// this is either an IPv4Address or a reg-name
|
||||
// if it is a reg-name, it is already stored in Unicode form
|
||||
@ -1278,31 +1280,44 @@ static const QChar *parseIpFuture(QString &host, const QChar *begin, const QChar
|
||||
// ONLY the IPv6 address is parsed here, WITHOUT the brackets
|
||||
static const QChar *parseIp6(QString &host, const QChar *begin, const QChar *end, QUrl::ParsingMode mode)
|
||||
{
|
||||
QIPAddressUtils::IPv6Address address;
|
||||
const QChar *ret = QIPAddressUtils::parseIp6(address, begin, end);
|
||||
if (ret) {
|
||||
// ### Update to use QStringView once QStringView::indexOf and QStringView::lastIndexOf exists
|
||||
QString decoded;
|
||||
if (mode == QUrl::TolerantMode) {
|
||||
// this struct is kept in automatic storage because it's only 4 bytes
|
||||
const ushort decodeColon[] = { decode(':'), 0 };
|
||||
|
||||
// IPv6 failed parsing, check if it was a percent-encoded character in
|
||||
// the middle and try again
|
||||
QString decoded;
|
||||
if (mode == QUrl::TolerantMode && qt_urlRecode(decoded, begin, end, 0, decodeColon)) {
|
||||
// recurse
|
||||
// if the parsing fails again, the qt_urlRecode above will return 0
|
||||
ret = parseIp6(host, decoded.constBegin(), decoded.constEnd(), mode);
|
||||
|
||||
// we can't return ret, otherwise it would be dangling
|
||||
return ret ? end : 0;
|
||||
}
|
||||
|
||||
// no transformation, nothing to re-parse
|
||||
return ret;
|
||||
if (qt_urlRecode(decoded, begin, end, QUrl::ComponentFormattingOption::PrettyDecoded, decodeColon) == 0)
|
||||
decoded = QString(begin, end-begin);
|
||||
} else {
|
||||
decoded = QString(begin, end-begin);
|
||||
}
|
||||
|
||||
host.reserve(host.size() + (end - begin));
|
||||
const QLatin1String zoneIdIdentifier("%25");
|
||||
QIPAddressUtils::IPv6Address address;
|
||||
QString zoneId;
|
||||
|
||||
const QChar *endBeforeZoneId = decoded.constEnd();
|
||||
|
||||
int zoneIdPosition = decoded.indexOf(zoneIdIdentifier);
|
||||
if ((zoneIdPosition != -1) && (decoded.lastIndexOf(zoneIdIdentifier) == zoneIdPosition)) {
|
||||
zoneId = decoded.mid(zoneIdPosition + zoneIdIdentifier.size());
|
||||
endBeforeZoneId = decoded.constBegin() + zoneIdPosition;
|
||||
|
||||
if (zoneId.isEmpty())
|
||||
return end;
|
||||
}
|
||||
|
||||
const QChar *ret = QIPAddressUtils::parseIp6(address, decoded.constBegin(), endBeforeZoneId);
|
||||
if (ret)
|
||||
return begin + (ret - decoded.constBegin());
|
||||
|
||||
host.reserve(host.size() + (decoded.constEnd() - decoded.constBegin()));
|
||||
host += QLatin1Char('[');
|
||||
QIPAddressUtils::toString(host, address);
|
||||
|
||||
if (!zoneId.isEmpty()) {
|
||||
host += zoneIdIdentifier;
|
||||
host += zoneId;
|
||||
}
|
||||
host += QLatin1Char(']');
|
||||
return 0;
|
||||
}
|
||||
|
@ -180,6 +180,8 @@ private slots:
|
||||
void testThreading();
|
||||
void matches_data();
|
||||
void matches();
|
||||
void ipv6_zoneId_data();
|
||||
void ipv6_zoneId();
|
||||
|
||||
private:
|
||||
void testThreadingHelper();
|
||||
@ -1876,6 +1878,24 @@ void tst_QUrl::ipv6_data()
|
||||
|
||||
QTest::newRow("encoded-digit") << "//[::%31]" << true << "//[::1]";
|
||||
QTest::newRow("encoded-colon") << "//[%3A%3A]" << true << "//[::]";
|
||||
|
||||
QTest::newRow("full ipv6 with zone id (decoded %)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%eth0]") << true
|
||||
<< "//[56:56:56:56:56:56:56:56%25eth0]";
|
||||
|
||||
QTest::newRow("full ipv6 with zone id (encoded %)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%25eth0]") << true
|
||||
<< "//[56:56:56:56:56:56:56:56%25eth0]";
|
||||
|
||||
QTest::newRow("full ipv6 with invalid zone id") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%]") << false << "";
|
||||
|
||||
QTest::newRow("full ipv6 with invalid zone id (encoded)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%25]") << false << "";
|
||||
|
||||
QTest::newRow("full ipv6 with zone id 25 (encoded)") << QString::fromLatin1("//[56:56:56:56:56:56:56:56%2525]") << true << "//[56:56:56:56:56:56:56:56%2525]";
|
||||
|
||||
QTest::newRow("case 4 with less and ip4 and port and useinfo and zone id")
|
||||
<< QString::fromLatin1("//user:pass@[56::56:56:56:127.0.0.1%ethernet_1]:99") << true
|
||||
<< "//user:pass@[56::56:56:56:7f00:1%25ethernet_1]:99";
|
||||
|
||||
QTest::newRow("encoded-digit including zone id") << "//[::%31%25eth0]" << true << "//[::1%25eth0]";
|
||||
}
|
||||
|
||||
void tst_QUrl::ipv6()
|
||||
@ -4120,6 +4140,38 @@ void tst_QUrl::matches()
|
||||
QCOMPARE(urlOne.matches(urlTwo, QUrl::FormattingOptions(options)), matches);
|
||||
}
|
||||
|
||||
void tst_QUrl::ipv6_zoneId_data()
|
||||
{
|
||||
QTest::addColumn<QUrl>("url");
|
||||
QTest::addColumn<QString>("decodedHost");
|
||||
QTest::addColumn<QString>("prettyHost");
|
||||
QTest::addColumn<QString>("encodedHost");
|
||||
|
||||
QTest::newRow("digit") << QUrl("x://[::%251]") << "::%1" << "::%251" << "::%251";
|
||||
QTest::newRow("eth0") << QUrl("x://[::%25eth0]") << "::%eth0" << "::%25eth0" << "::%25eth0";
|
||||
QTest::newRow("space") << QUrl("x://[::%25%20]") << "::% " << "::%25 " << "::%25%20";
|
||||
QTest::newRow("subdelims") << QUrl("x://[::%25eth%2B]") << "::%eth+" << "::%25eth%2B" << "::%25eth%2B";
|
||||
QTest::newRow("other") << QUrl("x://[::%25^]") << "::%^" << "::%25%5E" << "::%25%5E";
|
||||
QTest::newRow("control") << QUrl("x://[::%25%7F]") << "::%\x7f" << "::%25%7F" << "::%25%7F";
|
||||
QTest::newRow("unicode") << QUrl("x://[::%25wlán0]") << "::%wlán0" << "::%25wlán0" << "::%25wl%C3%A1n0";
|
||||
QTest::newRow("non-utf8") << QUrl("x://[::%25%80]") << QString("::%") + QChar(QChar::ReplacementCharacter) << "::%25%80" << "::%25%80";
|
||||
}
|
||||
|
||||
void tst_QUrl::ipv6_zoneId()
|
||||
{
|
||||
QFETCH(QUrl, url);
|
||||
QFETCH(QString, decodedHost);
|
||||
QFETCH(QString, prettyHost);
|
||||
QFETCH(QString, encodedHost);
|
||||
|
||||
QVERIFY2(url.isValid(), qPrintable(url.errorString()));
|
||||
QCOMPARE(url.host(QUrl::FullyDecoded), decodedHost);
|
||||
QCOMPARE(url.host(), decodedHost);
|
||||
QCOMPARE(url.host(QUrl::FullyEncoded), encodedHost);
|
||||
QCOMPARE(url.toString(), "x://[" + prettyHost + "]");
|
||||
QCOMPARE(url.toString(QUrl::FullyEncoded), "x://[" + encodedHost + "]");
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QUrl)
|
||||
|
||||
#include "tst_qurl.moc"
|
||||
|
Loading…
Reference in New Issue
Block a user