mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-20 06:28:27 +02:00
fix(network): close request-owned HTTP clients reliably
This commit is contained in:
1 parent
10db2f272c
commit
2ce3c33c87
3 files changed
+51
-5
No files matched your search
@@ -146,8 +146,13 @@ class ApkDownloader {
|
||||
_log.e('Update download paused after error: $e');
|
||||
return null;
|
||||
} finally {
|
||||
await sink?.close();
|
||||
if (ownedClient) effectiveClient.close();
|
||||
try {
|
||||
await sink?.close();
|
||||
} catch (error) {
|
||||
_log.w('Failed to close partial update file: $error');
|
||||
} finally {
|
||||
if (ownedClient) effectiveClient.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -377,13 +377,13 @@ class AppRemoteConfigService {
|
||||
static const _dismissedAnnouncementIdsKey =
|
||||
'app_remote_config_dismissed_announcement_ids';
|
||||
|
||||
final http.Client _client;
|
||||
final http.Client? _client;
|
||||
final String endpoint;
|
||||
|
||||
AppRemoteConfigService({
|
||||
http.Client? client,
|
||||
this.endpoint = AppInfo.remoteConfigApiUrl,
|
||||
}) : _client = client ?? http.Client();
|
||||
}) : _client = client;
|
||||
|
||||
Future<RemoteConfigSnapshot?> readCachedConfig() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
@@ -396,6 +396,9 @@ class AppRemoteConfigService {
|
||||
}
|
||||
|
||||
Future<RemoteConfigSnapshot?> fetchConfigSnapshot({String? locale}) async {
|
||||
// A default client belongs to this request; an injected client belongs to
|
||||
// its caller. Cached config and dismissal helpers need no HTTP resources.
|
||||
final client = _client ?? http.Client();
|
||||
try {
|
||||
final uri = Uri.parse(endpoint).replace(
|
||||
queryParameters: {
|
||||
@@ -406,7 +409,7 @@ class AppRemoteConfigService {
|
||||
},
|
||||
);
|
||||
|
||||
final response = await _client
|
||||
final response = await client
|
||||
.get(
|
||||
uri,
|
||||
headers: {
|
||||
@@ -443,6 +446,8 @@ class AppRemoteConfigService {
|
||||
} catch (e) {
|
||||
_log.w('Remote config fetch failed: $e');
|
||||
return null;
|
||||
} finally {
|
||||
if (_client == null) client.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,18 @@ import 'package:spotiflac_android/screens/settings/donate_page.dart';
|
||||
import 'package:spotiflac_android/services/app_remote_config_service.dart';
|
||||
import 'package:spotiflac_android/theme/mornye_theme.dart';
|
||||
|
||||
class _TrackedClient extends MockClient {
|
||||
_TrackedClient(super.handler);
|
||||
|
||||
bool closed = false;
|
||||
|
||||
@override
|
||||
void close() {
|
||||
closed = true;
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
@@ -18,6 +30,30 @@ void main() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
test('default request closes its client on success and failure', () async {
|
||||
for (final fail in [false, true]) {
|
||||
final client = _TrackedClient((_) async {
|
||||
if (fail) throw StateError('network unavailable');
|
||||
return http.Response('{}', 200);
|
||||
});
|
||||
final snapshot = await http.runWithClient(
|
||||
() => AppRemoteConfigService().fetchConfigSnapshot(),
|
||||
() => client,
|
||||
);
|
||||
expect(snapshot, fail ? isNull : isNotNull);
|
||||
expect(client.closed, isTrue);
|
||||
}
|
||||
});
|
||||
|
||||
test('injected client remains owned by its caller', () async {
|
||||
final client = _TrackedClient((_) async => http.Response('{}', 200));
|
||||
final service = AppRemoteConfigService(client: client);
|
||||
expect(await service.fetchConfigSnapshot(), isNotNull);
|
||||
expect(client.closed, isFalse);
|
||||
expect(await service.fetchConfigSnapshot(), isNotNull);
|
||||
client.close();
|
||||
});
|
||||
|
||||
test(
|
||||
'parses monthly donation progress and display settings from the API',
|
||||
() {
|
||||
|
||||
Reference in new issue
Block a user