Skip to content

Add JSON-RPC methods to connect, disconnect and query connection state#3806

Draft
mcfnord wants to merge 6 commits into
jamulussoftware:mainfrom
mcfnord:client-connection-rpc
Draft

Add JSON-RPC methods to connect, disconnect and query connection state#3806
mcfnord wants to merge 6 commits into
jamulussoftware:mainfrom
mcfnord:client-connection-rpc

Conversation

@mcfnord

@mcfnord mcfnord commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

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 CClient connection state machine introduced in #3805. This closes the largest UI/RPC parity gap named in #3801 (join/leave) and makes a --nogui client 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 existing connected/disconnected notifications, 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.md regenerated via tools/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: --nogui client + local server on Linux/Qt 5.15, driven entirely over JSON-RPC. Scripted checks (16/16 pass): initial state disconnected; connectconnecting notification → connectionStateChanged: connectingconnected (channel ID) → connectionStateChanged: connectedgetConnectionState returns server name; connect-while-connected performs a clean reconnect (disconnectedconnectingconnected); disconnectdisconnected + connectionStateChanged: disconnected, serverName cleared; double disconnect is a no-op.

Checklist

  • I've verified that this Pull Request follows the general code principles
  • I tested my code and it does what I want
  • My code follows the style guide
  • I waited some time after this Pull Request was opened and all GitHub checks completed without errors.
  • I've filled all the content above

AUTOBUILD: Please build all targets

🤖 Generated with Claude Code

pgScorpio and others added 2 commits July 19, 2026 01:01
This is an extract from jamulussoftware#2550
Co-authored-by: ann0see <20726856+ann0see@users.noreply.github.com>
Introduce EConnectionState (disconnected / connecting / connected) owned
by CClient as the single source of truth. A connection is 'requested'
when the audio stream starts (CS_CONNECTING) and 'established' once the
server assigns our channel ID (CS_CONNECTED). Every transition emits
ConnectionStateChanged.

Rename the Connected(name) signal emitted from Start() to
Connecting(name), since at that point the connection is only requested,
not established. CClient::Connect() now terminates any current
connection first, so connecting while connected behaves as a reconnect.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mcfnord

mcfnord commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author

Rebased this branch onto #3815 and added an optional directory parameter to jamulusclient/connect.

When directory is supplied, the client hole-punches through that directory (sends CLM_REQ_SERVER_LIST, which makes the directory poke its registered servers toward our socket) before connecting — giving RPC-driven clients the same reachability the GUI directory list has, for servers behind a cloud firewall/NAT. The server address is connected to verbatim and need not be listed by the directory. A non-string directory is rejected with invalid params. docs/JSON-RPC.md was regenerated via tools/generate_json_rpc_docs.py.

The stack is now #3805#3815 (--connectdirectory) → this PR, so please review those first. Verified locally: jamulusclient/connect with a directory sends the punch to the directory then connects; without it, behaviour is unchanged; a bogus directory type returns -32602.

Relates to jamulussoftware/jamuluswebsite#1122.

jrd and others added 4 commits July 21, 2026 05:55
- SIGTERM/SIGINT: route through Disconnect() instead of a raw Stop(),
  and guard Disconnect() on the connection state rather than IsRunning()
  (which tracks the audio device). IsRunning() is false while connecting
  and in headless mode, so the old guard could skip notifying the server
  on shutdown; the raw Stop() worked around that but also fired a
  spurious disconnect when idle. Now the server is notified iff a
  connection is pending or established, via the single Disconnect() path.
- Move EConnectionState from util.h to client.h (it is client specific).
- Add a doc-comment to SetConnectionState; reword two member comments.

Addresses review feedback on jamulussoftware#3805.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
-c / --connect connects straight to the given address and, unlike
selecting a server from the GUI directory list, never asks a directory
to open the target's firewall. Servers behind a cloud firewall/NAT that
the GUI reaches fine are therefore silently unreachable via -c.

Give CClient::Connect an optional directory argument: when set it sends
a CLM_REQ_SERVER_LIST to that directory (which makes the directory poke
its registered servers towards our socket -- the same UDP hole punch the
GUI relies on), waits HOLE_PUNCH_CONNECT_DELAY_MS, then connects. The
server address is used verbatim and need not be listed by the directory.

Expose it on the command line as --connectdirectory <host:port>, wired
through both the GUI and headless connect-on-startup paths in main.cpp.

Relates to jamulussoftware/jamuluswebsite#1122

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Mirror the new --connectdirectory option on the RPC side: connect now
accepts an optional "directory" param. When given, CClient::Connect
hole-punches through that directory (CLM_REQ_SERVER_LIST) before
connecting, so an RPC-driven client can reach a server behind a cloud
firewall/NAT the same way the GUI directory list does. A non-string
directory is rejected with invalid params; the server address is
connected to verbatim and need not be listed by the directory.

docs/JSON-RPC.md regenerated via tools/generate_json_rpc_docs.py.

Relates to jamulussoftware/jamuluswebsite#1122

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mcfnord
mcfnord force-pushed the client-connection-rpc branch from cecdea1 to 2fbbc35 Compare July 21, 2026 06:05
@pljones pljones added the JSON-RPC Related to the JSON-RPC API label Jul 21, 2026
@ann0see ann0see added the AI AI generated or potentially AI generated label Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI AI generated or potentially AI generated JSON-RPC Related to the JSON-RPC API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants