diff --git a/app/api.js b/app/api.js index b7915fa2..cbad2e29 100644 --- a/app/api.js +++ b/app/api.js @@ -11,6 +11,15 @@ if (!fileProtocolWssUrl) { fileProtocolWssUrl = 'wss://send.firefox.com/api/ws'; } +export class ConnectionError extends Error { + constructor(cancelled, duration, size) { + super(cancelled ? '0' : 'connection closed'); + this.cancelled = cancelled; + this.duration = duration; + this.size = size; + } +} + export function setFileProtocolWssUrl(url) { localStorage && localStorage.setItem('wssURL', url); fileProtocolWssUrl = url; @@ -150,10 +159,7 @@ function listenForResponse(ws, canceller) { function handleClose(event) { // a 'close' event before a 'message' event means the request failed ws.removeEventListener('message', handleMessage); - const error = canceller.cancelled - ? canceller.error - : new Error('connection closed'); - reject(error); + reject(new ConnectionError(canceller.cancelled)); } function handleMessage(msg) { ws.removeEventListener('close', handleClose); @@ -183,6 +189,8 @@ async function upload( onprogress, canceller ) { + let size = 0; + const start = Date.now(); const host = window.location.hostname; const port = window.location.port; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; @@ -210,7 +218,6 @@ async function upload( const reader = stream.getReader(); let state = await reader.read(); - let size = 0; while (!state.done) { if (canceller.cancelled) { ws.close(); @@ -236,7 +243,12 @@ async function upload( } await completedResponse; + uploadInfo.duration = Date.now() - start; return uploadInfo; + } catch (e) { + e.size = size; + e.duration = Date.now() - start; + throw e; } finally { if (![WebSocket.CLOSED, WebSocket.CLOSING].includes(ws.readyState)) { ws.close(); @@ -257,7 +269,6 @@ export function uploadWs( return { cancel: function() { - canceller.error = new Error(0); canceller.cancelled = true; }, @@ -297,7 +308,7 @@ async function downloadS(id, keychain, signal) { return response.body; } -async function tryDownloadStream(id, keychain, signal, tries = 1) { +async function tryDownloadStream(id, keychain, signal, tries = 2) { try { const result = await downloadS(id, keychain, signal); return result; @@ -319,7 +330,7 @@ export function downloadStream(id, keychain) { } return { cancel, - result: tryDownloadStream(id, keychain, controller.signal, 2) + result: tryDownloadStream(id, keychain, controller.signal) }; } @@ -359,7 +370,7 @@ async function download(id, keychain, onprogress, canceller) { }); } -async function tryDownload(id, keychain, onprogress, canceller, tries = 1) { +async function tryDownload(id, keychain, onprogress, canceller, tries = 2) { try { const result = await download(id, keychain, onprogress, canceller); return result; @@ -380,7 +391,7 @@ export function downloadFile(id, keychain, onprogress) { } return { cancel, - result: tryDownload(id, keychain, onprogress, canceller, 2) + result: tryDownload(id, keychain, onprogress, canceller) }; } diff --git a/app/controller.js b/app/controller.js index 0fe285d7..c460449e 100644 --- a/app/controller.js +++ b/app/controller.js @@ -176,14 +176,13 @@ export default function(state, emitter) { } catch (err) { if (err.message === '0') { //cancelled. do nothing - const duration = Date.now() - start; - metrics.cancelledUpload(archive, duration); + metrics.cancelledUpload(archive, err.duration); render(); } else { // eslint-disable-next-line no-console console.error(err); state.raven.captureException(err); - metrics.stoppedUpload(archive); + metrics.stoppedUpload(archive, err.duration); emitter.emit('pushState', '/error'); } } finally { diff --git a/app/fileReceiver.js b/app/fileReceiver.js index 38c8979c..33709d43 100644 --- a/app/fileReceiver.js +++ b/app/fileReceiver.js @@ -153,7 +153,7 @@ export default class FileReceiver extends Nanobus { const downloadPath = `/api/download/${this.fileInfo.id}`; let downloadUrl = getApiUrl(downloadPath); if (downloadUrl === downloadPath) { - downloadUrl = `${location.protocol}//${location.host}/api/download/${this.fileInfo.id}`; + downloadUrl = `${location.protocol}//${location.host}${downloadPath}`; } const a = document.createElement('a'); a.href = downloadUrl; diff --git a/app/fileSender.js b/app/fileSender.js index 6985282e..e8bc6bea 100644 --- a/app/fileSender.js +++ b/app/fileSender.js @@ -44,7 +44,6 @@ export default class FileSender extends Nanobus { } async upload(archive, bearerToken) { - const start = Date.now(); if (this.cancelled) { throw new Error(0); } @@ -76,7 +75,6 @@ export default class FileSender extends Nanobus { this.emit('progress'); // HACK to kick MS Edge try { const result = await this.uploadRequest.result; - const time = Date.now() - start; this.msg = 'notifyUploadEncryptDone'; this.uploadRequest = null; this.progress = [1, 1]; @@ -87,8 +85,8 @@ export default class FileSender extends Nanobus { name: archive.name, size: archive.size, manifest: archive.manifest, - time: time, - speed: archive.size / (time / 1000), + time: result.duration, + speed: archive.size / (result.duration / 1000), createdAt: Date.now(), expiresAt: Date.now() + archive.timeLimit * 1000, secretKey: secretKey, diff --git a/app/metrics.js b/app/metrics.js index 0d7fb995..759866df 100644 --- a/app/metrics.js +++ b/app/metrics.js @@ -107,9 +107,10 @@ function completedUpload(archive, duration) { }); } -function stoppedUpload(archive) { +function stoppedUpload(archive, duration = 0) { return addEvent('client_upload', { download_limit: archive.dlimit, + duration: sizeOrder(duration), file_count: archive.numFiles, password_protected: !!archive.password, size: sizeOrder(archive.size),