/
wireProtocol.ts
125 lines (111 loc) · 3.93 KB
/
wireProtocol.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as stream from 'stream';
import * as vscode from 'vscode';
import { Disposable } from './dispose';
const defaultSize: number = 8192;
const contentLength: string = 'Content-Length: ';
const contentLengthSize: number = Buffer.byteLength(contentLength, 'utf8');
const blank: number = Buffer.from(' ', 'utf8')[0];
const backslashR: number = Buffer.from('\r', 'utf8')[0];
const backslashN: number = Buffer.from('\n', 'utf8')[0];
class ProtocolBuffer {
private index: number = 0;
private buffer: Buffer = Buffer.allocUnsafe(defaultSize);
public append(data: string | Buffer): void {
let toAppend: Buffer | null = null;
if (Buffer.isBuffer(data)) {
toAppend = data;
} else {
toAppend = Buffer.from(data, 'utf8');
}
if (this.buffer.length - this.index >= toAppend.length) {
toAppend.copy(this.buffer, this.index, 0, toAppend.length);
} else {
let newSize = (Math.ceil((this.index + toAppend.length) / defaultSize) + 1) * defaultSize;
if (this.index === 0) {
this.buffer = Buffer.allocUnsafe(newSize);
toAppend.copy(this.buffer, 0, 0, toAppend.length);
} else {
this.buffer = Buffer.concat([this.buffer.slice(0, this.index), toAppend], newSize);
}
}
this.index += toAppend.length;
}
public tryReadContentLength(): number {
let result = -1;
let current = 0;
// we are utf8 encoding...
while (current < this.index && (this.buffer[current] === blank || this.buffer[current] === backslashR || this.buffer[current] === backslashN)) {
current++;
}
if (this.index < current + contentLengthSize) {
return result;
}
current += contentLengthSize;
let start = current;
while (current < this.index && this.buffer[current] !== backslashR) {
current++;
}
if (current + 3 >= this.index || this.buffer[current + 1] !== backslashN || this.buffer[current + 2] !== backslashR || this.buffer[current + 3] !== backslashN) {
return result;
}
let data = this.buffer.toString('utf8', start, current);
result = parseInt(data);
this.buffer = this.buffer.slice(current + 4);
this.index = this.index - (current + 4);
return result;
}
public tryReadContent(length: number): string | null {
if (this.index < length) {
return null;
}
let result = this.buffer.toString('utf8', 0, length);
let sourceStart = length;
while (sourceStart < this.index && (this.buffer[sourceStart] === backslashR || this.buffer[sourceStart] === backslashN)) {
sourceStart++;
}
this.buffer.copy(this.buffer, 0, sourceStart);
this.index = this.index - sourceStart;
return result;
}
}
export class Reader<T> extends Disposable {
private readonly buffer: ProtocolBuffer = new ProtocolBuffer();
private nextMessageLength: number = -1;
public constructor(readable: stream.Readable) {
super();
readable.on('data', data => this.onLengthData(data));
}
private readonly _onError = this._register(new vscode.EventEmitter<Error>());
public readonly onError = this._onError.event;
private readonly _onData = this._register(new vscode.EventEmitter<T>());
public readonly onData = this._onData.event;
private onLengthData(data: Buffer | string): void {
if (this.isDisposed) {
return;
}
try {
this.buffer.append(data);
while (true) {
if (this.nextMessageLength === -1) {
this.nextMessageLength = this.buffer.tryReadContentLength();
if (this.nextMessageLength === -1) {
return;
}
}
const msg = this.buffer.tryReadContent(this.nextMessageLength);
if (msg === null) {
return;
}
this.nextMessageLength = -1;
const json = JSON.parse(msg);
this._onData.fire(json);
}
} catch (e) {
this._onError.fire(e);
}
}
}