Skip to content

Fix bugs#51

Merged
atdrendel merged 3 commits intomainfrom
improve-reconnection
Apr 20, 2026
Merged

Fix bugs#51
atdrendel merged 3 commits intomainfrom
improve-reconnection

Conversation

@atdrendel
Copy link
Copy Markdown
Contributor

  • Throw an error when pushing to an unjoined channel
  • Rejoin a channel immediately after join fails
  • Send leave channel to server when join times out
  • Forward all properties in MakeWebSocket to created WebSocket
  • Reconnect to socket when messages ends
  • Ensure disconnection replies do not affect subsequent connections

- Rejoin channel immediately after join fails
- Throw error when pushing unjoined channel
- Leave channel on join timeout
Copilot AI review requested due to automatic review settings April 20, 2026 01:58
@atdrendel atdrendel merged commit 1c636a2 into main Apr 20, 2026
3 checks passed
@atdrendel atdrendel deleted the improve-reconnection branch April 20, 2026 01:59
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens Phoenix socket/channel lifecycle behavior around connect/disconnect/reconnect edge-cases, and improves protocol correctness during join/rejoin flows.

Changes:

  • Add stricter channel behavior (throw when sending/requesting before join(); send phx_leave after join timeout; ignore stale close/error messages while rejoining).
  • Improve socket resilience (reconnect when the incoming message stream finishes; handle connect() during client-driven closing; avoid stale close affecting new connections; fix reconnect backoff indexing).
  • Update WebSocket creation to forward MakeWebSocket parameters and bump websocket-apple to 4.1.1.

Reviewed changes

Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Tests/PhoenixTests/PhoenixSocketTests.swift Adds coverage for connect-while-closing and reconnect when the message stream ends unexpectedly.
Tests/PhoenixTests/PhoenixChannelTests.swift Adds/adjusts join/rejoin tests, validates pre-join send/request throws, and verifies leave-before-rejoin on join timeout + stale-close handling.
Tests/PhoenixTests/MessageTests.swift Adds regression test for decoding malformed/short messages.
Sources/Phoenix/Socket.swift Updates system socket factory to forward options/callbacks and propagate WebSocket id.
Sources/Phoenix/PhoenixSocket.swift Refines connection state transitions, push preparation, stream-finish handling, and reconnect delay calculation.
Sources/Phoenix/PhoenixError.swift Introduces channelNotJoined error case.
Sources/Phoenix/PhoenixChannel.swift Enforces join-before-send/request, tracks join refs for stale message filtering, and sends leave after join timeout.
Sources/Phoenix/Message.swift Adds array-length validation during decode to avoid out-of-bounds access.
Package.swift Bumps websocket-apple dependency constraint to 4.1.1.
Package.resolved Updates resolved websocket-apple version/revision accordingly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +278 to +285
let push = Push(
topic: topic,
event: .leave,
timeout: Date(timeIntervalSinceNow: timeout)
)

push.prepareToSend(ref: await socket.makeRef(), joinRef: joinRef)
try? await socket.send(push)
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sendLeaveAfterJoinTimeout awaits socket.send(push), which waits until the leave push is actually sent (and can itself take up to the socket/join timeout). This can extend the overall join() call beyond the intended timeout in cases where sending is blocked. Consider firing the leave push in a detached task (best-effort) or adding a non-blocking send path so the original join timeout returns promptly while still attempting to notify the server.

Suggested change
let push = Push(
topic: topic,
event: .leave,
timeout: Date(timeIntervalSinceNow: timeout)
)
push.prepareToSend(ref: await socket.makeRef(), joinRef: joinRef)
try? await socket.send(push)
Task.detached { [socket, topic] in
let push = Push(
topic: topic,
event: .leave,
timeout: Date(timeIntervalSinceNow: timeout)
)
push.prepareToSend(ref: await socket.makeRef(), joinRef: joinRef)
try? await socket.send(push)
}

Copilot uses AI. Check for mistakes.

private struct NotReadyToJoinError: Error {}

private struct JoinTimeOutError: Error {
Copy link

Copilot AI Apr 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JoinTimeOutError uses inconsistent capitalization compared to TimeoutError ("TimeOut" vs "Timeout"). Renaming to JoinTimeoutError would better match Swift naming conventions and the existing type name.

Suggested change
private struct JoinTimeOutError: Error {
private struct JoinTimeoutError: Error {

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants