Skip to content

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 18 Sep 12:46
· 82 commits to main since this release
v0.6.0
4815185

A transfer could die before it started, with "Out-of-order frame: expected 0, got
1".
Intermittently, and most often from the command line. Every control message —
the file's description, the replies to it, and the note each side sends about the
network route — carries a number that becomes part of its encryption, and the
receiving side refuses one that arrives out of turn. Each was numbered, then encrypted,
then sent, so two sent at the same moment could reach the other device in whichever
order they finished encrypting — and the command line sends its route note and the
file description back to back. Numbering, encrypting and sending are now one step,
taken in order for each direction. Nothing changes on the wire, so an older version on
the other end is unaffected.

The same message had a second cause, found by running two copies of the command line
against each other. A device threw away anything the other sent before it was ready to
listen, and the command line is not ready until its person has confirmed the four
symbols — while the other device may have been confirmed first and already sent its
route note. That lost note made the next message look out of turn. Messages from the
paired device that arrive early are now kept, a bounded few of them, until this side is
ready. This affected the command line receiving from another command line or from the
website, and the command line sending to another command line.

For code using the package directly: sendControl is now exported, and the ordering
asks one thing of callers — give createReceiver, sendFile and sendPathVerdict on
one side the same nextControlIndex function, not separate closures over one counter.

A receive that fails says why. Any failure to open the place a file was being
saved used to be reported as the save dialog having been closed, and the sender was
told the file had been declined. The real error now reaches both screens. And when a
receive failed partway, every piece of the file still in flight failed again and
replaced the message, so the one on screen was almost never the cause; the first
failure now ends the receive and is the one shown.

A cancelled or interrupted receive does not leave a partial file. Cancel in the
browser now abandons what was written, leaving the empty file the save dialog
created, as a Beam receive already did. The command line deletes the partial file
when the sender disconnects mid-transfer or when you press Ctrl-C; its Ctrl-C handler
had long said it did this, and did not.

Ctrl-C on the command line tells the other device. It used to exit without
leaving the connection, so the other side carried on until the connection timed
out: a sender went on showing "Sent 340 MB" for 11 seconds after its receiver had
been interrupted. Measured again with the fix, the sender reported the disconnect
within a tenth of a second.

For code using the package directly: a paired room's close() now returns a promise
that settles once the peer has been told, and never rejects. A process about to exit
should await it.

A send whose receiver leaves partway through says so, and stops. It used to keep
sending the rest of the file to nobody and then wait forever for a reply that was
never coming — the command line printed "Sent 64 MB of 64 MB" and was still running
two minutes later, and the browser showed the disconnect while the send carried on
underneath it. The send now ends at the next piece of the file with "The other device
disconnected".

For code using the package directly: a control stream's fail() now lasts. Every
later next() and the flow gate reject with it, once no message that had already
arrived answers the wait, and a new throwIfFailed() is what sendFile checks
between chunks.

A receive in the browser whose sender leaves says so, and stops. The receiving
side had the same gap as the send above, most visibly when the sender left while the
save dialog was open: choosing a location then drew "Receiving, 0%" over the error,
and it stayed there. The receive now ends on "Transfer failed" with "The other device
disconnected" and gives up the file it had opened. The command line was not affected.

A finished send is no longer reported as "The other device disconnected". The
file arrived whole and both ends computed its digest, and then the sender said the
other device had gone and the command line exited 1. The receiving side sends "done"
and closes, and both reach the sender in that order — but "done" still has to be
decrypted when the news of the close is handled, so the close won. The sender now
finishes reading what had already arrived before it treats the other side as gone.
A decline, or an error the receiver reported just before closing, had the same
problem and now reaches the sender as what it was. This is the intermittent failure
of the command-line interop test, which had been blamed on the receiver closing
before its reply was sent.

For code using the package directly: createReceiver returns a settled() that
resolves once every frame already handed to it has been processed. Wait on it before
failing a control stream because the peer left.

For code using the package directly: a createSink may now resolve null to mean
the person chose not to save, which declines, while a rejection is a failure that
accept() passes back to its caller rather than turning into a decline. The web
createSink resolves null on a dismissed picker instead of rejecting with
AbortError. createReceiver returns a cancel() that aborts the open sink.