PHP WebShell
Текущая директория: /usr/lib/node_modules/bitgo/node_modules/smoldot/dist/mjs/internals
Просмотр файла: remote-instance.js
// Smoldot
// Copyright (C) 2023 Pierre Krieger
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Contains an implementation of `Instance` that is remote.
//
// In terms of implementation, the logic is pretty straight forward, with two exceptions:
//
// - Connections are tracked on both sides in order to handle situations where one side has
// reset a connection or stream but the other is sending messages about this connection/stream.
//
// - JSON-RPC requests aren't sent back lazily one at a time. Instead, the client indicates that it
// is ready to accept more JSON-RPC responses, after which the server can send responses at any
// time and the client queues them locally.
import * as instance from './local-instance.js';
// Implementation note: it is unclear even in the official specification
// (https://html.spec.whatwg.org/multipage/web-messaging.html) whether both sides of a
// `MessagePort` should be closed, or if one is enough.
//
// It has been noticed that doing `port.postMessage(...); port.close();` doesn't deliver the
// message on Firefox (but it does on Chrome). The code below takes note of this, and only closes
// a port upon *receiving* the last possible message. It therefore assumes that closing only one
// side is enough. It is unclear whether this causes any memory leak.
export function connectToInstanceServer(config) {
return __awaiter(this, void 0, void 0, function* () {
// Send the wasm module and configuration to the server.
// Note that we await the `wasmModule` `Promise` here.
// If instead we used `wasmModule.then(...)`, the user would be able to start using the
// returned instance before the module has been sent to the server.
// In order to simplify the implementation, we create new ports and send one of them to
// the server. This is necessary so that the server can pause receiving messages while the
// instance is being initialized.
const { port1: portToServer, port2: serverToClient } = new MessageChannel();
const initialPort = config.portToServer;
const initialMessage = {
wasmModule: yield config.wasmModule,
serverToClient,
maxLogLevel: config.maxLogLevel,
cpuRateLimit: config.cpuRateLimit,
forbidWs: config.forbidWs,
forbidWss: config.forbidWss,
forbidNonLocalWs: config.forbidNonLocalWs,
forbidTcp: config.forbidTcp,
forbidWebRtc: config.forbidWebRtc
};
initialPort.postMessage(initialMessage, [serverToClient]);
// Note that closing `initialPort` here will lead to the message not being delivered on Firefox
// for some reason. It is therefore closed only on shutdown.
const state = {
jsonRpcResponses: new Map(),
connections: new Map(),
};
portToServer.onmessage = (messageEvent) => {
const message = messageEvent.data;
// Update some local state.
switch (message.ty) {
case "wasm-panic":
case "executor-shutdown": {
portToServer.close();
initialPort.close();
break;
}
case "add-chain-result": {
if (message.success) {
state.jsonRpcResponses.set(message.chainId, new Array);
const moreAccepted = { ty: "accept-more-json-rpc-answers", chainId: message.chainId };
for (let i = 0; i < 10; ++i)
portToServer.postMessage(moreAccepted);
}
break;
}
case "new-connection": {
state.connections.set(message.connectionId, new Set());
break;
}
case "connection-reset": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
state.connections.delete(message.connectionId);
break;
}
case "connection-stream-open": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
break;
}
case "connection-stream-reset": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
// The stream might have been reset locally in the past.
if (!state.connections.get(message.connectionId).has(message.streamId))
return;
break;
}
case "stream-send": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
// The stream might have been reset locally in the past.
if (message.streamId && !state.connections.get(message.connectionId).has(message.streamId))
return;
break;
}
case "stream-send-close": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
// The stream might have been reset locally in the past.
if (message.streamId && !state.connections.get(message.connectionId).has(message.streamId))
return;
break;
}
case "json-rpc-response": {
const queue = state.jsonRpcResponses.get(message.chainId);
// The chain might have been removed locally in the past.
if (queue) {
queue.push(message.response);
config.eventCallback({ ty: "json-rpc-responses-non-empty", chainId: message.chainId });
}
return;
}
}
config.eventCallback(message);
};
return {
addChain(chainSpec, databaseContent, potentialRelayChains, disableJsonRpc, jsonRpcMaxPendingRequests, jsonRpcMaxSubscriptions) {
return __awaiter(this, void 0, void 0, function* () {
const msg = { ty: "add-chain", chainSpec, databaseContent, potentialRelayChains, disableJsonRpc, jsonRpcMaxPendingRequests, jsonRpcMaxSubscriptions };
portToServer.postMessage(msg);
});
},
removeChain(chainId) {
state.jsonRpcResponses.delete(chainId);
const msg = { ty: "remove-chain", chainId };
portToServer.postMessage(msg);
},
request(request, chainId) {
const msg = { ty: "request", chainId, request };
portToServer.postMessage(msg);
return 0; // TODO: wrong return value
},
peekJsonRpcResponse(chainId) {
const item = state.jsonRpcResponses.get(chainId).shift();
if (!item)
return null;
const msg = { ty: "accept-more-json-rpc-answers", chainId };
portToServer.postMessage(msg);
return item;
},
shutdownExecutor() {
const msg = { ty: "shutdown" };
portToServer.postMessage(msg);
},
connectionReset(connectionId, message) {
state.connections.delete(connectionId);
const msg = { ty: "connection-reset", connectionId, message };
portToServer.postMessage(msg);
},
connectionMultiStreamSetHandshakeInfo(connectionId, info) {
const msg = { ty: "connection-multistream-set-info", connectionId, info };
portToServer.postMessage(msg);
},
streamMessage(connectionId, message, streamId) {
const msg = { ty: "stream-message", connectionId, message, streamId };
portToServer.postMessage(msg);
},
streamOpened(connectionId, streamId, direction) {
state.connections.get(connectionId).add(streamId);
const msg = { ty: "stream-opened", connectionId, streamId, direction };
portToServer.postMessage(msg);
},
streamWritableBytes(connectionId, numExtra, streamId) {
const msg = { ty: "stream-writable-bytes", connectionId, numExtra, streamId };
portToServer.postMessage(msg);
},
streamReset(connectionId, streamId, message) {
state.connections.get(connectionId).delete(streamId);
const msg = { ty: "stream-reset", connectionId, streamId, message };
portToServer.postMessage(msg);
},
};
});
}
/**
* Returns a `Promise` that resolves when the instance shuts down. Since the function is also
* an asynchronous function, the actual return type is `Promise<Promise<void>>`. That is, the
* outer `Promise` yields once the instance starts, and the inner `Promise` yields once the
* instance shuts down.
*/
export function startInstanceServer(config, initPortToClient) {
return __awaiter(this, void 0, void 0, function* () {
const { serverToClient: portToClient, wasmModule, maxLogLevel, cpuRateLimit, forbidTcp, forbidWs, forbidWss, forbidNonLocalWs, forbidWebRtc } = yield new Promise((resolve) => {
initPortToClient.onmessage = (event) => resolve(event.data);
});
initPortToClient.close();
const state = {
instance: null,
connections: new Map(),
acceptedJsonRpcResponses: new Map(),
};
const eventsCallback = (event) => {
switch (event.ty) {
case "add-chain-result": {
if (event.success) {
state.acceptedJsonRpcResponses.set(event.chainId, 0);
}
break;
}
case "executor-shutdown":
case "wasm-panic": {
if (state.onExecutorShutdownOrWasmPanic) {
const cb = state.onExecutorShutdownOrWasmPanic;
delete state.onExecutorShutdownOrWasmPanic;
cb();
}
break;
}
case "json-rpc-responses-non-empty": {
// Process this event asynchronously because we can't call into `instance`
// from within the events callback itself.
// TODO: do better than setTimeout?
setTimeout(() => {
const numAccepted = state.acceptedJsonRpcResponses.get(event.chainId);
if (numAccepted === undefined || numAccepted === 0)
return;
const response = state.instance.peekJsonRpcResponse(event.chainId);
if (response) {
state.acceptedJsonRpcResponses.set(event.chainId, numAccepted - 1);
const msg = { ty: "json-rpc-response", chainId: event.chainId, response };
portToClient.postMessage(msg);
}
}, 0);
return;
}
case "new-connection": {
state.connections.set(event.connectionId, new Set());
break;
}
case "connection-reset": {
state.connections.delete(event.connectionId);
break;
}
case "connection-stream-reset": {
state.connections.get(event.connectionId).delete(event.streamId);
break;
}
}
const ev = event;
portToClient.postMessage(ev);
};
// We create the `Promise` ahead of time in order to potentially catch potential `wasm-panic`
// events as early as during initialization.
const execFinishedPromise = new Promise((resolve) => state.onExecutorShutdownOrWasmPanic = resolve);
state.instance = yield instance.startLocalInstance(Object.assign({ forbidTcp,
forbidWs,
forbidNonLocalWs,
forbidWss,
forbidWebRtc,
cpuRateLimit,
maxLogLevel }, config), wasmModule, eventsCallback);
portToClient.onmessage = (messageEvent) => {
const message = messageEvent.data;
switch (message.ty) {
case "add-chain": {
state.instance.addChain(message.chainSpec, message.databaseContent, message.potentialRelayChains, message.disableJsonRpc, message.jsonRpcMaxPendingRequests, message.jsonRpcMaxSubscriptions);
break;
}
case "remove-chain": {
state.acceptedJsonRpcResponses.delete(message.chainId);
state.instance.removeChain(message.chainId);
break;
}
case "request": {
state.instance.request(message.request, message.chainId); // TODO: return value unused
break;
}
case "accept-more-json-rpc-answers": {
const response = state.instance.peekJsonRpcResponse(message.chainId);
if (response) {
const msg = { ty: "json-rpc-response", chainId: message.chainId, response };
portToClient.postMessage(msg);
}
else {
const numAccepted = state.acceptedJsonRpcResponses.get(message.chainId);
state.acceptedJsonRpcResponses.set(message.chainId, numAccepted + 1);
}
break;
}
case "shutdown": {
state.instance.shutdownExecutor();
break;
}
case "connection-reset": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
state.instance.connectionReset(message.connectionId, message.message);
break;
}
case "connection-multistream-set-info": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
state.instance.connectionMultiStreamSetHandshakeInfo(message.connectionId, message.info);
break;
}
case "stream-message": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
// The stream might have been reset locally in the past.
if (message.streamId !== undefined && !state.connections.get(message.connectionId).has(message.streamId))
return;
state.instance.streamMessage(message.connectionId, message.message, message.streamId);
break;
}
case "stream-opened": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
state.connections.get(message.connectionId).add(message.streamId);
state.instance.streamOpened(message.connectionId, message.streamId, message.direction);
break;
}
case "stream-writable-bytes": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
// The stream might have been reset locally in the past.
if (message.streamId !== undefined && !state.connections.get(message.connectionId).has(message.streamId))
return;
state.instance.streamWritableBytes(message.connectionId, message.numExtra, message.streamId);
break;
}
case "stream-reset": {
// The connection might have been reset locally in the past.
if (!state.connections.has(message.connectionId))
return;
// The stream might have been reset locally in the past.
if (!state.connections.get(message.connectionId).has(message.streamId))
return;
state.connections.get(message.connectionId).delete(message.streamId);
state.instance.streamReset(message.connectionId, message.streamId, message.message);
break;
}
}
};
return execFinishedPromise;
});
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!