Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(emulator): protect against double useEmulator calls natively #6615

Merged
merged 6 commits into from
Oct 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class ReactNativeFirebaseAuthModule extends ReactNativeFirebaseModule {
private static final String TAG = "Auth";
private static HashMap<String, FirebaseAuth.AuthStateListener> mAuthListeners = new HashMap<>();
private static HashMap<String, FirebaseAuth.IdTokenListener> mIdTokenListeners = new HashMap<>();
private static HashMap<String, String> emulatorConfigs = new HashMap<>();
private String mVerificationId;
private String mLastPhoneNumber;
private PhoneAuthProvider.ForceResendingToken mForceResendingToken;
Expand Down Expand Up @@ -1561,10 +1562,13 @@ public void verifyPasswordResetCode(String appName, String code, final Promise p

@ReactMethod
public void useEmulator(String appName, String host, int port) {
Log.d(TAG, "useEmulator");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.useEmulator(host, port);

if (emulatorConfigs.get(appName) == null) {
emulatorConfigs.put(appName, "true");
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(firebaseApp);
firebaseAuth.useEmulator(host, port);
}
}

/* ------------------
Expand Down
7 changes: 6 additions & 1 deletion packages/auth/ios/RNFBAuth/RNFBAuthModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@

static __strong NSMutableDictionary *authStateHandlers;
static __strong NSMutableDictionary *idTokenHandlers;
static __strong NSMutableDictionary *emulatorConfigs;
// Used for caching credentials between method calls.
static __strong NSMutableDictionary<NSString *, FIRAuthCredential *> *credentials;

Expand All @@ -69,6 +70,7 @@ - (id)init {
dispatch_once(&onceToken, ^{
authStateHandlers = [[NSMutableDictionary alloc] init];
idTokenHandlers = [[NSMutableDictionary alloc] init];
emulatorConfigs = [[NSMutableDictionary alloc] init];
credentials = [[NSMutableDictionary alloc] init];
});
return self;
Expand Down Expand Up @@ -940,7 +942,10 @@ - (void)invalidate {
: (FIRApp *)firebaseApp
: (nonnull NSString *)host
: (NSInteger)port) {
[[FIRAuth authWithApp:firebaseApp] useEmulatorWithHost:host port:port];
if (!emulatorConfigs[firebaseApp.name]) {
[[FIRAuth authWithApp:firebaseApp] useEmulatorWithHost:host port:port];
emulatorConfigs[firebaseApp.name] = @YES;
}
}

- (FIRAuthCredential *)getCredentialForProvider:(NSString *)provider
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,14 @@ static FirebaseDatabase getDatabaseForApp(String appName, String dbURL) {
setDatabaseConfig(firebaseDatabase, appName, dbURL);

HashMap emulatorConfig = getEmulatorConfig(appName, dbURL);
if (emulatorConfig != null) {

if (emulatorConfig != null && emulatorConfig.get("configured") == null) {
firebaseDatabase.useEmulator(
(String) emulatorConfig.get("host"), (Integer) emulatorConfig.get("port"));
// The underlying SDK may only be configured once, but with hot-reloads in the
// javascript bundle, javascript cannot hold SDK configuration state. Keep track here
// so we only configure once
emulatorConfig.put("configured", "true");
}

return firebaseDatabase;
Expand Down
20 changes: 18 additions & 2 deletions packages/database/ios/RNFBDatabase/RNFBDatabaseModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
#import "RNFBDatabaseModule.h"
#import "RNFBPreferences.h"

static __strong NSMutableDictionary *emulatorSettings;

@implementation RNFBDatabaseModule
#pragma mark -
#pragma mark Module Setup
Expand Down Expand Up @@ -58,8 +60,22 @@ - (dispatch_queue_t)methodQueue {
: (NSString *)dbURL
: (nonnull NSString *)host
: (NSInteger)port) {
[[RNFBDatabaseCommon getDatabaseForApp:firebaseApp dbURL:dbURL] useEmulatorWithHost:host
port:port];
// javascript may hot reload, losing state, and native throws an error if you double-request
// so we keep track of useEmulator calls here to avoid calling native twice
if (emulatorSettings == nil) {
emulatorSettings = [NSMutableDictionary dictionary];
}

NSMutableString *configKey = [firebaseApp.name mutableCopy];
if (dbURL != nil && dbURL.length > 0) {
[configKey appendString:dbURL];
}

if (!emulatorSettings[configKey]) {
[[RNFBDatabaseCommon getDatabaseForApp:firebaseApp dbURL:dbURL] useEmulatorWithHost:host
port:port];
emulatorSettings[configKey] = @YES;
}
}

RCT_EXPORT_METHOD(setPersistenceEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@
import io.invertase.firebase.common.UniversalFirebaseModule;
import io.invertase.firebase.common.UniversalFirebasePreferences;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class UniversalFirebaseFirestoreModule extends UniversalFirebaseModule {

private static HashMap<String, String> emulatorConfigs = new HashMap<>();

UniversalFirebaseFirestoreModule(Context context, String serviceName) {
super(context, serviceName);
}
Expand All @@ -49,7 +52,10 @@ Task<Void> useEmulator(String appName, String host, int port) {
return Tasks.call(
getExecutor(),
() -> {
getFirestoreForApp(appName).useEmulator(host, port);
if (emulatorConfigs.get(appName) == null) {
emulatorConfigs.put(appName, "true");
getFirestoreForApp(appName).useEmulator(host, port);
}
return null;
});
}
Expand Down
22 changes: 15 additions & 7 deletions packages/firestore/ios/RNFBFirestore/RNFBFirestoreModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#import "RNFBFirestoreCommon.h"
#import "RNFBPreferences.h"

NSMutableDictionary *emulatorConfigs;

@implementation RNFBFirestoreModule
#pragma mark -
#pragma mark Module Setup
Expand Down Expand Up @@ -142,13 +144,19 @@ + (BOOL)requiresMainQueueSetup {
: (FIRApp *)firebaseApp
: (nonnull NSString *)host
: (NSInteger)port) {
FIRFirestore *firestore = [RNFBFirestoreCommon getFirestoreForApp:firebaseApp];
[firestore useEmulatorWithHost:host port:port];

// It is not sufficient to just use emulator. You have toggle SSL off too.
FIRFirestoreSettings *settings = firestore.settings;
settings.sslEnabled = FALSE;
firestore.settings = settings;
if (emulatorConfigs == nil) {
emulatorConfigs = [[NSMutableDictionary alloc] init];
}
if (!emulatorConfigs[firebaseApp.name]) {
FIRFirestore *firestore = [RNFBFirestoreCommon getFirestoreForApp:firebaseApp];
[firestore useEmulatorWithHost:host port:port];
emulatorConfigs[firebaseApp.name] = @YES;

// It is not sufficient to just use emulator. You have toggle SSL off too.
FIRFirestoreSettings *settings = firestore.settings;
settings.sslEnabled = FALSE;
firestore.settings = settings;
}
}

RCT_EXPORT_METHOD(waitForPendingWrites
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
public class ReactNativeFirebaseStorageModule extends ReactNativeFirebaseModule {
private static final String TAG = "Storage";

private static HashMap<String, String> emulatorConfigs = new HashMap<>();

ReactNativeFirebaseStorageModule(ReactApplicationContext reactContext) {
super(reactContext, TAG);
}
Expand All @@ -54,7 +56,9 @@ public void onCatalystInstanceDestroy() {
super.onCatalystInstanceDestroy();
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#delete
*/
@ReactMethod
public void delete(String appName, String url, final Promise promise) {
try {
Expand Down Expand Up @@ -97,7 +101,9 @@ public void getDownloadURL(String appName, final String url, final Promise promi
}
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#getMetadata
*/
@ReactMethod
public void getMetadata(String appName, String url, Promise promise) {
try {
Expand All @@ -118,7 +124,9 @@ public void getMetadata(String appName, String url, Promise promise) {
}
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#list */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#list
*/
@ReactMethod
public void list(String appName, String url, ReadableMap listOptions, Promise promise) {
try {
Expand Down Expand Up @@ -148,7 +156,9 @@ public void list(String appName, String url, ReadableMap listOptions, Promise pr
}
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#listAll */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#listAll
*/
@ReactMethod
public void listAll(String appName, String url, Promise promise) {
try {
Expand Down Expand Up @@ -267,16 +277,23 @@ public void setMaxUploadRetryTime(String appName, double milliseconds, Promise p
promise.resolve(null);
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Storage#useEmulator */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Storage#useEmulator
*/
@ReactMethod
public void useEmulator(String appName, String host, int port, Promise promise) {
FirebaseApp firebaseApp = FirebaseApp.getInstance(appName);
FirebaseStorage firebaseStorage = FirebaseStorage.getInstance(firebaseApp);
firebaseStorage.useEmulator(host, port);
if (emulatorConfigs.get(appName) == null) {
firebaseStorage.useEmulator(host, port);
emulatorConfigs.put(appName, "true");
}
promise.resolve(null);
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#writeToFile */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#writeToFile
*/
@ReactMethod
public void writeToFile(
String appName, String url, String localFilePath, int taskId, Promise promise) {
Expand All @@ -300,7 +317,9 @@ public void writeToFile(
}
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putString */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putString
*/
@ReactMethod
public void putString(
String appName,
Expand All @@ -321,7 +340,9 @@ public void putString(
}
}

/** @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putFile */
/**
* @link https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putFile
*/
@ReactMethod
public void putFile(
String appName,
Expand Down
16 changes: 8 additions & 8 deletions packages/storage/ios/RNFBStorage/RNFBStorageModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
// The iOS SDK has a short memory on settings, store these globally and set them in each time
static NSString *emulatorHost = nil;
static NSInteger emulatorPort = 0;
static bool useEmulatorCalled = false;
static NSMutableDictionary *emulatorConfigs;
static NSTimeInterval maxDownloadRetryTime = 600;
static NSTimeInterval maxUploadRetryTime = 600;
static NSTimeInterval maxOperationRetryTime = 120;
Expand All @@ -56,6 +56,7 @@ - (id)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
PENDING_TASKS = [[NSMutableDictionary alloc] init];
emulatorConfigs = [[NSMutableDictionary alloc] init];
});

return self;
Expand Down Expand Up @@ -506,12 +507,10 @@ - (void)invalidate {
: (NSInteger)port) {
emulatorHost = host;
emulatorPort = port;
if (useEmulatorCalled == true) {
return;
if (!emulatorConfigs[firebaseApp.name]) {
[[FIRStorage storageForApp:firebaseApp] useEmulatorWithHost:host port:port];
emulatorConfigs[firebaseApp.name] = @YES;
}

[[FIRStorage storageForApp:firebaseApp] useEmulatorWithHost:host port:port];
useEmulatorCalled = true;
}

/**
Expand Down Expand Up @@ -669,10 +668,11 @@ - (FIRStorageReference *)getReferenceFromUrl:(NSString *)url app:(FIRApp *)fireb
storage = [FIRStorage storageForApp:firebaseApp URL:bucket];

NSLog(@"Setting emulator - host %@ port %ld", emulatorHost, (long)emulatorPort);
if (![emulatorHost isEqual:[NSNull null]] && emulatorHost != nil && useEmulatorCalled == false) {
if (![emulatorHost isEqual:[NSNull null]] && emulatorHost != nil &&
!emulatorConfigs[firebaseApp.name]) {
@try {
[storage useEmulatorWithHost:emulatorHost port:emulatorPort];
useEmulatorCalled = true;
emulatorConfigs[firebaseApp.name] = @YES;
} @catch (NSException *e) {
NSLog(@"WARNING: Unable to set the Firebase Storage emulator settings. These must be set "
@"before any usages of Firebase Storage. If you see this log after a hot "
Expand Down
Loading