179 lines
3.4 KiB
JavaScript
179 lines
3.4 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var VALID_HOSTNAME_VALUE = 0;
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Return min(a, b), handling possible `null` values.
|
||
|
*
|
||
|
* @param {number|null} a
|
||
|
* @param {number|null} b
|
||
|
* @return {number|null}
|
||
|
*/
|
||
|
function minIndex(a, b) {
|
||
|
if (a === null) {
|
||
|
return b;
|
||
|
} else if (b === null) {
|
||
|
return a;
|
||
|
}
|
||
|
|
||
|
return a < b ? a : b;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Insert a public suffix rule in the `trie`.
|
||
|
*
|
||
|
* @param {object} rule
|
||
|
* @param {object} trie
|
||
|
* @return {object} trie (updated)
|
||
|
*/
|
||
|
function insertInTrie(rule, trie) {
|
||
|
var parts = rule.parts;
|
||
|
var node = trie;
|
||
|
|
||
|
for (var i = 0; i < parts.length; i += 1) {
|
||
|
var part = parts[i];
|
||
|
var nextNode = node[part];
|
||
|
if (nextNode === undefined) {
|
||
|
nextNode = Object.create(null);
|
||
|
node[part] = nextNode;
|
||
|
}
|
||
|
|
||
|
node = nextNode;
|
||
|
}
|
||
|
|
||
|
node.$ = VALID_HOSTNAME_VALUE;
|
||
|
|
||
|
return trie;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Recursive lookup of `parts` (starting at `index`) in the tree.
|
||
|
*
|
||
|
* @param {array} parts
|
||
|
* @param {object} trie
|
||
|
* @param {number} index - when to start in `parts` (initially: length - 1)
|
||
|
* @return {number} size of the suffix found (in number of parts matched)
|
||
|
*/
|
||
|
function lookupInTrie(parts, trie, index) {
|
||
|
var part;
|
||
|
var nextNode;
|
||
|
var publicSuffixIndex = null;
|
||
|
|
||
|
// We have a match!
|
||
|
if (trie.$ !== undefined) {
|
||
|
publicSuffixIndex = index + 1;
|
||
|
}
|
||
|
|
||
|
// No more `parts` to look for
|
||
|
if (index === -1) {
|
||
|
return publicSuffixIndex;
|
||
|
}
|
||
|
|
||
|
part = parts[index];
|
||
|
|
||
|
// Check branch corresponding to next part of hostname
|
||
|
nextNode = trie[part];
|
||
|
if (nextNode !== undefined) {
|
||
|
publicSuffixIndex = minIndex(
|
||
|
publicSuffixIndex,
|
||
|
lookupInTrie(parts, nextNode, index - 1)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Check wildcard branch
|
||
|
nextNode = trie['*'];
|
||
|
if (nextNode !== undefined) {
|
||
|
publicSuffixIndex = minIndex(
|
||
|
publicSuffixIndex,
|
||
|
lookupInTrie(parts, nextNode, index - 1)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return publicSuffixIndex;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Contains the public suffix ruleset as a Trie for efficient look-up.
|
||
|
*
|
||
|
* @constructor
|
||
|
*/
|
||
|
function SuffixTrie(rules) {
|
||
|
this.exceptions = Object.create(null);
|
||
|
this.rules = Object.create(null);
|
||
|
|
||
|
if (rules) {
|
||
|
for (var i = 0; i < rules.length; i += 1) {
|
||
|
var rule = rules[i];
|
||
|
if (rule.exception) {
|
||
|
insertInTrie(rule, this.exceptions);
|
||
|
} else {
|
||
|
insertInTrie(rule, this.rules);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Load the trie from JSON (as serialized by JSON.stringify).
|
||
|
*/
|
||
|
SuffixTrie.fromJson = function (json) {
|
||
|
var trie = new SuffixTrie();
|
||
|
|
||
|
trie.exceptions = json.exceptions;
|
||
|
trie.rules = json.rules;
|
||
|
|
||
|
return trie;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Check if `value` is a valid TLD.
|
||
|
*/
|
||
|
SuffixTrie.prototype.hasTld = function (value) {
|
||
|
// All TLDs are at the root of the Trie.
|
||
|
return this.rules[value] !== undefined;
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Check if `hostname` has a valid public suffix in `trie`.
|
||
|
*
|
||
|
* @param {string} hostname
|
||
|
* @return {string|null} public suffix
|
||
|
*/
|
||
|
SuffixTrie.prototype.suffixLookup = function (hostname) {
|
||
|
var parts = hostname.split('.');
|
||
|
|
||
|
// Look for a match in rules
|
||
|
var publicSuffixIndex = lookupInTrie(
|
||
|
parts,
|
||
|
this.rules,
|
||
|
parts.length - 1
|
||
|
);
|
||
|
|
||
|
if (publicSuffixIndex === null) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
// Look for exceptions
|
||
|
var exceptionIndex = lookupInTrie(
|
||
|
parts,
|
||
|
this.exceptions,
|
||
|
parts.length - 1
|
||
|
);
|
||
|
|
||
|
if (exceptionIndex !== null) {
|
||
|
return parts.slice(exceptionIndex + 1).join('.');
|
||
|
}
|
||
|
|
||
|
return parts.slice(publicSuffixIndex).join('.');
|
||
|
};
|
||
|
|
||
|
|
||
|
module.exports = SuffixTrie;
|