X-Git-Url: https://git.sesse.net/?p=remoteglot;a=blobdiff_plain;f=server%2Fserve-analysis.js;h=41610e1439d733d2f73c0389351f711454a5e9a0;hp=86f2d9083aa9c229451a42acfa9edfba62daa97b;hb=HEAD;hpb=04509c20d2edea71df6e9c440d3260458e3cc34a diff --git a/server/serve-analysis.js b/server/serve-analysis.js index 86f2d90..8448cfa 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 = []; @@ -134,6 +137,17 @@ 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); 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,34 @@ 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. + '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, + }; }); }); }); @@ -231,6 +279,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 +343,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 +414,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)) {