add hostname matching and https exception
This commit is contained in:
parent
61bb3e63e7
commit
59dd087084
|
@ -0,0 +1,123 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// TODO detect infinite redirects
|
||||||
|
|
||||||
|
module.exports.compile = module.exports.sortOpts = function (opts) {
|
||||||
|
var redirects = opts.redirects;
|
||||||
|
var dups = {};
|
||||||
|
var results = {
|
||||||
|
conflicts: {}
|
||||||
|
, patterns: []
|
||||||
|
, matchesMap: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
redirects.forEach(function (r) {
|
||||||
|
var bare;
|
||||||
|
var www;
|
||||||
|
|
||||||
|
if ('.' === r.id[0]) {
|
||||||
|
// for consistency
|
||||||
|
// TODO this should happen at the database level
|
||||||
|
r.id = '*' + r.id;
|
||||||
|
}
|
||||||
|
if ('*' === r.id[0]) {
|
||||||
|
// TODO check that we are not trying to redirect a tld (.com, .co.uk, .org, etc)
|
||||||
|
// tlds should follow the global policy
|
||||||
|
if (r.id[1] && '.' !== r.id[1]) {
|
||||||
|
// this is not a good place to throw as the consequences of a bug would be
|
||||||
|
// very bad, but errors should never be silent, so we'll compromise
|
||||||
|
console.warn("[NON-FATAL ERROR]: ignoring redirect pattern '" + r.id + "'");
|
||||||
|
results.conflicts[r.id] = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nix the '*' for easier matching
|
||||||
|
r.id = r.id.slice(1);
|
||||||
|
if (!r.id) {
|
||||||
|
r.id = '*';
|
||||||
|
}
|
||||||
|
if (dups[r.id]) {
|
||||||
|
results.conflicts[r.id] = r;
|
||||||
|
console.warn("[NON-FATAL ERROR]: duplicate entry for redirect pattern '" + r.id + "'");
|
||||||
|
}
|
||||||
|
dups[r.id] = true;
|
||||||
|
results.patterns.push(r);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bare = r.id.replace(/^www\./i, '');
|
||||||
|
www = r.id.replace(/^(www\.)?/i, 'www.');
|
||||||
|
|
||||||
|
if (true === r.value) {
|
||||||
|
// implicit add www
|
||||||
|
results.matchesMap[bare] = www;
|
||||||
|
results.matchesMap[www] = www;
|
||||||
|
} else if (false === r.value) {
|
||||||
|
// implicit remove www
|
||||||
|
results.matchesMap[bare] = bare;
|
||||||
|
results.matchesMap[www] = bare;
|
||||||
|
} else if (!r.value) {
|
||||||
|
// (null, '', 0, undefined)
|
||||||
|
// explicitly no change
|
||||||
|
results.matchesMap[r.id] = r.id;
|
||||||
|
} else {
|
||||||
|
// explicit value
|
||||||
|
results.matchesMap[r.id] = r.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
results.patterns.sort(function (a, b) {
|
||||||
|
return b.id.length - a.id.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.redirectTo = function (hostname, opts) {
|
||||||
|
var redir = opts.matchesMap[hostname];
|
||||||
|
|
||||||
|
if (redir) {
|
||||||
|
if (redir === hostname) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return redir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// longest to shortest
|
||||||
|
var hasWww = ('www.' === hostname.slice(0, 4));
|
||||||
|
//var noWww = (hasWww && hostname.slice(4)) || hostname;
|
||||||
|
//var yesWww = (hasWww && hostname) || ('www.' + hostname);
|
||||||
|
|
||||||
|
redir = false;
|
||||||
|
opts.patterns.some(function (r) {
|
||||||
|
// r.id begins with a dot, such as '.foo.example.com'
|
||||||
|
if (r.id !== hostname.slice(hostname.length - r.id.length)) {
|
||||||
|
// except for the default, which is an *
|
||||||
|
if ('*' !== r.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (true === r.value) {
|
||||||
|
// implicit add www
|
||||||
|
redir = hasWww ? hostname : ('www.' + hostname);
|
||||||
|
} else if (false === r.value) {
|
||||||
|
// implicit remove www
|
||||||
|
redir = hasWww ? hostname.slice(4) : hostname;
|
||||||
|
} else if (!r.value) {
|
||||||
|
// (null, '', 0, undefined)
|
||||||
|
// explicitly no change
|
||||||
|
redir = false;
|
||||||
|
} else {
|
||||||
|
// explicit value
|
||||||
|
redir = r.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (redir === hostname) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return redir;
|
||||||
|
};
|
|
@ -1,14 +1,26 @@
|
||||||
module.exports.scrubTheDub = function (req, res) {
|
'use strict';
|
||||||
|
|
||||||
|
module.exports.scrubTheDub = function (req, res, redirectives) {
|
||||||
// hack for bricked app-cache
|
// hack for bricked app-cache
|
||||||
// Also 301 redirects will not work for appcache (must issue html)
|
// Also 301 redirects will not work for appcache (must issue html)
|
||||||
if (require('./unbrick-appcache').unbrick(req, res)) {
|
if (require('./unbrick-appcache').unbrick(req, res)) {
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO port number for non-443
|
// TODO port number for non-443
|
||||||
var escapeHtml = require('escape-html');
|
var escapeHtml = require('escape-html');
|
||||||
var newLocation = 'https://' + req.hostname.replace(/^www\./, '') + req.url;
|
var newLocation;
|
||||||
var safeLocation = escapeHtml(newLocation);
|
var safeLocation;
|
||||||
|
|
||||||
|
if (redirectives) {
|
||||||
|
newLocation = require('./hostname-redirects').redirectTo(req.hostname, redirectives);
|
||||||
|
if (!newLocation) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newLocation = 'https://' + req.hostname.replace(/^www\./, '') + req.url;
|
||||||
|
}
|
||||||
|
safeLocation = escapeHtml(newLocation);
|
||||||
|
|
||||||
var metaRedirect = ''
|
var metaRedirect = ''
|
||||||
+ '<html>\n'
|
+ '<html>\n'
|
||||||
|
@ -24,4 +36,6 @@ module.exports.scrubTheDub = function (req, res) {
|
||||||
;
|
;
|
||||||
|
|
||||||
res.end(metaRedirect);
|
res.end(metaRedirect);
|
||||||
|
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
//var escapeRe;
|
||||||
|
//var insecureRedirects;
|
||||||
|
if (require('./unbrick-appcache').unbrick(req, res)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// because I have domains for which I don't want to pay for SSL certs
|
||||||
|
insecureRedirects = (redirects||[]).sort(function (a, b) {
|
||||||
|
var hlen = b.from.hostname.length - a.from.hostname.length;
|
||||||
|
var plen;
|
||||||
|
if (!hlen) {
|
||||||
|
plen = b.from.path.length - a.from.path.length;
|
||||||
|
return plen;
|
||||||
|
}
|
||||||
|
return hlen;
|
||||||
|
}).forEach(function (redirect) {
|
||||||
|
var origHost = host;
|
||||||
|
|
||||||
|
if (!escapeRe) {
|
||||||
|
escapeRe = require('escape-string-regexp');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO if '*' === hostname[0], omit '^'
|
||||||
|
host = host.replace(
|
||||||
|
new RegExp('^' + escapeRe(redirect.from.hostname))
|
||||||
|
, redirect.to.hostname
|
||||||
|
);
|
||||||
|
if (host === origHost) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
url = url.replace(
|
||||||
|
new RegExp('^' + escapeRe(redirect.from.path))
|
||||||
|
, redirect.to.path
|
||||||
|
);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var opts = {
|
||||||
|
redirects: [
|
||||||
|
{ "id": "*", "value": true }
|
||||||
|
, { "id": "ns2.redirect-www.org", "value": false }
|
||||||
|
, { "id": "hellabit.com", "value": false }
|
||||||
|
, { "id": "*.hellabit.com", "value": false }
|
||||||
|
, { "id": "redirect-www.org", "value": null }
|
||||||
|
, { "id": "www.redirect-www.org", "value": null }
|
||||||
|
, { "id": "no.redirect-www.org", "value": false }
|
||||||
|
, { "id": "*.redirect-www.org", "value": false }
|
||||||
|
, { "id": "*.yes.redirect-www.org", "value": true }
|
||||||
|
, { "id": "yes.redirect-www.org", "value": true }
|
||||||
|
, { "id": "*.maybe.redirect-www.org", "value": null }
|
||||||
|
, { "id": "maybe.redirect-www.org", "value": null }
|
||||||
|
, { "id": "blog.coolaj86.com", "value": 'coolaj86.com' } // TODO pathname
|
||||||
|
]
|
||||||
|
, matchesMap: null
|
||||||
|
, patternsMap: null
|
||||||
|
, patterns: null
|
||||||
|
};
|
||||||
|
|
||||||
|
var redirectTo = require('../lib/hostname-redirects').redirectTo;
|
||||||
|
var sortOpts = require('../lib/hostname-redirects').sortOpts;
|
||||||
|
|
||||||
|
var domains = {
|
||||||
|
// maybewww
|
||||||
|
'redirect-www.org': false
|
||||||
|
, 'www.redirect-www.org': false
|
||||||
|
, 'maybe.redirect-www.org': false
|
||||||
|
, 'www.maybe.redirect-www.org': false
|
||||||
|
|
||||||
|
// yeswww
|
||||||
|
, 'yes.redirect-www.org': 'www.yes.redirect-www.org'
|
||||||
|
, 'foo.yes.redirect-www.org': 'www.foo.yes.redirect-www.org'
|
||||||
|
|
||||||
|
// nowww
|
||||||
|
, 'www.no.redirect-www.org': 'no.redirect-www.org'
|
||||||
|
, 'www.foo.no.redirect-www.org': 'foo.no.redirect-www.org'
|
||||||
|
|
||||||
|
, 'ns2.redirect-www.org': false
|
||||||
|
, 'www.ns2.redirect-www.org': 'ns2.redirect-www.org'
|
||||||
|
|
||||||
|
, 'ns1.redirect-www.org': false
|
||||||
|
, 'www.ns1.redirect-www.org': 'ns1.redirect-www.org'
|
||||||
|
|
||||||
|
, 'hellabit.com': false
|
||||||
|
, 'www.hellabit.com': 'hellabit.com'
|
||||||
|
|
||||||
|
// default policy (yeswww)
|
||||||
|
, 'ahellabit.com': 'www.ahellabit.com'
|
||||||
|
, 'www.ahellabit.com': false
|
||||||
|
, 'example.com': 'www.example.com'
|
||||||
|
, 'www.example.com': false
|
||||||
|
};
|
||||||
|
|
||||||
|
var redirects = sortOpts(opts);
|
||||||
|
|
||||||
|
console.log(redirects);
|
||||||
|
|
||||||
|
Object.keys(domains).forEach(function (domain, i) {
|
||||||
|
var redir = domains[domain];
|
||||||
|
var result = redirectTo(domain, redirects);
|
||||||
|
|
||||||
|
if (redir !== result) {
|
||||||
|
throw new Error("For domain #" + i + " '" + domain + "' expected '" + redir + "' but got '" + result + "'");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Didn't throw any errors. Must have worked, eh?");
|
||||||
|
console.log("TODO: detect and report infinite redirects");
|
Loading…
Reference in New Issue