-
Notifications
You must be signed in to change notification settings - Fork 5
/
handler.ts
95 lines (84 loc) · 3.07 KB
/
handler.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import {Chunk, ChunkType} from './chunk';
import {Command, Ref} from './command';
import {CommandFactory} from './commandfactory';
import {NamedLock} from './lock';
import {Readable, Writable} from 'stream';
class TrackingRef {
private value = true;
constructor(private readonly delegate: Ref) {
}
getValue() {
return this.value;
}
ref() {
this.value = true;
this.delegate.ref();
}
unref() {
this.value = false;
this.delegate.unref();
}
}
export class Handler {
private readonly lock = new NamedLock();
private readonly ref: TrackingRef
constructor(private readonly commandFactory: CommandFactory, ref: Ref) {
this.ref = new TrackingRef(ref);
}
handle(reader: Readable, writer: Writable) {
const args: string[] = [];
const env = new Map<string, string>();
let workingDirectory: string | undefined;
let me = this;
reader.on('data', function listener(chunk: Chunk) {
switch (chunk.type) {
case ChunkType.Argument:
args.push(chunk.data.toString());
break;
case ChunkType.Command:
if (workingDirectory == null) {
throw new MissingWorkingDirectory();
}
const commandString = chunk.data.toString();
let command: Command;
try {
command = me.commandFactory.create(workingDirectory, commandString);
} catch (e) {
writer.write(new Chunk(ChunkType.Stderr, Buffer.from(`${e.stack}\n`)));
writer.write(new Chunk(ChunkType.Exit, Buffer.from((2).toString())));
writer.end();
return;
}
this.removeListener('data', listener);
const params = {args, env, workingDirectory};
me.lock.acquire([commandString].concat(args).join(' '), () => command.invoke(params, reader, writer, me.ref).then(code => {
writer.write(new Chunk(ChunkType.Exit, Buffer.from(code.toString())));
writer.end();
}));
break;
case ChunkType.Environment:
const [name, value] = chunk.data.toString().split('=', 2) as [string, string|undefined];
if (value != null) {
env.set(name, value);
}
break;
case ChunkType.WorkingDirectory:
workingDirectory = chunk.data.toString();
break;
default:
throw new Error('Unexpected chunk type');
}
});
}
status() {
return {
lock: this.lock.status(),
waitingOnInput: this.ref.getValue(),
}
}
}
class MissingWorkingDirectory extends Error {
constructor() {
super('Missing working directory');
}
}