github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/net/publicsuffix/list_test.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package publicsuffix 6 7 import ( 8 "sort" 9 "strings" 10 "testing" 11 ) 12 13 func TestNodeLabel(t *testing.T) { 14 for i, want := range nodeLabels { 15 got := nodeLabel(uint32(i)) 16 if got != want { 17 t.Errorf("%d: got %q, want %q", i, got, want) 18 } 19 } 20 } 21 22 func TestFind(t *testing.T) { 23 testCases := []string{ 24 "", 25 "a", 26 "a0", 27 "aaaa", 28 "ao", 29 "ap", 30 "ar", 31 "aro", 32 "arp", 33 "arpa", 34 "arpaa", 35 "arpb", 36 "az", 37 "b", 38 "b0", 39 "ba", 40 "z", 41 "zu", 42 "zv", 43 "zw", 44 "zx", 45 "zy", 46 "zz", 47 "zzzz", 48 } 49 for _, tc := range testCases { 50 got := find(tc, 0, numTLD) 51 want := notFound 52 for i := uint32(0); i < numTLD; i++ { 53 if tc == nodeLabel(i) { 54 want = i 55 break 56 } 57 } 58 if got != want { 59 t.Errorf("%q: got %d, want %d", tc, got, want) 60 } 61 } 62 } 63 64 func TestICANN(t *testing.T) { 65 testCases := map[string]bool{ 66 "foo.org": true, 67 "foo.co.uk": true, 68 "foo.dyndns.org": false, 69 "foo.go.dyndns.org": false, 70 "foo.blogspot.co.uk": false, 71 "foo.intranet": false, 72 } 73 for domain, want := range testCases { 74 _, got := PublicSuffix(domain) 75 if got != want { 76 t.Errorf("%q: got %v, want %v", domain, got, want) 77 } 78 } 79 } 80 81 var publicSuffixTestCases = []struct { 82 domain, want string 83 }{ 84 // Empty string. 85 {"", ""}, 86 87 // The .ao rules are: 88 // ao 89 // ed.ao 90 // gv.ao 91 // og.ao 92 // co.ao 93 // pb.ao 94 // it.ao 95 {"ao", "ao"}, 96 {"www.ao", "ao"}, 97 {"pb.ao", "pb.ao"}, 98 {"www.pb.ao", "pb.ao"}, 99 {"www.xxx.yyy.zzz.pb.ao", "pb.ao"}, 100 101 // The .ar rules are: 102 // ar 103 // com.ar 104 // edu.ar 105 // gob.ar 106 // gov.ar 107 // int.ar 108 // mil.ar 109 // net.ar 110 // org.ar 111 // tur.ar 112 // blogspot.com.ar 113 {"ar", "ar"}, 114 {"www.ar", "ar"}, 115 {"nic.ar", "ar"}, 116 {"www.nic.ar", "ar"}, 117 {"com.ar", "com.ar"}, 118 {"www.com.ar", "com.ar"}, 119 {"blogspot.com.ar", "blogspot.com.ar"}, 120 {"www.blogspot.com.ar", "blogspot.com.ar"}, 121 {"www.xxx.yyy.zzz.blogspot.com.ar", "blogspot.com.ar"}, 122 {"logspot.com.ar", "com.ar"}, 123 {"zlogspot.com.ar", "com.ar"}, 124 {"zblogspot.com.ar", "com.ar"}, 125 126 // The .arpa rules are: 127 // arpa 128 // e164.arpa 129 // in-addr.arpa 130 // ip6.arpa 131 // iris.arpa 132 // uri.arpa 133 // urn.arpa 134 {"arpa", "arpa"}, 135 {"www.arpa", "arpa"}, 136 {"urn.arpa", "urn.arpa"}, 137 {"www.urn.arpa", "urn.arpa"}, 138 {"www.xxx.yyy.zzz.urn.arpa", "urn.arpa"}, 139 140 // The relevant {kobe,kyoto}.jp rules are: 141 // jp 142 // *.kobe.jp 143 // !city.kobe.jp 144 // kyoto.jp 145 // ide.kyoto.jp 146 {"jp", "jp"}, 147 {"kobe.jp", "jp"}, 148 {"c.kobe.jp", "c.kobe.jp"}, 149 {"b.c.kobe.jp", "c.kobe.jp"}, 150 {"a.b.c.kobe.jp", "c.kobe.jp"}, 151 {"city.kobe.jp", "kobe.jp"}, 152 {"www.city.kobe.jp", "kobe.jp"}, 153 {"kyoto.jp", "kyoto.jp"}, 154 {"test.kyoto.jp", "kyoto.jp"}, 155 {"ide.kyoto.jp", "ide.kyoto.jp"}, 156 {"b.ide.kyoto.jp", "ide.kyoto.jp"}, 157 {"a.b.ide.kyoto.jp", "ide.kyoto.jp"}, 158 159 // The .tw rules are: 160 // tw 161 // edu.tw 162 // gov.tw 163 // mil.tw 164 // com.tw 165 // net.tw 166 // org.tw 167 // idv.tw 168 // game.tw 169 // ebiz.tw 170 // club.tw 171 // 網路.tw (xn--zf0ao64a.tw) 172 // 組織.tw (xn--uc0atv.tw) 173 // 商業.tw (xn--czrw28b.tw) 174 // blogspot.tw 175 {"tw", "tw"}, 176 {"aaa.tw", "tw"}, 177 {"www.aaa.tw", "tw"}, 178 {"xn--czrw28b.aaa.tw", "tw"}, 179 {"edu.tw", "edu.tw"}, 180 {"www.edu.tw", "edu.tw"}, 181 {"xn--czrw28b.edu.tw", "edu.tw"}, 182 {"xn--czrw28b.tw", "xn--czrw28b.tw"}, 183 {"www.xn--czrw28b.tw", "xn--czrw28b.tw"}, 184 {"xn--uc0atv.xn--czrw28b.tw", "xn--czrw28b.tw"}, 185 {"xn--kpry57d.tw", "tw"}, 186 187 // The .uk rules are: 188 // uk 189 // ac.uk 190 // co.uk 191 // gov.uk 192 // ltd.uk 193 // me.uk 194 // net.uk 195 // nhs.uk 196 // org.uk 197 // plc.uk 198 // police.uk 199 // *.sch.uk 200 // blogspot.co.uk 201 {"uk", "uk"}, 202 {"aaa.uk", "uk"}, 203 {"www.aaa.uk", "uk"}, 204 {"mod.uk", "uk"}, 205 {"www.mod.uk", "uk"}, 206 {"sch.uk", "uk"}, 207 {"mod.sch.uk", "mod.sch.uk"}, 208 {"www.sch.uk", "www.sch.uk"}, 209 {"blogspot.co.uk", "blogspot.co.uk"}, 210 {"blogspot.nic.uk", "uk"}, 211 {"blogspot.sch.uk", "blogspot.sch.uk"}, 212 213 // The .рф rules are 214 // рф (xn--p1ai) 215 {"xn--p1ai", "xn--p1ai"}, 216 {"aaa.xn--p1ai", "xn--p1ai"}, 217 {"www.xxx.yyy.xn--p1ai", "xn--p1ai"}, 218 219 // The .zw rules are: 220 // *.zw 221 {"zw", "zw"}, 222 {"www.zw", "www.zw"}, 223 {"zzz.zw", "zzz.zw"}, 224 {"www.zzz.zw", "zzz.zw"}, 225 {"www.xxx.yyy.zzz.zw", "zzz.zw"}, 226 227 // There are no .nosuchtld rules. 228 {"nosuchtld", "nosuchtld"}, 229 {"foo.nosuchtld", "nosuchtld"}, 230 {"bar.foo.nosuchtld", "nosuchtld"}, 231 } 232 233 func BenchmarkPublicSuffix(b *testing.B) { 234 for i := 0; i < b.N; i++ { 235 for _, tc := range publicSuffixTestCases { 236 List.PublicSuffix(tc.domain) 237 } 238 } 239 } 240 241 func TestPublicSuffix(t *testing.T) { 242 for _, tc := range publicSuffixTestCases { 243 got := List.PublicSuffix(tc.domain) 244 if got != tc.want { 245 t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) 246 } 247 } 248 } 249 250 func TestSlowPublicSuffix(t *testing.T) { 251 for _, tc := range publicSuffixTestCases { 252 got := slowPublicSuffix(tc.domain) 253 if got != tc.want { 254 t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) 255 } 256 } 257 } 258 259 // slowPublicSuffix implements the canonical (but O(number of rules)) public 260 // suffix algorithm described at http://publicsuffix.org/list/. 261 // 262 // 1. Match domain against all rules and take note of the matching ones. 263 // 2. If no rules match, the prevailing rule is "*". 264 // 3. If more than one rule matches, the prevailing rule is the one which is an exception rule. 265 // 4. If there is no matching exception rule, the prevailing rule is the one with the most labels. 266 // 5. If the prevailing rule is a exception rule, modify it by removing the leftmost label. 267 // 6. The public suffix is the set of labels from the domain which directly match the labels of the prevailing rule (joined by dots). 268 // 7. The registered or registrable domain is the public suffix plus one additional label. 269 // 270 // This function returns the public suffix, not the registrable domain, and so 271 // it stops after step 6. 272 func slowPublicSuffix(domain string) string { 273 match := func(rulePart, domainPart string) bool { 274 switch rulePart[0] { 275 case '*': 276 return true 277 case '!': 278 return rulePart[1:] == domainPart 279 } 280 return rulePart == domainPart 281 } 282 283 domainParts := strings.Split(domain, ".") 284 var matchingRules [][]string 285 286 loop: 287 for _, rule := range rules { 288 ruleParts := strings.Split(rule, ".") 289 if len(domainParts) < len(ruleParts) { 290 continue 291 } 292 for i := range ruleParts { 293 rulePart := ruleParts[len(ruleParts)-1-i] 294 domainPart := domainParts[len(domainParts)-1-i] 295 if !match(rulePart, domainPart) { 296 continue loop 297 } 298 } 299 matchingRules = append(matchingRules, ruleParts) 300 } 301 if len(matchingRules) == 0 { 302 matchingRules = append(matchingRules, []string{"*"}) 303 } else { 304 sort.Sort(byPriority(matchingRules)) 305 } 306 prevailing := matchingRules[0] 307 if prevailing[0][0] == '!' { 308 prevailing = prevailing[1:] 309 } 310 if prevailing[0][0] == '*' { 311 replaced := domainParts[len(domainParts)-len(prevailing)] 312 prevailing = append([]string{replaced}, prevailing[1:]...) 313 } 314 return strings.Join(prevailing, ".") 315 } 316 317 type byPriority [][]string 318 319 func (b byPriority) Len() int { return len(b) } 320 func (b byPriority) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 321 func (b byPriority) Less(i, j int) bool { 322 if b[i][0][0] == '!' { 323 return true 324 } 325 if b[j][0][0] == '!' { 326 return false 327 } 328 return len(b[i]) > len(b[j]) 329 } 330 331 // eTLDPlusOneTestCases come from 332 // https://github.com/publicsuffix/list/blob/master/tests/test_psl.txt 333 var eTLDPlusOneTestCases = []struct { 334 domain, want string 335 }{ 336 // Empty input. 337 {"", ""}, 338 // Unlisted TLD. 339 {"example", ""}, 340 {"example.example", "example.example"}, 341 {"b.example.example", "example.example"}, 342 {"a.b.example.example", "example.example"}, 343 // TLD with only 1 rule. 344 {"biz", ""}, 345 {"domain.biz", "domain.biz"}, 346 {"b.domain.biz", "domain.biz"}, 347 {"a.b.domain.biz", "domain.biz"}, 348 // TLD with some 2-level rules. 349 {"com", ""}, 350 {"example.com", "example.com"}, 351 {"b.example.com", "example.com"}, 352 {"a.b.example.com", "example.com"}, 353 {"uk.com", ""}, 354 {"example.uk.com", "example.uk.com"}, 355 {"b.example.uk.com", "example.uk.com"}, 356 {"a.b.example.uk.com", "example.uk.com"}, 357 {"test.ac", "test.ac"}, 358 // TLD with only 1 (wildcard) rule. 359 {"mm", ""}, 360 {"c.mm", ""}, 361 {"b.c.mm", "b.c.mm"}, 362 {"a.b.c.mm", "b.c.mm"}, 363 // More complex TLD. 364 {"jp", ""}, 365 {"test.jp", "test.jp"}, 366 {"www.test.jp", "test.jp"}, 367 {"ac.jp", ""}, 368 {"test.ac.jp", "test.ac.jp"}, 369 {"www.test.ac.jp", "test.ac.jp"}, 370 {"kyoto.jp", ""}, 371 {"test.kyoto.jp", "test.kyoto.jp"}, 372 {"ide.kyoto.jp", ""}, 373 {"b.ide.kyoto.jp", "b.ide.kyoto.jp"}, 374 {"a.b.ide.kyoto.jp", "b.ide.kyoto.jp"}, 375 {"c.kobe.jp", ""}, 376 {"b.c.kobe.jp", "b.c.kobe.jp"}, 377 {"a.b.c.kobe.jp", "b.c.kobe.jp"}, 378 {"city.kobe.jp", "city.kobe.jp"}, 379 {"www.city.kobe.jp", "city.kobe.jp"}, 380 // TLD with a wildcard rule and exceptions. 381 {"ck", ""}, 382 {"test.ck", ""}, 383 {"b.test.ck", "b.test.ck"}, 384 {"a.b.test.ck", "b.test.ck"}, 385 {"www.ck", "www.ck"}, 386 {"www.www.ck", "www.ck"}, 387 // US K12. 388 {"us", ""}, 389 {"test.us", "test.us"}, 390 {"www.test.us", "test.us"}, 391 {"ak.us", ""}, 392 {"test.ak.us", "test.ak.us"}, 393 {"www.test.ak.us", "test.ak.us"}, 394 {"k12.ak.us", ""}, 395 {"test.k12.ak.us", "test.k12.ak.us"}, 396 {"www.test.k12.ak.us", "test.k12.ak.us"}, 397 // Punycoded IDN labels 398 {"xn--85x722f.com.cn", "xn--85x722f.com.cn"}, 399 {"xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"}, 400 {"www.xn--85x722f.xn--55qx5d.cn", "xn--85x722f.xn--55qx5d.cn"}, 401 {"shishi.xn--55qx5d.cn", "shishi.xn--55qx5d.cn"}, 402 {"xn--55qx5d.cn", ""}, 403 {"xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"}, 404 {"www.xn--85x722f.xn--fiqs8s", "xn--85x722f.xn--fiqs8s"}, 405 {"shishi.xn--fiqs8s", "shishi.xn--fiqs8s"}, 406 {"xn--fiqs8s", ""}, 407 } 408 409 func TestEffectiveTLDPlusOne(t *testing.T) { 410 for _, tc := range eTLDPlusOneTestCases { 411 got, _ := EffectiveTLDPlusOne(tc.domain) 412 if got != tc.want { 413 t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) 414 } 415 } 416 }