Skip to content
This repository has been archived by the owner on Nov 20, 2018. It is now read-only.

S3 Signature on Worker #1702

Closed
wants to merge 19 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 5 additions & 27 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -111,25 +111,20 @@ s3-files-only = \
$(js-src-dir)/s3/util.js \
$(js-src-dir)/non-traditional-common/uploader.basic.api.js \
$(js-src-dir)/s3/uploader.basic.js \
$(js-src-dir)/s3/request-signer.worker-manager.js \
$(js-src-dir)/s3/request-signer.js \
$(js-src-dir)/uploadsuccess.ajax.requester.js \
$(js-src-dir)/s3/multipart.initiate.ajax.requester.js \
$(js-src-dir)/s3/multipart.complete.ajax.requester.js \
$(js-src-dir)/s3/multipart.abort.ajax.requester.js \
$(js-src-dir)/s3/s3.xhr.upload.handler.js \
$(js-src-dir)/s3/s3.form.upload.handler.js
$(js-src-dir)/s3/s3.form.upload.handler.js \
$(build-out-dir)/s3.fine-uploader.worker-inline.js

s3-files = \
$(core-files) \
$(s3-files-only)

s3-files-with-worker-only = \
$(build-out-dir)/s3.fine-uploader.worker-inline.js

s3-files-with-worker = \
$(s3-files) \
$(s3-files-with-worker-only)

s3-ui-files-only = \
$(js-src-dir)/s3/uploader.js

Expand Down Expand Up @@ -178,7 +173,6 @@ all-core-files = \
$(core-files) \
$(traditional-files-only) \
$(s3-files-only) \
$(s3-files-with-worker-only) \
$(azure-files-only)

all-core-jquery-files = \
Expand All @@ -190,7 +184,6 @@ all-files = \
$(traditional-files-only) \
$(ui-files) \
$(s3-files-only) \
$(s3-files-with-worker-only) \
$(s3-ui-files-only) \
$(azure-files-only) \
$(azure-ui-files-only)
Expand Down Expand Up @@ -250,18 +243,12 @@ _build-s3-inline-worker: _build
$(uglify-worker) $(js-src-dir)/s3/worker.start.js $(cryptojs-files) $(js-src-dir)/s3/worker.end.js -o $(build-out-dir)/s3.fine-uploader.worker.min.js --source-map $(build-out-dir)/s3.fine-uploader.worker.js.map
node workerToInline $(build-out-dir)/s3.fine-uploader.worker.min.js $(build-out-dir)/s3.fine-uploader.worker-inline.js

build-core-s3: _build
build-core-s3: _build _build-s3-inline-worker
$(uglify) $(s3-files) -o $(build-out-dir)/s3.fine-uploader.core.js --source-map $(build-out-dir)/s3.fine-uploader.core.js.map

build-core-s3-min: _build
build-core-s3-min: _build _build-s3-inline-worker
$(uglify-min) $(s3-files) -o $(build-out-dir)/s3.fine-uploader.core.min.js --source-map $(build-out-dir)/s3.fine-uploader.core.min.js.map

build-s3-files-with-worker: _build _build-s3-inline-worker
$(uglify) $(s3-files-with-worker) -o $(build-out-dir)/s3.fine-uploader.core.worker.js --source-map $(build-out-dir)/s3.fine-uploader.core.worker.js.map

build-s3-files-with-worker-min: _build _build-s3-inline-worker
$(uglify-min) $(s3-files-with-worker) -o $(build-out-dir)/s3.fine-uploader.core.worker.min.js --source-map $(build-out-dir)/s3.fine-uploader.core.worker.min.js.map

build-ui-s3: _build
$(uglify) $(s3-ui-files) -o $(build-out-dir)/s3.fine-uploader.js --source-map $(build-out-dir)/s3.fine-uploader.js.map

Expand Down Expand Up @@ -319,8 +306,6 @@ build: \
build-ui-s3-min \
build-ui-s3-jquery \
build-ui-s3-jquery-min \
build-s3-files-with-worker \
build-s3-files-with-worker-min \
build-core-azure \
build-core-azure-min \
build-ui-azure \
Expand Down Expand Up @@ -391,12 +376,6 @@ endif
cp $(build-out-dir)/$(PUB-SUBDIR).min* $(build-out-dir)/$(PUB-SUBDIR).js* $(pub-dir)/$(PUB-SUBDIR)
cp $(build-out-dir)/fine-uploader*.css* $(pub-dir)/$(PUB-SUBDIR)

copy-s3-worker-to-dist:
cp $(build-out-dir)/$(PUB-SUBDIR).core.worker.js* \
$(build-out-dir)/$(PUB-SUBDIR).core.worker.min* \
$(build-out-dir)/$(PUB-SUBDIR).worker.min* \
$(pub-dir)/$(PUB-SUBDIR)/

copy-dnd:
mkdir -p $(pub-dir)/dnd
cp $(build-out-dir)/dnd*.* $(pub-dir)/dnd
Expand All @@ -411,7 +390,6 @@ copy-traditional-jquery-dist:

copy-s3-dist:
make copy-build-to-dist PUB-SUBDIR=s3.fine-uploader
make copy-s3-worker-to-dist PUB-SUBDIR=s3.fine-uploader

copy-s3-jquery-dist:
make copy-build-to-dist PUB-SUBDIR=s3.jquery.fine-uploader
Expand Down
69 changes: 20 additions & 49 deletions client/js/s3/request-signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ qq.s3.RequestSigner = function(o) {
endpoint: null,
customHeaders: {},
version: 2,
useWorker: false
workerUrl: null
},
maxConnections: 3,
endpointStore: {},
Expand All @@ -46,6 +46,7 @@ qq.s3.RequestSigner = function(o) {
log: function(str, level) {}
},
credentialsProvider,
workerManager,

generateHeaders = function(signatureConstructor, signature, promise) {
var headers = signatureConstructor.getHeaders();
Expand Down Expand Up @@ -153,52 +154,18 @@ qq.s3.RequestSigner = function(o) {
},

getEncodedHashedPayload: function(body) {
var promise = new qq.Promise(),
reader,
self = this;

var promise,
reader;
if (qq.isBlob(body)) {
// Only if a worker is requested will we load it.
// We will fallback to the inline reader if the worker
// fails to load.
if (options.signatureSpec.useWorker) {
if (!this._worker) {
try {
// allow the worker path to be overwritten.. Eg. we aren't using an inline worker.
var workerUrl = options.signatureSpec.useWorker;
if (workerUrl === true) {
if (!qq.s3.worker) {
throw new Error("Missing inline s3 worker.");
}
workerUrl = qq.s3.worker();
}
this._worker = new Worker(workerUrl);
this._workerPromises = {};
this._worker.onmessage = function (e) {
if (!self._workerPromises[e.data.id]) {
options.log("Worker returned a result for an request we dont know about.");
return;
}
if (e.data.err) {
self._workerPromises[e.data.id].failure(e.data.err);
} else {
self._workerPromises[e.data.id].success(e.data.resp);
}
delete self._workerPromises[e.data.id];
};
} catch (ex) {
// worker is not supported or invalid
options.log("Worker failed to be created. Defaulting back to main thread processing.", ex);
options.signatureSpec.useWorker = false;
}
}
if (this._worker) {
var task = {file: body, id: qq.getUniqueId()};
this._workerPromises[task.id] = promise;
this._worker.postMessage(task);
// We will fallback to the inline reader if the worker was
// not loaded correctly
if (workerManager) {
promise = workerManager.generateSignature(body);
if (promise !== null) {

This comment was marked as spam.

This comment was marked as spam.

return promise;
}
}
promise = new qq.Promise();
reader = new FileReader();
reader.onloadend = function(e) {
if (e.target.readyState === FileReader.DONE) {
Expand All @@ -212,12 +179,11 @@ qq.s3.RequestSigner = function(o) {
}
};
reader.readAsArrayBuffer(body);
return promise;
}
else {
body = body || "";
promise.success(qq.CryptoJS.SHA256(body).toString());
}

promise = new qq.Promise();
body = body || "";
promise.success(qq.CryptoJS.SHA256(body).toString());
return promise;
},

Expand Down Expand Up @@ -301,6 +267,12 @@ qq.s3.RequestSigner = function(o) {

qq.extend(options, o, true);
credentialsProvider = options.signatureSpec.credentialsProvider;
if (options.signatureSpec.workerUrl !== null) {
workerManager = new qq.s3.RequestSignerWorkerManager({
workerUrl: options.signatureSpec.workerUrl,
log: options.log,
});
}

function handleSignatureReceived(id, xhrOrXdr, isError) {
var responseJson = xhrOrXdr.responseText,
Expand Down Expand Up @@ -435,7 +407,6 @@ qq.s3.RequestSigner = function(o) {
}

endOfUrl = requestInfo.key + "?" + endOfUrl;

if (version === 4) {
v4.getEncodedHashedPayload(requestInfo.content).then(function(hashedContent) {
requestInfo.headers["x-amz-content-sha256"] = hashedContent;
Expand Down
88 changes: 88 additions & 0 deletions client/js/s3/request-signer.worker-manager.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/* globals qq, CryptoJS */

/**
* Manages creation and communication of s3 signature workers.
*/
qq.s3.RequestSignerWorkerManager = function (o) {
"use strict";
var _worker = null,
_workerPromises = {},

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

options = {
workerUrl: null,
log: function(str, level) {}
};
qq.extend(options, o, true);

function init() {
var workerUrl;
switch (typeof options.workerUrl) {
case "string":
if (options.workerUrl !== "inline") {
workerUrl = options.workerUrl;
} else {
if (!qq.s3.createS3InlineWorkerUrl) {

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

qq.Error("Missing inline s3 worker");

This comment was marked as spam.

This comment was marked as spam.

return;
}
workerUrl = qq.s3.createS3InlineWorkerUrl();
}
break;
case "function":
workerUrl = options.workerUrl();
break;
default:
break;
}
if (!workerUrl) {
return;
}
try {
_worker = new Worker(workerUrl);
_worker.onerror = function (e) {
// Prevent the event from bubbling
e.preventDefault();
// log the error, and fail any pending promises.
options.log("Worker encountered an error. Disabling. " + e.message, "warn");
_worker = null;
var outstandingRequests = Object.keys(_workerPromises),
i;
for (i = 0; i < outstandingRequests.length; i++) {
_workerPromises[outstandingRequests[i]].failure(e);
delete _workerPromises[outstandingRequests[i]];
}
};
_worker.onmessage = function (e) {
if (!_workerPromises[e.data.id]) {
options.log("Worker returned a result for an request we dont know about.");

This comment was marked as spam.

return;
}
if (e.data.err) {

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

_workerPromises[e.data.id].failure(e.data.err);
} else {
_workerPromises[e.data.id].success(e.data.resp);
}
delete _workerPromises[e.data.id];
};
} catch (ex) {
// worker is not supported or invalid
options.log("Worker failed to be created. Defaulting back to main thread processing." + ex, "warn");
_worker = null;
}
}
init();
/*
Generates the signuare of the given file.
@param file the file/slice to generate the signature for.
@returns a promise or null if we can't generate signatures at all.
*/
this.generateSignature = function (file) {
if (!_worker) {
return null;
}
var promise = new qq.Promise(),
task = {file: file, id: qq.getUniqueId()};
_workerPromises[task.id] = promise;
_worker.postMessage(task);
return promise;
};
};
1 change: 1 addition & 0 deletions config/karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = function(config, options) {
path.resolve("test/static/local/blob-maker.js"),
path.resolve("test/static/third-party/q/q-1.0.1.js"),
path.resolve("node_modules/pica/dist/pica.js"),
path.resolve("node_modules/timemachine/timemachine.min.js"),
path.resolve("test/static/local/helpme.js"),
path.resolve("test/unit/**/*.js")
],
Expand Down
2 changes: 1 addition & 1 deletion docs/api/options-s3.jmd
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ alert("The [`request.customHeaders` option](options.html#request.customHeaders)
("signature.customHeaders", "customHeaders", "Additional headers sent along with each signature request. If you declare a function as the value, the associated file's ID will be passed to your function when it is invoked.", "Object, Function", "{}",),
("signature.endpoint", "endpoint", "The endpoint that Fine Uploader can use to send policy documents (HTML form uploads) or other strings to sign (REST requests) before sending requests off to S3.", "String", "null",),
("signature.version", "version", "The AWS/S3 signature version to use. Currently supported values are `2` and `4`. Directly related to [`objectProperties.region`](#objectProperties.region).", "Integer", "2",),
("signature.useWorker", "useWorker", "Make client side signature processing for v4 signatures occur in a background worker thread. Set to true to use the inline worker. Otherwise set the URL to the worker to use.", "Boolean/String", "false",),
("signature.workerUrl", "workerUrl", "Make client side signature processing for v4 signatures occur in a background worker thread. Set to 'inline' to use the inline worker. Otherwise set the URL for the worker to use.", "String", "null",),

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

)
)}}

Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"url": "https://github.com/FineUploader/fine-uploader/issues"
},
"devDependencies": {
"clean-css": "3.4.19",
"clean-css": "3.4.23",
"js-string-escape": "^1.0.1",
"jscs": "3.0.7",
"jshint": "2.9.4",
Expand All @@ -57,6 +57,7 @@
"mocha": "3.2.0",
"node-static": "0.7.9",
"pica": "latest",
"timemachine": "^0.2.8",

This comment was marked as spam.

"uglify-js": "2.7.5"
},
"scripts": {
Expand Down