X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=server%2Fserve-analysis.js;h=0008cd78fdcac69c2c84a48133e41ac899c118a6;hb=18cc671e78bcf74545535ab2ab7e4dc0ef3b6b58;hp=ec419162b033c18106e23b34675e42859618f1ab;hpb=38b3144868385f5573f8f98d7568f48c17d9e93b;p=remoteglot diff --git a/server/serve-analysis.js b/server/serve-analysis.js index ec41916..0008cd7 100644 --- a/server/serve-analysis.js +++ b/server/serve-analysis.js @@ -23,9 +23,11 @@ var json_filename = '/srv/analysis.sesse.net/www/analysis.json'; 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]; @@ -53,6 +55,7 @@ var json_lock = 0; // 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 = []; @@ -132,8 +135,19 @@ var create_json_historic_diff = function(new_json, history_left, new_diff_json, } var histobj = history_left.shift(); - var diff = delta.JSON_delta.diff(histobj.parsed, new_json.parsed, true); + 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] = { @@ -146,6 +160,23 @@ var create_json_historic_diff = function(new_json, history_left, new_diff_json, }); } +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; @@ -162,17 +193,33 @@ var reread_file = function(event, filename) { 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. + }; + 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, + }; }); }); }); @@ -183,7 +230,7 @@ var reread_file = function(event, filename) { 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() { @@ -231,6 +278,24 @@ var send_json = function(response, ims, accept_gzip, num_viewers) { } 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(); @@ -277,7 +342,7 @@ if (COUNT_FROM_VARNISH_LOG) { // 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, @@ -348,22 +413,22 @@ server.on('request', function(request, response) { 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)) {