X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/3c06d760206dd94cc7f4ef5d59cbc1f419694f8d..b936759134f8d2b1a68b19be06de8a3f41f782d8:/apps/workbench/app/assets/javascripts/upload_to_collection.js diff --git a/apps/workbench/app/assets/javascripts/upload_to_collection.js b/apps/workbench/app/assets/javascripts/upload_to_collection.js index c6850172cd..d66be63853 100644 --- a/apps/workbench/app/assets/javascripts/upload_to_collection.js +++ b/apps/workbench/app/assets/javascripts/upload_to_collection.js @@ -1,3 +1,7 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + var app = angular.module('Workbench', ['Arvados']); app.controller('UploadToCollection', UploadToCollection); app.directive('arvUuid', arvUuid); @@ -65,6 +69,7 @@ function UploadToCollection($scope, $filter, $q, $timeout, //////////////////////////////// var keepProxy; + var defaultErrorMessage = 'A network error occurred: either the server was unreachable, or there is a server configuration problem. Please check your browser debug console for a more specific error message (browser security features prevent us from showing the details here).'; function SliceReader(_slice) { var that = this; @@ -117,7 +122,20 @@ function UploadToCollection($scope, $filter, $q, $timeout, // resolve(locator) when the block is accepted by the // proxy. _deferred = $.Deferred(); - goSend(); + if (proxyUriBase().match(/^http:/) && + window.location.origin.match(/^https:/)) { + // In this case, requests will fail, and no ajax + // success/fail handlers will be called (!), which + // will leave our status saying "uploading" and the + // user waiting for something to happen. Better to + // give up now. + _deferred.reject({ + textStatus: 'error', + err: 'There is a server configuration problem. Proxy ' + proxyUriBase() + ' cannot be used from origin ' + window.location.origin + ' due to the browser\'s mixed-content (https/http) policy.' + }); + } else { + goSend(); + } return _deferred.promise(); } function stop() { @@ -262,7 +280,8 @@ function UploadToCollection($scope, $filter, $q, $timeout, that.state = 'Paused'; setProgress(_readPos); _currentUploader = null; - _deferred.reject(reason); + if (_deferred) + _deferred.reject(reason); } function onUploaderProgress(sliceDone, sliceSize) { setProgress(_readPos + sliceDone); @@ -282,7 +301,10 @@ function UploadToCollection($scope, $filter, $q, $timeout, } function setProgress(bytesDone) { var kBps; - that.progress = Math.min(100, 100 * bytesDone / that.file.size) + if (that.file.size == 0) + that.progress = 100; + else + that.progress = Math.min(100, 100 * bytesDone / that.file.size); if (bytesDone > _startByte) { kBps = (bytesDone - _startByte) / (Date.now() - _startTime); @@ -311,13 +333,14 @@ function UploadToCollection($scope, $filter, $q, $timeout, $filter('date')(Date.now(), 'shortTime'); _finishTime = Date.now(); } - _deferred.notify(); + if (_deferred) + _deferred.notify(); } } function QueueUploader() { $.extend(this, { - state: 'Idle', + state: 'Idle', // Idle, Running, Stopped, Failed stateReason: null, statusSuccess: null, go: go, @@ -344,9 +367,6 @@ function UploadToCollection($scope, $filter, $q, $timeout, if (_deferred) { _deferred.reject({}); } - if (_deferredAppend) { - _deferredAppend.reject({}); - } for (var i=0; i<$scope.uploadQueue.length; i++) $scope.uploadQueue[i].stop(); onQueueProgress(); @@ -363,20 +383,28 @@ function UploadToCollection($scope, $filter, $q, $timeout, return doQueueWork(); } function doQueueWork() { - if (!_deferred) { - // Queue work has been stopped. - return; - } - that.stateReason = null; // If anything is not Done, do it. if ($scope.uploadQueue.length > 0 && $scope.uploadQueue[0].state !== 'Done') { - return $scope.uploadQueue[0].go(). - then(appendToCollection, null, onQueueProgress). - then(doQueueWork, onQueueReject); + if (_deferred) { + that.stateReason = null; + return $scope.uploadQueue[0].go(). + then(appendToCollection, null, onQueueProgress). + then(doQueueWork, onQueueReject); + } else { + // Queue work has been stopped. Just update the + // view. + onQueueProgress(); + return; + } } - // If everything is Done, resolve the promise and clean up. - return onQueueResolve(); + // If everything is Done, resolve the promise and clean + // up. Note this can happen even after the _deferred + // promise has been rejected: specifically, when stop() is + // called too late to prevent completion of the last + // upload. In that case we want to update state to "Idle", + // rather than leave it at "Stopped". + onQueueResolve(); } function onQueueReject(reason) { if (!_deferred) { @@ -384,13 +412,14 @@ function UploadToCollection($scope, $filter, $q, $timeout, return; } + that.state = 'Failed'; that.stateReason = ( (reason.textStatus || 'Error') + (reason.xhr && reason.xhr.options ? (' (from ' + reason.xhr.options.url + ')') : '') + ': ' + - (reason.err || '')); + (reason.err || defaultErrorMessage)); if (reason.xhr && reason.xhr.responseText) that.stateReason += ' -- ' + reason.xhr.responseText; _deferred.reject(reason); @@ -399,7 +428,8 @@ function UploadToCollection($scope, $filter, $q, $timeout, function onQueueResolve() { that.state = 'Idle'; that.stateReason = 'Done!'; - _deferred.resolve(); + if (_deferred) + _deferred.resolve(); onQueueProgress(); } function onQueueProgress() { @@ -414,11 +444,18 @@ function UploadToCollection($scope, $filter, $q, $timeout, then(function(collection) { var manifestText = ''; $.each(uploads, function(_, upload) { + var locators = upload.locators; + if (locators.length === 0) { + // Every stream must have at least one + // data locator, even if it is zero bytes + // long: + locators = ['d41d8cd98f00b204e9800998ecf8427e+0']; + } filename = ArvadosClient.uniqueNameForManifest( collection.manifest_text, '.', upload.file.name); collection.manifest_text += '. ' + - upload.locators.join(' ') + + locators.join(' ') + ' 0:' + upload.file.size.toString() + ':' + filename + '\n'; @@ -447,11 +484,11 @@ function UploadToCollection($scope, $filter, $q, $timeout, } }). then(_deferredAppend.resolve, - _deferredAppend.reject). + _deferredAppend.reject); + return _deferredAppend.promise(). always(function() { _deferredAppend = null; }); - return _deferredAppend.promise(); } } }