Skip to content

EXPERIMENTAL: Improve WebSocket Re-Connections #1216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 31 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
82e9b58
shouldAttach change
jpsantosbh Apr 16, 2025
be09de2
reenable tests
jpsantosbh Apr 17, 2025
e195e97
Merge branch 'main' into joao/fix_network_handoff
jpsantosbh Apr 22, 2025
d90b023
no `verto.invite` for simple re connections
jpsantosbh Apr 22, 2025
2fd2f12
changeset
jpsantosbh Apr 22, 2025
783d4e8
Merge branch 'main' into joao/fix_network_handoff
jpsantosbh Apr 22, 2025
571132f
Merge branch 'main' into joao/fix_network_handoff
jpsantosbh Apr 23, 2025
8e7faa8
review changes
jpsantosbh Apr 23, 2025
26a9a10
use dialAddress
jpsantosbh Apr 24, 2025
d6a9bed
manually tested
jpsantosbh Apr 25, 2025
2cea4c1
fix ws re-connections
jpsantosbh May 9, 2025
2982f35
revert expect auth)state
jpsantosbh May 12, 2025
f549670
outbound calls reconnects
jpsantosbh May 16, 2025
02cb470
fix import
jpsantosbh May 16, 2025
595fd47
skip test
jpsantosbh May 16, 2025
67b77c0
restore auth retry
jpsantosbh May 19, 2025
5c60b10
incoming calls reattach
jpsantosbh May 21, 2025
26cb6f1
Merge branch 'main' into joao/fix_reconnections
jpsantosbh May 22, 2025
85c58cc
cleanup
jpsantosbh May 23, 2025
406877b
calee reattach fixed
jpsantosbh May 27, 2025
39f46b0
cleanup
jpsantosbh May 27, 2025
1e44780
Merge branch 'main' into joao/fix_reconnections
jpsantosbh May 29, 2025
475184b
merge changes from slipped
jpsantosbh May 30, 2025
7a6e1f6
merge changes from slipped
jpsantosbh May 30, 2025
91e824e
export interface
jpsantosbh Jun 3, 2025
21e1106
Merge branch 'main' into joao/fix_reconnections
jpsantosbh Jun 4, 2025
26dec6c
merge changes from splited
jpsantosbh Jun 4, 2025
3e681da
formatting
jpsantosbh Jun 4, 2025
1ae44a6
quick test
jpsantosbh Jun 4, 2025
6714c64
revert
jpsantosbh Jun 4, 2025
dbedf20
fix
jpsantosbh Jun 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/floppy-schools-send.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@signalwire/webrtc': minor
'@signalwire/core': minor
'@signalwire/js': minor
'@sw-internal/e2e-js': patch
---

CHANGED improved the handling of WebSockets reconnections.
7 changes: 7 additions & 0 deletions .changeset/twelve-worlds-mate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@sw-internal/e2e-js': patch
'@signalwire/webrtc': patch
'@signalwire/js': patch
---

Fix CF network re-connections
335 changes: 1 addition & 334 deletions internal/e2e-js/tests/callfabric/websocket_reconnect.spec.ts
Original file line number Diff line number Diff line change
@@ -1,334 +1 @@
import { test } from '../../fixtures'
import { SERVER_URL, createCFClient, expectMCUVisible } from '../../utils'

test.describe('CallFabric Reconnections', () => {
test.skip('Should reconnect the WebSocket as soon it gets onClose event, without media renegotiation', async ({
createCustomPage,
}) => {
const page = await createCustomPage({ name: '[page]' })

await page.goto(SERVER_URL)

const roomName = 'cf-e2e-test-room'

await createCFClient(page)

page.resetWsTraffic()
// Dial an address and join a video room
await page.evaluate(
async ({ roomName }) => {
return new Promise<any>(async (resolve, _reject) => {
const client = window._client!

const call = await client.dial({
to: `/public/${roomName}`,
rootElement: document.getElementById('rootElement'),
})

call.on('room.joined', resolve)
call.on('room.updated', () => {})

// @ts-expect-error
window._roomObj = call

await call.start()
})
},
{ roomName }
)

await page.expectWsTraffic({
assertations: [
{
type: 'send',
name: 'connect',
expect: {
method: 'signalwire.connect',
'params.version.major': 4,
},
},
{
type: 'recv',
name: 'connect-response',
expect: {
'result.authorization.jti': /.+/,
'result.authorization.project_id':
'cb1e91b6-ae04-4be0-89ae-0dffc5ea6aed',
'result.authorization.fabric_subscriber.subscriber_id':
'48fe0d0c-ac31-4222-93c9-39590ce92d78',
},
},
{
type: 'recv',
name: 'authorization-state',
expect: {
method: 'signalwire.event',
'params.event_type': 'signalwire.authorization.state',
'params.params.authorization_state': /.+/,
},
},
{
type: 'send',
name: 'invite',
expect: {
method: 'webrtc.verto',
'params.message.method': 'verto.invite',
'params.message.params.dialogParams.callID': /.+/,
'params.message.params.dialogParams.destination_number':
'/public/cf-e2e-test-room',
'params.message.params.sdp':
/^(?=.*a=setup:actpass.*)(?=.*^m=audio.*)(?=.*^m=video.*)/ms,
},
},
{
type: 'recv',
name: 'conversation-call_started',
expect: {
method: 'signalwire.event',
'params.event_type': 'conversation.message',
'params.params.type': 'message',
'params.params.kind': 'call_started',
},
},
{
type: 'recv',
name: 'call-created',
expect: {
'result.code': '200',
'result.result.result.message': 'CALL CREATED',
},
},
{
type: 'recv',
name: 'verto-answer',
expect: {
method: 'signalwire.event',
'params.event_type': 'webrtc.message',
'params.params.method': 'verto.answer',
'params.params.params.sdp':
/^(?=.*a=setup:(?:active|passive))(?=.*^m=audio.*)(?=.*^m=video.*)/ms,
},
},
{
type: 'recv',
name: 'mediaParams',
expect: {
method: 'signalwire.event',
'params.event_type': 'webrtc.message',
'params.params.method': 'verto.mediaParams',
},
},
{
type: 'recv',
name: 'mediaParams',
expect: {
method: 'signalwire.event',
'params.event_type': 'call.joined',
},
},
],
})

await expectMCUVisible(page)

// simulate ws
page.resetWsTraffic()
await page.evaluate(async () => {
//@ts-ignore
window._roomObj._closeWSConnection()
return new Promise((res) => {
setTimeout(() => res(null), 15000)
})
})

await page.expectWsTraffic({
assertations: [
{
type: 'send',
name: 'reconnect',
expect: {
method: 'signalwire.connect',
'params.version.major': 4,
'params.authorization_state': /.+/,
},
},
{
type: 'send',
name: 'invite',
expectNot: {
method: 'webrtc.verto',
'params.message.method': 'verto.invite',
'params.message.params.dialogParams.callID': /.+/,
'params.message.params.dialogParams.destination_number': /^\/.+/,
'params.message.params.sdp':
/^(?=.*a=setup:actpass.*)(?=.*^m=audio.*)(?=.*^m=video.*)/ms,
},
},
],
})
})

test.skip('Should reconnect the WebSocket when network is up (before FS timeout), without media renegotiation', async ({
createCustomPage,
}) => {
const page = await createCustomPage({ name: '[page]' })

await page.goto(SERVER_URL)

const roomName = 'cf-e2e-test-room'

await createCFClient(page)

page.resetWsTraffic()
// Dial an address and join a video room
await page.evaluate(
async ({ roomName }) => {
return new Promise<any>(async (resolve, _reject) => {
const client = window._client!

const call = await client.dial({
to: `/public/${roomName}`,
rootElement: document.getElementById('rootElement'),
})

call.on('room.joined', resolve)
call.on('room.updated', () => {})

// @ts-expect-error
window._roomObj = call

await call.start()
})
},
{ roomName }
)

await page.expectWsTraffic({
assertations: [
{
type: 'send',
name: 'connect',
expect: {
method: 'signalwire.connect',
'params.version.major': 4,
},
},
{
type: 'recv',
name: 'connect-response',
expect: {
'result.authorization.jti': /.+/,
'result.authorization.project_id':
'cb1e91b6-ae04-4be0-89ae-0dffc5ea6aed',
'result.authorization.fabric_subscriber.subscriber_id':
'48fe0d0c-ac31-4222-93c9-39590ce92d78',
},
},
{
type: 'recv',
name: 'authorization-state',
expect: {
method: 'signalwire.event',
'params.event_type': 'signalwire.authorization.state',
'params.params.authorization_state': /.+/,
},
},
{
type: 'send',
name: 'invite',
expect: {
method: 'webrtc.verto',
'params.message.method': 'verto.invite',
'params.message.params.dialogParams.callID': /.+/,
'params.message.params.dialogParams.destination_number':
'/public/cf-e2e-test-room',
'params.message.params.sdp':
/^(?=.*a=setup:actpass.*)(?=.*^m=audio.*)(?=.*^m=video.*)/ms,
},
},
{
type: 'recv',
name: 'conversation-call_started',
expect: {
method: 'signalwire.event',
'params.event_type': 'conversation.message',
'params.params.type': 'message',
'params.params.kind': 'call_started',
},
},
{
type: 'recv',
name: 'call-created',
expect: {
'result.code': '200',
'result.result.result.message': 'CALL CREATED',
},
},
{
type: 'recv',
name: 'verto-answer',
expect: {
method: 'signalwire.event',
'params.event_type': 'webrtc.message',
'params.params.method': 'verto.answer',
'params.params.params.sdp':
/^(?=.*a=setup:(?:active|passive))(?=.*^m=audio.*)(?=.*^m=video.*)/ms,
},
},
{
type: 'recv',
name: 'mediaParams',
expect: {
method: 'signalwire.event',
'params.event_type': 'webrtc.message',
'params.params.method': 'verto.mediaParams',
},
},
{
type: 'recv',
name: 'mediaParams',
expect: {
method: 'signalwire.event',
'params.event_type': 'call.joined',
},
},
],
})

await expectMCUVisible(page)

page.resetWsTraffic()
await page.swNetworkDown()
await page.waitForTimeout(14_500)
await page.swNetworkUp()

//wait network traffic
await page.waitForTimeout(5000)

await page.expectWsTraffic({
assertations: [
{
type: 'send',
name: 'reconnect',
expect: {
method: 'signalwire.connect',
'params.version.major': 4,
'params.authorization_state': /.+/,
},
},
{
type: 'send',
name: 'invite',
expectNot: {
method: 'webrtc.verto',
'params.message.method': 'verto.invite',
'params.message.params.dialogParams.callID': /.+/,
'params.message.params.dialogParams.destination_number': /^\/.+/,
'params.message.params.sdp':
/^(?=.*a=setup:actpass.*)(?=.*^m=audio.*)(?=.*^m=video.*)/ms,
},
},
],
})
})
})
// TODO needs cloud-product/issues/14634
10 changes: 10 additions & 0 deletions packages/core/src/BaseComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,16 @@ export class BaseComponent<
return this._attachWorker(name, def)
}

public cancelWorker(workerTask: Task) {
const foundTaskIndex = this._runningWorkers.findIndex(
(task) => task === workerTask
)
if (foundTaskIndex > -1) {
this._runningWorkers.splice(foundTaskIndex, 1)
workerTask.cancel()
}
}

private _setWorker<Hooks extends SDKWorkerHooks = SDKWorkerHooks>(
name: string,
def: SDKWorkerDefinition<Hooks>
Expand Down
Loading
Loading