if (process.argv.length >= 3) {
json_filename = process.argv[2];
}
+var html_filename = '/srv/analysis.sesse.net/www/index.html';
// Expected destination filenames.
var serve_url = '/analysis.pl';
+var html_serve_url = '/index-inline.html';
var hash_serve_url = '/hash';
if (process.argv.length >= 4) {
serve_url = process.argv[3];
// The current contents of the file to hand out, and its last modified time.
var json = undefined;
+var html = undefined;
// The last five timestamps, and diffs from them to the latest version.
var historic_json = [];
}
var histobj = history_left.shift();
- var diff = delta.JSON_delta.diff(histobj.parsed, new_json.parsed, false);
+ var diff = delta.JSON_delta.diff(histobj.parsed, new_json.parsed);
var diff_text = JSON.stringify(diff);
+
+ // Verify that the delta is correct
+ var base = JSON.parse(histobj.plain);
+ delta.JSON_delta.patch(base, diff);
+ var correct_pv = JSON.stringify(base['pv']);
+ var wrong_pv = JSON.stringify(new_json.parsed['pv']);
+ if (correct_pv !== wrong_pv) {
+ console.log("Patch went wrong:", histobj.plain, new_json.plain);
+ exit();
+ }
+
zlib.gzip(diff_text, function(err, buffer) {
if (err) throw err;
new_diff_json[histobj.last_modified] = {
});
}
+function read_entire_file(filename, callback) {
+ fs.open(filename, 'r', function(err, fd) {
+ if (err) throw err;
+ fs.fstat(fd, function(err, st) {
+ if (err) throw err;
+ var buffer = new Buffer(1048576);
+ fs.read(fd, buffer, 0, 1048576, 0, function(err, bytesRead, buffer) {
+ if (err) throw err;
+ fs.close(fd, function() {
+ var contents = buffer.toString('utf8', 0, bytesRead);
+ callback(contents, st.mtime.getTime());
+ });
+ });
+ });
+ });
+}
+
var reread_file = function(event, filename) {
if (filename != path.basename(json_filename)) {
return;
json_lock = 1;
console.log("Rereading " + json_filename);
- fs.open(json_filename, 'r', function(err, fd) {
- if (err) throw err;
- fs.fstat(fd, function(err, st) {
- if (err) throw err;
- var buffer = new Buffer(1048576);
- fs.read(fd, buffer, 0, 1048576, 0, function(err, bytesRead, buffer) {
+ read_entire_file(json_filename, function(new_json_contents, mtime) {
+ replace_json(new_json_contents, mtime);
+
+ // The HTML can go async, it's not too hopeless if it's out of date by a few milliseconds
+ read_entire_file(html_filename, function(new_html_contents, html_mtime) {
+ var json_headers = {
+ 'X-RGLM': mtime,
+ 'X-RGNV': count_viewers(), // May be slightly out of date.
+ 'Date': (new Date).toUTCString(),
+ };
+ if (MINIMUM_VERSION) {
+ json_headers['X-RGMV'] = MINIMUM_VERSION;
+ }
+ let inline_json = {
+ 'data': JSON.parse(new_json_contents),
+ 'headers': json_headers,
+ };
+ delete inline_json['data']['internal'];
+
+ new_html_contents = new_html_contents.replace(
+ '/*REPLACE:inlinejson*/',
+ 'window.inline_json=' + JSON.stringify(inline_json) + ';');
+ zlib.gzip(new_html_contents, function(err, buffer) {
if (err) throw err;
- fs.close(fd, function() {
- var new_json_contents = buffer.toString('utf8', 0, bytesRead);
- replace_json(new_json_contents, st.mtime.getTime());
- });
+ html = {
+ plain: new_html_contents,
+ gzip: buffer,
+ };
});
});
});
touch_timer = setTimeout(function() {
console.log("Touching analysis.json due to no other activity");
var now = Date.now() / 1000;
- fs.utimes(json_filename, now, now);
+ fs.utimes(json_filename, now, now, function() {});
}, 30000);
}
var possibly_wakeup_clients = function() {
}
response.end();
}
+var send_html = function(response, accept_gzip, num_viewers) {
+ var headers = {
+ 'Content-type': 'text/html; charset=utf-8',
+ 'Vary': 'Accept-Encoding',
+ };
+
+ if (accept_gzip) {
+ headers['Content-Length'] = html.gzip.length;
+ headers['Content-Encoding'] = 'gzip';
+ response.writeHead(200, headers);
+ response.write(html.gzip);
+ } else {
+ headers['Content-Length'] = html.plain.length;
+ response.writeHead(200, headers);
+ response.write(html.plain);
+ }
+ response.end();
+}
var mark_recently_seen = function(unique) {
if (unique) {
last_seen_clients[unique] = (new Date).getTime();
// Note: We abuse serve_url as a regex.
var varnishncsa = child_process.spawn(
'varnishncsa', ['-F', '%{%s}t %U %q tffb=%{Varnish:time_firstbyte}x',
- '-q', 'ReqURL ~ "^' + serve_url + '"']);
+ '-q', 'ReqURL ~ "^(' + serve_url + '|' + html_serve_url + ')"']);
var rl = readline.createInterface({
input: varnishncsa.stdout,
output: varnishncsa.stdin,
hash_lookup.handle_request(fen, response);
return;
}
- if (u.pathname !== serve_url) {
+ if (u.pathname !== serve_url && u.pathname !== html_serve_url) {
// This is not the request you are looking for.
send_404(response);
return;
}
- mark_recently_seen(unique);
-
var accept_encoding = request.headers['accept-encoding'];
- var accept_gzip;
- if (accept_encoding !== undefined && accept_encoding.match(/\bgzip\b/)) {
- accept_gzip = true;
- } else {
- accept_gzip = false;
+ let accept_gzip = (accept_encoding !== undefined && accept_encoding.match(/\bgzip\b/));
+
+ if (u.pathname === html_serve_url) {
+ send_html(response, accept_gzip, count_viewers());
+ return;
}
+ mark_recently_seen(unique);
+
// If we already have something newer than what the user has,
// just send it out and be done with it.
if (json !== undefined && (!ims || json.last_modified > ims)) {