From 673435498e4f27e675191bc2df12356deb0c9747 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 4 Aug 2026 21:03:43 +0000 Subject: [PATCH 1/2] feat(sql_connect): update websocket URL for GSLB soft stickiness --- .../lib/src/network/websocket_transport.dart | 5 +- .../src/network/websocket_transport_test.dart | 48 ++++++++++++++++--- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart index f456ec3fc06d..325bf670f605 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart @@ -53,13 +53,16 @@ class WebSocketTransport implements DataConnectTransport { final host = transportOptions.host; final port = transportOptions.port ?? 443; final location = options.location; + final projectId = options.projectId; + final serviceId = options.serviceId; _url = Uri( scheme: protocol, host: host, port: port, path: - '/ws/google.firebase.dataconnect.v1.ConnectorStreamService/Connect/locations/$location', + '/ws/google.firebase.dataconnect.v1.ConnectorStreamService.Connect/' + + '$projectId/locations/$location/services/$serviceId', ).toString(); _currentUid = auth?.currentUser?.uid; diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart index e45a58b63fad..afbbff322873 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/websocket_transport_test.dart @@ -13,6 +13,7 @@ // limitations under the License. import 'dart:async'; +import 'dart:io'; import 'package:firebase_app_check/firebase_app_check.dart'; import 'package:firebase_auth/firebase_auth.dart'; @@ -32,13 +33,15 @@ void main() { late MockUser mockUser1; late MockUser mockUser2; late StreamController authChangesController; + late HttpServer localHttpServer; - setUp(() { + setUp(() async { mockAuth = MockFirebaseAuth(); mockAppCheck = MockFirebaseAppCheck(); mockUser1 = MockUser(); mockUser2 = MockUser(); authChangesController = StreamController.broadcast(); + addTearDown(() => authChangesController.close()); when(mockUser1.uid).thenReturn('uid-1'); when(mockUser2.uid).thenReturn('uid-2'); @@ -46,8 +49,12 @@ void main() { when(mockAuth.idTokenChanges()) .thenAnswer((_) => authChangesController.stream); + localHttpServer = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); + addTearDown(() => localHttpServer.close(force: true)); + transport = WebSocketTransport( - TransportOptions('testhost', 443, true), + TransportOptions( + localHttpServer.address.host, localHttpServer.port, false), DataConnectOptions( 'testProject', 'testLocation', @@ -59,10 +66,7 @@ void main() { mockAppCheck, mockAuth, ); - }); - - tearDown(() async { - await authChangesController.close(); + addTearDown(() => transport.disconnect()); }); group('WebSocketTransport Idle Reconnection Guard', () { @@ -86,4 +90,36 @@ void main() { expect(transport.isConnected, isFalse); }); }); + + group('WebSocketTransport URL Validation', () { + test('should connect with the correct sticky URL path', () async { + final pathCompleter = Completer(); + localHttpServer.listen((HttpRequest request) async { + pathCompleter.complete(request.uri.path); + await request.response.close(); + }); + + final stream = transport.invokeStreamQuery( + 'testOpId', + 'testQuery', + (json) => json, + null, + null, + null, + ); + + final subscription = stream.listen((_) {}); + final actualPath = await pathCompleter.future.timeout( + const Duration(seconds: 3), + onTimeout: () => + fail('Server did not receive connection request in time'), + ); + await subscription.cancel(); + + final expectedPath = + '/ws/google.firebase.dataconnect.v1.ConnectorStreamService.Connect' + '/testProject/locations/testLocation/services/testService'; + expect(actualPath, equals(expectedPath)); + }); + }); } From 4f8b98382df6414bd0cd78371ce188e5275ccb26 Mon Sep 17 00:00:00 2001 From: Denver Coneybeare Date: Tue, 4 Aug 2026 17:51:41 -0400 Subject: [PATCH 2/2] websocket_transport.dart: fix analyze-ci error: websocket_transport.dart:64:80 - String literals shouldn't be concatenated by the '+' operator. Try removing the operator to use adjacent strings. - prefer_adjacent_string_concatenation --- .../lib/src/network/websocket_transport.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart index 325bf670f605..0cdb73bed93d 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/websocket_transport.dart @@ -60,9 +60,8 @@ class WebSocketTransport implements DataConnectTransport { scheme: protocol, host: host, port: port, - path: - '/ws/google.firebase.dataconnect.v1.ConnectorStreamService.Connect/' + - '$projectId/locations/$location/services/$serviceId', + path: '/ws/google.firebase.dataconnect.v1.ConnectorStreamService.Connect/' + '$projectId/locations/$location/services/$serviceId', ).toString(); _currentUid = auth?.currentUser?.uid;