From 13a02c56ecbb18d0cf91ec6db24922815fbba725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Mon, 13 Dec 2021 12:13:50 +0100 Subject: [PATCH] [ivette/server] added client-socket --- ivette/src/frama-c/client_socket.ts | 193 ++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 ivette/src/frama-c/client_socket.ts diff --git a/ivette/src/frama-c/client_socket.ts b/ivette/src/frama-c/client_socket.ts new file mode 100644 index 00000000000..23777c7a5f2 --- /dev/null +++ b/ivette/src/frama-c/client_socket.ts @@ -0,0 +1,193 @@ +/* ************************************************************************ */ +/* */ +/* This file is part of Frama-C. */ +/* */ +/* Copyright (C) 2007-2021 */ +/* CEA (Commissariat à l'énergie atomique et aux énergies */ +/* alternatives) */ +/* */ +/* you can redistribute it and/or modify it under the terms of the GNU */ +/* Lesser General Public License as published by the Free Software */ +/* Foundation, version 2.1. */ +/* */ +/* It 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 Lesser General Public License for more details. */ +/* */ +/* See the GNU Lesser General Public License version 2.1 */ +/* for more details (enclosed in the file licenses/LGPLv2.1). */ +/* */ +/* ************************************************************************ */ + +import Net from 'net'; +import Emitter from 'events'; +import { json } from 'dome/data/json'; +import { Client } from './client'; + +// -------------------------------------------------------------------------- +// --- Frama-C Server API +// -------------------------------------------------------------------------- + +class SocketClient implements Client { + + constructor() { } + + events = new Emitter(); + running = false; + socket: Net.Socket | undefined; + queue: json[] = []; + buffer: Buffer = Buffer.from(''); + + /** Connection */ + connect(sockaddr: string): void { + if (this.socket) { + this.socket.destroy(); + } + this.socket = Net.createConnection(sockaddr, () => { + console.log('Socket server connected'); + this.running = true; + this._flush(); + }); + // Using Buffer data encoding at this level + this.socket.on('end', () => this.disconnect()); + this.socket.on('data', (data: Buffer) => this._receive(data)); + this.socket.on('error', (err: Error) => { + console.warn('Socket error', err); + }); + } + + disconnect(): void { + this.queue = []; + if (this.socket) { + console.log('Socket disconnected'); + this.socket.destroy(); + this.socket = undefined; + } + } + + /** Send Request */ + send(kind: string, id: string, request: string, data: any): void { + this.queue.push({ cmd: kind, id, request, data }); + this._flush(); + } + + /** Signal ON */ + sigOn(id: string): void { this.queue.push({ cmd: 'SIGON', id }); this._flush(); } + + /** Signal ON */ + sigOff(id: string): void { this.queue.push({ cmd: 'SIGOFF', id }); this._flush(); } + + /** Kill Request */ + kill(id: string): void { this.queue.push({ cmd: 'KILL', id }); this._flush(); } + + /** Polling */ + poll(): void { this.queue.push('POLL'); this._flush(); } + + /** Shutdown the server */ + shutdown(): void { this.queue.push('SHUTDOWN'); this._flush(); } + + /** Request data callback */ + onData(callback: (id: string, data: json) => void): void { + this.events.on('DATA', callback); + } + + /** Rejected request callback */ + onRejected(callback: (id: string, err: string) => void): void { + this.events.on('REJECT', callback); + } + + /** Request error callback */ + onError(callback: (msg: string) => void): void { + this.events.on('ERROR', callback); + } + + /** Killed request callback */ + onKilled(callback: (id: string) => void): void { + this.events.on('KILL', callback); + } + + /** Signal callback */ + onSignal(callback: (id: string) => void): void { + this.events.on('SIGNAL', callback); + } + + /** Signal callback */ + onIdle(callback: () => void): void { + this.events.on('IDLE', callback); + } + + // -------------------------------------------------------------------------- + // --- Low-Level Management + // -------------------------------------------------------------------------- + + _flush() { + if (this.running) { + this.queue.forEach((cmd) => { + this._send(Buffer.from(JSON.stringify(cmd), 'utf8')); + }); + this.queue = []; + } + } + + _send(data: Buffer) { + const s = this.socket; + if (s) { + const len = data.length; + const hex = Number(len).toString(16).toUpperCase(); + const padding = '0000000000000000'; + const header = + len < 0xFFF ? 'S' + padding.substring(hex.length, 3) : + len < 0xFFFFFFF ? 'L' + padding.substring(hex.length, 7) : + 'W' + padding.substring(hex.length, 15); + s.write(Buffer.from(header + hex)); + s.write(data); + } + } + + _fetch(): undefined | string { + const msg = this.buffer; + const len = msg.length; + this.buffer = msg; + if (len < 1) return; + const hd = msg.readInt8(0); + // 'S': 83, 'L': 76, 'W': 87 + const nhex = hd == 83 ? 3 : hd == 76 ? 7 : 15; + if (len < 1 + nhex) return; + const size = Number.parseInt(msg.slice(1, nhex).toString('ascii'), 16); + const offset = 1 + nhex + size; + if (len < offset) return; + this.buffer = msg.slice(offset); + return msg.slice(1 + nhex, offset).toString('utf8'); + } + + _receive(chunk: Buffer) { + this.buffer = Buffer.concat([this.buffer, chunk]); + while (1) { + const data = this._fetch(); + if (data === undefined) break; + try { + const cmd: any = JSON.parse(data); + if (cmd !== null && typeof (cmd) === 'object') { + switch (cmd.res) { + case 'DATA': this.events.emit('DATA', cmd.id, cmd.data); break; + case 'ERROR': this.events.emit('ERROR', cmd.msg); break; + case 'KILLED': this.events.emit('KILLED', cmd.id); break; + case 'REJECTED': this.events.emit('REJECT', cmd.id); break; + case 'SIGNAL': this.events.emit('SIGNAL', cmd.id); break; + default: + console.log('Unknown command', cmd); + } + } else + console.log('Misformed data', data); + } catch (err) { + console.log('Misformed JSON', data, err); + } + } + } + +} + +export const client: Client = new SocketClient(); + +// -------------------------------------------------------------------------- -- GitLab