From 2b3094cada875b41bca1faf801e643de0e6e5aa3 Mon Sep 17 00:00:00 2001 From: Rossen Georgiev Date: Sun, 1 Mar 2015 02:00:56 +0000 Subject: [PATCH 1/8] Fix bug with content misalignment due to UTF8 Sometimes data.length != content-length due to UTF8. This can cause the following request to contain extra bytes already fetched by the first request. --- logtail.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/logtail.js b/logtail.js index 94601b0..a21cfbf 100644 --- a/logtail.js +++ b/logtail.js @@ -44,6 +44,7 @@ function get_log() { loading = false; var size; + var content_size; if (xhr.status === 206) { if (data.length > load) @@ -54,20 +55,23 @@ function get_log() { throw "Server did not respond with a Content-Range"; size = parseInt(c_r.split("/")[1]); + content_size = xhr.getResponseHeader("Content-Length"); + if (isNaN(size)) throw "Invalid Content-Range size"; } else if (xhr.status === 200) { if (log_size > 1) throw "Expected 206 Partial Content"; - size = data.length; + size = xhr.getResponseHeader("Content-Length"); + content_size = size; } var added = false; if (log_size === 0) { /* Clip leading part-line if not the whole file */ - if (data.length < size) { + if (content_size < size) { var start = data.indexOf("\n"); log_data = data.substring(start + 1); } else { From e6bcb8fb443523c62d32f8b4e5d745f2dbe6dde8 Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sun, 1 Mar 2015 13:00:05 +0000 Subject: [PATCH 2/8] make first-load/not case explicit --- logtail.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/logtail.js b/logtail.js index a21cfbf..ca0fb7a 100644 --- a/logtail.js +++ b/logtail.js @@ -24,12 +24,16 @@ function get_log() { loading = true; var range; - if (log_size === 0) + var first_load; + if (log_size === 0) { /* Get the last 'load' bytes */ range = "-" + load.toString(); - else + first_load = true; + } else { /* Get the (log_size - 1)th byte, onwards. */ range = (log_size - 1).toString() + "-"; + first_load = false; + } /* The "log_size - 1" deliberately reloads the last byte, which we already * have. This is to prevent a 416 "Range unsatisfiable" error: a response @@ -60,7 +64,7 @@ function get_log() { if (isNaN(size)) throw "Invalid Content-Range size"; } else if (xhr.status === 200) { - if (log_size > 1) + if (!first_load) throw "Expected 206 Partial Content"; size = xhr.getResponseHeader("Content-Length"); @@ -69,7 +73,7 @@ function get_log() { var added = false; - if (log_size === 0) { + if (first_load) { /* Clip leading part-line if not the whole file */ if (content_size < size) { var start = data.indexOf("\n"); From 5510051bc48f2a6bceee74d2f5e75c9ba1687e6a Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sun, 1 Mar 2015 13:02:10 +0000 Subject: [PATCH 3/8] remove now-useless variable size --- logtail.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/logtail.js b/logtail.js index ca0fb7a..6ea8295 100644 --- a/logtail.js +++ b/logtail.js @@ -47,7 +47,6 @@ function get_log() { success: function (data, s, xhr) { loading = false; - var size; var content_size; if (xhr.status === 206) { @@ -58,24 +57,23 @@ function get_log() { if (!c_r) throw "Server did not respond with a Content-Range"; - size = parseInt(c_r.split("/")[1]); + log_size = parseInt(c_r.split("/")[1]); content_size = xhr.getResponseHeader("Content-Length"); - if (isNaN(size)) + if (isNaN(log_size)) throw "Invalid Content-Range size"; } else if (xhr.status === 200) { if (!first_load) throw "Expected 206 Partial Content"; - size = xhr.getResponseHeader("Content-Length"); - content_size = size; + content_size = log_size = xhr.getResponseHeader("Content-Length"); } var added = false; if (first_load) { /* Clip leading part-line if not the whole file */ - if (content_size < size) { + if (content_size < log_size) { var start = data.indexOf("\n"); log_data = data.substring(start + 1); } else { @@ -96,7 +94,6 @@ function get_log() { added = true; } - log_size = size; if (added) show_log(added); setTimeout(get_log, poll); From 897f6cb959db87e06bec2f8691536652aede1181 Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sun, 1 Mar 2015 13:02:38 +0000 Subject: [PATCH 4/8] rename variable log_size --- logtail.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/logtail.js b/logtail.js index 6ea8295..8348cc5 100644 --- a/logtail.js +++ b/logtail.js @@ -17,7 +17,7 @@ var loading = false; var pause = false; var reverse = true; var log_data = ""; -var log_size = 0; +var log_file_size = 0; function get_log() { if (kill | loading) return; @@ -25,17 +25,17 @@ function get_log() { var range; var first_load; - if (log_size === 0) { + if (log_file_size === 0) { /* Get the last 'load' bytes */ range = "-" + load.toString(); first_load = true; } else { - /* Get the (log_size - 1)th byte, onwards. */ - range = (log_size - 1).toString() + "-"; + /* Get the (log_file_size - 1)th byte, onwards. */ + range = (log_file_size - 1).toString() + "-"; first_load = false; } - /* The "log_size - 1" deliberately reloads the last byte, which we already + /* The "log_file_size - 1" deliberately reloads the last byte, which we already * have. This is to prevent a 416 "Range unsatisfiable" error: a response * of length 1 tells us that the file hasn't changed yet. A 416 shows that * the file has been trucnated */ @@ -57,23 +57,23 @@ function get_log() { if (!c_r) throw "Server did not respond with a Content-Range"; - log_size = parseInt(c_r.split("/")[1]); + log_file_size = parseInt(c_r.split("/")[1]); content_size = xhr.getResponseHeader("Content-Length"); - if (isNaN(log_size)) + if (isNaN(log_file_size)) throw "Invalid Content-Range size"; } else if (xhr.status === 200) { if (!first_load) throw "Expected 206 Partial Content"; - content_size = log_size = xhr.getResponseHeader("Content-Length"); + content_size = log_file_size = xhr.getResponseHeader("Content-Length"); } var added = false; if (first_load) { /* Clip leading part-line if not the whole file */ - if (content_size < log_size) { + if (content_size < log_file_size) { var start = data.indexOf("\n"); log_data = data.substring(start + 1); } else { @@ -105,7 +105,7 @@ function get_log() { /* 416: Requested range not satisfiable: log was truncated. */ /* 404: Retry soon, I guess */ - log_size = 0; + log_file_size = 0; log_data = ""; show_log(); From bab1fc89a4d04ff04f97f53eb12afcc25235eedc Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sun, 1 Mar 2015 15:26:04 +0000 Subject: [PATCH 5/8] tolerate weird server behaviour An example (only the tip of the iceberg): > Range: bytes=-1000000 < HTTP/1.1 200 OK < Content-Length: 16681 === test two === > Range: bytes=0- < HTTP/1.1 206 Partial Content < Content-Length: 16681 --- logtail.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/logtail.js b/logtail.js index 8348cc5..2bad8ef 100644 --- a/logtail.js +++ b/logtail.js @@ -25,14 +25,17 @@ function get_log() { var range; var first_load; + var must_get_206; if (log_file_size === 0) { /* Get the last 'load' bytes */ range = "-" + load.toString(); first_load = true; + must_get_206 = false; } else { /* Get the (log_file_size - 1)th byte, onwards. */ range = (log_file_size - 1).toString() + "-"; first_load = false; + must_get_206 = log_file_size > 1; } /* The "log_file_size - 1" deliberately reloads the last byte, which we already @@ -50,25 +53,27 @@ function get_log() { var content_size; if (xhr.status === 206) { - if (data.length > load) - throw "Expected 206 Partial Content"; - var c_r = xhr.getResponseHeader("Content-Range"); if (!c_r) throw "Server did not respond with a Content-Range"; log_file_size = parseInt(c_r.split("/")[1]); content_size = xhr.getResponseHeader("Content-Length"); - - if (isNaN(log_file_size)) - throw "Invalid Content-Range size"; } else if (xhr.status === 200) { - if (!first_load) + if (must_get_206) throw "Expected 206 Partial Content"; content_size = log_file_size = xhr.getResponseHeader("Content-Length"); + } else { + throw "Unexpected status " + xhr.status; } + if (isNaN(content_size) || isNaN(log_file_size)) + throw "Invalid content_size or log_file_size"; + + if (first_load && data.length > load) + throw "Server's response was too long"; + var added = false; if (first_load) { From 9a414a2e8a3072006aeb1aec6f9c296dee4f99a2 Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sun, 1 Mar 2015 15:39:18 +0000 Subject: [PATCH 6/8] consistent error handling --- logtail.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/logtail.js b/logtail.js index 2bad8ef..22ec9b0 100644 --- a/logtail.js +++ b/logtail.js @@ -116,10 +116,7 @@ function get_log() { setTimeout(get_log, poll); } else { - if (s == "error") - error(xhr.statusText); - else - error("AJAX Error: " + s); + throw "Unknown AJAX Error (status " + xhr.status + ")"; } } }); @@ -163,10 +160,12 @@ function error(what) { "Reloading may help; no promises.\r\n" + what); scroll(0); + + return false; } $(document).ready(function () { - $(window).error(error); + window.onerror = error; /* If URL is /logtail/?noreverse display in chronological order */ var hash = location.search.replace(/^\?/, ""); From 572a3ad1966c80c7618b921eb3014dfa5e1b96ff Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Mon, 2 Mar 2015 10:49:05 +0000 Subject: [PATCH 7/8] parseInt Content-Length --- logtail.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/logtail.js b/logtail.js index 22ec9b0..4a0c61a 100644 --- a/logtail.js +++ b/logtail.js @@ -58,12 +58,13 @@ function get_log() { throw "Server did not respond with a Content-Range"; log_file_size = parseInt(c_r.split("/")[1]); - content_size = xhr.getResponseHeader("Content-Length"); + content_size = parseInt(xhr.getResponseHeader("Content-Length")); } else if (xhr.status === 200) { if (must_get_206) throw "Expected 206 Partial Content"; - content_size = log_file_size = xhr.getResponseHeader("Content-Length"); + content_size = log_file_size = + parseInt(xhr.getResponseHeader("Content-Length")); } else { throw "Unexpected status " + xhr.status; } From 4aa1c837455a0a3e8454445efeff4fcb2ea6e774 Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Mon, 2 Mar 2015 15:17:41 +0000 Subject: [PATCH 8/8] stricter int parsing --- logtail.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/logtail.js b/logtail.js index 4a0c61a..6506b9b 100644 --- a/logtail.js +++ b/logtail.js @@ -19,6 +19,14 @@ var reverse = true; var log_data = ""; var log_file_size = 0; +/* :-( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt */ +function parseInt2(value) { + if(!(/^[0-9]+$/.test(value))) throw "Invalid integer " + value; + var v = Number(value); + if (isNaN(v)) throw "Invalid integer " + value; + return v; +} + function get_log() { if (kill | loading) return; loading = true; @@ -57,21 +65,18 @@ function get_log() { if (!c_r) throw "Server did not respond with a Content-Range"; - log_file_size = parseInt(c_r.split("/")[1]); - content_size = parseInt(xhr.getResponseHeader("Content-Length")); + log_file_size = parseInt2(c_r.split("/")[1]); + content_size = parseInt2(xhr.getResponseHeader("Content-Length")); } else if (xhr.status === 200) { if (must_get_206) throw "Expected 206 Partial Content"; content_size = log_file_size = - parseInt(xhr.getResponseHeader("Content-Length")); + parseInt2(xhr.getResponseHeader("Content-Length")); } else { throw "Unexpected status " + xhr.status; } - if (isNaN(content_size) || isNaN(log_file_size)) - throw "Invalid content_size or log_file_size"; - if (first_load && data.length > load) throw "Server's response was too long";