A => cache.js +72 -0
@@ 0,0 1,72 @@
+/* Heu heu hue
+ */
+
+var http = require('http');
+var fs = require('fs');
+var config = require('./config');
+
+var cache = {};
+
+function AddCacheItem(path) {
+ var item = {path: path, subscribers: []};
+ cache[path] = item
+ item.inProgress = false;
+ fetch(item);
+ setInterval(function() { fetch(item); }, config.expiry);
+ return item;
+}
+
+function fetch(item) {
+ console.log('going to fetch' + item.path);
+ if(item.inProgress) return; // Oh dear!
+
+ item.inProgress = true;
+ http.get({host: config.target, port: 80, path: item.path}, function(res) {
+ item.data = '';
+
+ res.on('data', function(chunk) {
+ item.data += chunk.toString('binary');
+ });
+
+ res.on('end', function() {
+ item.statusCode = res.statusCode;
+ item.headers = {};
+ ['connection', 'content-type', 'content-length', 'location'].map(function(header){
+ if (res.headers[header]) // We may want to do this replacement in the body as well
+ item.headers[header] = res.headers[header].replace(config.target, config.host);
+ })
+ if(item.headers['content-type'].lastIndexOf('text/html', 0) == 0) {
+ var sexy_message = '$1 - Page cached by ' + config.host + ' at ' + new Date() + '$2';
+ item.data = item.data.replace(/(<title>.*)(<\/title>)/, sexy_message);
+ }
+
+ item.inProgress = false;
+
+ while (item.subscribers.length)
+ item.subscribers.pop()(item);
+ });
+
+ }).on('error', function(e) {
+ console.log('Tried to cache ' + item.path + ' but encountered a wild "' + e.message + '"');
+ item.inProgress = false;
+ // Try again?
+ fetch(item);
+ });
+}
+
+
+// Pushes content immediately if it's available or queues callback
+function pushContent(item, callback) {
+ if (item.inProgress)
+ item.subscribers.push(callback);
+ else
+ callback(item);
+}
+
+// Exports
+module.exports.getPage = function(path, callback) {
+ console.log('getPage ' + path);
+ var item = path in cache ? cache[path] : AddCacheItem(path);
+ if (callback != undefined)
+ pushContent(item, callback);
+}
A => config.js +14 -0
@@ 0,0 1,14 @@
+// Since our unit time is microseconds, some constants have been provided to
+// help express time in larger units
+var second = 1000;
+var minute = 60 * second;
+var hour = 60 * minute;
+var day = 24 * hour;
+var week = 7 * day;
+
+
+module.exports = {
+ target: 'wiki.mythea.com',
+ port: 8012,
+ expiry: 5 * day,
+}
A => imitare.js +35 -0
@@ 0,0 1,35 @@
+/* Imitare is a piece of software used to create a mirror of a website */
+
+var http = require('http');
+var urlmodule = require('url');
+var config = require('./config');
+var cache = require('./cache');
+
+////////////////////////////////////////////////////////////////////////////////
+
+function givePageToResponse(path, res) {
+ //console.log('givePageToResponse');
+ cache.getPage(path, function(item) {
+ //console.log('page gotted');
+ res.writeHead(item.statusCode, item.headers);
+ //console.log('sending...')
+ //console.log(item.statusCode);
+ //console.log(item.headers);
+ //console.log('/sending...')
+ res.end(item.data, 'binary');
+ })
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+http.createServer(function(req, res) {
+ // We get signal!
+ var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
+ var path = urlmodule.parse(req.url).path;
+ var host = req.headers.host;
+ config.host = host;
+ console.log(ip + ' - ' + path);
+ givePageToResponse(path, res);
+}).listen(config.port, '127.0.0.1');
+
+console.log('imitare for ' + config.target + ' started on port ' + config.port);