diff --git a/jvm/src/test/scala/io/lemonlabs/uri/ApexDomainTests.scala b/jvm/src/test/scala/io/lemonlabs/uri/ApexDomainTests.scala index 7f5837d1..3f590f61 100644 --- a/jvm/src/test/scala/io/lemonlabs/uri/ApexDomainTests.scala +++ b/jvm/src/test/scala/io/lemonlabs/uri/ApexDomainTests.scala @@ -16,6 +16,18 @@ class ApexDomainTests extends AnyFlatSpec with Matchers { Url.parse("http://maps.google.com").apexDomain should equal(Some("google.com")) } + "DomainName" should "return apex domain for domain name in punycode" in { + DomainName.parse("www.example.xn--6frz82g").apexDomain should equal(Some("example.xn--6frz82g")) + } + + "DomainName" should "return apex domain for domain name in unicode" in { + DomainName.parse("www.example.移动").apexDomain should equal(Some("example.移动")) + } + + "DomainName" should "return apex domain for fully qualified domain name" in { + DomainName.parse("www.hello.example.com.").apexDomain should equal(Some("example.com.")) + } + it should "return itself for apexDomain when it is the apex domain" in { Url.parse("http://google.com").apexDomain should equal(Some("google.com")) } diff --git a/shared/src/main/scala/io/lemonlabs/uri/Host.scala b/shared/src/main/scala/io/lemonlabs/uri/Host.scala index 10e49adc..60ce431a 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/Host.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/Host.scala @@ -116,16 +116,21 @@ final case class DomainName(value: String)(implicit val conf: UriConfig = UriCon with PunycodeSupport { type Self = DomainName - private def isValidPublicSuffix(suffix: String): Boolean = - if (PublicSuffixes.set.contains(suffix)) { true } - else if (PublicSuffixes.exceptions.contains(suffix)) { false } - else { - val dotIndex = suffix.indexOf('.') - if (dotIndex < 1) { false } - else { - PublicSuffixes.wildcardPrefixes.contains(suffix.substring(dotIndex + 1)) + private def isValidPublicSuffix(suffix: String): Boolean = { + Try(toPunycode(suffix).stripSuffix(".")) + .map { normalisedSuffix => + if (PublicSuffixes.set.contains(normalisedSuffix)) { true } + else if (PublicSuffixes.exceptions.contains(normalisedSuffix)) { false } + else { + val dotIndex = normalisedSuffix.indexOf('.') + if (dotIndex < 1) { false } + else { + PublicSuffixes.wildcardPrefixes.contains(normalisedSuffix.substring(dotIndex + 1)) + } + } } - } + .getOrElse(false) + } def withConfig(config: UriConfig): DomainName = DomainName(value)(config) diff --git a/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala b/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala index 1059a0c1..7ec91d42 100644 --- a/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala +++ b/shared/src/main/scala/io/lemonlabs/uri/inet/PublicSuffixes.scala @@ -1,6 +1,9 @@ package io.lemonlabs.uri.inet -object PublicSuffixes { +/* +All suffixes are normalised in punycode + */ +object PublicSuffixes extends PunycodeSupport { lazy val exceptions: Set[String] = Set( "www.ck", "city.kawasaki.jp", @@ -33,7 +36,8 @@ object PublicSuffixes { "sch.uk" ) - lazy val set: Set[String] = publicSuffixes0 ++ publicSuffixes1 + lazy val set: Set[String] = (publicSuffixes0 ++ publicSuffixes1).map(toPunycode) + private def publicSuffixes0 = Set( "ac", diff --git a/shared/src/test/scala/io/lemonlabs/uri/PublicSuffixTests.scala b/shared/src/test/scala/io/lemonlabs/uri/PublicSuffixTests.scala index 2ed2c2cd..00716972 100644 --- a/shared/src/test/scala/io/lemonlabs/uri/PublicSuffixTests.scala +++ b/shared/src/test/scala/io/lemonlabs/uri/PublicSuffixTests.scala @@ -41,6 +41,16 @@ class PublicSuffixTests extends AnyFlatSpec with Matchers { uri.publicSuffix should equal(None) } + it should "handle punycode correctly" in { + val uri = Url.parse("http://www.example.xn--6frz82g") + uri.publicSuffix should equal(Some("xn--6frz82g")) + } + + it should "should handle trailing dot correctly" in { + val uri = Url.parse("http://www.example.com.") + uri.publicSuffix should equal(Some("com.")) + } + "RelativeUrls" should "not return public suffixes" in { val uri = RelativeUrl.parse("/blah") uri.publicSuffix should equal(None)