mirror of
https://github.com/silvanshade/tower-lsp-web-demo.git
synced 2025-01-22 19:56:04 +00:00
Implement framed decoding for demuxer
This commit is contained in:
parent
dfabf15298
commit
82503d3f28
1 changed files with 53 additions and 2 deletions
|
@ -14,6 +14,23 @@ export class Bytes {
|
||||||
static decode(input: Uint8Array): string {
|
static decode(input: Uint8Array): string {
|
||||||
return decoder.decode(input);
|
return decoder.decode(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static append<T extends { length: number; set(arr: T, offset: number): void }>(
|
||||||
|
constructor: { new (length: number): T },
|
||||||
|
...arrays: T[]
|
||||||
|
) {
|
||||||
|
let totalLength = 0;
|
||||||
|
for (const arr of arrays) {
|
||||||
|
totalLength += arr.length;
|
||||||
|
}
|
||||||
|
const result = new constructor(totalLength);
|
||||||
|
let offset = 0;
|
||||||
|
for (const arr of arrays) {
|
||||||
|
result.set(arr, offset);
|
||||||
|
offset += arr.length;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Headers {
|
export class Headers {
|
||||||
|
@ -128,10 +145,44 @@ export class StreamDemuxer extends Queue<Uint8Array> {
|
||||||
|
|
||||||
// FIXME: we needs to actually do framed reads here since `bytes` may not be a complete message
|
// FIXME: we needs to actually do framed reads here since `bytes` may not be a complete message
|
||||||
private async start(): Promise<void> {
|
private async start(): Promise<void> {
|
||||||
|
let contentLength: null | number = null;
|
||||||
|
let buffer = new Uint8Array();
|
||||||
|
|
||||||
for await (const bytes of this) {
|
for await (const bytes of this) {
|
||||||
const delimited = Bytes.decode(bytes);
|
buffer = Bytes.append(Uint8Array, buffer, bytes);
|
||||||
const message = JSON.parse(Headers.remove(delimited)) as vsrpc.Message;
|
|
||||||
|
// check if the content length is known
|
||||||
|
if (null == contentLength) {
|
||||||
|
// if not, try to match the prefixed headers
|
||||||
|
const match = Bytes.decode(buffer).match(/^Content-Length:\s*(\d+)\s*/);
|
||||||
|
if (null == match) continue;
|
||||||
|
|
||||||
|
// try to parse the content-length from the headers
|
||||||
|
const length = parseInt(match[1]);
|
||||||
|
if (isNaN(length)) throw new Error("invalid content length");
|
||||||
|
|
||||||
|
// slice the headers since we now have the content length
|
||||||
|
buffer = buffer.slice(match[0].length);
|
||||||
|
|
||||||
|
// set the content length
|
||||||
|
contentLength = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the buffer doesn't contain a full message; await another iteration
|
||||||
|
if (buffer.length < contentLength) continue;
|
||||||
|
|
||||||
|
// decode buffer to a string
|
||||||
|
const delimited = Bytes.decode(buffer);
|
||||||
|
|
||||||
|
// reset the buffer
|
||||||
|
buffer = buffer.slice(contentLength);
|
||||||
|
// reset the contentLength
|
||||||
|
contentLength = null;
|
||||||
|
|
||||||
|
const message = JSON.parse(delimited) as vsrpc.Message;
|
||||||
Tracer.server(message);
|
Tracer.server(message);
|
||||||
|
|
||||||
|
// demux the message stream
|
||||||
if (vsrpc.Message.isResponse(message) && null != message.id) {
|
if (vsrpc.Message.isResponse(message) && null != message.id) {
|
||||||
this.responses.set(message.id, message);
|
this.responses.set(message.id, message);
|
||||||
continue;
|
continue;
|
||||||
|
|
Loading…
Reference in a new issue