Hello dear mentors.
I’m interested in the GSoC project “Migrate IntelliJ Plugins off Weberknecht WebSocket library”, and I’ve been exploring the issue by attempting a local reproduction and partial migration.
To better understand the root cause, I created a small DTD simulation server, appended at the end of this email, that:
Accepts a VM Service connection
Completes the getVersion handshake
Periodically sends WebSocket ping frames
Terminates the connection if no pong is received
Using the current Weberknecht-based implementation, the client, appended at the end of this email you can find a smile .kt file I wrote that uses VmService.connect:
Connects successfully
Completes the handshake
Fails to respond to server pings
Is terminated shortly afterward
Meanwhile, the client continues running normally, unaware that the connection has been dropped.
Logs of the Server:
[2026-02-21T09:57:02.015Z] DTD simulation server running on ws://localhost:8080
[2026-02-21T09:57:40.161Z] Client connected
[2026-02-21T09:57:40.171Z] Received: {"jsonrpc":"2.0","id":"1","method":"getVersion","params":{}}
[2026-02-21T09:57:40.172Z] Sent getVersion response
[2026-02-21T09:57:45.161Z] Sending ping
[2026-02-21T09:57:50.163Z] No pong received — terminating (simulates Norton dropping idle socket)
[2026-02-21T09:57:50.166Z] Connection closed
- 09:57:40 — Client connected, getVersion handshake completed
- 09:57:45 — Server sent a ping- 09:57:50 — Server got no pong, terminated the connection- 09:58:40 — Client finished its 60-second sleep and exited normally, completely unaware the connection was dropped
I then migrated the transport layer in:
VmServiceBase.java
WebSocketRequestSink.java
from Weberknecht to the JDK’s built-in java.net.http.WebSocket.
With no application-level ping/pong handling added, the connection remained stable and responded correctly to server pings
[2026-02-21T10:55:35.068Z] DTD simulation server running on ws://localhost:8080
[2026-02-21T10:55:41.047Z] Client connected
[2026-02-21T10:55:41.065Z] Received: {"jsonrpc":"2.0","id":"1","method":"getVersion","params":{}}
[2026-02-21T10:55:41.066Z] Sent getVersion response
[2026-02-21T10:55:46.049Z] Sending ping
[2026-02-21T10:55:46.051Z] Pong received — connection is alive
[2026-02-21T10:55:51.050Z] Sending ping
[2026-02-21T10:55:51.051Z] Pong received — connection is alive
[2026-02-21T10:55:56.051Z] Sending ping
[2026-02-21T10:55:56.052Z] Pong received — connection is alive
[2026-02-21T10:56:01.052Z] Sending ping
[2026-02-21T10:56:01.053Z] Pong received — connection is alive
[2026-02-21T10:56:06.053Z] Sending ping
[2026-02-21T10:56:06.054Z] Pong received — connection is alive
[2026-02-21T10:56:11.054Z] Sending ping
[2026-02-21T10:56:11.055Z] Pong received — connection is alive
[2026-02-21T10:56:16.054Z] Sending ping
[2026-02-21T10:56:16.056Z] Pong received — connection is alive
[2026-02-21T10:56:21.056Z] Sending ping
[2026-02-21T10:56:21.057Z] Pong received — connection is alive
[2026-02-21T10:56:26.058Z] Sending ping
[2026-02-21T10:56:26.059Z] Pong received — connection is alive
[2026-02-21T10:56:31.059Z] Sending ping
[2026-02-21T10:56:31.060Z] Pong received — connection is alive
[2026-02-21T10:56:36.060Z] Sending ping
[2026-02-21T10:56:36.061Z] Pong received — connection is alive
[2026-02-21T10:56:41.060Z] Sending ping
[2026-02-21T10:56:41.061Z] Pong received — connection is alive
[2026-02-21T10:56:41.460Z] Connection closedThe client ran for the full 60 seconds without being dropped. The JDK WebSocket is automatically responding to the server's pings with pongs, keeping the connection alive.
There are also other places in which weberknecht is used that are needed to be migrated.
Downstream catch sitesBoth:
src/io/flutter/run/daemon/FlutterApp.java
src/io/flutter/FlutterInitializer.java
import and catch WebSocketException.
This suggests the migration scope includes:
Transport replacement
Exception model cleanup
Dependency cleanup
Test on different platforms
Thanks very much for your time.
Best regards,
Javad
server.js:
```
const WebSocket = require('ws');
function ts() {
return new Date().toISOString();
}
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
console.log(`[${ts()}] Client connected`);
ws.isAlive = true;
ws.on('message', function incoming(data) {
let msg;
try {
msg = JSON.parse(data);
} catch (e) {
return;
}
console.log(`[${ts()}] Received: ${data}`);
// Satisfy VmServiceBase.connect()'s getVersion handshake
if (msg.method === 'getVersion') {
ws.send(JSON.stringify({
jsonrpc: '2.0',
result: { type: 'Version', major: 4, minor: 0 },
id: msg.id,
}));
console.log(`[${ts()}] Sent getVersion response`);
}
});
ws.on('pong', () => {
console.log(`[${ts()}] Pong received — connection is alive`);
ws.isAlive = true;
});
ws.on('close', () => {
clearInterval(interval);
console.log(`[${ts()}] Connection closed`);
});
ws.on('error', (err) => {
console.error(`[${ts()}] Error: ${err.message}`);
});
const interval = setInterval(() => {
if (ws.isAlive === false) {
console.log(`[${ts()}] No pong received — terminating (simulates Norton dropping idle socket)`);
clearInterval(interval);
return ws.terminate();
}
ws.isAlive = false;
console.log(`[${ts()}] Sending ping`);
ws.ping();
}, 5000);
});
console.log(`[${ts()}] DTD simulation server running on ws://localhost:8080`);
```
VmServiceTest.kt:
package org.dartlang.vm.service
import java.time.Instant
fun ts(): String = Instant.now().toString()
fun main() {
try {
println("[${ts()}] Attempting connection...")
VmService.connect("ws://localhost:8080/")
println("[${ts()}] Connected successfully.")
Thread.sleep(60_000)
println("[${ts()}] Finished waiting.")
} catch (e: Exception) {
println("[${ts()}] Exception occurred:")
e.printStackTrace()
}
}
--
You received this message because you are subscribed to the Google Groups "dart-gsoc" group.
To unsubscribe from this group and stop receiving emails from it, send an email to dart-gsoc+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/dart-gsoc/87cd6b2e-d808-4553-b3c6-50252caa83ean%40googlegroups.com.