From 2b7b4f54cb3fbb3f411963211b83819f5127b1c1 Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 19 Mar 2026 09:30:43 +0100 Subject: [PATCH 1/2] fix(data_connect): fix UTF 8 characters decoding in data connect --- .../lib/src/network/rest_transport.dart | 7 ++-- .../test/src/network/rest_transport_test.dart | 36 +++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart index 708d6fa02010..2680f692e350 100644 --- a/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart +++ b/packages/firebase_data_connect/firebase_data_connect/lib/src/network/rest_transport.dart @@ -116,11 +116,12 @@ class RestTransport implements DataConnectTransport { headers: headers, ); Map bodyJson = - jsonDecode(r.body) as Map; + jsonDecode(utf8.decode(r.bodyBytes)) as Map; if (r.statusCode != 200) { - String message = - bodyJson.containsKey('message') ? bodyJson['message']! : r.body; + String message = bodyJson.containsKey('message') + ? bodyJson['message']! + : utf8.decode(r.bodyBytes); throw DataConnectError( r.statusCode == 401 ? DataConnectErrorCode.unauthorized diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart index 83a03779c5b9..a9122b96d5ef 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart @@ -274,6 +274,42 @@ void main() { ).called(1); }); + test( + 'regression #17290 - invokeOperation should correctly decode UTF-8 response with international characters', + () async { + // Simulate a server response with Korean characters, where the + // Content-Type header does NOT include charset=utf-8 (which is + // what the Firebase emulator sends). Without explicit UTF-8 + // decoding, the http package defaults to latin1, corrupting + // multi-byte characters. + final koreanJson = '{"data": {"name": "\ud55c\uad6d\uc5b4 \ud14c\uc2a4\ud2b8"}}'; + final mockResponse = http.Response.bytes( + utf8.encode(koreanJson), + 200, + headers: {'content-type': 'application/json'}, + ); + when( + mockHttpClient.post( + any, + headers: anyNamed('headers'), + body: anyNamed('body'), + ), + ).thenAnswer((_) async => mockResponse); + + final deserializer = (String data) => 'Deserialized Data'; + + final result = await transport.invokeOperation( + 'testQuery', + 'executeQuery', + deserializer, + null, + null, + null, + ); + + expect(result.data['data']['name'], equals('\ud55c\uad6d\uc5b4 \ud14c\uc2a4\ud2b8')); + }); + test( 'invokeOperation should handle missing auth and appCheck tokens gracefully', () async { From 0d34819d950fc8802632f69fe27a1b265860189f Mon Sep 17 00:00:00 2001 From: Guillaume Bernos Date: Thu, 19 Mar 2026 10:55:16 +0100 Subject: [PATCH 2/2] format --- .../test/src/network/rest_transport_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart index a9122b96d5ef..bad97d364d32 100644 --- a/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart +++ b/packages/firebase_data_connect/firebase_data_connect/test/src/network/rest_transport_test.dart @@ -282,7 +282,8 @@ void main() { // what the Firebase emulator sends). Without explicit UTF-8 // decoding, the http package defaults to latin1, corrupting // multi-byte characters. - final koreanJson = '{"data": {"name": "\ud55c\uad6d\uc5b4 \ud14c\uc2a4\ud2b8"}}'; + const koreanJson = + '{"data": {"name": "\ud55c\uad6d\uc5b4 \ud14c\uc2a4\ud2b8"}}'; final mockResponse = http.Response.bytes( utf8.encode(koreanJson), 200, @@ -307,7 +308,8 @@ void main() { null, ); - expect(result.data['data']['name'], equals('\ud55c\uad6d\uc5b4 \ud14c\uc2a4\ud2b8')); + expect(result.data['data']['name'], + equals('\ud55c\uad6d\uc5b4 \ud14c\uc2a4\ud2b8')); }); test(