Skip to content

Commit 38b8088

Browse files
refactor: use environment variables to communicate gcs.Storage config (#186)
* Make server work with google_cloud_storage * Use pub version * Update storage.dart * Env var * Reverts * Update storage.dart * Update storage.dart * Fix diff a bit * Update pubspec.yaml --------- Co-authored-by: Ademola Fadumo <48495111+demolaf@users.noreply.github.com>
1 parent 10c920a commit 38b8088

File tree

3 files changed

+54
-45
lines changed

3 files changed

+54
-45
lines changed

packages/dart_firebase_admin/lib/src/storage/storage.dart

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,14 @@
11
import 'dart:convert';
22

33
import 'package:google_cloud_storage/google_cloud_storage.dart' as gcs;
4-
import 'package:http/http.dart' as http;
54
import 'package:meta/meta.dart';
65
import '../app.dart';
6+
import '../utils/native_environment.dart';
77

88
part '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-
3110
class Storage implements FirebaseService {
3211
Storage._(this.app) {
33-
String? apiEndpoint;
3412
final isEmulator = Environment.isStorageEmulatorEnabled();
3513

3614
if (isEmulator) {
@@ -42,25 +20,9 @@ class Storage implements FirebaseService {
4220
'FIREBASE_STORAGE_EMULATOR_HOST should not contain a protocol (http or https).',
4321
);
4422
}
45-
// The new gcs.Storage adds the http:// scheme automatically when
46-
// useAuthWithCustomEndpoint is false, so pass only host:port.
47-
apiEndpoint = emulatorHost;
23+
setNativeEnvironmentVariable('STORAGE_EMULATOR_HOST', emulatorHost);
4824
}
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-
);
25+
_delegate = gcs.Storage();
6426
}
6527

6628
@internal
@@ -73,7 +35,7 @@ class Storage implements FirebaseService {
7335

7436
late final gcs.Storage _delegate;
7537

76-
gcs.Bucket bucket(String? name) {
38+
gcs.Bucket bucket([String? name]) {
7739
final bucketName = name ?? app.options.storageBucket;
7840
if (bucketName == null || bucketName.isEmpty) {
7941
throw FirebaseAppException(
@@ -134,8 +96,6 @@ class Storage implements FirebaseService {
13496

13597
@override
13698
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.
13999
_delegate.close();
140100
}
141101
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import 'dart:ffi';
2+
import 'dart:io';
3+
4+
import 'package:ffi/ffi.dart';
5+
import 'package:meta/meta.dart';
6+
7+
import '../../dart_firebase_admin.dart';
8+
9+
final int Function(Pointer<Utf8>, Pointer<Utf8>, int) _setenv =
10+
DynamicLibrary.process().lookupFunction<
11+
Int32 Function(Pointer<Utf8>, Pointer<Utf8>, Int32),
12+
int Function(Pointer<Utf8>, Pointer<Utf8>, int)
13+
>('setenv');
14+
15+
final int Function(Pointer<Utf16>, Pointer<Utf16>) _setEnvironmentVariableW =
16+
DynamicLibrary.open('kernel32.dll').lookupFunction<
17+
Int32 Function(Pointer<Utf16>, Pointer<Utf16>),
18+
int Function(Pointer<Utf16>, Pointer<Utf16>)
19+
>('SetEnvironmentVariableW');
20+
21+
@internal
22+
void setNativeEnvironmentVariable(String name, String value) {
23+
if (Platform.isWindows) {
24+
using((arena) {
25+
final namePtr = name.toNativeUtf16(allocator: arena);
26+
final valuePtr = value.toNativeUtf16(allocator: arena);
27+
if (_setEnvironmentVariableW(namePtr, valuePtr) == 0) {
28+
throw FirebaseAppException(
29+
AppErrorCode.internalError,
30+
'Failed to set native environment variable: $name',
31+
);
32+
}
33+
});
34+
} else {
35+
using((arena) {
36+
final namePtr = name.toNativeUtf8(allocator: arena);
37+
final valuePtr = value.toNativeUtf8(allocator: arena);
38+
if (_setenv(namePtr, valuePtr, 1) == -1) {
39+
throw FirebaseAppException(
40+
AppErrorCode.internalError,
41+
'Failed to set native environment variable: $name',
42+
);
43+
}
44+
});
45+
}
46+
}

packages/dart_firebase_admin/pubspec.yaml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ dependencies:
1414
collection: ^1.18.0
1515
dart_jsonwebtoken: ^3.0.0
1616
equatable: ^2.0.7
17+
ffi: ^2.1.2
1718
google_cloud: ^0.3.0
1819
google_cloud_firestore: ^0.1.0
19-
google_cloud_storage: ^0.5.1
20+
# `google_cloud_storage` will not change the surface used by
21+
# `dart_firebase_admin`.
22+
google_cloud_storage: ">=0.5.1"
2023
googleapis: ^15.0.0
2124
googleapis_auth: ^2.2.0
2225
googleapis_beta: ^9.0.0

0 commit comments

Comments
 (0)