From a594b9111ce4bd267a3d479af54c7b7c38e1142a Mon Sep 17 00:00:00 2001 From: Luc Didry Date: Sat, 10 Oct 2015 00:42:30 +0200 Subject: [PATCH] Handle WebSocket errors --- lib/Lufi/Controller/Files.pm | 40 ++++++++------ themes/default/lib/Lufi/I18N/en.po | 42 +++++++++------ themes/default/lib/Lufi/I18N/fr.po | 42 +++++++++------ themes/default/public/js/lufi-down.js | 50 +++++++++++++---- themes/default/public/js/lufi-up.js | 75 ++++++++++++++++++-------- themes/default/templates/index.html.ep | 2 + 6 files changed, 168 insertions(+), 83 deletions(-) diff --git a/lib/Lufi/Controller/Files.pm b/lib/Lufi/Controller/Files.pm index 0c6d1f4..ef19b25 100644 --- a/lib/Lufi/Controller/Files.pm +++ b/lib/Lufi/Controller/Files.pm @@ -82,27 +82,33 @@ sub upload { ); $f->write; } - # Create directory - my $dir = catdir('files', $f->short); - mkdir($dir, 0700) unless (-d $dir); - # Create slice file - my $file = catfile($dir, $json->{part}.'.part'); - my $s = Lufi::Slice->new( - short => $f->short, - j => $json->{part}, - path => $file - ); - spurt $text, $file; - push @{$f->slices}, $s; + # If we already have a part, it's a resend because the websocket has been broken + # In this case, we don't need to rewrite the file + unless ($f->slices->grep(sub { $_->j == $json->{part} })->size) { + # Create directory + my $dir = catdir('files', $f->short); + mkdir($dir, 0700) unless (-d $dir); - if (($json->{part} + 1) == $json->{total}) { - $f->complete(1); - $f->created_at(time); - $c->provisioning; + # Create slice file + my $file = catfile($dir, $json->{part}.'.part'); + my $s = Lufi::Slice->new( + short => $f->short, + j => $json->{part}, + path => $file + ); + spurt $text, $file; + push @{$f->slices}, $s; + + if (($json->{part} + 1) == $json->{total}) { + $f->complete(1); + $f->created_at(time); + } + + $f->write; } - $f->write; + $c->provisioning; $ws->send(sprintf('{"success": true, "i": %d, "j": %d, "parts": %d, "short": "%s", "name": "%s", "size": %d, "del_at_first_view": %s, "created_at": %d, "delay": %d, "token": "%s", "sent_delay": %d}', $json->{i}, $json->{part}, $json->{total}, $f->short, $f->filename, $f->filesize, (($f->delete_at_first_view) ? 'true' : 'false'), $f->created_at, $f->delete_at_day, $f->mod_token, $json->{delay})); } diff --git a/themes/default/lib/Lufi/I18N/en.po b/themes/default/lib/Lufi/I18N/en.po index 5ca046a..e8ad16d 100644 --- a/themes/default/lib/Lufi/I18N/en.po +++ b/themes/default/lib/Lufi/I18N/en.po @@ -65,11 +65,11 @@ msgstr "" msgid "Copy to clipboard" msgstr "" -#: lib/Lufi/Controller/Files.pm:284 +#: lib/Lufi/Controller/Files.pm:288 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "" -#: lib/Lufi/Controller/Files.pm:212 +#: lib/Lufi/Controller/Files.pm:216 msgid "Could not find the file. Are you sure of the URL?" msgstr "" @@ -117,19 +117,23 @@ msgstr "" msgid "Emails" msgstr "" -#: lib/Lufi/Controller/Files.pm:148 +#: themes/default/templates/index.html.ep:87 +msgid "Encrypting part XX1 of XX2" +msgstr "" + +#: lib/Lufi/Controller/Files.pm:152 msgid "Error: the file existed but has been deleted." msgstr "" -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:193 msgid "Error: the file has not been send entirely." msgstr "" -#: lib/Lufi/Controller/Files.pm:194 +#: lib/Lufi/Controller/Files.pm:198 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "" -#: themes/default/templates/index.html.ep:87 +#: themes/default/templates/index.html.ep:88 msgid "Expiration:" msgstr "" @@ -141,7 +145,7 @@ msgstr "" msgid "Export localStorage data" msgstr "" -#: lib/Lufi/Controller/Files.pm:268 +#: lib/Lufi/Controller/Files.pm:272 msgid "File deleted" msgstr "" @@ -161,11 +165,11 @@ msgstr "" msgid "Here's some files" msgstr "" -#: themes/default/templates/index.html.ep:89 +#: themes/default/templates/index.html.ep:90 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:89 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "" @@ -218,11 +222,11 @@ msgid "My files" msgstr "" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:47 +#: lib/Lufi/Controller/Files.pm:45 msgid "No enough space available on the server for this file (size: %1)." msgstr "" -#: themes/default/templates/files.html.ep:41 themes/default/templates/index.html.ep:91 +#: themes/default/templates/files.html.ep:41 themes/default/templates/index.html.ep:92 msgid "No expiration delay" msgstr "" @@ -251,7 +255,7 @@ msgstr "" msgid "Rows in red means that the file has expired and is no longer available." msgstr "" -#: themes/default/templates/index.html.ep:90 +#: themes/default/templates/index.html.ep:91 msgid "Send all links by email" msgstr "" @@ -263,6 +267,10 @@ msgstr "" msgid "Send with your own mail software" msgstr "" +#: themes/default/templates/index.html.ep:93 +msgid "Sending part XX1 of XX2" +msgstr "" + #. (url_for('/') #: themes/default/templates/mail.html.ep:75 msgid "Share your files in total privacy on %1" @@ -272,7 +280,7 @@ msgstr "" msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "" -#: lib/Lufi/Controller/Files.pm:35 +#: lib/Lufi/Controller/Files.pm:33 msgid "Sorry, uploading is disabled." msgstr "" @@ -292,7 +300,7 @@ msgstr "" msgid "The email subject can't be empty." msgstr "" -#: lib/Lufi/Controller/Files.pm:265 +#: lib/Lufi/Controller/Files.pm:269 msgid "The file has already been deleted" msgstr "" @@ -319,12 +327,12 @@ msgid "This server sets limitations according to the file size. The expiration d msgstr "" #. ($short) -#: lib/Lufi/Controller/Files.pm:249 +#: lib/Lufi/Controller/Files.pm:253 msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage." msgstr "" #. ($short) -#: lib/Lufi/Controller/Files.pm:239 +#: lib/Lufi/Controller/Files.pm:243 msgid "Unable to get counter for %1. The token is unvalid." msgstr "" @@ -373,7 +381,7 @@ msgid "You must give email addresses." msgstr "" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:41 +#: lib/Lufi/Controller/Files.pm:39 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "" diff --git a/themes/default/lib/Lufi/I18N/fr.po b/themes/default/lib/Lufi/I18N/fr.po index 4781398..71e0e8d 100644 --- a/themes/default/lib/Lufi/I18N/fr.po +++ b/themes/default/lib/Lufi/I18N/fr.po @@ -71,11 +71,11 @@ msgstr "Copier tous les liens dans le presse-papier" msgid "Copy to clipboard" msgstr "Copier dans le presse-papier" -#: lib/Lufi/Controller/Files.pm:284 +#: lib/Lufi/Controller/Files.pm:288 msgid "Could not find the file. Are you sure of the URL and the token?" msgstr "Impossible de retrouver le fichier. Êtes-vous sûr(e) que l’URL et le jeton sont les bons ?" -#: lib/Lufi/Controller/Files.pm:212 +#: lib/Lufi/Controller/Files.pm:216 msgid "Could not find the file. Are you sure of the URL?" msgstr "Impossible de retrouver le fichier. Êtes-vous sûr(e) que l’URL est la bonne ?" @@ -123,19 +123,23 @@ msgstr "Sujet du mail" msgid "Emails" msgstr "Mails" -#: lib/Lufi/Controller/Files.pm:148 +#: themes/default/templates/index.html.ep:87 +msgid "Encrypting part XX1 of XX2" +msgstr "Chiffrement du fragment XX1 sur XX2" + +#: lib/Lufi/Controller/Files.pm:152 msgid "Error: the file existed but has been deleted." msgstr "Erreur : le fichier existait mais a été supprimé" -#: lib/Lufi/Controller/Files.pm:189 +#: lib/Lufi/Controller/Files.pm:193 msgid "Error: the file has not been send entirely." msgstr "Erreur : le fichier n’a pas été envoyé dans son intégralité" -#: lib/Lufi/Controller/Files.pm:194 +#: lib/Lufi/Controller/Files.pm:198 msgid "Error: unable to find the file. Are you sure of your URL?" msgstr "Erreur : impossible de retrouver le fichier. Êtes-vous sûr(e) de l’URL ?" -#: themes/default/templates/index.html.ep:87 +#: themes/default/templates/index.html.ep:88 msgid "Expiration:" msgstr "Expiration :" @@ -147,7 +151,7 @@ msgstr "Expire le" msgid "Export localStorage data" msgstr "Exporter les données localStorage" -#: lib/Lufi/Controller/Files.pm:268 +#: lib/Lufi/Controller/Files.pm:272 msgid "File deleted" msgstr "Fichier supprimé" @@ -167,11 +171,11 @@ msgstr "Bonjour,\\n\\nVoici quelques fichiers que je souhaite partager avec toi msgid "Here's some files" msgstr "Voici quelques fichiers" -#: themes/default/templates/index.html.ep:89 +#: themes/default/templates/index.html.ep:90 msgid "Hit Enter, then Ctrl+C to copy all the download links" msgstr "Appuyez sur la touche Entrée puis faites Ctrl+C pour copier tous les liens de téléchargement" -#: themes/default/templates/index.html.ep:88 +#: themes/default/templates/index.html.ep:89 msgid "Hit Enter, then Ctrl+C to copy the download link" msgstr "Appuyez sur la touche Entrée puis faites Ctrl+C pour copier le lien de téléchargement" @@ -224,11 +228,11 @@ msgid "My files" msgstr "Mes fichiers" #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:47 +#: lib/Lufi/Controller/Files.pm:45 msgid "No enough space available on the server for this file (size: %1)." msgstr "Espace disque insuffisant sur le serveur pour ce fichier (taille du fichier: %1)." -#: themes/default/templates/files.html.ep:41 themes/default/templates/index.html.ep:91 +#: themes/default/templates/files.html.ep:41 themes/default/templates/index.html.ep:92 msgid "No expiration delay" msgstr "Pas de délai d’expiration" @@ -261,7 +265,7 @@ msgstr "Supprimer du localStorage les fichiers expirés" msgid "Rows in red means that the file has expired and is no longer available." msgstr "Les lignes en rouge indiquent que le fichier a expiré et n’est plus disponible." -#: themes/default/templates/index.html.ep:90 +#: themes/default/templates/index.html.ep:91 msgid "Send all links by email" msgstr "Envoyer tous les liens par mail" @@ -273,6 +277,10 @@ msgstr "Envoyer avec ce serveur" msgid "Send with your own mail software" msgstr "Envoyer avec votre propre logiciel de mail" +#: themes/default/templates/index.html.ep:93 +msgid "Sending part XX1 of XX2" +msgstr "Envoi du fragment XX1 sur XX2" + #. (url_for('/') #: themes/default/templates/mail.html.ep:75 msgid "Share your files in total privacy on %1" @@ -282,7 +290,7 @@ msgstr "Partagez vos fichiers en toute confidentialité sur %1" msgid "Sorry, the uploading is currently disabled. Please try again later." msgstr "Désolé, l’envoi de fichier est actuellement désactivé. Veuillez réessayer plus tard." -#: lib/Lufi/Controller/Files.pm:35 +#: lib/Lufi/Controller/Files.pm:33 msgid "Sorry, uploading is disabled." msgstr "Désolé, l’envoi de fichier est désactivé." @@ -302,7 +310,7 @@ msgstr "Le corps du mail ne peut être vide." msgid "The email subject can't be empty." msgstr "Le sujet du mail ne peut être vide." -#: lib/Lufi/Controller/Files.pm:265 +#: lib/Lufi/Controller/Files.pm:269 msgid "The file has already been deleted" msgstr "Le fichier a déjà été supprimé" @@ -333,12 +341,12 @@ msgid "Unable to get counter for %1. The file does not exists." msgstr "Impossible de récupérer le compteur de %1. Le fichier n’existe pas." #. ($short) -#: lib/Lufi/Controller/Files.pm:249 +#: lib/Lufi/Controller/Files.pm:253 msgid "Unable to get counter for %1. The file does not exists. It will be removed from your localStorage." msgstr "Impossible de récupérer le compteur pour %1. Le fichier n’existe pas. Il va être supprimé de votre localStorage." #. ($short) -#: lib/Lufi/Controller/Files.pm:239 +#: lib/Lufi/Controller/Files.pm:243 msgid "Unable to get counter for %1. The token is unvalid." msgstr "Impossible de récupérer le compteur pour %1. Le jeton est invalide." @@ -387,7 +395,7 @@ msgid "You must give email addresses." msgstr "Vous devez envoyer des adresses mail." #. (format_bytes($json->{size}) -#: lib/Lufi/Controller/Files.pm:41 +#: lib/Lufi/Controller/Files.pm:39 msgid "Your file is too big: %1 (maximum size allowed: %2)" msgstr "Votre fichier est trop volumineux : %1 (la taille maximum autorisée est %2)" diff --git a/themes/default/public/js/lufi-down.js b/themes/default/public/js/lufi-down.js index 9811ea4..d4f8f3b 100644 --- a/themes/default/public/js/lufi-down.js +++ b/themes/default/public/js/lufi-down.js @@ -42,14 +42,20 @@ function addAlert(msg) { } // Spawn WebSocket -function spawnWebsocket() { +function spawnWebsocket(pa) { var ws = new WebSocket(ws_url); ws.onopen = function() { console.log('Connection is established!'); - ws.send('{"part":0}'); + window.ws.send('{"part":'+pa+'}'); }; ws.onclose = function() { console.log('Connection is closed'); + if (!window.completed) { + console.log('Connection closed. Retrying to get slice '+pa+' in 5 seconds'); + setTimeout(function() { + window.ws = spawnWebsocket(pa); + }, 5000); + } } ws.onmessage = function(e) { var res = e.data.split('XXMOJOXX'); @@ -58,8 +64,10 @@ function spawnWebsocket() { if (data.msg !== undefined) { addAlert(data.msg); + console.log(data.msg); window.onbeforeunload = null; } else { + console.log('Getting slice '+(data.part + 1)+' of '+data.total); var slice = JSON.parse(res.shift()); var percent = Math.round(100 * (data.part + 1)/data.total); var pb = document.getElementById('pb'); @@ -68,7 +76,7 @@ function spawnWebsocket() { document.getElementById('pbt').innerHTML = percent+'%'; try { var b64 = sjcl.decrypt(window.key, slice); - window.a.push(base64ToArrayBuffer(b64)); + window.a[data.part] = base64ToArrayBuffer(b64); if (data.part + 1 === data.total) { var blob = new File(a, data.name, {type: data.type}); @@ -78,13 +86,30 @@ function spawnWebsocket() { pbd.setAttribute('class', ''); pbd.innerHTML = ''+i18n.download+''; - ws.send('{"ended":true}'); + window.ws.send('{"ended":true}'); window.onbeforeunload = null; + window.completed = true; } else { if (ws.readyState === 3) { - ws = spawnWebsocket(); + window.ws = spawnWebsocket(data.part + 1); + } else { + window.ws.onclose = function() { + console.log('Connection is closed'); + if (!window.completed) { + console.log('Connection closed. Retrying to get slice '+(data.part + 1)+' in 5 seconds'); + setTimeout(function() { + window.ws = spawnWebsocket(data.part + 1); + }, 5000); + } + } + window.ws.onerror = function() { + console.log('Error. Retrying to get slice '+(data.part + 1)+' in 5 seconds'); + setTimeout(function() { + window.ws = spawnWebsocket(data.part + 1); + }, 5000); + }; + window.ws.send('{"part":'+(data.part + 1)+'}'); } - ws.send('{"part":'+(data.part + 1)+'}'); } } catch(err) { if (err.message === 'ccm: tag doesn\'t match') { @@ -97,17 +122,22 @@ function spawnWebsocket() { } } ws.onerror = function() { - console.log('error'); + console.log('Error. Retrying to get slice '+pa+' in 5 seconds'); + setTimeout(function() { + window.ws = spawnWebsocket(pa); + }, 5000); } + return ws; } // When it's ready document.addEventListener('DOMContentLoaded', function() { - window.a = new Array(); - window.key = pageKey(); + window.a = new Array(); + window.key = pageKey(); + window.completed = false; if (key !== '=') { // Set websocket - ws = spawnWebsocket(); + window.ws = spawnWebsocket(0); // Prevent exiting page before full download window.onbeforeunload = confirmExit; diff --git a/themes/default/public/js/lufi-up.js b/themes/default/public/js/lufi-up.js index 4c3a06f..baa4881 100644 --- a/themes/default/public/js/lufi-up.js +++ b/themes/default/public/js/lufi-up.js @@ -147,7 +147,7 @@ function uploadFile(i, delay, del_at_first_view) { var r = document.getElementById('ul-results'); var w = document.createElement('li'); w.setAttribute('class', 'list-group-item'); - w.innerHTML='

'+file.name+'

'+file.name+'0%
'; + w.innerHTML='

'+file.name+'

'+file.name+'0%
'; r.appendChild(w); sliceAndUpload(randomkey, i, parts, 0, delay, del_at_first_view, null); @@ -159,6 +159,8 @@ function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short) var slice = file.slice(j * window.sliceLength, (j + 1) * window.sliceLength, file.type); var fr = new FileReader(); fr.onloadend = function() { + var sl = document.getElementById('parts-'+window.fc); + // Get the binary result var bin = fr.result; @@ -166,6 +168,7 @@ function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short) var b = window.btoa(bin); // Encrypt it + sl.innerHTML = i18n.encrypting.replace(/XX1(.*)XX2/, (j+1)+'$1'+parts); var encrypted = sjcl.encrypt(randomkey, b); // Prepare json @@ -184,12 +187,23 @@ function sliceAndUpload(randomkey, i, parts, j, delay, del_at_first_view, short) console.log('sending slice '+(j + 1)+'/'+parts+' of file '+file.name); + sl.innerHTML = i18n.sending.replace(/XX1(.*)XX2/, (j+1)+'$1'+parts); + // Verify that we have a websocket and send json if (window.ws.readyState === 3) { - window.ws = spawnWebsocket(function() { + window.ws = spawnWebsocket(0, function() { window.ws.send(data+'XXMOJOXX'+JSON.stringify(encrypted)); }); } else { + window.ws.onerror = function() { + console.log('Error on Websocket, waiting 10sec.'); + setTimeout(function() { + window.ws = spawnWebsocket(0, function() { + console.log('sending again slice '+(j + 1)+'/'+parts+' of file '+file.name); + window.ws.send(data+'XXMOJOXX'+JSON.stringify(encrypted)); + }); + }, 10000); + }; window.ws.send(data+'XXMOJOXX'+JSON.stringify(encrypted)); } } @@ -214,6 +228,7 @@ function updateProgressBar(data) { var key = dp.getAttribute('data-key'); if (j + 1 === parts) { + document.getElementById('parts-'+window.fc).remove(); var n = document.getElementById('name-'+window.fc); var d = document.createElement('div'); var url = baseURL+'r/'+short+'#'+key; @@ -289,26 +304,33 @@ function updateProgressBar(data) { sliceAndUpload(key, i, parts, j, delay, del_at_first_view, short); } } else { - var n = document.getElementById('name-'+window.fc); - var p = document.getElementById('progress-'+window.fc); - var d = document.createElement('div'); + addAlertOnFile(data.msg, i, delay, del_at_first_view); + } +} - p.parentNode.remove(); - d.innerHTML = data.msg; - d.setAttribute('class', 'alert alert-danger'); - n.parentNode.appendChild(d); - // Upload next file - window.fc++; - i++; - if (i < window.files.length) { - uploadFile(i, sent_delay, del_at_first_view); - } else { - // We have finished - window.onbeforeunload = null; - document.getElementById('delete-day').removeAttribute('disabled'); - document.getElementById('first-view').removeAttribute('disabled'); - } + +// Write message instead in a file block +function addAlertOnFile(msg, i, sent_delay, del_at_first_view) { + var n = document.getElementById('name-'+window.fc); + var p = document.getElementById('progress-'+window.fc); + var d = document.createElement('div'); + + p.parentNode.remove(); + d.innerHTML = msg; + d.setAttribute('class', 'alert alert-danger'); + n.parentNode.appendChild(d); + + // Upload next file + window.fc++; + i++; + if (i < window.files.length) { + uploadFile(i, sent_delay, del_at_first_view); + } else { + // We have finished + window.onbeforeunload = null; + document.getElementById('delete-day').removeAttribute('disabled'); + document.getElementById('first-view').removeAttribute('disabled'); } } @@ -327,7 +349,10 @@ function handleDragOver(evt) { } // Spawn websocket -function spawnWebsocket(callback) { +function spawnWebsocket(i, callback) { + if (i === undefined || i === null) { + i = 0; + } var ws = new WebSocket(ws_url); ws.onopen = function() { console.log('Connection is established!'); @@ -342,6 +367,12 @@ function spawnWebsocket(callback) { updateProgressBar(JSON.parse(e.data)); } ws.onerror = function() { + if (i < 5 && callback !== undefined) { + setTimeout(function() { + console.log('Retrying to send file '+i); + window.ws = spawnWebsocket(i + 1, callback); + }, 10000); + } console.log('error'); } return ws; @@ -355,7 +386,7 @@ document.addEventListener('DOMContentLoaded', function() { dropZone.addEventListener('drop', handleDrop, false); // Set websocket - window.ws = spawnWebsocket(); + window.ws = spawnWebsocket(0, function() {return null;}); // Use slice of 10MB window.sliceLength = 2000000; diff --git a/themes/default/templates/index.html.ep b/themes/default/templates/index.html.ep index 316cb9f..3c9f76b 100644 --- a/themes/default/templates/index.html.ep +++ b/themes/default/templates/index.html.ep @@ -84,11 +84,13 @@ var i18n = { delText: '<%= l('Deletion link') %>', dlText: '<%= l('Download link') %>', download: '<%= l('Download') %>', + encrypting: '<%= l('Encrypting part XX1 of XX2') %>', expiration: '<%= l('Expiration:') %>', hit: '<%= l('Hit Enter, then Ctrl+C to copy the download link') %>', hits: '<%= l('Hit Enter, then Ctrl+C to copy all the download links') %>', mailTo: '<%= l('Send all links by email') %>', noLimit: '<%= l('No expiration delay') %>', + sending: '<%= l('Sending part XX1 of XX2') %>', }; % end %= javascript '/js/sjcl.js'