qml: fix the Protocols.* bindings a manual QML corpus exercises - #939
Merged
Merged
Conversation
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
jcelerier
had a problem deploying
to
Apple Certificate
September 14, 2026 03:05 — with
GitHub Actions
Error
The Protocols.* bindings had a number of defects that a corpus of manual QML scenarios makes visible as soon as they are run against real sockets: - line framing dropped every complete line after the first of a coalesced read, and never routed EOF through validate_stream, so a line-framed socket could not observe a remote close; - an Intel HEX / S-record EOF record, which decodes to no payload, was dispatched as an empty message; - TCP, Unix-stream and serial sockets only armed their read loop when the script had a message callback, so a client without one never saw the peer close; an abortive close was never reported at all; - onFail, which the documentation advertises, was never read: only onError was; - inboundWS did not listen: it allocated a state object, called onOpen and returned; - the WebSocket client invoked its QJSValue callbacks straight from the websocketpp thread; - the HTTP client had two implementations, the legacy one dropping the query string, sending a made-up Host, and reporting neither non-2xx responses nor errors; both mishandled HEAD, bodiless responses, interim 1xx replies, truncated bodies and Content-Length as an upper bound; - MIDI/UMP endpoints could not be reopened after a round-trip through QML, since port_information::api was not serialized; - an unqualified Device address resolved against every device of the document rather than the script's own; - sockets opened during QML property evaluation ran their onOpen before the Mapper's tree existed, silently dropping the Device.write they made. Also make every target that compiles boost.asio agree on BOOST_ASIO_ENABLE_BUFFER_DEBUGGING: libremidi did not, which is an ODR violation in a Debug build.
effective_type() returns what the port declares and falls back to the address, but the case covering a port that declares a unit still required the address's one, so the suite went red on master.
jcelerier
force-pushed
the
fix/qml-protocol-transport-defects
branch
from
September 14, 2026 05:21
6a68086 to
614d49c
Compare
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.
A corpus of manual QML protocol scenarios, run against real sockets from score's Mapper device, turns up a number of defects in the
Protocols.*bindings. Each one below is covered by an integration test in the companion score PR, and each was proven by reverting the fix and watching that test go red.Framing and transports
line_framing_decoder::receive()cleared the shared read buffer, butasync_read_untilreports the delimiter without consuming what follows it, so the rest of a coalesced read was destroyed. Now every complete line of a read is dispatched in order and a trailing partial line is kept for the next one.ec.failed()beforevalidate_stream, so the framing most hardware protocols use could not report a close. EOF now routes throughvalidate_streamwithout re-arming a dead socket.on_closeis emitted at most once.onFailwas dead. The documentation block advertises it; every factory read onlyonError. It is now a documented alias.WebSocket
inboundWSdid not listen: it allocated a state object, calledonOpenand returned. It now runs a real server on the shared asio context, with a per-connection object, and releases the listener on destruction.QJSValuecallbacks straight from the websocketpp thread; they now hop to the object's Qt thread like everything else.HTTP
There were two client implementations; the legacy
Protocols.http(url, cb, verb)used the older one, which dropped the query string, sent a hard-codedHost, silently discarded every non-2xx response and had a no-op error path. It is now an adapter over the single implementation, which additionally learned: bodiless responses (HEAD, 1xx, 204, 304, 2xx-to-CONNECT) complete at end of headers, a body truncated beforeContent-Lengthis an error rather than a success,Content-Lengthis an upper bound, and the authority is built correctly (port when non-default, brackets for IPv6, punycode for IDN, userinfo, CRLF rejected in header values).MIDI / UMP
port_information::apiwas not serialized, so an endpoint that round-tripped through QML came back asAPI(0)and libremidi refused to open it. It now round-trips, and an endpoint without a usable API reports why.Mapper scripting
Device.write("/leaf")resolved against every device in the document and could land on a sibling; it now resolves in the script's own device, whilename:/pathstill reaches any.onOpenbefore the Mapper's tree existed, so theDevice.writethey made was silently dropped. Opening is deferred to the event loop.Build
Every target that compiles boost.asio in one binary must agree on
BOOST_ASIO_ENABLE_BUFFER_DEBUGGING; libremidi did not, which is an ODR violation in Debug and shows up under ASan as anew-delete-type-mismatchon the resolver thread.