Skip to content
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

Improve handling of debugging WebSocket messages #1258

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 18 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1685,7 +1685,7 @@
"@types/mocha": "^7.0.2",
"@types/node": "^14.18.0",
"@types/vscode": "1.75.0",
"@types/ws": "^7.2.5",
"@types/ws": "8.5.4",
"@types/xmldom": "^0.1.29",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
Expand Down Expand Up @@ -1720,6 +1720,6 @@
"@vscode/debugadapter": "^1.61.0",
"@vscode/debugprotocol": "^1.61.0",
"vscode-extension-telemetry": "^0.1.6",
"ws": "^7.4.6"
"ws": "^8.14.2"
}
}
35 changes: 25 additions & 10 deletions src/debug/dbgp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,23 @@ export class DbgpConnection extends EventEmitter {
private _chunks: Buffer[];
private _dataLength: number;
private _parser: DOMParser;
private _messages: Buffer[] = [];
private _processingMessages = false;

public constructor(socket: WebSocket) {
super();
this._socket = socket;
this._parsingState = ParsingState.DataLength;
this._chunksDataLength = 0;
this._chunks = [];
socket.on("message", (data: string): void => this._handleDataChunk(Buffer.from(data)));
socket.on("message", (data: string): void => {
this._messages.push(Buffer.from(data));
if (!this._processingMessages) {
this._processingMessages = true;
this._handleDataChunk();
this._processingMessages = false;
}
});
socket.on("error", (error: Error): boolean => this.emit("error", error));
socket.on("close", (): boolean => this.emit("close"));
this._parser = new DOMParser({
Expand Down Expand Up @@ -61,7 +70,9 @@ export class DbgpConnection extends EventEmitter {
});
}

private _handleDataChunk(data: Buffer) {
private _handleDataChunk() {
if (!this._messages.length) return; // Shouldn't ever happen
const data: Buffer = this._messages.shift();
if (this._parsingState === ParsingState.DataLength) {
// does data contain a NULL byte?
const separatorIndex = data.indexOf("|");
Expand All @@ -80,11 +91,13 @@ export class DbgpConnection extends EventEmitter {
if (data.length > separatorIndex + 1) {
// handle the rest of the packet as part of the response
const rest = data.slice(separatorIndex + 1, this._dataLength + separatorIndex + 1);
this._handleDataChunk(rest);
this._messages.unshift(rest);
this._handleDataChunk();
// more then one data chunk in one message
const restData = data.slice(this._dataLength + separatorIndex + 1);
if (restData.length) {
this._handleDataChunk(restData);
this._messages.unshift(restData);
this._handleDataChunk();
}
}
} else {
Expand All @@ -99,7 +112,7 @@ export class DbgpConnection extends EventEmitter {
// append the last piece of the response
const lastResponsePiece = data.slice(0, this._dataLength - this._chunksDataLength);
this._chunks.push(lastResponsePiece);
this._chunksDataLength += data.length;
this._chunksDataLength += lastResponsePiece.length;
const response = Buffer.concat(this._chunks, this._chunksDataLength).toString("ascii");
// call response handler
const xml = iconv.decode(Buffer.from(response, "base64"), ENCODING);
Expand All @@ -110,17 +123,19 @@ export class DbgpConnection extends EventEmitter {
this._chunksDataLength = 0;
// switch to data length parsing state
this._parsingState = ParsingState.DataLength;
// if data contains more info (except the NULL byte)
if (data.length > lastResponsePiece.length + 1) {
// handle the rest of the packet (after the NULL byte) as data length
const rest = data.slice(lastResponsePiece.length + 1);
this._handleDataChunk(rest);
// if data contains more info
if (data.length > lastResponsePiece.length) {
// handle the rest of the packet as data length
const rest = data.slice(lastResponsePiece.length);
this._messages.unshift(rest);
this._handleDataChunk();
}
} else {
// NO -> this is not the whole response yet. We buffer it and wait for the next data event.
this._chunks.push(data);
this._chunksDataLength += data.length;
}
}
while (this._messages.length) this._handleDataChunk();
}
}