Add JSON-RPC methods to connect, disconnect and query connection state - #3806
Add JSON-RPC methods to connect, disconnect and query connection state#3806mcfnord wants to merge 1 commit into
Conversation
965d397 to
cecdea1
Compare
|
Rebased this branch onto #3815 and added an optional When The stack is now #3805 → #3815 ( Relates to jamulussoftware/jamuluswebsite#1122. |
cecdea1 to
2fbbc35
Compare
|
Please rebase. |
|
Also, I think the directory option should be part of a new PR, not this one. |
2fbbc35 to
2b04390
Compare
New methods: jamulusclient/connect, jamulusclient/disconnect and jamulusclient/getConnectionState. New notifications: jamulusclient/connecting, jamulusclient/connectingFailed and jamulusclient/connectionStateChanged. Together with the existing connected/disconnected notifications this gives JSON-RPC full parity with the UI for joining and leaving servers (jamulussoftware#3801) and makes a --nogui client fully scriptable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2b04390 to
a474d52
Compare
|
🤖 AI: The directory option is out of this PR — Taking those commits out was treated as cleanup of this PR rather than as opening a new one, so nothing new was opened while the moratorium runs. If it belongs in its own PR now instead, that can be done. |
| /// @param {string} params.address - Socket address of the server (host:port). | ||
| /// @param {string} params.serverName - Optional human readable server name used for display purposes. Defaults to the address. | ||
| /// @result {string} result - "ok" once the connection attempt has been initiated. | ||
| pRpcServer->HandleMethod ( "jamulusclient/connect", [=] ( const QJsonObject& params, QJsonObject& response ) { |
There was a problem hiding this comment.
maybe requestConnection would be better - as this usually returns ok.
|
|
||
| | Name | Type | Description | | ||
| | --- | --- | --- | | ||
| | params.state | string | The new connection state (disconnected, connecting, or connected). | |
There was a problem hiding this comment.
I think this one status message is enough. The notifications are redundant and I don't see why people would only subscribe to for example jamulusclient/connecting. Why wouldn't you want to know about the other states?
| | params.id | number | The channel ID assigned to the client. | | ||
|
|
||
|
|
||
| ### jamulusclient/connecting |
There was a problem hiding this comment.
connecting is already part of jamulusclient/connectionStateChanged. Do we need both?
| | params.serverName | string | The human readable server name (or the address if no name is known). | | ||
|
|
||
|
|
||
| ### jamulusclient/connectingFailed |
There was a problem hiding this comment.
Could this be refactored into jamulusclient/connectionStateChanged so we don't need to subscribe to multiple endpoints for the same info?
|
🤖 AI finding: I've been stress-testing the client RPC methods. Reliable crash, verified on this branch (ASan build, dummy JACK). Repro Run a client with JSON-RPC enabled, keep a server reachable at the address below, then run this (Python 3, no deps): import socket, json, time
HOST, PORT, SECRET = "127.0.0.1", 22134, "supersecret1234567890"
ADDRESS = "localhost:22124"
def fire(method, params):
s = socket.create_connection((HOST, PORT), timeout=5)
s.sendall((json.dumps({"jsonrpc":"2.0","id":1,"method":"jamulus/apiAuth",
"params":{"secret":SECRET}})+"\n").encode())
s.sendall((json.dumps({"jsonrpc":"2.0","id":2,"method":method,
"params":params})+"\n").encode())
s.close() # fire-and-forget: close without reading the response
# Crash 1: two overlapping connects
fire("jamulusclient/connect", {"address": ADDRESS})
time.sleep(0.003)
fire("jamulusclient/connect", {"address": ADDRESS})
# Crash 2 (same bug): disconnect while the connect is still in flight
# fire("jamulusclient/connect", {"address": ADDRESS})
# time.sleep(0.003)
# fire("jamulusclient/disconnect", {})
# time.sleep(0.003)
# fire("jamulusclient/disconnect", {})The overlap is the point: each request connects, sends Why
Server-side RPC is unaffected (its handlers never call Smaller things I noticed while reviewing
|
This comment was marked as outdated.
This comment was marked as outdated.
|
Fix idea Root cause:
The wait in QTime DieTime = QTime::currentTime().addMSecs ( 100 );
while ( QTime::currentTime() < DieTime )
{
QCoreApplication::processEvents ( QEventLoop::ExcludeUserInputEvents, 100 );
}For that it only needs to wait — it doesn't need to process events. // wait for approx. 100 ms so no audio packet is still in the network queue
QThread::msleep ( 100 );Nothing is lost: the disconnect message is only created after the wait (client.cpp:1130), queued events run once
So — to be clear — the fix itself is small: 3 lines, one function ( @ann0see probably prefers this — root cause, not a new error code.
|
|
For option 3 as "fix" - not sure. We'll probably need to iterate over those proposals... Especially
Is not nice. |
A sleep still feels like a workaround. I haven't looked at the code yet, but I'd investigate a possible lock mechanism in case colliding request arrive. |
|
Yes. Had the same idea but the agent claimed that it's the wrong way to fix it (since it happens in the same thread) - to me this doesn't make too much sense - but it's very likely that I don't fully understand the codebase well enough. |
Short description of changes
Stacked on #3805 — please review only the last commit here until that one merges.
Adds JSON-RPC control over the client's connection, as a symmetric consumer of the
CClientconnection state machine introduced in #3805. This closes the largest UI/RPC parity gap named in #3801 (join/leave) and makes a--noguiclient fully scriptable — e.g. a local web frontend where clicking a server joins it.New methods:
jamulusclient/connect{"address": "host:port", "serverName": "optional display name"}— terminates any current connection first, then connects (asynchronously; progress arrives via notifications).jamulusclient/disconnect— idempotent.jamulusclient/getConnectionState— returns{state: disconnected|connecting|connected, serverName}.New notifications:
jamulusclient/connecting,jamulusclient/connectingFailed,jamulusclient/connectionStateChanged. Together with the existingconnected/disconnectednotifications, an RPC consumer can follow the full lifecycle.Context: Fixes an issue?
Toward #3801 (full UI/RPC parity). Complementary to #3660 (directory getters).
Does this change need documentation? What needs to be documented and how?
docs/JSON-RPC.mdregenerated viatools/generate_json_rpc_docs.py(included).Status of this Pull Request
Draft until #3805 merges; the RPC commit itself is ready for review.
What is missing until this pull request can be merged?
#3805, then review.
Tested end-to-end:
--noguiclient + local server on Linux/Qt 5.15, driven entirely over JSON-RPC. Scripted checks (16/16 pass): initial statedisconnected;connect→connectingnotification →connectionStateChanged: connecting→connected(channel ID) →connectionStateChanged: connected→getConnectionStatereturns server name; connect-while-connected performs a clean reconnect (disconnected→connecting→connected);disconnect→disconnected+connectionStateChanged: disconnected,serverNamecleared; double disconnect is a no-op.Checklist
AUTOBUILD: Please build all targets
🤖 Generated with Claude Code