PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@polkadot/rpc-provider/substrate-connect
Просмотр файла: Health.js
import { stringify } from '@polkadot/util';
/*
* Creates a new health checker.
*
* The role of the health checker is to report to the user the health of a smoldot chain.
*
* In order to use it, start by creating a health checker, and call `setSendJsonRpc` to set the
* way to send a JSON-RPC request to a chain. The health checker is disabled by default. Use
* `start()` in order to start the health checks. The `start()` function must be passed a callback called
* when an update to the health of the node is available.
*
* In order to send a JSON-RPC request to the chain, you **must** use the `sendJsonRpc` function
* of the health checker. The health checker rewrites the `id` of the requests it receives.
*
* When the chain send a JSON-RPC response, it must be passed to `responsePassThrough()`. This
* function intercepts the responses destined to the requests that have been emitted by the health
* checker and returns `null`. If the response doesn't concern the health checker, the response is
* simply returned by the function.
*
* # How it works
*
* The health checker periodically calls the `system_health` JSON-RPC call in order to determine
* the health of the chain.
*
* In addition to this, as long as the health check reports that `isSyncing` is `true`, the
* health checker also maintains a subscription to new best blocks using `chain_subscribeNewHeads`.
* Whenever a new block is notified, a health check is performed immediately in order to determine
* whether `isSyncing` has changed to `false`.
*
* Thanks to this subscription, the latency of the report of the switch from `isSyncing: true` to
* `isSyncing: false` is very low.
*
*/
export function healthChecker() {
// `null` if health checker is not started.
let checker = null;
let sendJsonRpc = null;
return {
responsePassThrough: (jsonRpcResponse) => {
if (checker === null) {
return jsonRpcResponse;
}
return checker.responsePassThrough(jsonRpcResponse);
},
sendJsonRpc: (request) => {
if (!sendJsonRpc) {
throw new Error('setSendJsonRpc must be called before sending requests');
}
if (checker === null) {
sendJsonRpc(request);
}
else {
checker.sendJsonRpc(request);
}
},
setSendJsonRpc: (cb) => {
sendJsonRpc = cb;
},
start: (healthCallback) => {
if (checker !== null) {
throw new Error("Can't start the health checker multiple times in parallel");
}
else if (!sendJsonRpc) {
throw new Error('setSendJsonRpc must be called before starting the health checks');
}
checker = new InnerChecker(healthCallback, sendJsonRpc);
checker.update(true);
},
stop: () => {
if (checker === null) {
return;
} // Already stopped.
checker.destroy();
checker = null;
}
};
}
class InnerChecker {
__internal__healthCallback;
__internal__currentHealthCheckId = null;
__internal__currentHealthTimeout = null;
__internal__currentSubunsubRequestId = null;
__internal__currentSubscriptionId = null;
__internal__requestToSmoldot;
__internal__isSyncing = false;
__internal__nextRequestId = 0;
constructor(healthCallback, requestToSmoldot) {
this.__internal__healthCallback = healthCallback;
this.__internal__requestToSmoldot = (request) => requestToSmoldot(stringify(request));
}
sendJsonRpc = (request) => {
// Replace the `id` in the request to prefix the request ID with `extern:`.
let parsedRequest;
try {
parsedRequest = JSON.parse(request);
}
catch {
return;
}
if (parsedRequest.id) {
const newId = 'extern:' + stringify(parsedRequest.id);
parsedRequest.id = newId;
}
this.__internal__requestToSmoldot(parsedRequest);
};
responsePassThrough = (jsonRpcResponse) => {
let parsedResponse;
try {
parsedResponse = JSON.parse(jsonRpcResponse);
}
catch {
return jsonRpcResponse;
}
// Check whether response is a response to `system_health`.
if (parsedResponse.id && this.__internal__currentHealthCheckId === parsedResponse.id) {
this.__internal__currentHealthCheckId = null;
// Check whether query was successful. It is possible for queries to fail for
// various reasons, such as the client being overloaded.
if (!parsedResponse.result) {
this.update(false);
return null;
}
this.__internal__healthCallback(parsedResponse.result);
this.__internal__isSyncing = parsedResponse.result.isSyncing;
this.update(false);
return null;
}
// Check whether response is a response to the subscription or unsubscription.
if (parsedResponse.id &&
this.__internal__currentSubunsubRequestId === parsedResponse.id) {
this.__internal__currentSubunsubRequestId = null;
// Check whether query was successful. It is possible for queries to fail for
// various reasons, such as the client being overloaded.
if (!parsedResponse.result) {
this.update(false);
return null;
}
if (this.__internal__currentSubscriptionId) {
this.__internal__currentSubscriptionId = null;
}
else {
this.__internal__currentSubscriptionId = parsedResponse.result;
}
this.update(false);
return null;
}
// Check whether response is a notification to a subscription.
if (parsedResponse.params &&
this.__internal__currentSubscriptionId &&
parsedResponse.params.subscription === this.__internal__currentSubscriptionId) {
// Note that after a successful subscription, a notification containing
// the current best block is always returned. Considering that a
// subscription is performed in response to a health check, calling
// `startHealthCheck()` here will lead to a second health check.
// It might seem redundant to perform two health checks in a quick
// succession, but doing so doesn't lead to any problem, and it is
// actually possible for the health to have changed in between as the
// current best block might have been updated during the subscription
// request.
this.update(true);
return null;
}
// Response doesn't concern us.
if (parsedResponse.id) {
const id = parsedResponse.id;
// Need to remove the `extern:` prefix.
if (!id.startsWith('extern:')) {
throw new Error('State inconsistency in health checker');
}
const newId = JSON.parse(id.slice('extern:'.length));
parsedResponse.id = newId;
}
return stringify(parsedResponse);
};
update = (startNow) => {
// If `startNow`, clear `#currentHealthTimeout` so that it is set below.
if (startNow && this.__internal__currentHealthTimeout) {
clearTimeout(this.__internal__currentHealthTimeout);
this.__internal__currentHealthTimeout = null;
}
if (!this.__internal__currentHealthTimeout) {
const startHealthRequest = () => {
this.__internal__currentHealthTimeout = null;
// No matter what, don't start a health request if there is already one in progress.
// This is sane to do because receiving a response to a health request calls `update()`.
if (this.__internal__currentHealthCheckId) {
return;
}
// Actual request starting.
this.__internal__currentHealthCheckId = `health-checker:${this.__internal__nextRequestId}`;
this.__internal__nextRequestId += 1;
this.__internal__requestToSmoldot({
id: this.__internal__currentHealthCheckId,
jsonrpc: '2.0',
method: 'system_health',
params: []
});
};
if (startNow) {
startHealthRequest();
}
else {
this.__internal__currentHealthTimeout = setTimeout(startHealthRequest, 1000);
}
}
if (this.__internal__isSyncing &&
!this.__internal__currentSubscriptionId &&
!this.__internal__currentSubunsubRequestId) {
this.startSubscription();
}
if (!this.__internal__isSyncing &&
this.__internal__currentSubscriptionId &&
!this.__internal__currentSubunsubRequestId) {
this.endSubscription();
}
};
startSubscription = () => {
if (this.__internal__currentSubunsubRequestId || this.__internal__currentSubscriptionId) {
throw new Error('Internal error in health checker');
}
this.__internal__currentSubunsubRequestId = `health-checker:${this.__internal__nextRequestId}`;
this.__internal__nextRequestId += 1;
this.__internal__requestToSmoldot({
id: this.__internal__currentSubunsubRequestId,
jsonrpc: '2.0',
method: 'chain_subscribeNewHeads',
params: []
});
};
endSubscription = () => {
if (this.__internal__currentSubunsubRequestId || !this.__internal__currentSubscriptionId) {
throw new Error('Internal error in health checker');
}
this.__internal__currentSubunsubRequestId = `health-checker:${this.__internal__nextRequestId}`;
this.__internal__nextRequestId += 1;
this.__internal__requestToSmoldot({
id: this.__internal__currentSubunsubRequestId,
jsonrpc: '2.0',
method: 'chain_unsubscribeNewHeads',
params: [this.__internal__currentSubscriptionId]
});
};
destroy = () => {
if (this.__internal__currentHealthTimeout) {
clearTimeout(this.__internal__currentHealthTimeout);
this.__internal__currentHealthTimeout = null;
}
};
}
export class HealthCheckError extends Error {
__internal__cause;
getCause() {
return this.__internal__cause;
}
constructor(response, message = 'Got error response asking for system health') {
super(message);
this.__internal__cause = response;
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!