Repository navigation
Sync onto upstream 0.7.3 (2dfa0d1) - #21
Merged
Merged
Conversation
send_dialog_request moved the dialog to Early on every non-100 provisional, including responses to our own re-INVITE or UPDATE on an established dialog. A 183 to a session-refresh re-INVITE therefore regressed a Confirmed dialog to Early for good (the final 200 does not restore it): bye() is then refused outside Confirmed and hangup() falls through to a CANCEL of the long-completed INVITE, which times out, so the call can no longer be torn down. RFC 3261 §12 has a dialog move from early to confirmed and never back, and a provisional to a mid-dialog request does not create early state. Only take the Early transition while the dialog is still in a pre-confirmation state (Calling / Trying / Early). On a confirmed dialog the provisional is still notified as before, so callers keep seeing it, but the stored state stays Confirmed. The initial INVITE path (process_invite) is unchanged, and reliable provisionals to a re-INVITE are still PRACKed. Adds tests driving a raw UDP peer: the initial INVITE's 183 still reports Early, while a 100/183/200 to an in-dialog re-INVITE or UPDATE keeps the dialog Confirmed, the 183 is still notified, and BYE succeeds.
DialogInner::transition sent the new state to subscribers before deciding whether to apply it, so ignored transitions were still notified: a second Terminated when two teardown paths race (a local BYE completing while the peer's BYE is handled), and a WaitAck on an already confirmed dialog. The late-update check added in 0.7.0 reads the state under a lock it then releases, so a concurrent transition can still slip a notification past it. Decide under the state lock, update the state, then send the notification while still holding the lock, so every notification describes a transition that happened and notifications follow the order of state changes. Updates arriving after Terminated, including the event-only variants (Updated/Notify/Info/Options/Refer), stay dropped as in 0.7.0; while the dialog is live, event-only variants are sent as before. Adds an end-to-end test over UDP through DialogLayer: the peer sends an INFO, then a BYE before the application answers it; once the INFO is answered, no Confirmed may follow the Terminated notification.
…d (RFC 3515) handle_refer left the dialog without the closing return_to_confirmed that handle_message/handle_notify have: answering a REFER (usually 202) never re-surfaced a Confirmed event, so TU-side logic waiting on it — e.g. an active-call bot's pending 100 Trying NOTIFY for the implicit refer subscription — starved, and the referrer saw the 202 but no NOTIFY ever. The stored dialog state was always correct (in-dialog request events do not change it); only the notification was missing. Adds a regression test that fails without the fix: answer the REFER, expect a Confirmed event and a working notify_refer.
… ends it Since restsend#169 the matching ACK terminates an Accepted server INVITE transaction, and cleanup() took last_response before the dialog built DialogState::Confirmed from it. Confirmed carried Response::default() (no CSeq) for the initial INVITE and every re-INVITE, so a TU could not tell which INVITE it confirms. The server INVITE transaction now keeps last_response and hands a copy to finished_transactions.
Since the unified `Dialog::Invite(InviteDialog)` (0.6.0) the lookup
matches every INVITE dialog with the Call-ID, so a B2BUA that keeps the
Call-ID across its legs gets the inbound (UAS) leg back as a client
dialog. Check the role, as the documentation says ("client-side INVITE
dialogs (UAC)") and as `Dialog::ClientInvite` did in 0.5.x.
Some WARN records carried a whole SIP message, with the From/To/Contact URIs, display names and any body: - the dialog layer's "failed to send request" (send_prack_request and send_dialog_request) printed the full request; - the WebSocket transport's "Error parsing SIP message" printed the raw text frame; - the "bye skipped" WARN of ClientInviteDialog, InviteDialog and ServerInviteDialog printed the dialog state with Debug, which for Early, WaitAck and Confirmed includes the whole response; the returned error did the same. The send failure now logs the method at WARN and the request at DEBUG. The WebSocket parse failure logs the frame length; the frame is already logged at INFO when it is received. The BYE paths use the Display of DialogState (dialog id and state name).
RFC 3261 §15.1.1: the session ends once the BYE is passed to the client transaction, and the dialog ends on a 481, a 408 or no response. `bye_with_headers` notified `Terminated` only after the BYE transaction returned Ok. When it returned an error (the target locator fails, or a 401/407 to the BYE cannot be answered) the dialog stayed `Confirmed`, and callers that remove the dialog on `Terminated` kept it forever. `DialogInner::send_bye`, used by `InviteDialog` and both deprecated wrappers, notifies `Terminated` (`UacBye` / `UasBye`, as before) whatever the transaction returns and still returns its error.
- platform::tls: TlsConnector/TlsStream/TcpStream traits (poll-based, no_std-safe) + TlsClientConfig + process-wide connector registry - transport::tls::TlsConnection::connect consults the registered connector first (embedded backends: embassy-net + embedded-tls etc., the connector owns the TCP dial); without one, the built-in rustls path is unchanged - client halves are boxed so rustls and seam streams share one TlsConnectionInner::Client representation - e2e test: a registered connector bypasses rustls entirely - readme: no_std/embedded support notes
…rly-regression fix: keep confirmed dialogs confirmed on 1xx to in-dialog requests
…ter-terminated fix: notify dialog state only for transitions that are applied
…the-final-response fix(transaction): keep a server INVITE's final response after the ACK ends it
…pect-role fix(dialog): get_client_dialog_by_call_id returns only UAC dialogs
…sip-message fix: keep whole SIP messages out of WARN logs
…g-whatever-the-outcome fix(dialog): a BYE ends the dialog even when its transaction fails
Every 2xx to the INVITE with a new To tag establishes its own dialog. The transaction already re-ACKs a forked 2xx with its own tag and remote target (restsend#172), but the Accepted-window drainer dropped it: no dialog was created for the branch and no BYE was ever sent, so the forked callee kept retransmitting its 2xx until it gave up. The drainer now inspects every message the client INVITE transaction delivers after confirmation: a 2xx whose To tag differs from the confirmed dialog's is ended with a BYE built from that response's own Contact (remote target) and Record-Route (route set), CSeq continuing the forked dialog's. The confirmed dialog's state is untouched, no dialog is registered for the branch, and no state is notified.
fix(dialog): BYE a forked 2xx's dialog (RFC 3261 §13.2.2.4)
When the `do_invite` future is dropped mid-INVITE, `DialogGuardForUnconfirmed` finds the dialog by removing it from the `DialogLayer` and did nothing when it was not there. An application that called `remove_dialog` before dropping the future (for example on its own hangup) therefore got no `Terminated`, no CANCEL after a provisional, and no BYE for a 2xx that answered the abandoned INVITE, leaving the callee in a session. The guard now keeps the INVITE's dialog and falls back to it when the layer entry is gone, until `process_invite` returns. A dialog that is still registered is handled exactly as before.
…ixes 1. A provisional response to an in-dialog request was still notified through the raw state_sender when the dialog had already terminated (the else arm of the restsend#147 fix bypassed transition's Terminated guard). A 1xx racing a peer BYE could notify Early after Terminated, breaking the nothing-after-Terminated contract. The fallback now checks is_terminated first. 2. The forked-2xx drainer sent one BYE per received 2xx, so a forked 2xx retransmission in flight before its ACK landed produced duplicate BYEs. The drainer now tracks the forked tags it already ended and sends one BYE per branch. Both regressions are pinned by tests that fail without the fixes: test_provisional_after_terminated_is_not_notified and the retransmission window of test_forked_2xx_is_byed_without_touching_the_confirmed_dialog.
…rd-after-remove-dialog fix(dialog): end a dropped INVITE whose dialog was already removed
Follow-up to restsend#183. The removed-dialog variant of the drop guard is only exercised with a crossing 2xx in the InviteOkFirst ordering. Two gaps: - A CANCEL that wins the race (487 to the INVITE) with the dialog already removed: run_cancel_answered_487 now takes a provisional code and a remove flag, so test_removed_cancel_answered_487_sends_no_bye covers Early (180) and Trying (100) — CANCEL, ACK of the 487, no BYE, exactly one Terminated(UacCancel), never Confirmed. The pre-existing test gains the same Terminated/Confirmed assertions. - The CancelOkFirst wire ordering (200 to the CANCEL before the 2xx) with the dialog removed: test_removed_dialog_2xx_after_the_cancel_response_is_acked_and_byed.
…InviteDialog The deprecated ClientInviteDialog has its own handle_reinvite, which missed the teardown that InviteDialog and ServerInviteDialog apply (RFC 3261 13.3.1.4): when the callee re-INVITEs a UAC dialog handled through the wrapper and never ACKs the 2xx, the 2xx was retransmitted until 64*T1 and then nothing happened. The dialog stayed Confirmed, with no event and no BYE. Track the 2xx and the ACK as the other two handlers do and call end_session_without_ack, so the dialog ends with TerminatedReason::Timeout and a BYE.
…k fails at once When the target locator fails, Transaction::send returns before the transaction enters Calling, so no timer is armed and nothing is ever sent. A server dialog then looks for a dial-back address; with none it went on to wait in tx.receive() for a response that cannot come, and BYE, re-INVITE, INFO and every other in-dialog request never returned. Return the send error in that case, as a client dialog already does. The dial-back retry is unchanged.
…ceeding A non-INVITE client transaction runs Timer F as Timer B, and on_timer handled Timer B only in Calling and Trying. After a provisional other than 100 (e.g. 180 to a BYE) the transaction is in Proceeding, Timer F was ignored there, and a request answered with one 1xx and then nothing never ended. RFC 3261 section 17.1.2.2: if Timer F fires in Proceeding, the TU must be informed of a timeout and the transaction terminated. Handle Timer B for a non-INVITE client in Proceeding like Timer C: a local 408 to the TU, which terminates the transaction.
…og-unacked-reinvite fix(dialog): end the session on a never-ACKed re-INVITE 2xx in ClientInviteDialog
…without-route-fails-fast fix(dialog): a server in-dialog request with no route and no dial-back fails at once
fix(transaction): Timer F ends a non-INVITE client transaction in Proceeding
0.7.3 contains every fix the fork offered upstream. Upstream's version replaces the fork's for: - R2 (restsend#147): a 1xx to an in-dialog request does not regress Confirmed. - R3 (restsend#148): only applied dialog transitions are notified. - R21 (restsend#183): a dropped INVITE whose dialog was removed still ends. The fork's take_dialog variant (no second CANCEL) is gone. - R22b (restsend#178): WARN logs without the SIP message. - R23 client half (restsend#176): get_client_dialog_by_call_id returns UAC dialogs only. The fork's UAS-only check in get_or_create_server_invite is dropped. - R24 (restsend#180): a BYE ends the dialog whatever its transaction returns. - R27 (restsend#174): Confirmed carries the 2xx its ACK confirms. Still fork-only: R15, R16, R17, R18, R19 (no Early notified for an in-dialog 1xx), R20, R22a, R24 (a UAS notifies Terminated before its BYE), R25, R28 (wire_reason), RECURSIVECX.md. Upstream's test_warn_logs now expects the WebSocket receive log at DEBUG (R22a). Fork tests for dropped rows are removed (test_late_reinvite_ack, the taken-dialog races, the role-typed lookup test, the UAC BYE test).
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.
Sync the fork onto upstream 0.7.3 (2dfa0d1, crates.io). 0.7.3 contains every fix the fork offered upstream, so upstream's version replaces the fork's variant for:
transitionunder the lockget_client_dialog_by_call_idUAC only. The fork's UAS-only check inget_or_create_server_inviteis dropped: rcx routes in-dialog requests throughmatch_dialog/get_dialogbye()that fails now returns the error (fork returnedOk)Confirmedcarries the 2xx its ACK confirmsStill fork-only (rcx needs them until its ELIMINATE PRs land): R15, R16, R17, R18, R19 (no
Earlynotified for a 1xx to an in-dialog request), R20, R22a, R24 (a UAS notifiesTerminatedbefore sending its BYE), R25, R28 (Response::wire_reason, #20), and RECURSIVECX.md.Tests
cargo test --features bench: 427 + 65 passed.cargo test: 426 + 65 passed.cargo check --no-default-features --features platform-embassy: clean.--all-targets --features bench: same warnings as upstream 0.7.3, none new.--checkon every file that differs from 2dfa0d1: clean.test_warn_logsnow expects the WebSocket receive log at DEBUG (R22a).test_in_dialog_provisionalasserts R19 on upstream's file.test_late_reinvite_ackpasses, and so do the taken-dialog races except the "no second CANCEL" assertion (R21, dropped on purpose). The R23 server lookup test and the UAC BYEOktest fail, as expected for the dropped behavior.Fork diff (
git diff --stat): vs 2dfa0d1, 50 files +2186/-2094 before, 38 files +1505/-78 after.🤖 Generated with Claude Code
https://claude.ai/code/session_01L1Gu5CifqgBjASbxYmJ6mr