diff --git a/packages/core/realtime-js/src/lib/transformers.ts b/packages/core/realtime-js/src/lib/transformers.ts index ba5a19972..c034a0b67 100644 --- a/packages/core/realtime-js/src/lib/transformers.ts +++ b/packages/core/realtime-js/src/lib/transformers.ts @@ -251,8 +251,21 @@ export const toTimestampString = (value: RecordValue): RecordValue => { } export const httpEndpointURL = (socketUrl: string): string => { - let url = socketUrl - url = url.replace(/^ws/i, 'http') - url = url.replace(/(\/socket\/websocket|\/socket|\/websocket)\/?$/i, '') - return url.replace(/\/+$/, '') + '/api/broadcast' + const wsUrl = new URL(socketUrl) + + wsUrl.protocol = wsUrl.protocol.replace(/^ws/i, 'http') + + wsUrl.pathname = wsUrl.pathname + .replace(/\/+$/, '') // remove all trailing slashes + .replace(/\/socket\/websocket$/i, '') // remove the socket/websocket path + .replace(/\/socket$/i, '') // remove the socket path + .replace(/\/websocket$/i, '') // remove the websocket path + + if (wsUrl.pathname === '' || wsUrl.pathname === '/') { + wsUrl.pathname = '/api/broadcast' + } else { + wsUrl.pathname = wsUrl.pathname + '/api/broadcast' + } + + return wsUrl.href } diff --git a/packages/core/realtime-js/test/transformers.test.ts b/packages/core/realtime-js/test/transformers.test.ts index f2b40a9b5..139868743 100644 --- a/packages/core/realtime-js/test/transformers.test.ts +++ b/packages/core/realtime-js/test/transformers.test.ts @@ -6,6 +6,7 @@ import { convertChangeData, convertColumn, toArray, + httpEndpointURL, toJson, toTimestampString, } from '../src/lib/transformers' @@ -150,3 +151,74 @@ test('toArray with non-array strings', () => { assert.strictEqual(toArray('missing_closing', 'text'), 'missing_closing') assert.strictEqual(toArray('missing_opening}', 'text'), 'missing_opening}') }) + +test('httpEndpointURL', () => { + // Test basic ws to http conversion + assert.strictEqual( + httpEndpointURL('ws://example.com/socket/websocket'), + 'http://example.com/api/broadcast' + ) + + // Test wss to https conversion + assert.strictEqual( + httpEndpointURL('wss://example.com/socket/websocket'), + 'https://example.com/api/broadcast' + ) + + // Test with /socket path + assert.strictEqual(httpEndpointURL('ws://example.com/socket'), 'http://example.com/api/broadcast') + + // Test with /websocket path + assert.strictEqual( + httpEndpointURL('ws://example.com/websocket'), + 'http://example.com/api/broadcast' + ) + + // Test with trailing slash + assert.strictEqual( + httpEndpointURL('ws://example.com/socket/websocket/'), + 'http://example.com/api/broadcast' + ) + + // Test with port number + assert.strictEqual( + httpEndpointURL('ws://example.com:8080/socket/websocket'), + 'http://example.com:8080/api/broadcast' + ) + + // Test with path prefix + assert.strictEqual( + httpEndpointURL('ws://example.com/prefix/socket/websocket'), + 'http://example.com/prefix/api/broadcast' + ) + + // Test with query parameters + assert.strictEqual( + httpEndpointURL('ws://example.com/socket/websocket?apikey=test'), + 'http://example.com/api/broadcast?apikey=test' + ) + + // Test already http protocol (should remain unchanged) + assert.strictEqual( + httpEndpointURL('http://example.com/socket/websocket'), + 'http://example.com/api/broadcast' + ) + + // Test already https protocol (should remain unchanged) + assert.strictEqual( + httpEndpointURL('https://example.com/socket/websocket'), + 'https://example.com/api/broadcast' + ) + + // Test with multiple trailing slashes + assert.strictEqual( + httpEndpointURL('ws://example.com/socket/websocket///'), + 'http://example.com/api/broadcast' + ) + + // Test with no websocket-specific paths + assert.strictEqual( + httpEndpointURL('ws://example.com/some/path'), + 'http://example.com/some/path/api/broadcast' + ) +})