@nimiq/rpc
Advanced tools
Comparing version 0.4.0-beta.2 to 0.4.0
@@ -167,8 +167,7 @@ 'use strict'; | ||
var ResponseMethod; | ||
(function (ResponseMethod) { | ||
ResponseMethod["POST"] = "post"; | ||
ResponseMethod["URL"] = "url"; | ||
ResponseMethod["MESSAGE"] = "message"; | ||
})(ResponseMethod || (ResponseMethod = {})); | ||
ResponseMethod["HTTP_POST"] = "http-post"; | ||
ResponseMethod["HTTP_GET"] = "http-get"; | ||
ResponseMethod["POST_MESSAGE"] = "post-message"; | ||
})(exports.ResponseMethod || (exports.ResponseMethod = {})); | ||
(function (ResponseStatus) { | ||
@@ -178,3 +177,2 @@ ResponseStatus["OK"] = "ok"; | ||
})(exports.ResponseStatus || (exports.ResponseStatus = {})); | ||
const POSTMESSAGE_RETURN_URL = '<postMessage>'; | ||
@@ -273,15 +271,12 @@ class RequestIdStorage { | ||
fragment.delete('returnURL'); | ||
// guess the responseMethod in messages without one. | ||
let responseMethod; | ||
// Guess the responseMethod in messages without one. | ||
let responseMethod = exports.ResponseMethod.HTTP_GET; | ||
if (fragment.has('responseMethod')) { | ||
responseMethod = fragment.get('responseMethod'); | ||
fragment.delete('responseMethod'); | ||
if (!Object.values(exports.ResponseMethod).includes(responseMethod)) { | ||
throw new Error('Invalid ResponseMethod'); | ||
} | ||
} | ||
else if (returnURL === POSTMESSAGE_RETURN_URL) { | ||
responseMethod = ResponseMethod.MESSAGE; | ||
} | ||
else { | ||
responseMethod = ResponseMethod.URL; | ||
} | ||
const answerByPostMessage = responseMethod === ResponseMethod.MESSAGE | ||
const answerByPostMessage = responseMethod === exports.ResponseMethod.POST_MESSAGE | ||
&& (window.opener || window.parent); | ||
@@ -314,4 +309,4 @@ // Only allow returning to same origin | ||
returnURL, | ||
responseMethod: responseMethod, | ||
source: responseMethod === ResponseMethod.MESSAGE ? (window.opener || window.parent) : null, | ||
responseMethod, | ||
source: responseMethod === exports.ResponseMethod.POST_MESSAGE ? (window.opener || window.parent) : null, | ||
}; | ||
@@ -612,10 +607,47 @@ } | ||
close() { } | ||
call(returnURL, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.URL, null, command, handleHistoryBack, ...args); | ||
call(returnURL, command, optionsOrHandleHistory, ...args) { | ||
if (!optionsOrHandleHistory || typeof optionsOrHandleHistory === 'boolean') { | ||
if (typeof optionsOrHandleHistory === 'boolean') { | ||
console.warn('RedirectRpcClient.call(string, string, boolean, any[]) is deprecated.' | ||
+ ' Use RedirectRpcClient.call(string, string, CallOptions, any[]) with an' | ||
+ ' appropriate CallOptions object instead.'); | ||
} | ||
this._call(returnURL, command, { | ||
responseMethod: exports.ResponseMethod.HTTP_GET, | ||
handleHistoryBack: !!optionsOrHandleHistory, | ||
}, ...args); | ||
} | ||
else if (typeof optionsOrHandleHistory === 'object') { | ||
// Options are given, warn in case they do not make sense. | ||
// ResponseMethod.POST_MESSAGE does not have a single strong use case for redirects and could be omitted | ||
// until at least one of the following use cases is properly implemented. | ||
// TODO: We might want to support those cases in the future. See comment. | ||
if (optionsOrHandleHistory.responseMethod === exports.ResponseMethod.POST_MESSAGE) { | ||
if (!window.opener && !window.parent) { | ||
throw new Error('Window has no opener or parent,' | ||
+ ' responseMethod: ResponseMethod.POST_MESSAGE would fail.'); | ||
} | ||
else { | ||
// Could be mitigated i.e. by having the 'middle' rpcServer communicate to the initial rpcClient | ||
// the new requestId the request is going to be answered by in case of a redirect within a popup. | ||
// In case of opening a popup with url encoded parameters instead of post message, thereby | ||
// circumventing client.call(), the popup opening behaviour could be implemented in _call() to | ||
// re-enable usage of the call() method. | ||
console.warn('Response will skip at least one rpc call, which will result in an unknown response.'); | ||
} | ||
} | ||
this._call(returnURL, command, optionsOrHandleHistory, ...args); | ||
} | ||
} | ||
callAndPOSTResponse(returnURL, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.POST, null, command, handleHistoryBack, ...args); | ||
} | ||
/** | ||
* @deprecated Use call() with an appropriate `CallOptions` object instead. | ||
*/ | ||
callAndSaveLocalState(returnURL, state, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.URL, state, command, handleHistoryBack, ...args); | ||
console.warn('RedirectRpcClient.callAndSaveLocalState() is deprecated. Use RedirectRpcClient.call()' | ||
+ ' with an apropriate CallOptions object instead.'); | ||
this._call(returnURL, command, { | ||
responseMethod: exports.ResponseMethod.HTTP_GET, | ||
state: state ? state : undefined, | ||
handleHistoryBack, | ||
}, ...args); | ||
} | ||
@@ -629,7 +661,8 @@ _receive(response, persistMessage = true) { | ||
} | ||
_call(returnURL, responseMethod, state, command, handleHistoryBack = false, ...args) { | ||
_call(returnURL, command, callOptions, ...args) { | ||
const id = RandomUtils.generateRandomId(); | ||
const responseMethod = callOptions.responseMethod || exports.ResponseMethod.HTTP_GET; | ||
const url = UrlRpcEncoder.prepareRedirectInvocation(this._target, id, returnURL, command, args, responseMethod); | ||
this._waitingRequests.add(id, command, state); | ||
if (handleHistoryBack) { | ||
this._waitingRequests.add(id, command, callOptions.state || null); | ||
if (callOptions.handleHistoryBack) { | ||
/** | ||
@@ -668,2 +701,28 @@ * The rpcBackRejectionId in the history.state is used to detect in the client | ||
class FormRpcEncoder { | ||
static prepareFormReply(state, status, result) { | ||
const $form = document.createElement('form'); | ||
$form.setAttribute('method', 'post'); | ||
$form.setAttribute('action', state.returnURL); | ||
$form.style.display = 'none'; | ||
const $statusInput = document.createElement('input'); | ||
$statusInput.setAttribute('type', 'text'); | ||
$statusInput.setAttribute('name', 'status'); | ||
$statusInput.setAttribute('value', status); | ||
$form.appendChild($statusInput); | ||
const $resultInput = document.createElement('input'); | ||
$resultInput.setAttribute('type', 'text'); | ||
$resultInput.setAttribute('name', 'result'); | ||
$resultInput.setAttribute('value', JSONUtils.stringify(result)); | ||
$form.appendChild($resultInput); | ||
const $idInput = document.createElement('input'); | ||
$idInput.setAttribute('type', 'text'); | ||
$idInput.setAttribute('name', 'rpcId'); | ||
$idInput.setAttribute('value', state.id.toString()); | ||
$form.appendChild($idInput); | ||
document.body.appendChild($form); | ||
return $form; | ||
} | ||
} | ||
class State { | ||
@@ -694,7 +753,7 @@ get id() { | ||
this._id = message.data.id; | ||
this._responseMethod = 'responseMethod' in message | ||
this._responseMethod = 'responseMethod' in message && !!message.responseMethod | ||
? message.responseMethod | ||
: 'source' in message && !('returnURL' in message && message.returnURL === POSTMESSAGE_RETURN_URL) | ||
? ResponseMethod.MESSAGE | ||
: ResponseMethod.URL; | ||
: 'source' in message && !('returnURL' in message) | ||
? exports.ResponseMethod.POST_MESSAGE | ||
: exports.ResponseMethod.HTTP_GET; | ||
this._returnURL = 'returnURL' in message ? message.returnURL : null; | ||
@@ -710,3 +769,3 @@ this._data = message.data; | ||
}; | ||
if (this._responseMethod === ResponseMethod.MESSAGE) { | ||
if (this._responseMethod === exports.ResponseMethod.POST_MESSAGE) { | ||
if (this._source === window.opener) { | ||
@@ -736,3 +795,3 @@ obj.source = 'opener'; | ||
// TODO: Clear waiting request storage? | ||
if (this._responseMethod === ResponseMethod.MESSAGE) { | ||
if (this._responseMethod === exports.ResponseMethod.POST_MESSAGE) { | ||
// Send via postMessage (e.g., popup or url-persisted popup) | ||
@@ -763,3 +822,3 @@ let target; | ||
else if (this._returnURL) { | ||
if (this._responseMethod === ResponseMethod.URL) { | ||
if (this._responseMethod === exports.ResponseMethod.HTTP_GET) { | ||
// Send via top-level navigation | ||
@@ -769,24 +828,5 @@ const reply = UrlRpcEncoder.prepareRedirectReply(this, status, result); | ||
} | ||
else if (this._responseMethod === ResponseMethod.POST) { | ||
else if (this._responseMethod === exports.ResponseMethod.HTTP_POST) { | ||
// send via form to server | ||
const $form = document.createElement('form'); | ||
$form.setAttribute('method', 'post'); | ||
$form.setAttribute('action', this.returnURL); | ||
$form.setAttribute('style', 'display: none;'); | ||
const $statusInput = document.createElement('input'); | ||
$statusInput.setAttribute('type', 'text'); | ||
$statusInput.setAttribute('name', 'status'); | ||
$statusInput.setAttribute('value', status); | ||
$form.appendChild($statusInput); | ||
const $resultInput = document.createElement('input'); | ||
$resultInput.setAttribute('type', 'text'); | ||
$resultInput.setAttribute('name', 'result'); | ||
$resultInput.setAttribute('value', JSONUtils.stringify(result)); | ||
$form.appendChild($resultInput); | ||
const $idInput = document.createElement('input'); | ||
$idInput.setAttribute('type', 'text'); | ||
$idInput.setAttribute('name', 'rpcId'); | ||
$idInput.setAttribute('value', this.id.toString()); | ||
$form.appendChild($idInput); | ||
document.body.appendChild($form); | ||
const $form = FormRpcEncoder.prepareFormReply(this, status, result); | ||
$form.submit(); | ||
@@ -797,4 +837,4 @@ } | ||
toRequestObject() { | ||
if (this._responseMethod !== ResponseMethod.MESSAGE && !this._returnURL) { | ||
throw new Error('ReturnURL is needed'); | ||
if (this._responseMethod !== exports.ResponseMethod.POST_MESSAGE && !this._returnURL) { | ||
throw new Error('Cannot convert to request object: returnURL is missing'); | ||
} | ||
@@ -804,3 +844,3 @@ return { | ||
data: this._data, | ||
returnURL: this._returnURL || 'post-message', | ||
returnURL: this._returnURL || '', | ||
source: typeof this._source === 'string' ? this._source : null, | ||
@@ -807,0 +847,0 @@ responseMethod: this._responseMethod, |
@@ -165,5 +165,5 @@ /* tslint:disable:no-bitwise */ | ||
(function (ResponseMethod) { | ||
ResponseMethod["POST"] = "post"; | ||
ResponseMethod["URL"] = "url"; | ||
ResponseMethod["MESSAGE"] = "message"; | ||
ResponseMethod["HTTP_POST"] = "http-post"; | ||
ResponseMethod["HTTP_GET"] = "http-get"; | ||
ResponseMethod["POST_MESSAGE"] = "post-message"; | ||
})(ResponseMethod || (ResponseMethod = {})); | ||
@@ -175,3 +175,2 @@ var ResponseStatus; | ||
})(ResponseStatus || (ResponseStatus = {})); | ||
const POSTMESSAGE_RETURN_URL = '<postMessage>'; | ||
@@ -270,15 +269,12 @@ class RequestIdStorage { | ||
fragment.delete('returnURL'); | ||
// guess the responseMethod in messages without one. | ||
let responseMethod; | ||
// Guess the responseMethod in messages without one. | ||
let responseMethod = ResponseMethod.HTTP_GET; | ||
if (fragment.has('responseMethod')) { | ||
responseMethod = fragment.get('responseMethod'); | ||
fragment.delete('responseMethod'); | ||
if (!Object.values(ResponseMethod).includes(responseMethod)) { | ||
throw new Error('Invalid ResponseMethod'); | ||
} | ||
} | ||
else if (returnURL === POSTMESSAGE_RETURN_URL) { | ||
responseMethod = ResponseMethod.MESSAGE; | ||
} | ||
else { | ||
responseMethod = ResponseMethod.URL; | ||
} | ||
const answerByPostMessage = responseMethod === ResponseMethod.MESSAGE | ||
const answerByPostMessage = responseMethod === ResponseMethod.POST_MESSAGE | ||
&& (window.opener || window.parent); | ||
@@ -311,4 +307,4 @@ // Only allow returning to same origin | ||
returnURL, | ||
responseMethod: responseMethod, | ||
source: responseMethod === ResponseMethod.MESSAGE ? (window.opener || window.parent) : null, | ||
responseMethod, | ||
source: responseMethod === ResponseMethod.POST_MESSAGE ? (window.opener || window.parent) : null, | ||
}; | ||
@@ -609,10 +605,47 @@ } | ||
close() { } | ||
call(returnURL, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.URL, null, command, handleHistoryBack, ...args); | ||
call(returnURL, command, optionsOrHandleHistory, ...args) { | ||
if (!optionsOrHandleHistory || typeof optionsOrHandleHistory === 'boolean') { | ||
if (typeof optionsOrHandleHistory === 'boolean') { | ||
console.warn('RedirectRpcClient.call(string, string, boolean, any[]) is deprecated.' | ||
+ ' Use RedirectRpcClient.call(string, string, CallOptions, any[]) with an' | ||
+ ' appropriate CallOptions object instead.'); | ||
} | ||
this._call(returnURL, command, { | ||
responseMethod: ResponseMethod.HTTP_GET, | ||
handleHistoryBack: !!optionsOrHandleHistory, | ||
}, ...args); | ||
} | ||
else if (typeof optionsOrHandleHistory === 'object') { | ||
// Options are given, warn in case they do not make sense. | ||
// ResponseMethod.POST_MESSAGE does not have a single strong use case for redirects and could be omitted | ||
// until at least one of the following use cases is properly implemented. | ||
// TODO: We might want to support those cases in the future. See comment. | ||
if (optionsOrHandleHistory.responseMethod === ResponseMethod.POST_MESSAGE) { | ||
if (!window.opener && !window.parent) { | ||
throw new Error('Window has no opener or parent,' | ||
+ ' responseMethod: ResponseMethod.POST_MESSAGE would fail.'); | ||
} | ||
else { | ||
// Could be mitigated i.e. by having the 'middle' rpcServer communicate to the initial rpcClient | ||
// the new requestId the request is going to be answered by in case of a redirect within a popup. | ||
// In case of opening a popup with url encoded parameters instead of post message, thereby | ||
// circumventing client.call(), the popup opening behaviour could be implemented in _call() to | ||
// re-enable usage of the call() method. | ||
console.warn('Response will skip at least one rpc call, which will result in an unknown response.'); | ||
} | ||
} | ||
this._call(returnURL, command, optionsOrHandleHistory, ...args); | ||
} | ||
} | ||
callAndPOSTResponse(returnURL, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.POST, null, command, handleHistoryBack, ...args); | ||
} | ||
/** | ||
* @deprecated Use call() with an appropriate `CallOptions` object instead. | ||
*/ | ||
callAndSaveLocalState(returnURL, state, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.URL, state, command, handleHistoryBack, ...args); | ||
console.warn('RedirectRpcClient.callAndSaveLocalState() is deprecated. Use RedirectRpcClient.call()' | ||
+ ' with an apropriate CallOptions object instead.'); | ||
this._call(returnURL, command, { | ||
responseMethod: ResponseMethod.HTTP_GET, | ||
state: state ? state : undefined, | ||
handleHistoryBack, | ||
}, ...args); | ||
} | ||
@@ -626,7 +659,8 @@ _receive(response, persistMessage = true) { | ||
} | ||
_call(returnURL, responseMethod, state, command, handleHistoryBack = false, ...args) { | ||
_call(returnURL, command, callOptions, ...args) { | ||
const id = RandomUtils.generateRandomId(); | ||
const responseMethod = callOptions.responseMethod || ResponseMethod.HTTP_GET; | ||
const url = UrlRpcEncoder.prepareRedirectInvocation(this._target, id, returnURL, command, args, responseMethod); | ||
this._waitingRequests.add(id, command, state); | ||
if (handleHistoryBack) { | ||
this._waitingRequests.add(id, command, callOptions.state || null); | ||
if (callOptions.handleHistoryBack) { | ||
/** | ||
@@ -665,2 +699,28 @@ * The rpcBackRejectionId in the history.state is used to detect in the client | ||
class FormRpcEncoder { | ||
static prepareFormReply(state, status, result) { | ||
const $form = document.createElement('form'); | ||
$form.setAttribute('method', 'post'); | ||
$form.setAttribute('action', state.returnURL); | ||
$form.style.display = 'none'; | ||
const $statusInput = document.createElement('input'); | ||
$statusInput.setAttribute('type', 'text'); | ||
$statusInput.setAttribute('name', 'status'); | ||
$statusInput.setAttribute('value', status); | ||
$form.appendChild($statusInput); | ||
const $resultInput = document.createElement('input'); | ||
$resultInput.setAttribute('type', 'text'); | ||
$resultInput.setAttribute('name', 'result'); | ||
$resultInput.setAttribute('value', JSONUtils.stringify(result)); | ||
$form.appendChild($resultInput); | ||
const $idInput = document.createElement('input'); | ||
$idInput.setAttribute('type', 'text'); | ||
$idInput.setAttribute('name', 'rpcId'); | ||
$idInput.setAttribute('value', state.id.toString()); | ||
$form.appendChild($idInput); | ||
document.body.appendChild($form); | ||
return $form; | ||
} | ||
} | ||
class State { | ||
@@ -691,7 +751,7 @@ get id() { | ||
this._id = message.data.id; | ||
this._responseMethod = 'responseMethod' in message | ||
this._responseMethod = 'responseMethod' in message && !!message.responseMethod | ||
? message.responseMethod | ||
: 'source' in message && !('returnURL' in message && message.returnURL === POSTMESSAGE_RETURN_URL) | ||
? ResponseMethod.MESSAGE | ||
: ResponseMethod.URL; | ||
: 'source' in message && !('returnURL' in message) | ||
? ResponseMethod.POST_MESSAGE | ||
: ResponseMethod.HTTP_GET; | ||
this._returnURL = 'returnURL' in message ? message.returnURL : null; | ||
@@ -707,3 +767,3 @@ this._data = message.data; | ||
}; | ||
if (this._responseMethod === ResponseMethod.MESSAGE) { | ||
if (this._responseMethod === ResponseMethod.POST_MESSAGE) { | ||
if (this._source === window.opener) { | ||
@@ -733,3 +793,3 @@ obj.source = 'opener'; | ||
// TODO: Clear waiting request storage? | ||
if (this._responseMethod === ResponseMethod.MESSAGE) { | ||
if (this._responseMethod === ResponseMethod.POST_MESSAGE) { | ||
// Send via postMessage (e.g., popup or url-persisted popup) | ||
@@ -760,3 +820,3 @@ let target; | ||
else if (this._returnURL) { | ||
if (this._responseMethod === ResponseMethod.URL) { | ||
if (this._responseMethod === ResponseMethod.HTTP_GET) { | ||
// Send via top-level navigation | ||
@@ -766,24 +826,5 @@ const reply = UrlRpcEncoder.prepareRedirectReply(this, status, result); | ||
} | ||
else if (this._responseMethod === ResponseMethod.POST) { | ||
else if (this._responseMethod === ResponseMethod.HTTP_POST) { | ||
// send via form to server | ||
const $form = document.createElement('form'); | ||
$form.setAttribute('method', 'post'); | ||
$form.setAttribute('action', this.returnURL); | ||
$form.setAttribute('style', 'display: none;'); | ||
const $statusInput = document.createElement('input'); | ||
$statusInput.setAttribute('type', 'text'); | ||
$statusInput.setAttribute('name', 'status'); | ||
$statusInput.setAttribute('value', status); | ||
$form.appendChild($statusInput); | ||
const $resultInput = document.createElement('input'); | ||
$resultInput.setAttribute('type', 'text'); | ||
$resultInput.setAttribute('name', 'result'); | ||
$resultInput.setAttribute('value', JSONUtils.stringify(result)); | ||
$form.appendChild($resultInput); | ||
const $idInput = document.createElement('input'); | ||
$idInput.setAttribute('type', 'text'); | ||
$idInput.setAttribute('name', 'rpcId'); | ||
$idInput.setAttribute('value', this.id.toString()); | ||
$form.appendChild($idInput); | ||
document.body.appendChild($form); | ||
const $form = FormRpcEncoder.prepareFormReply(this, status, result); | ||
$form.submit(); | ||
@@ -794,4 +835,4 @@ } | ||
toRequestObject() { | ||
if (this._responseMethod !== ResponseMethod.MESSAGE && !this._returnURL) { | ||
throw new Error('ReturnURL is needed'); | ||
if (this._responseMethod !== ResponseMethod.POST_MESSAGE && !this._returnURL) { | ||
throw new Error('Cannot convert to request object: returnURL is missing'); | ||
} | ||
@@ -801,3 +842,3 @@ return { | ||
data: this._data, | ||
returnURL: this._returnURL || 'post-message', | ||
returnURL: this._returnURL || '', | ||
source: typeof this._source === 'string' ? this._source : null, | ||
@@ -914,2 +955,2 @@ responseMethod: this._responseMethod, | ||
export { RpcClient, PostMessageRpcClient, RedirectRpcClient, RpcServer, State, ResponseStatus }; | ||
export { ResponseMethod, RpcClient, PostMessageRpcClient, RedirectRpcClient, RpcServer, State, ResponseStatus }; |
@@ -169,8 +169,7 @@ (function (global, factory) { | ||
var ResponseMethod; | ||
(function (ResponseMethod) { | ||
ResponseMethod["POST"] = "post"; | ||
ResponseMethod["URL"] = "url"; | ||
ResponseMethod["MESSAGE"] = "message"; | ||
})(ResponseMethod || (ResponseMethod = {})); | ||
ResponseMethod["HTTP_POST"] = "http-post"; | ||
ResponseMethod["HTTP_GET"] = "http-get"; | ||
ResponseMethod["POST_MESSAGE"] = "post-message"; | ||
})(exports.ResponseMethod || (exports.ResponseMethod = {})); | ||
(function (ResponseStatus) { | ||
@@ -180,3 +179,2 @@ ResponseStatus["OK"] = "ok"; | ||
})(exports.ResponseStatus || (exports.ResponseStatus = {})); | ||
const POSTMESSAGE_RETURN_URL = '<postMessage>'; | ||
@@ -275,15 +273,12 @@ class RequestIdStorage { | ||
fragment.delete('returnURL'); | ||
// guess the responseMethod in messages without one. | ||
let responseMethod; | ||
// Guess the responseMethod in messages without one. | ||
let responseMethod = exports.ResponseMethod.HTTP_GET; | ||
if (fragment.has('responseMethod')) { | ||
responseMethod = fragment.get('responseMethod'); | ||
fragment.delete('responseMethod'); | ||
if (!Object.values(exports.ResponseMethod).includes(responseMethod)) { | ||
throw new Error('Invalid ResponseMethod'); | ||
} | ||
} | ||
else if (returnURL === POSTMESSAGE_RETURN_URL) { | ||
responseMethod = ResponseMethod.MESSAGE; | ||
} | ||
else { | ||
responseMethod = ResponseMethod.URL; | ||
} | ||
const answerByPostMessage = responseMethod === ResponseMethod.MESSAGE | ||
const answerByPostMessage = responseMethod === exports.ResponseMethod.POST_MESSAGE | ||
&& (window.opener || window.parent); | ||
@@ -316,4 +311,4 @@ // Only allow returning to same origin | ||
returnURL, | ||
responseMethod: responseMethod, | ||
source: responseMethod === ResponseMethod.MESSAGE ? (window.opener || window.parent) : null, | ||
responseMethod, | ||
source: responseMethod === exports.ResponseMethod.POST_MESSAGE ? (window.opener || window.parent) : null, | ||
}; | ||
@@ -614,10 +609,47 @@ } | ||
close() { } | ||
call(returnURL, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.URL, null, command, handleHistoryBack, ...args); | ||
call(returnURL, command, optionsOrHandleHistory, ...args) { | ||
if (!optionsOrHandleHistory || typeof optionsOrHandleHistory === 'boolean') { | ||
if (typeof optionsOrHandleHistory === 'boolean') { | ||
console.warn('RedirectRpcClient.call(string, string, boolean, any[]) is deprecated.' | ||
+ ' Use RedirectRpcClient.call(string, string, CallOptions, any[]) with an' | ||
+ ' appropriate CallOptions object instead.'); | ||
} | ||
this._call(returnURL, command, { | ||
responseMethod: exports.ResponseMethod.HTTP_GET, | ||
handleHistoryBack: !!optionsOrHandleHistory, | ||
}, ...args); | ||
} | ||
else if (typeof optionsOrHandleHistory === 'object') { | ||
// Options are given, warn in case they do not make sense. | ||
// ResponseMethod.POST_MESSAGE does not have a single strong use case for redirects and could be omitted | ||
// until at least one of the following use cases is properly implemented. | ||
// TODO: We might want to support those cases in the future. See comment. | ||
if (optionsOrHandleHistory.responseMethod === exports.ResponseMethod.POST_MESSAGE) { | ||
if (!window.opener && !window.parent) { | ||
throw new Error('Window has no opener or parent,' | ||
+ ' responseMethod: ResponseMethod.POST_MESSAGE would fail.'); | ||
} | ||
else { | ||
// Could be mitigated i.e. by having the 'middle' rpcServer communicate to the initial rpcClient | ||
// the new requestId the request is going to be answered by in case of a redirect within a popup. | ||
// In case of opening a popup with url encoded parameters instead of post message, thereby | ||
// circumventing client.call(), the popup opening behaviour could be implemented in _call() to | ||
// re-enable usage of the call() method. | ||
console.warn('Response will skip at least one rpc call, which will result in an unknown response.'); | ||
} | ||
} | ||
this._call(returnURL, command, optionsOrHandleHistory, ...args); | ||
} | ||
} | ||
callAndPOSTResponse(returnURL, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.POST, null, command, handleHistoryBack, ...args); | ||
} | ||
/** | ||
* @deprecated Use call() with an appropriate `CallOptions` object instead. | ||
*/ | ||
callAndSaveLocalState(returnURL, state, command, handleHistoryBack = false, ...args) { | ||
this._call(returnURL, ResponseMethod.URL, state, command, handleHistoryBack, ...args); | ||
console.warn('RedirectRpcClient.callAndSaveLocalState() is deprecated. Use RedirectRpcClient.call()' | ||
+ ' with an apropriate CallOptions object instead.'); | ||
this._call(returnURL, command, { | ||
responseMethod: exports.ResponseMethod.HTTP_GET, | ||
state: state ? state : undefined, | ||
handleHistoryBack, | ||
}, ...args); | ||
} | ||
@@ -631,7 +663,8 @@ _receive(response, persistMessage = true) { | ||
} | ||
_call(returnURL, responseMethod, state, command, handleHistoryBack = false, ...args) { | ||
_call(returnURL, command, callOptions, ...args) { | ||
const id = RandomUtils.generateRandomId(); | ||
const responseMethod = callOptions.responseMethod || exports.ResponseMethod.HTTP_GET; | ||
const url = UrlRpcEncoder.prepareRedirectInvocation(this._target, id, returnURL, command, args, responseMethod); | ||
this._waitingRequests.add(id, command, state); | ||
if (handleHistoryBack) { | ||
this._waitingRequests.add(id, command, callOptions.state || null); | ||
if (callOptions.handleHistoryBack) { | ||
/** | ||
@@ -670,2 +703,28 @@ * The rpcBackRejectionId in the history.state is used to detect in the client | ||
class FormRpcEncoder { | ||
static prepareFormReply(state, status, result) { | ||
const $form = document.createElement('form'); | ||
$form.setAttribute('method', 'post'); | ||
$form.setAttribute('action', state.returnURL); | ||
$form.style.display = 'none'; | ||
const $statusInput = document.createElement('input'); | ||
$statusInput.setAttribute('type', 'text'); | ||
$statusInput.setAttribute('name', 'status'); | ||
$statusInput.setAttribute('value', status); | ||
$form.appendChild($statusInput); | ||
const $resultInput = document.createElement('input'); | ||
$resultInput.setAttribute('type', 'text'); | ||
$resultInput.setAttribute('name', 'result'); | ||
$resultInput.setAttribute('value', JSONUtils.stringify(result)); | ||
$form.appendChild($resultInput); | ||
const $idInput = document.createElement('input'); | ||
$idInput.setAttribute('type', 'text'); | ||
$idInput.setAttribute('name', 'rpcId'); | ||
$idInput.setAttribute('value', state.id.toString()); | ||
$form.appendChild($idInput); | ||
document.body.appendChild($form); | ||
return $form; | ||
} | ||
} | ||
class State { | ||
@@ -696,7 +755,7 @@ get id() { | ||
this._id = message.data.id; | ||
this._responseMethod = 'responseMethod' in message | ||
this._responseMethod = 'responseMethod' in message && !!message.responseMethod | ||
? message.responseMethod | ||
: 'source' in message && !('returnURL' in message && message.returnURL === POSTMESSAGE_RETURN_URL) | ||
? ResponseMethod.MESSAGE | ||
: ResponseMethod.URL; | ||
: 'source' in message && !('returnURL' in message) | ||
? exports.ResponseMethod.POST_MESSAGE | ||
: exports.ResponseMethod.HTTP_GET; | ||
this._returnURL = 'returnURL' in message ? message.returnURL : null; | ||
@@ -712,3 +771,3 @@ this._data = message.data; | ||
}; | ||
if (this._responseMethod === ResponseMethod.MESSAGE) { | ||
if (this._responseMethod === exports.ResponseMethod.POST_MESSAGE) { | ||
if (this._source === window.opener) { | ||
@@ -738,3 +797,3 @@ obj.source = 'opener'; | ||
// TODO: Clear waiting request storage? | ||
if (this._responseMethod === ResponseMethod.MESSAGE) { | ||
if (this._responseMethod === exports.ResponseMethod.POST_MESSAGE) { | ||
// Send via postMessage (e.g., popup or url-persisted popup) | ||
@@ -765,3 +824,3 @@ let target; | ||
else if (this._returnURL) { | ||
if (this._responseMethod === ResponseMethod.URL) { | ||
if (this._responseMethod === exports.ResponseMethod.HTTP_GET) { | ||
// Send via top-level navigation | ||
@@ -771,24 +830,5 @@ const reply = UrlRpcEncoder.prepareRedirectReply(this, status, result); | ||
} | ||
else if (this._responseMethod === ResponseMethod.POST) { | ||
else if (this._responseMethod === exports.ResponseMethod.HTTP_POST) { | ||
// send via form to server | ||
const $form = document.createElement('form'); | ||
$form.setAttribute('method', 'post'); | ||
$form.setAttribute('action', this.returnURL); | ||
$form.setAttribute('style', 'display: none;'); | ||
const $statusInput = document.createElement('input'); | ||
$statusInput.setAttribute('type', 'text'); | ||
$statusInput.setAttribute('name', 'status'); | ||
$statusInput.setAttribute('value', status); | ||
$form.appendChild($statusInput); | ||
const $resultInput = document.createElement('input'); | ||
$resultInput.setAttribute('type', 'text'); | ||
$resultInput.setAttribute('name', 'result'); | ||
$resultInput.setAttribute('value', JSONUtils.stringify(result)); | ||
$form.appendChild($resultInput); | ||
const $idInput = document.createElement('input'); | ||
$idInput.setAttribute('type', 'text'); | ||
$idInput.setAttribute('name', 'rpcId'); | ||
$idInput.setAttribute('value', this.id.toString()); | ||
$form.appendChild($idInput); | ||
document.body.appendChild($form); | ||
const $form = FormRpcEncoder.prepareFormReply(this, status, result); | ||
$form.submit(); | ||
@@ -799,4 +839,4 @@ } | ||
toRequestObject() { | ||
if (this._responseMethod !== ResponseMethod.MESSAGE && !this._returnURL) { | ||
throw new Error('ReturnURL is needed'); | ||
if (this._responseMethod !== exports.ResponseMethod.POST_MESSAGE && !this._returnURL) { | ||
throw new Error('Cannot convert to request object: returnURL is missing'); | ||
} | ||
@@ -806,3 +846,3 @@ return { | ||
data: this._data, | ||
returnURL: this._returnURL || 'post-message', | ||
returnURL: this._returnURL || '', | ||
source: typeof this._source === 'string' ? this._source : null, | ||
@@ -809,0 +849,0 @@ responseMethod: this._responseMethod, |
export * from './RpcClient'; | ||
export * from './RpcServer'; | ||
export { ResponseMethod } from './Messages'; |
@@ -27,5 +27,5 @@ interface Message { | ||
export declare enum ResponseMethod { | ||
POST = "post", | ||
URL = "url", | ||
MESSAGE = "message" | ||
HTTP_POST = "http-post", | ||
HTTP_GET = "http-get", | ||
POST_MESSAGE = "post-message" | ||
} | ||
@@ -36,3 +36,2 @@ export declare enum ResponseStatus { | ||
} | ||
export declare const POSTMESSAGE_RETURN_URL = "<postMessage>"; | ||
export {}; |
@@ -1,2 +0,2 @@ | ||
import { ResponseMessage } from './Messages'; | ||
import { ResponseMessage, ResponseMethod } from './Messages'; | ||
import { RequestIdStorage } from './RequestIdStorage'; | ||
@@ -42,2 +42,7 @@ import { ObjectType } from './ObjectType'; | ||
export { PostMessageRpcClient }; | ||
export interface CallOptions { | ||
state?: ObjectType; | ||
handleHistoryBack?: boolean; | ||
responseMethod?: ResponseMethod; | ||
} | ||
export declare class RedirectRpcClient extends RpcClient { | ||
@@ -49,4 +54,6 @@ protected readonly _preserveRequests: boolean; | ||
close(): void; | ||
call(returnURL: string, command: string, handleHistoryBack?: boolean, ...args: any[]): void; | ||
callAndPOSTResponse(returnURL: string, command: string, handleHistoryBack?: boolean, ...args: any[]): void; | ||
call(returnURL: string, command: string, optionsOrHandleHistory?: CallOptions | boolean | null, ...args: any[]): void; | ||
/** | ||
* @deprecated Use call() with an appropriate `CallOptions` object instead. | ||
*/ | ||
callAndSaveLocalState(returnURL: string, state: ObjectType | null, command: string, handleHistoryBack?: boolean, ...args: any[]): void; | ||
@@ -53,0 +60,0 @@ protected _receive(response: ResponseMessage, persistMessage?: boolean): boolean; |
{ | ||
"name": "@nimiq/rpc", | ||
"version": "0.4.0-beta.2", | ||
"version": "0.4.0", | ||
"description": "RPC Client/Server", | ||
@@ -17,2 +17,3 @@ "repository": { | ||
"test": "jest", | ||
"dev": "tsc-watch --onSuccess \"rollup -c\" --onFirstSuccess \"node demo/server.js\"", | ||
"build": "tsc && rollup -c" | ||
@@ -28,6 +29,11 @@ }, | ||
"@types/jest": "^23.3.1", | ||
"body-parser": "^1.19.0", | ||
"express": "^4.17.1", | ||
"jest": "^23.4.2", | ||
"rollup": "^0.64.0", | ||
"serve-index": "^1.9.1", | ||
"serve-static": "^1.14.1", | ||
"ts-jest": "^23.1.2", | ||
"ts-loader": "^4.4.2", | ||
"tsc-watch": "^4.0.0", | ||
"tslint": "^5.11.0", | ||
@@ -34,0 +40,0 @@ "typescript": "^3.0.1" |
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
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
139202
19
3011
12