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

Conversation

fromkeith
Copy link
Contributor

Brief description of the changes [REQUIRED]

Have the option for the S3 Signing process (v4) to be done in a worker thread. My browser no longer freezes on large files.

What browsers and operating systems have you tested these changes on? [REQUIRED]

Windows 10

  • Chrome 56.0
  • Firefox 50.1.0
  • Edge 38
  • Internet Explorer 11

OSX

  • Safari 10.0.2

Are all automated tests passing? [REQUIRED]

Yes.
I haven't build the docs though...

Is this pull request against develop or some other non-master branch? [REQUIRED]

Develop

More Info

You might consider this a scary change :)

A couple things I want to point out, slash get your feedback on.

  • The worker can be implemented as either Inline or as a separate file.
  • From what I can find IE is the only one that doesn't support the inline worker.
    • So I've added in a graceful fallback to just the old in 'main thread' method.
  • Some people might not want the extra overhead of the inline file.
    • The extra overhead in this case is really the CryptoJS library.
    • I've made the option 'useWorker' to be able to specify a string. That way people can override with their own external worker.
  • The inline worker file is generated at compile time. See the _build-s3-inline-worker build task.
    • It's done by concatenating 3 files.
      • worker.start.js, CyrptoJS files, worker.end.js
    • A simple script (workerToInline) then converts that file into a string and adds a function to qq.
    • We can now include the inline worker as a build file, as per normal js files.
  • A new build artefact is added for the external worker.
  • I've added a new S3 output that includes the inline worker. The regular output for s3 should not include it.
  • Guidance on how you want the build artefacts to look like would be great.

Feedback, changes, clarity, etc.. are all welcome.

@fromkeith
Copy link
Contributor Author

Looks like lint failures... fixing that

postMessage and onmessage are defined in the worker scope.
we need to set qq on the worker prefix so that crypto has it defined inside the worker scope.
Copy link
Member

@rnicholus rnicholus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting, and it looks like you put a lot of work into this. I appreciate the effort.

My concern is having yet another JS build artifact. How many bytes does this add to the existing S3 JS build files? Unless the increase is notable, I'd rather just bundle this in with the existing core and UI S3 builds.

Also, I really think unit tests are prudent here, plus some documentation should probably be added to the S3 feature page. I'm happy to assist any way I can, but I'm finding I have less and less time to evolve this library these days.

// We will fallback to the inline reader if the worker
// fails to load.
if (options.signatureSpec.useWorker) {
if (!this._worker) {

This comment was marked as spam.

if (!qq.s3.worker) {
throw new Error("Missing inline s3 worker.");
}
workerUrl = qq.s3.worker();

This comment was marked as spam.

var workerUrl = options.signatureSpec.useWorker;
if (workerUrl === true) {
if (!qq.s3.worker) {
throw new Error("Missing inline s3 worker.");

This comment was marked as spam.

@fromkeith
Copy link
Contributor Author

I never actually looked at the file size differences...

s3-core.min: 142KB
s3-core-worker.min: 150KB

I'll take a look at unit tests and incorporating your code comments a bit later.

@rnicholus
Copy link
Member

rnicholus commented Dec 17, 2016

8K difference - after gzipping, that's almost a wash. Using web workers for this task seems like a big win to me, so I'm leaning towards including it in all existing S3 build files. But anything you can do to make the difference even smaller is worthwhile too.

New file to manage the workers.
Add unit tests to ensure worker is working.
Rename from useWorker to workerUrl.
'qq.s3.worker' func renamed to 'qq.s3.createS3InlineWorkerUrl'.
Updated docs for 'useWorker' to 'workerUrl' option change.
Propogate options.log to the manager.
Get error handling for bad worker urls working.
Set 'inline' as the text to set for using our default inline worker.
@fromkeith
Copy link
Contributor Author

From your feedback:

  • 'useWorker' changed to 'workerUrl'
  • 'qq.s3.worker()' changed to 'qq.s3.createS3InlineWorkerUrl()'
  • Moved the new code from request-signer into a new file 'request-signer.worker-manager.js'
  • Added in unit tests. I had to fix Date so that it was constant, so I can verify the signatures not changing.
  • Removed the error from being thrown. Instead it is logged.

@fromkeith
Copy link
Contributor Author

I also want to do a bit more testing on multiple browsers with these changes. I've just done a quick one on Firefox, IE, Edge and Chrome.. not as indepth as I want yet. But I thought you would want to look at the reformatting sooner rather than later.

One thing that I could use guidance on... is that because RequestSigner gets created multiple times, my worker-manager gets created multiple times.. Is there a better place to create the manager, but still get the options from the signer?

Copy link
Member

@rnicholus rnicholus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, but a few changes are needed.

// not loaded correctly
if (workerManager) {
promise = workerManager.generateSignature(body);
if (promise !== null) {

This comment was marked as spam.

This comment was marked as spam.

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.

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.

workerUrl = options.workerUrl;
} else {
if (!qq.s3.createS3InlineWorkerUrl) {
qq.Error("Missing inline s3 worker");

This comment was marked as spam.

This comment was marked as spam.

};
_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.

options.log("Worker returned a result for an request we dont know about.");
return;
}
if (e.data.err) {

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

@@ -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.

package.json Outdated
@@ -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.

@@ -23,7 +23,7 @@ fs.readFile(source, 'UTF-8', function (err, data) {
let sourceCode = jsStringEscape(data);

let outCode = `

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

@rnicholus
Copy link
Member

because RequestSigner gets created multiple times, my worker-manager gets created multiple times.. Is there a better place to create the manager, but still get the options from the signer?

Instead of passing the workerUrl as an option to RequestSigner, why not construct the RequestSignerWorkerManager as part of the S3 upload instance initialization or in qq.s3.XhrUploadHandler, and pass the RequestSignerWorkerManager instance to RequestSigner when it is constructed as part of the signatureSpec?

@rnicholus
Copy link
Member

Interested in pushing this through. Are you able to address the remaining issues?

@fromkeith
Copy link
Contributor Author

Sorry. Got distracted by other things.
I think I have a working copy that I've restructured mostly. I think the last main task I had left was moving the worker creation to a different module per your suggestion.

@fromkeith
Copy link
Contributor Author

Anything I need to do to progress?

@rnicholus
Copy link
Member

Hey there. Sorry for the silence. Reviewing this is on my to-do list, but I have a few things ahead in the queue, and I have some other new commitments that I have to attend to as well.

@fromkeith
Copy link
Contributor Author

No worries. Just wanted to make sure I wasn't blocking anything.

@rnicholus rnicholus closed this Nov 8, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants