Limit action port message size (#587)

* Add onDisconnect handler

* Update how error is posted

* Update action ports to send long messages in fragments

* Remove ack timer

* Move message destructuring into try block
This commit is contained in:
toasted-nutbread 2020-05-31 18:17:12 -04:00 committed by GitHub
parent cfd3a1ec3a
commit 2c58b1c109
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 27 deletions

View File

@ -861,6 +861,7 @@ class Backend {
_createActionListenerPort(port, sender, handlers) { _createActionListenerPort(port, sender, handlers) {
let hasStarted = false; let hasStarted = false;
let messageString = '';
const onProgress = (...data) => { const onProgress = (...data) => {
try { try {
@ -871,12 +872,34 @@ class Backend {
} }
}; };
const onMessage = async ({action, params}) => { const onMessage = (message) => {
if (hasStarted) { return; } if (hasStarted) { return; }
hasStarted = true;
port.onMessage.removeListener(onMessage);
try { try {
const {action, data} = message;
switch (action) {
case 'fragment':
messageString += data;
break;
case 'invoke':
{
hasStarted = true;
port.onMessage.removeListener(onMessage);
const messageData = JSON.parse(messageString);
messageString = null;
onMessageComplete(messageData);
}
break;
}
} catch (e) {
cleanup(e);
}
};
const onMessageComplete = async (message) => {
try {
const {action, params} = message;
port.postMessage({type: 'ack'}); port.postMessage({type: 'ack'});
const messageHandler = handlers.get(action); const messageHandler = handlers.get(action);
@ -893,25 +916,29 @@ class Backend {
const result = async ? await promiseOrResult : promiseOrResult; const result = async ? await promiseOrResult : promiseOrResult;
port.postMessage({type: 'complete', data: result}); port.postMessage({type: 'complete', data: result});
} catch (e) { } catch (e) {
if (port !== null) { cleanup(e);
port.postMessage({type: 'error', data: errorToJson(e)});
}
cleanup();
} }
}; };
const cleanup = () => { const onDisconnect = () => {
cleanup(null);
};
const cleanup = (error) => {
if (port === null) { return; } if (port === null) { return; }
if (error !== null) {
port.postMessage({type: 'error', data: errorToJson(error)});
}
if (!hasStarted) { if (!hasStarted) {
port.onMessage.removeListener(onMessage); port.onMessage.removeListener(onMessage);
} }
port.onDisconnect.removeListener(cleanup); port.onDisconnect.removeListener(onDisconnect);
port = null; port = null;
handlers = null; handlers = null;
}; };
port.onMessage.addListener(onMessage); port.onMessage.addListener(onMessage);
port.onDisconnect.addListener(cleanup); port.onDisconnect.addListener(onDisconnect);
} }
_getErrorLevelValue(errorLevel) { _getErrorLevelValue(errorLevel) {

View File

@ -236,7 +236,6 @@ const api = (() => {
_invokeWithProgress(action, params, onProgress, timeout=5000) { _invokeWithProgress(action, params, onProgress, timeout=5000) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let timer = null;
let port = null; let port = null;
if (typeof onProgress !== 'function') { if (typeof onProgress !== 'function') {
@ -245,12 +244,6 @@ const api = (() => {
const onMessage = (message) => { const onMessage = (message) => {
switch (message.type) { switch (message.type) {
case 'ack':
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
break;
case 'progress': case 'progress':
try { try {
onProgress(...message.data); onProgress(...message.data);
@ -275,10 +268,6 @@ const api = (() => {
}; };
const cleanup = () => { const cleanup = () => {
if (timer !== null) {
clearTimeout(timer);
timer = null;
}
if (port !== null) { if (port !== null) {
port.onMessage.removeListener(onMessage); port.onMessage.removeListener(onMessage);
port.onDisconnect.removeListener(onDisconnect); port.onDisconnect.removeListener(onDisconnect);
@ -288,17 +277,20 @@ const api = (() => {
onProgress = null; onProgress = null;
}; };
timer = setTimeout(() => {
cleanup();
reject(new Error('Timeout'));
}, timeout);
(async () => { (async () => {
try { try {
port = await this._createActionPort(timeout); port = await this._createActionPort(timeout);
port.onMessage.addListener(onMessage); port.onMessage.addListener(onMessage);
port.onDisconnect.addListener(onDisconnect); port.onDisconnect.addListener(onDisconnect);
port.postMessage({action, params});
// Chrome has a maximum message size that can be sent, so longer messages must be fragmented.
const messageString = JSON.stringify({action, params});
const fragmentSize = 1e7; // 10 MB
for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) {
const data = messageString.substring(i, i + fragmentSize);
port.postMessage({action: 'fragment', data});
}
port.postMessage({action: 'invoke'});
} catch (e) { } catch (e) {
cleanup(); cleanup();
reject(e); reject(e);