Migrate Intellij Plugins off weberknecht web socket library

62 views
Skip to first unread message

Javad Asadi

unread,
Feb 24, 2026, 11:45:35 AM (14 days ago) Feb 24
to dart-gsoc

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 closed
The 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 sites

Both:

  • 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()
}
}

Helin Shiah

unread,
Feb 24, 2026, 4:49:46 PM (14 days ago) Feb 24
to Javad Asadi, dart-gsoc, Phil Quitslund
Hi Javad,

Thanks for looking into this project! This seems like a great proof of concept.

A couple tips as you move towards creating an application:
- We don't know a lot about the current library or websockets in general, so documentation on this project's needs, what websocket libraries exist and how to decide on which one would be helpful.
- The weberknecht library is also used in the Dart plugin (which we recently took over managing), so we'd also want to evaluate replacing its use there.

--
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.
Reply all
Reply to author
Forward
0 new messages