mirror of
https://github.com/spring-projects/spring-framework.git
synced 2026-09-17 16:39:29 +00:00
Align domainToAscii with current WhatWG spec
The WhatWG URL Standard changed its "domain to ASCII" algorithm when beStrict is false and the domain is an ASCII string, it now returns the domain lowercased regardless of Unicode ToASCII's outcome, for web compatibility. Invalid or ambiguous "xn--" (ACE) labels are no longer rejected or validated; they are lowercased and accepted, matching browsers and the web-platform-tests URL cases. This supersedes the earlier spec revision that only lowercased ASCII domains whose labels did not start with "xn--". Drop the now-obsolete "xn--" label detection (which was dead code anyway due to a typo) and unconditionally lowercase ASCII domains. Closes gh-37018
This commit is contained in:
@@ -55,6 +55,7 @@ import org.springframework.util.Assert;
|
||||
* with {@code EXTRA}.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.2
|
||||
*/
|
||||
@SuppressWarnings({"SameParameterValue", "BooleanMethodIsAlwaysInverted"})
|
||||
@@ -277,31 +278,12 @@ final class WhatWgUrlParser {
|
||||
}
|
||||
|
||||
private static String domainToAscii(String domain, boolean beStrict) {
|
||||
// If beStrict is false, domain is an ASCII string, and strictly splitting domain on U+002E (.)
|
||||
// does not produce any item that starts with an ASCII case-insensitive match for "xn--",
|
||||
// this step is equivalent to ASCII lowercasing domain.
|
||||
// If beStrict is false and domain is an ASCII string, the algorithm returns domain lowercased
|
||||
// regardless of Unicode ToASCII's outcome, due to web compatibility. In particular, the WhatWG
|
||||
// spec deliberately does not reject invalid or ambiguous "xn--" (ACE) labels here. See the note in
|
||||
// https://url.spec.whatwg.org/#concept-domain-to-ascii and web-platform-tests url cases
|
||||
if (!beStrict && containsOnlyAscii(domain)) {
|
||||
int dotIdx = domain.indexOf('.');
|
||||
boolean onlyLowerCase = true;
|
||||
while (dotIdx != -1) {
|
||||
if (domain.length() - dotIdx > 4) {
|
||||
// ASCII case-insensitive match for "xn--"
|
||||
int ch0 = domain.codePointAt(dotIdx + 1);
|
||||
int ch1 = domain.codePointAt(dotIdx + 2);
|
||||
int ch2 = domain.codePointAt(dotIdx + 3);
|
||||
int ch3 = domain.codePointAt(dotIdx + 4);
|
||||
if ((ch0 == 'x' || ch0 == 'X') &&
|
||||
(ch1 == 'n' || ch1 == 'N') &&
|
||||
ch2 == '-' && ch3 == '_') {
|
||||
onlyLowerCase = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
dotIdx = domain.indexOf('.', dotIdx + 1);
|
||||
}
|
||||
if (onlyLowerCase) {
|
||||
return domain.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
return domain.toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
// Let result be the result of running Unicode ToASCII (https://www.unicode.org/reports/tr46/#ToASCII)
|
||||
// with domain_name set to domain, UseSTD3ASCIIRules set to beStrict, CheckHyphens set to false,
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class WhatWgUrlParserTests {
|
||||
|
||||
@@ -43,6 +44,25 @@ class WhatWgUrlParserTests {
|
||||
testParse("//other.info/parent/../foo/bar", "", "other.info", null, "/foo/bar", null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseAsciiHost() {
|
||||
// Pure ASCII host is lowercased
|
||||
testParse("https://EXAMPLE.com/foo", "https", "example.com", null, "/foo", null, null);
|
||||
// ASCII "xn--" (ACE) labels are accepted and lowercased (in any case), not validated or rejected.
|
||||
// See https://url.spec.whatwg.org/#concept-domain-to-ascii and web-platform-tests url cases.
|
||||
testParse("https://a.b.c.xn--pokxncvks", "https", "a.b.c.xn--pokxncvks", null, "", null, null);
|
||||
testParse("https://a.b.c.XN--pokxncvks", "https", "a.b.c.xn--pokxncvks", null, "", null, null);
|
||||
testParse("https://a.b.c.Xn--pokxncvks", "https", "a.b.c.xn--pokxncvks", null, "", null, null);
|
||||
// A trailing non-numeric "xn--" label keeps a numeric-looking host as a domain, not an IPv4 address.
|
||||
testParse("https://10.0.0.xn--pokxncvks", "https", "10.0.0.xn--pokxncvks", null, "", null, null);
|
||||
testParse("https://10.0.0.XN--pokxncvks", "https", "10.0.0.xn--pokxncvks", null, "", null, null);
|
||||
testParse("https://10.0.0.xN--pokxncvks", "https", "10.0.0.xn--pokxncvks", null, "", null, null);
|
||||
// Leading ACE label is handled too, and an empty "xn--" label is accepted (not a failure)
|
||||
testParse("https://XN--pokxncvks.example", "https", "xn--pokxncvks.example", null, "", null, null);
|
||||
testParse("https://xn--/", "https", "xn--", null, "/", null, null);
|
||||
testParse("file://xn--/p", "file", "xn--", null, "/p", null, null);
|
||||
}
|
||||
|
||||
private void testParse(String input, String scheme, @Nullable String host, @Nullable String port, String path, @Nullable String query, @Nullable String fragment) {
|
||||
WhatWgUrlParser.UrlRecord result = WhatWgUrlParser.parse(input, EMPTY_URL_RECORD, null, null);
|
||||
assertThat(result.scheme()).as("Invalid scheme").isEqualTo(scheme);
|
||||
|
||||
Reference in New Issue
Block a user