Repository navigation
Conversation
|
A usability point found while building the GraphQL WebSocket client on this PR (#1594): The most common way to create a client, Options:
Option 1 seems better, since |
There was a problem hiding this comment.
🟡 Changes recommended
A concurrent connect() can register a channel after shutdown closes the channel group, leaving it open on a borrowed event loop; the group needs stayClosed = true to close late registrations.
1 open issue
What changed in this PR
This PR lets WebSocket clients reuse a started Netty HTTP client’s event loop, reducing duplicate I/O threads while preserving independently owned threads by default.
Changes:
- Adds borrowed-client configuration and a core event-loop accessor.
- Tracks WebSocket connections for cleanup without stopping borrowed resources.
- Adds lifecycle and validation tests, fixes helper exposure through
Client.setContext(), and updates the changelog.
| File | Description |
|---|---|
| org.restlet/src/main/java/org/restlet/engine/netty/NettyClientHelper.java | Exposes the connector’s event loop for extensions. |
| org.restlet/src/main/java/org/restlet/Client.java | Publishes the helper when setting a context. |
| org.restlet.ext.websocket/src/test/java/org/restlet/ext/websocket/WebSocketSharedEventLoopTestCase.java | Tests borrowed-loop lifecycle and client validation. |
| org.restlet.ext.websocket/src/main/java/org/restlet/ext/websocket/WebSocketClient.java | Adds borrowed-client settings and startup validation. |
| org.restlet.ext.websocket/src/main/java/org/restlet/ext/websocket/internal/NettyWebSocketConnector.java | Supports shared event loops and explicit connection cleanup. |
| changes.md | Documents event-loop sharing and the context-helper fix. |
Review effort: Balanced
c24fe96 to
eec9acc
Compare
WebSocketClient created its own Netty event loop group in every instance, next to the one of the core HTTP client connector, with a thread count that could not be configured. - Add NettyClientHelper.getEventLoopGroup(), an engine extension point exposing the client connector's event loop group. - Add WebSocketClient.setClient(Client): a borrowed, started Netty HTTP client whose event loop is shared. Without it, the client still creates its own threads. - Track the WebSocket connections in a channel group, so stopping the client closes its sessions without shutting down a shared event loop. - Store the helper in the "org.restlet.engine.helper" context attribute in Client.setContext(), as the constructors do. Clients added with Component.getClients().add(Protocol) get their context this way, and did not expose their helper. Fixes #1567 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
A connect() racing with stop() could register its channel after the channel group was closed. With a borrowed event loop, which isn't shut down, that connection stayed open. Keep the group closed so that late channels are closed when added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Jerome Louvel <374450+jlouvel@users.noreply.github.com>
eec9acc to
5c2aa48
Compare
|




Fixes #1567.
Summary
Each
WebSocketClientcreated its own Netty event loop group, next to the one of the core HTTP client connector, with a thread count that couldn't be configured. A WebSocket client can now borrow a started Netty HTTPClientand run on its event loop:Without
setClient, the WebSocket client still creates its own threads, so existing code is unaffected.Changes
Core
NettyClientHelper.getEventLoopGroup(): a new engine extension point that exposes the client connector's event loop group, ornullwhen the connector is stopped. It is documented likeNettyConnectionUpgrade: meant for Restlet extensions, and callers must not shut the group down.Client.setContext()now stores the helper in theorg.restlet.engine.helpercontext attribute, as the constructors already do. Clients added withComponent.getClients().add(Protocol)get their context throughsetContext(), so they didn't expose their helper, contrary to theClientJavadoc.WebSocket extension
WebSocketClient.setClient(Client)/getClient(). Onstart(), the client looks up the borrowed client'sNettyClientHelperthrough the context attribute. It throwsIllegalStateExceptionif the client has no context, doesn't use the Netty HTTP connector, or isn't started.NettyWebSocketConnectortracks its connections in a channel group. Onstop(), it closes them and shuts down the event loop only if it created it. It used to rely on shutting down its own group to close sessions.connect()throws anIOException.Only the event loop is shared. TLS and the other settings remain those of the
WebSocketClient. Sharing the client connector's TLS configuration could be a follow-up.Testing
WebSocketSharedEventLoopTestCase(4 tests):mvn -pl org.restlet.ext.websocket verifypasses locally: 32 tests.org.restlet, theClient,ClientList,ClientResource, child client dispatcher, SSL client and Netty connection upgrade tests pass locally (55 tests in those classes). The full core suite was not run locally.🤖 Generated with Claude Code