From 6e416fea8ab191303be2f3c3b5c41859aa773d48 Mon Sep 17 00:00:00 2001 From: calzoneman Date: Sun, 17 Jul 2016 16:30:35 -0700 Subject: [PATCH] Add a hack to detect distrust of Let's Encrypt Many older devices do not support the Let's Encrypt CA, for various reasons. This causes connection issues for sites using Let's Encrypt to support HTTPS connections. This commit adds a hack that can be enabled with a switch in callbacks.js to try to detect when the user's browser does not trust the certificate and permit the user to connect to an insecure endpoint instead. Unfortunately, the AJAX API does not allow to distinguish between *why* a request fails, so the best we can do is detect that the HTTPS request failed, try to make a request over plain HTTP, and if it works, assume the HTTPS request failed due to a certificate error. It's not 100% foolproof since the HTTPS endpoint could just be down for some reason, but it should work well enough in most cases. Closes #602 --- www/js/callbacks.js | 82 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 11 deletions(-) diff --git a/www/js/callbacks.js b/www/js/callbacks.js index c732e1ce..e894faa4 100644 --- a/www/js/callbacks.js +++ b/www/js/callbacks.js @@ -35,7 +35,7 @@ Callbacks = { return; $("
") .addClass("server-msg-disconnect") - .text("Disconnected from server. Attempting reconnection...") + .text("Disconnected from server.") .appendTo($("#messagebuffer")); scrollChat(); }, @@ -1123,7 +1123,18 @@ function ioServerConnect(socketConfig) { window.socket = io(chosenServer.url, opts); } -(function () { +var USING_LETS_ENCRYPT = false; + +function initSocketIO(socketConfig) { + function genericConnectionError() { + var message = "The socket.io library could not be loaded from " + + source + ". Ensure that it is not being blocked " + + "by a script blocking extension or firewall and try again."; + makeAlert("Error", message, "alert-danger") + .appendTo($("#announcements")); + Callbacks.disconnect(); + } + if (typeof io === "undefined") { var script = document.getElementById("socketio-js"); var source = "unknown"; @@ -1131,21 +1142,70 @@ function ioServerConnect(socketConfig) { source = script.src; } - var message = "The socket.io library could not be loaded from " + - source + ". Ensure that it is not being blocked " + - "by a script blocking extension or firewall and try again."; - makeAlert("Error", message, "alert-danger") - .appendTo($("#announcements")); - Callbacks.disconnect(); + if (/^https/.test(source) && location.protocol === "http:" + && USING_LETS_ENCRYPT) { + checkLetsEncrypt(socketConfig, genericConnectionError); + return; + } + + genericConnectionError(); return; } + ioServerConnect(socketConfig); + setupCallbacks(); +} + +function checkLetsEncrypt(socketConfig, nonLetsEncryptError) { + var servers = socketConfig.servers.filter(function (server) { + return !server.secure && !server.ipv6Only + }); + + if (servers.length === 0) { + nonLetsEncryptError(); + return; + } + + $.ajax({ + url: servers[0].url + "/socket.io/socket.io.js", + dataType: "script", + timeout: 10000 + }).done(function () { + var message = "Your browser cannot connect securely because it does " + + "not support the newer Let's Encrypt certificate " + + "authority. Click below to acknowledge and continue " + + "connecting over an unencrypted connection. See " + + "here " + + "for more details."; + var connectionAlert = makeAlert("Error", message, "alert-danger") + .appendTo($("#announcements")); + + var button = document.createElement("button"); + button.className = "btn btn-default"; + button.textContent = "Connect Anyways"; + + var alertBox = connectionAlert.find(".alert")[0]; + alertBox.appendChild(document.createElement("hr")); + alertBox.appendChild(button); + + button.onclick = function connectAnyways() { + ioServerConnect({ + servers: servers + }); + setupCallbacks(); + }; + }).error(function () { + nonLetsEncryptError(); + }); +} + +(function () { $.getJSON("/socketconfig/" + CHANNEL.name + ".json") .done(function (socketConfig) { - ioServerConnect(socketConfig); - setupCallbacks(); + initSocketIO(socketConfig); }).fail(function () { - makeAlert("Error", "Failed to retrieve socket.io configuration", + makeAlert("Error", "Failed to retrieve socket.io configuration. " + + "Please try again in a few minutes.", "alert-danger") .appendTo($("#announcements")); Callbacks.disconnect();