The built-in WebSocket transport is optional - #42
Merged
Merged
Conversation
Courier auto-registers "ws" only when Config::host is set and defaultTransport is unset or "ws". Sandbox's constructor asked for it unconditionally, so a Sandbox whose default lane is another transport — defaultTransport = "mqtt", for a device consolidating everything onto one MQTT connection — tripped Client::transport<T>'s assert, and under NDEBUG bound a null reference that crashed at the first ws() call. _ws now stays null when Courier did not register one, hasWs() reports it, and both internal ws() callers are gated on it: the WS-path default in onCourierTransportsWillConnect and the mic-stream fallback in updateMicStream. ws() keeps its assert, so a caller that needs WS still fails loudly rather than late. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The legacy un-channelled path logged "[deprecated] un-channelled ..." and then offered the frame to onMessageFilter. But the filter is the documented interposition point, so a host that consumes a frame there never took delivery of a deprecated message. It matters for a platform wrapper whose default transport routes its own traffic. Courier's client hook fires ALONGSIDE a per-transport hook rather than instead of it, so a wrapper that routes, say, MQTT by topic sees every inbound frame a second time on the client lane and consumes it in the filter. Logging each of those as deprecated was both noise and a lie. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Condense the changelog entries to one line each and drop the -dev suffix from library.json / idf_component.yml. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two fixes a consumer needs before it can stop holding a WebSocket it does not use.
The built-in WebSocket transport is optional
Courier auto-registers
"ws"only whenConfig::hostis set anddefaultTransportis unset or"ws", and it logs the skip otherwise.Sandbox'sconstructor did not mirror that: it cached
courier().transport<WebSocketTransport>("ws")unconditionally. So a Sandboxpointed at another default lane —
defaultTransport = "mqtt", for a deviceconsolidating everything onto one MQTT connection — tripped
Client::transport<T>'s assert, and underNDEBUGbound a null reference thatcrashed at the first
ws()call instead._wsnow stays null when Courier did not register one,hasWs()reports it, andboth internal
ws()callers are gated on it: the WS-path default inonCourierTransportsWillConnect, and the mic-stream fallback inupdateMicStream.ws()keeps its assert, so a caller that genuinely needs WSstill fails loudly rather than late.
The message filter runs before the deprecation notice
The legacy un-channelled path logged
[deprecated] un-channelled ...and thenoffered the frame to
onMessageFilter. But the filter is the documentedinterposition point, so a host that consumes a frame there never took delivery of
a deprecated message.
It matters for a platform wrapper whose default transport routes its own traffic.
Courier's client hook fires alongside a per-transport hook rather than instead
of it, so a wrapper that routes (say) MQTT by topic sees every inbound frame a
second time on the client lane and consumes it in the filter. Logging each of
those as deprecated was both noise and a lie — and at MQTT message rates, a line
per message.
Verification
Consumed by
inanimate-tech/hawthorn-firmwareon its voice-over-MQTT branch andrun on hardware (M5Stack StopWatch, ESP32-S3): boots, registers, connects MQTT as
its only persistent transport, and streams a voice session with no WebSocket
present. 481 native tests pass in that repo, including its Sandbox and
message-dispatch suites.
🤖 Generated with Claude Code