aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-cli
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2023-01-26 12:48:35 +0100
committerFlorian Dold <florian@dold.me>2023-01-26 12:48:35 +0100
commit3cc26d00f85209c804d9f356598c0f749367ea74 (patch)
tree7b9d175cbc0de4312c98d80771fbda27f3fc8497 /packages/taler-wallet-cli
parentc57ba4c0cea133059ac30eae3c7e527886240059 (diff)
downloadwallet-core-3cc26d00f85209c804d9f356598c0f749367ea74.tar.xz
put taler wallet RPC into taler-util, make it cross-platform
Diffstat (limited to 'packages/taler-wallet-cli')
-rw-r--r--packages/taler-wallet-cli/package.json2
-rw-r--r--packages/taler-wallet-cli/src/index.ts8
-rw-r--r--packages/taler-wallet-cli/src/rpc.ts266
-rw-r--r--packages/taler-wallet-cli/tsconfig.json2
4 files changed, 7 insertions, 271 deletions
diff --git a/packages/taler-wallet-cli/package.json b/packages/taler-wallet-cli/package.json
index 1e0586afd..2caf3487f 100644
--- a/packages/taler-wallet-cli/package.json
+++ b/packages/taler-wallet-cli/package.json
@@ -42,7 +42,7 @@
"rollup-plugin-sourcemaps": "^0.6.3",
"rollup-plugin-terser": "^7.0.2",
"typedoc": "^0.23.16",
- "typescript": "^4.8.4"
+ "typescript": "^4.9.4"
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:*",
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 14000aefd..67d0e3784 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -24,7 +24,6 @@ import {
clk,
codecForList,
codecForString,
- CoreApiMessageEnvelope,
CoreApiRequestEnvelope,
CoreApiResponse,
decodeCrock,
@@ -45,7 +44,6 @@ import {
openPromise,
TalerCryptoInterface,
TalerError,
- WalletCoreResponseType,
} from "@gnu-taler/taler-wallet-core";
import {
CryptoDispatcher,
@@ -64,7 +62,11 @@ import {
} from "@gnu-taler/taler-wallet-core";
import fs from "fs";
import os from "os";
-import { connectRpc, JsonMessage, runRpcServer } from "./rpc.js";
+import {
+ connectRpc,
+ JsonMessage,
+ runRpcServer,
+} from "@gnu-taler/taler-util/twrpc";
// This module also serves as the entry point for the crypto
// thread worker, and thus must expose these two handlers.
diff --git a/packages/taler-wallet-cli/src/rpc.ts b/packages/taler-wallet-cli/src/rpc.ts
deleted file mode 100644
index 8070b96c5..000000000
--- a/packages/taler-wallet-cli/src/rpc.ts
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2023 Taler Systems S.A.
-
- GNU Taler 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, or (at your option) any later version.
-
- GNU Taler 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
- GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Implementation for the wallet-core IPC protocol.
- *
- * Currently the protcol is completely unstable and only used internally
- * by the wallet for testing purposes.
- */
-
-/**
- * Imports.
- */
-import * as net from "node:net";
-import * as fs from "node:fs";
-import { bytesToString, Logger, typedArrayConcat } from "@gnu-taler/taler-util";
-
-const logger = new Logger("rpc.ts");
-
-export type JsonMessage =
- | string
- | number
- | boolean
- | null
- | JsonMessage[]
- | { [key: string]: JsonMessage };
-
-export interface RpcServerClientHandlers {
- onMessage(msg: JsonMessage): void;
- onDisconnect(): void;
-}
-
-export interface RpcServerClient {
- sendResponse(message: JsonMessage): void;
-}
-
-export interface RpcServerArgs {
- socketFilename: string;
- onConnect(client: RpcServerClient): RpcServerClientHandlers;
-}
-
-export interface RpcClientServerConnection {
- sendMessage(m: JsonMessage): void;
- close(): void;
-}
-
-export interface RpcConnectArgs<T> {
- socketFilename: string;
- onEstablished(connection: RpcClientServerConnection): {
- result: T;
- onDisconnect(): void;
- onMessage(m: JsonMessage): void;
- };
-}
-
-export interface ReadLinewiseArgs {
- onLine(lineData: Uint8Array): void;
- sock: net.Socket;
-}
-
-function readStreamLinewise(args: ReadLinewiseArgs): void {
- let chunks: Uint8Array[] = [];
- args.sock.on("data", (buf: Uint8Array) => {
- logger.info(`received ${buf.length} bytes`);
- // Process all newlines in the newly received buffer
- while (1) {
- const newlineIdx = buf.indexOf("\n".charCodeAt(0));
- if (newlineIdx >= 0) {
- let left = buf.subarray(0, newlineIdx + 1);
- let right = buf.subarray(newlineIdx + 1);
- chunks.push(left);
- const line = typedArrayConcat(chunks);
- args.onLine(line);
- chunks = [];
- buf = right;
- } else {
- chunks.push(buf);
- break;
- }
- }
- });
-}
-
-export async function connectRpc<T>(args: RpcConnectArgs<T>): Promise<T> {
- let sockFilename = args.socketFilename;
- return new Promise((resolve, reject) => {
- const client = net.createConnection(sockFilename);
- client.on("connect", () => {
- let parsingBody: string | undefined = undefined;
- let bodyChunks: string[] = [];
-
- logger.info("connected!");
- client.write("%hello-from-client\n");
- const res = args.onEstablished({
- sendMessage(m) {
- client.write("%request\n");
- client.write(JSON.stringify(m));
- client.write("\n");
- client.write("%end\n");
- },
- close() {
- client.destroy();
- },
- });
- readStreamLinewise({
- sock: client,
- onLine(line) {
- const lineStr = bytesToString(line);
- logger.info(`got line from server: ${lineStr}`);
- // Are we currently parsing the body of a request?
- if (!parsingBody) {
- const strippedLine = lineStr.trim();
- if (strippedLine == "%message") {
- logger.info("got message start");
- parsingBody = "message";
- } else if (strippedLine == "%hello-from-server") {
- logger.info("got hello from server");
- } else if (strippedLine.startsWith("%error:")) {
- logger.info("got error from server, disconnecting");
- client.end();
- res.onDisconnect();
- } else {
- logger.info("got unknown request");
- client.write("%error: invalid message\n");
- client.end();
- }
- } else if (parsingBody == "message") {
- const strippedLine = lineStr.trim();
- if (strippedLine == "%end") {
- logger.info("finished request");
- let req = bodyChunks.join("");
- let reqJson: any = undefined;
- try {
- reqJson = JSON.parse(req);
- } catch (e) {
- logger.warn("JSON request was invalid");
- }
- if (reqJson !== undefined) {
- logger.info(`request: ${req}`);
- res.onMessage(reqJson);
- } else {
- client.write("%error: invalid JSON");
- client.end();
- }
- bodyChunks = [];
- } else {
- bodyChunks.push(lineStr);
- }
- } else {
- logger.info("invalid parser state");
- client.write("%error: internal error\n");
- client.end();
- }
- },
- });
- client.on("close", () => {
- res.onDisconnect();
- });
- client.on("data", () => {});
- resolve(res.result);
- });
- });
-}
-
-export async function runRpcServer(args: RpcServerArgs): Promise<void> {
- let sockFilename = args.socketFilename;
- try {
- fs.unlinkSync(sockFilename);
- } catch (e) {
- // Do nothing!
- }
- return new Promise((resolve, reject) => {
- const server = net.createServer((sock) => {
- // Are we currently parsing the body of a request?
- let parsingBody: string | undefined = undefined;
- let bodyChunks: string[] = [];
-
- logger.info("got new connection");
- sock.write("%hello-from-server\n");
- const handlers = args.onConnect({
- sendResponse(message) {
- sock.write("%message\n");
- sock.write(JSON.stringify(message));
- sock.write("\n");
- sock.write("%end\n");
- },
- });
-
- sock.on("error", (err) => {
- logger.info(`connection error: ${err}`);
- });
-
- function processLine(line: Uint8Array) {
- const lineStr = bytesToString(line);
- logger.info(`got line: ${lineStr}`);
- if (!parsingBody) {
- const strippedLine = lineStr.trim();
- if (strippedLine == "%request") {
- logger.info("got request start");
- parsingBody = "request";
- } else if (strippedLine === "%hello-from-client") {
- console.log("got hello from client");
- } else if (strippedLine.startsWith("%error:")) {
- console.log("got error from client");
- sock.end();
- handlers.onDisconnect();
- } else {
- logger.info("got unknown request");
- sock.write("%error: invalid request\n");
- sock.end();
- }
- } else if (parsingBody == "request") {
- const strippedLine = lineStr.trim();
- if (strippedLine == "%end") {
- logger.info("finished request");
- let req = bodyChunks.join("");
- let reqJson: any = undefined;
- try {
- reqJson = JSON.parse(req);
- } catch (e) {
- logger.warn("JSON request was invalid");
- }
- if (reqJson !== undefined) {
- logger.info(`request: ${req}`);
- handlers.onMessage(reqJson);
- } else {
- sock.write("%error: invalid JSON");
- sock.end();
- }
- bodyChunks = [];
- } else {
- bodyChunks.push(lineStr);
- }
- } else {
- logger.info("invalid parser state");
- sock.write("%error: internal error\n");
- sock.end();
- }
- }
-
- readStreamLinewise({
- sock,
- onLine: processLine,
- });
-
- sock.on("close", (hadError: boolean) => {
- logger.info(`connection closed, hadError=${hadError}`);
- handlers.onDisconnect();
- });
- });
- server.listen("wallet-core.sock");
- });
-}
diff --git a/packages/taler-wallet-cli/tsconfig.json b/packages/taler-wallet-cli/tsconfig.json
index 447d3f946..100339e43 100644
--- a/packages/taler-wallet-cli/tsconfig.json
+++ b/packages/taler-wallet-cli/tsconfig.json
@@ -21,7 +21,7 @@
"baseUrl": "./src",
"typeRoots": ["./node_modules/@types"]
},
- "include": ["src/**/*"],
+ "include": ["src/**/*", "../taler-util/src/twrpc.ts"],
"references": [
{
"path": "../taler-wallet-core/"