browserbox
Advanced tools
Comparing version 0.8.3 to 0.9.0
{ | ||
"name": "browserbox", | ||
"version": "0.8.3", | ||
"version": "0.9.0", | ||
"homepage": "https://github.com/whiteout-io/browserbox", | ||
@@ -46,2 +46,2 @@ "description": "IMAP client for browsers.", | ||
} | ||
} | ||
} |
@@ -76,2 +76,4 @@ # browserbox | ||
* **requireTLS** – if set to true, always use STARTTLS before authentication even if the host does not advertise it. If STARTTLS fails, do not try to authenticate the user | ||
* **enableCompression** - if set to true then use IMAP COMPRESS extension (rfc4978) if the server supports it (Gmail does). All data sent and received in this case is compressed with *deflate* | ||
* **compressionWorkerPath** (optional) offloads de-/compression computation to a web worker, this is the path to the browserified browserbox-compressor-worker.js | ||
@@ -95,2 +97,4 @@ Default STARTTLS support is opportunistic – if the server advertises STARTTLS capability, the client tries to use it. If STARTTLS is not advertised, the clients sends passwords in the plain. You can use `ignoreTLS` and `requireTLS` to change this behavior by explicitly enabling or disabling STARTTLS usage. | ||
**Use of web workers with compression**: If you use compression, we can spin up a Web Worker to handle the TLS-related computation off the main thread. To do this, you need to **browserify** `browserbox-compressor-worker.js`, specify the path via `options.compressionWorkerPath` | ||
## Initiate connection | ||
@@ -97,0 +101,0 @@ |
@@ -25,11 +25,11 @@ // Copyright (c) 2014 Andris Reinman | ||
if (typeof define === 'function' && define.amd) { | ||
define(['tcp-socket', 'imap-handler', 'mimefuncs', 'axe'], function(TCPSocket, imapHandler, mimefuncs, axe) { | ||
return factory(TCPSocket, imapHandler, mimefuncs, axe); | ||
define(['tcp-socket', 'imap-handler', 'mimefuncs', 'browserbox-compression', 'axe'], function(TCPSocket, imapHandler, mimefuncs, compression, axe) { | ||
return factory(TCPSocket, imapHandler, mimefuncs, compression, axe); | ||
}); | ||
} else if (typeof exports === 'object') { | ||
module.exports = factory(require('tcp-socket'), require('wo-imap-handler'), require('mimefuncs'), require('axe-logger')); | ||
module.exports = factory(require('tcp-socket'), require('wo-imap-handler'), require('mimefuncs'), require('./browserbox-compression'), require('axe-logger'), null); | ||
} else { | ||
root.BrowserboxImapClient = factory(navigator.TCPSocket, root.imapHandler, root.mimefuncs, root.axe); | ||
root.BrowserboxImapClient = factory(navigator.TCPSocket, root.imapHandler, root.mimefuncs, root.BrowserboxCompressor, root.axe); | ||
} | ||
}(this, function(TCPSocket, imapHandler, mimefuncs, axe) { | ||
}(this, function(TCPSocket, imapHandler, mimefuncs, Compression, axe) { | ||
'use strict'; | ||
@@ -39,2 +39,11 @@ | ||
// | ||
// constants used for communication with the worker | ||
// | ||
var MESSAGE_START = 'start'; | ||
var MESSAGE_INFLATE = 'inflate'; | ||
var MESSAGE_INFLATED_DATA_READY = 'inflated_ready'; | ||
var MESSAGE_DEFLATE = 'deflate'; | ||
var MESSAGE_DEFLATED_DATA_READY = 'deflated_ready'; | ||
/** | ||
@@ -50,2 +59,3 @@ * Creates a connection object to an IMAP server. Call `connect` method to inititate | ||
* @param {Boolean} [options.useSecureTransport] Set to true, to use encrypted connection | ||
* @param {String} [options.compressionWorkerPath] offloads de-/compression computation to a web worker, this is the path to the browserified browserbox-compressor-worker.js | ||
*/ | ||
@@ -89,3 +99,6 @@ function ImapClient(host, port, options) { | ||
// Private properties | ||
/** | ||
* Is the connection compressed and needs inflating/deflating | ||
*/ | ||
this.compressed = false; | ||
@@ -154,2 +167,9 @@ /** | ||
this._socketTimeoutTimer = false; | ||
/** | ||
* The path for the compressor's worker script | ||
*/ | ||
this._workerPath = this.options.compressionWorkerPath; | ||
this._compression = new Compression(); | ||
} | ||
@@ -247,2 +267,4 @@ | ||
ImapClient.prototype.close = function() { | ||
this._disableCompression(); | ||
if (this.socket && this.socket.readyState === 'open') { | ||
@@ -312,3 +334,7 @@ this.socket.close(); | ||
this.waitDrain = this.socket.send(buffer); | ||
if (this.compressed) { | ||
this._sendCompressed(buffer); | ||
} else { | ||
this.waitDrain = this.socket.send(buffer); | ||
} | ||
}; | ||
@@ -337,2 +363,7 @@ | ||
ImapClient.prototype._onError = function(evt) { | ||
if (this.destroyed) { | ||
// ignore errors that happen after we close the connection | ||
return; | ||
} | ||
if (this.isError(evt)) { | ||
@@ -373,2 +404,3 @@ this.onerror(evt); | ||
ImapClient.prototype._onClose = function() { | ||
this._disableCompression(); | ||
this._destroy(); | ||
@@ -826,3 +858,126 @@ }; | ||
// COMPRESSION RELATED METHODS | ||
/** | ||
* Sets up deflate/inflate for the IO | ||
*/ | ||
ImapClient.prototype.enableCompression = function() { | ||
this._socketOnData = this.socket.ondata; | ||
this.compressed = true; | ||
if (typeof window !== 'undefined' && window.Worker && typeof this._workerPath === 'string') { | ||
// | ||
// web worker support | ||
// | ||
this._compressionWorker = new Worker(this._workerPath); | ||
this._compressionWorker.onmessage = function(e) { | ||
var message = e.data.message, | ||
buffer = e.data.buffer; | ||
switch (message) { | ||
case MESSAGE_INFLATED_DATA_READY: | ||
this._socketOnData({ | ||
data: buffer | ||
}); | ||
break; | ||
case MESSAGE_DEFLATED_DATA_READY: | ||
this.waitDrain = this.socket.send(buffer); | ||
break; | ||
} | ||
}.bind(this); | ||
this._compressionWorker.onerror = function(e) { | ||
var error = new Error('Error handling compression web worker: Line ' + e.lineno + ' in ' + e.filename + ': ' + e.message); | ||
axe.error(DEBUG_TAG, error); | ||
this._onError(error); | ||
}.bind(this); | ||
// first message starts the worker | ||
this._compressionWorker.postMessage(this._createMessage(MESSAGE_START)); | ||
} else { | ||
// | ||
// without web worker support | ||
// | ||
this._compression.inflatedReady = function(buffer) { | ||
// emit inflated data | ||
this._socketOnData({ | ||
data: buffer | ||
}); | ||
}.bind(this); | ||
this._compression.deflatedReady = function(buffer) { | ||
// write deflated data to socket | ||
if (!this.compressed) { | ||
return; | ||
} | ||
this.waitDrain = this.socket.send(buffer); | ||
}.bind(this); | ||
} | ||
// override data handler, decompress incoming data | ||
this.socket.ondata = function(evt) { | ||
if (!this.compressed) { | ||
return; | ||
} | ||
// inflate | ||
if (this._compressionWorker) { | ||
this._compressionWorker.postMessage(this._createMessage(MESSAGE_INFLATE, evt.data), [evt.data]); | ||
} else { | ||
this._compression.inflate(evt.data); | ||
} | ||
}.bind(this); | ||
}; | ||
/** | ||
* Undoes any changes related to compression. This only be called when closing the connection | ||
*/ | ||
ImapClient.prototype._disableCompression = function() { | ||
if (!this.compressed) { | ||
return; | ||
} | ||
this.compressed = false; | ||
this.socket.ondata = this._socketOnData; | ||
this._socketOnData = null; | ||
if (this._compressionWorker) { | ||
// terminate the worker | ||
this._compressionWorker.terminate(); | ||
this._compressionWorker = null; | ||
} | ||
}; | ||
/** | ||
* Outgoing payload needs to be compressed and sent to socket | ||
* | ||
* @param {ArrayBuffer} buffer Outgoing uncompressed arraybuffer | ||
*/ | ||
ImapClient.prototype._sendCompressed = function(buffer) { | ||
// deflate | ||
if (this._compressionWorker) { | ||
this._compressionWorker.postMessage(this._createMessage(MESSAGE_DEFLATE, buffer), [buffer]); | ||
} else { | ||
this._compression.deflate(buffer); | ||
} | ||
}; | ||
ImapClient.prototype._createMessage = function(message, buffer) { | ||
return { | ||
message: message, | ||
buffer: buffer | ||
}; | ||
}; | ||
return ImapClient; | ||
})); |
@@ -634,3 +634,5 @@ 'use strict'; | ||
}; | ||
client._addToClientQueue({}, undefined, {ctx: ctx}); | ||
client._addToClientQueue({}, undefined, { | ||
ctx: ctx | ||
}); | ||
} | ||
@@ -695,3 +697,3 @@ }]; | ||
define('#isError', function() { | ||
describe('#isError', function() { | ||
it('should detect if an object is an error', function() { | ||
@@ -702,3 +704,29 @@ expect(client.isError(new RangeError('abc'))).to.be.true; | ||
}); | ||
describe('#enableCompression', function() { | ||
it('should create inflater and deflater streams', function() { | ||
client.socket.ondata = function() {}; | ||
sinon.stub(client.socket, 'ondata'); | ||
expect(client.compressed).to.be.false; | ||
client.enableCompression(); | ||
expect(client.compressed).to.be.true; | ||
sinon.stub(client._compression, 'inflate', function() { | ||
client._compression.inflatedReady(new Uint8Array([1, 2, 3]).buffer); | ||
}); | ||
sinon.stub(client._compression, 'deflate', function() { | ||
client._compression.deflatedReady(new Uint8Array([4, 5, 6]).buffer); | ||
}); | ||
client.send('a'); | ||
client.socket.ondata(new Uint8Array([1]).buffer); | ||
expect(socketStub.send.args[0][0]).to.deep.equal(new Uint8Array([4, 5, 6]).buffer); | ||
expect(client._socketOnData.args[0][0]).to.deep.equal({ | ||
data: new Uint8Array([1, 2, 3]).buffer | ||
}); | ||
}); | ||
}); | ||
}); | ||
})); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
615634
25
15095
842
9
1