11import 'dart:convert' ;
22
33import 'package:google_cloud_storage/google_cloud_storage.dart' as gcs;
4+ import 'package:http/http.dart' as http;
45import 'package:meta/meta.dart' ;
56import '../app.dart' ;
6- import '../utils/native_environment.dart' ;
77
88part 'storage_exception.dart' ;
99
10+ /// An [http.BaseClient] that lazily resolves the real client on each request.
11+ ///
12+ /// This allows [Storage] to be constructed synchronously even though
13+ /// [FirebaseApp.client] is asynchronous. The underlying client lifecycle is
14+ /// managed externally (by [FirebaseApp] ), so [close] is a no-op here.
15+ class _DeferredHttpClient extends http.BaseClient {
16+ _DeferredHttpClient (this ._clientFuture);
17+
18+ final Future <http.Client > _clientFuture;
19+
20+ @override
21+ Future <http.StreamedResponse > send (http.BaseRequest request) async {
22+ return (await _clientFuture).send (request);
23+ }
24+
25+ @override
26+ void close () {
27+ // The underlying client is managed externally; do not close it here.
28+ }
29+ }
30+
1031class Storage implements FirebaseService {
1132 Storage ._(this .app) {
33+ String ? apiEndpoint;
1234 final isEmulator = Environment .isStorageEmulatorEnabled ();
1335
1436 if (isEmulator) {
@@ -20,9 +42,25 @@ class Storage implements FirebaseService {
2042 'FIREBASE_STORAGE_EMULATOR_HOST should not contain a protocol (http or https).' ,
2143 );
2244 }
23- setNativeEnvironmentVariable ('STORAGE_EMULATOR_HOST' , emulatorHost);
45+ // The new gcs.Storage adds the http:// scheme automatically when
46+ // useAuthWithCustomEndpoint is false, so pass only host:port.
47+ apiEndpoint = emulatorHost;
2448 }
25- _delegate = gcs.Storage ();
49+
50+ // For emulator, use the provided httpClient (e.g. a mock in tests) or a
51+ // plain unauthenticated client. For production, wrap the async auth client
52+ // in a _DeferredHttpClient so construction stays synchronous.
53+ final http.Client httpClient = isEmulator
54+ ? _DeferredHttpClient (
55+ Future .value (app.options.httpClient ?? http.Client ()),
56+ )
57+ : _DeferredHttpClient (app.client);
58+
59+ _delegate = gcs.Storage (
60+ client: httpClient,
61+ apiEndpoint: apiEndpoint,
62+ useAuthWithCustomEndpoint: false ,
63+ );
2664 }
2765
2866 @internal
@@ -35,7 +73,7 @@ class Storage implements FirebaseService {
3573
3674 late final gcs.Storage _delegate;
3775
38- gcs.Bucket bucket ([ String ? name] ) {
76+ gcs.Bucket bucket (String ? name) {
3977 final bucketName = name ?? app.options.storageBucket;
4078 if (bucketName == null || bucketName.isEmpty) {
4179 throw FirebaseAppException (
@@ -96,6 +134,8 @@ class Storage implements FirebaseService {
96134
97135 @override
98136 Future <void > delete () async {
137+ // _delegate.close() calls close() on our _DeferredHttpClient, which is a
138+ // no-op, so the externally-managed http client is not closed here.
99139 _delegate.close ();
100140 }
101141}
0 commit comments