Skip to content

updated library to work with websockets #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 39 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
3cd12ba
dependencies
OwenMcDonnell Feb 8, 2022
9aa2d2a
renaming
OwenMcDonnell Feb 8, 2022
57f1ce5
add controls folder. Placeholder connection and page calls.
OwenMcDonnell Feb 12, 2022
33cf6af
Placeholder calls
OwenMcDonnell Feb 12, 2022
681edd0
Add basic protocol types/interfaces
OwenMcDonnell Feb 14, 2022
af05824
Reconnecting websocket object
OwenMcDonnell Feb 15, 2022
ff0aff2
Update index.ts
OwenMcDonnell Feb 16, 2022
adfe8be
change connectPage options parameter
OwenMcDonnell Feb 17, 2022
6725348
scaffolding/notes
OwenMcDonnell Mar 2, 2022
1a30062
...
OwenMcDonnell Mar 11, 2022
2e6e88a
...
OwenMcDonnell Mar 11, 2022
de6a5ae
...
OwenMcDonnell Mar 12, 2022
67c5279
send register host client payload.
OwenMcDonnell Mar 17, 2022
2b446cb
add sentMessageHash and openBrowser()
OwenMcDonnell Mar 18, 2022
6f7ae46
Add colored logging, call resolve in onMessage.
OwenMcDonnell Mar 21, 2022
5888dbc
fix command interface and control.getCmdAttrs()
OwenMcDonnell Mar 22, 2022
56f863a
change command interface to class with initialized values, update Co…
OwenMcDonnell Mar 22, 2022
63dc77a
Session not found or access denied error on page update.
OwenMcDonnell Mar 24, 2022
0bc2051
fix session error ("zero session" string), fix page clean method (con…
OwenMcDonnell Mar 24, 2022
6b53116
...
OwenMcDonnell Mar 24, 2022
b682ded
...
OwenMcDonnell Mar 25, 2022
8f51736
return ids
OwenMcDonnell Mar 25, 2022
52f90e4
Don't call resolve callback for events
OwenMcDonnell Mar 26, 2022
1947e18
clean up client code (TODO serveApp function), move openBrowser to in…
OwenMcDonnell Mar 28, 2022
6625c54
serveApp function added. TODO account for either zeror session or ses…
OwenMcDonnell Mar 30, 2022
2405482
...
OwenMcDonnell Mar 30, 2022
5a8f496
remove onevent binding in page constructor
OwenMcDonnell Mar 31, 2022
2128154
add todo test
OwenMcDonnell Apr 1, 2022
6ae0104
Split payload result ids
OwenMcDonnell Apr 4, 2022
8a7312b
set attrs as strings
OwenMcDonnell Apr 4, 2022
2798172
cleanup
OwenMcDonnell Apr 5, 2022
9cd8608
begin rewriting tests
OwenMcDonnell Apr 11, 2022
037d67a
unit tests passing
OwenMcDonnell Apr 12, 2022
9e101e6
update node to v15
OwenMcDonnell Apr 12, 2022
b071157
remove connection logging
OwenMcDonnell Apr 12, 2022
f48931b
add debug package (TODO replace console.log with per module debugging)
OwenMcDonnell Apr 12, 2022
cb910b2
replace console logging with 'debug' module (set DEBUG=debug|warn|inf…
OwenMcDonnell Apr 13, 2022
6bcf9a6
more cleanup, extend debug to index.
OwenMcDonnell Apr 14, 2022
a6ad3c1
Final cleanup
OwenMcDonnell May 19, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ skip_branch_with_pr: true
image: Visual Studio 2019

environment:
NODE_VERSION: 12
NODE_VERSION: 15
NPM_TOKEN:
secure: k2cSmOKcnk4Va2hNea/oVjUCIyWl+iAMMS0ZgxWzXFHQg/0q5f+T7JyKYEIEF56Y

Expand Down
6,377 changes: 6,368 additions & 9 deletions package-lock.json

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"scripts": {
"test": "jest",
"test-multiple": "FOR /L %i IN (1,1,5) DO (jest)",
"test-debug": "set DEBUG=* & jest",
"coverage": "jest --coverage"
},
"repository": {
Expand All @@ -29,15 +30,19 @@
},
"homepage": "https://github.com/pglet/pglet-node#readme",
"dependencies": {
"@types/node": "^14.14.28",
"@types/node": "^17.0.21",
"@types/request": "^2.48.5",
"compare-versions": "^3.6.0",
"debug": "^4.3.4",
"diff": "^5.0.0",
"request": "^2.88.2"
"reconnecting-websocket": "^4.4.0",
"request": "^2.88.2",
"ws": "^8.4.2"
},
"devDependencies": {
"@types/diff": "^5.0.0",
"@types/jest": "^26.0.20",
"@types/ws": "^8.2.2",
"jest": "^26.6.3",
"ps-node": "^0.1.6",
"ts-jest": "^26.5.2",
Expand Down
271 changes: 110 additions & 161 deletions src/Connection.ts
Original file line number Diff line number Diff line change
@@ -1,181 +1,104 @@
import os from 'os';
import crypto from 'crypto';
import cp from 'child_process';
import util from 'util';
import net from 'net';
import fs from 'fs';
import { Event } from './Event';
import { Event as PgletEvent } from './Event';
import Page from './Page'
import rws , { Event, Options } from 'reconnecting-websocket';
import { ReconnectingWebSocket } from './protocol/ReconnectingWebSocket';
import { MessageChannel } from 'worker_threads';
import { CommandResponse } from './protocol/CommandResponse';
import { Message as PgletMessage } from './protocol/Message';
import { Action } from './protocol/Actions';
import { resolve } from 'path';
import { Log, warn, info, debug } from './Utils';
const connectionDebug = debug.extend('connection');

export class Connection {
private connId = ""
private _commandClient: any;
private _commandResolve: any;
private _commandReject: any;
private _eventClient: any;
private _eventResolve: any;
private _eventHandlers: any = {};
private _rws: ReconnectingWebSocket;
private connId: string = "";
private _messageResolve: any;
private _messageReject: any;
private _pageUrl: string;
private _pageName: string;
private _sessions: { [key: string]: Page} = {};
sentMessageHash: { [key: string]: PgletMessage } = {};
onEvent: any;
onSessionCreated: any;
//onMessage: (evt: MessageEvent) => Promise<void>

constructor(connId: string) {
this.connId = connId;

if (os.type() === "Windows_NT") {
// open connections for command and event pipes
this._commandResolve = null;
this._commandReject = null;
this._commandClient = net.createConnection(os.type() === "Windows_NT" ? `\\\\.\\pipe\\${connId}` : `${os.tmpdir()}/CoreFxPipe_${connId}`, () => {
this._commandClient.setNoDelay(true);
});

this._commandClient.on('data', (data: any) => {
// parse result
const result = this.parseResult(data);
//console.log("commandClient data: ", result);

let fn = this._commandResolve;
let value = result.value;
if (result.error) {
let fn = this._commandReject;
let value = result.error;
}

this._commandResolve = null;
this._commandReject = null;

if (fn) {
fn(value);
}
});

this._eventResolve = null;
this._eventClient = net.createConnection(os.type() === "Windows_NT" ? `\\\\.\\pipe\\${connId}.events` : `${os.tmpdir()}/CoreFxPipe_${connId}.events`, () => {
});

this._eventClient.on('data', (data) => {
const result = this.parseEvent(data);

//call page private _onEvent
this.onEvent(result);
var fn = this._eventResolve;
this._eventResolve = null;

if (fn) {
fn(result);
}
});
}
}
constructor(Rws: ReconnectingWebSocket) {
this._rws = Rws;

async sendBatch (commands: string[]): Promise<string> {
await this._send("begin"); //returns null
for (const cmd of commands) {
await this._send(cmd); //returns null
this._rws.onMessage = this.onMessage.bind(this);
this._rws.onOpen = (msg: Event) => {
connectionDebug("connected");
}
return this._send("end"); //returns results of intervening commands in text list
this._rws.onClose = (msg: Event) => {
connectionDebug('connection closed');
}
this._messageResolve = null;
this._messageReject = null;
}

send(command: string): Promise<string> {
return this._send(command);
get pageUrl() {
return this._pageUrl;
}

private _send(command: string): Promise<string> {
let waitResult = !command.match(/\w+/g)[0].endsWith('f');
if (os.type() === "Windows_NT") {
// Windows
return this.sendWindows(command, waitResult);
} else {
// Linux/macOS - use FIFO
return this.sendLinux(command, waitResult);
}
set pageUrl(url: string) {
this._pageUrl = url;
}

// wait event pipe for new event
waitEvent(): Promise<string | Event> {
// register for result

return new Promise((resolve, reject) => {
if (os.type() === "Windows_NT") {
this._eventResolve = resolve;
} else {
fs.open(`${this.connId}.events`, 'r+', (err, fd) => {
if (err) {
reject(err);
} else {
var stream = fs.createReadStream(null, {
fd
});
stream.on('data', (data) => {
stream.close()
resolve(this.parseEvent(data));
});
}
});
}
});
get pageName() {
return this._pageName;
}
set pageName(name: string) {
this._pageName = name;
}
get sessions() {
return this._sessions;
}
set sessions(session: { [key: string]: Page}) {
this._sessions = session;
}

// async sendBatch (commands: string[]): Promise<string> {
// await this._send("begin"); //returns null
// for (const cmd of commands) {
// await this._send(cmd); //returns null
// }
// return this._send("end"); //returns results of intervening commands in text list
// }

private sendWindows(command: string, waitResult: boolean): Promise<string> {
if (waitResult) {

// command with result
return new Promise((resolve, reject) => {
this._commandResolve = resolve;
this._commandReject = reject;

// send command
this._commandClient.write(command + '\n');
});

} else {

// fire-and-forget command
return new Promise<string>((resolve, reject) => {
this._commandClient.write(command + '\n', (err) => {
if (err) {
reject(err);

} else {
resolve("");
}
});
});
async send(action: Action, command: any): Promise<string> {
let msg: PgletMessage = {
id: crypto.randomUUID(), //requires node >=15
action: action,
payload: command
}
this.sentMessageHash[msg.id] = msg;
connectionDebug("sending message: %O", msg);
return this.sendMessageInternal(msg);
}

private sendLinux(command: string, waitResult: boolean): Promise<string> {
return new Promise<string>((resolve, reject) => {

fs.writeFile(this.connId, command + '\n', (err) => {
if (err) {
reject(err);
} else {
if (waitResult) {
fs.readFile(this.connId, (err, data) => {
if (err) {
reject(err);
} else {
// parse result
const result = this.parseResult(data);

if (result.error) {
reject(result.error);
} else {
resolve(result.value);
}
}
})
} else {
resolve("");
}
}
});
private sendMessageInternal(msg: PgletMessage): Promise<string> {
return new Promise((res, rej) => {
this._rws.send(JSON.stringify(msg));

// wait for message to arrive in hash then these will be called in onMessage
this._messageResolve = res;
this._messageReject = rej;

});
}

// addEventHandlers(controlId: string, eventName: string, handler: any) {
// let controlEvents = controlId in this._eventHandlers ? this._eventHandlers[controlId] : {};
addEventHandlers(controlId: string, eventName: string, handler: any) {
let controlEvents = controlId in this._eventHandlers ? this._eventHandlers[controlId] : {};

// controlEvents[eventName] = handler;
// this._eventHandlers[controlId] = controlEvents;
// }
controlEvents[eventName] = handler;
this._eventHandlers[controlId] = controlEvents;
}

protected removeEventHandlers(controlId: string): void {
if (controlId in this._eventHandlers) {
Expand All @@ -201,12 +124,38 @@ export class Connection {
}

private parseEvent(data: any) {
const result = data.toString().trim();

let re = /(?<target>[^\s]+)\s(?<name>[^\s]+)(\s(?<data>.+))*/;
let match = re.exec(result);
const payload = data.payload

return new Event(match.groups.target, match.groups.name, match.groups.data);
return new PgletEvent(payload.eventTarget, payload.eventName, payload.eventData);
}

}

onMessage(msg: MessageEvent) {
let storedMsg: PgletMessage;
let msgData = JSON.parse(msg.data);

connectionDebug("msgData: " + msgData);

if (msgData.action === 'pageEventToHost') {
this.onEvent(msgData.payload);
return;
}

if (msgData.action === 'sessionCreated') {
connectionDebug("session created: " + msgData);
this.onSessionCreated(msgData.payload);
return;
}

let cb = msgData.payload.error ? this._messageReject : this._messageResolve;

if (cb) {
cb(JSON.stringify(msgData));
this._messageResolve = null;
this._messageReject = null;
}
}



}

Loading