Skip to content

Commit

Permalink
fix(emulator): add notice on localhost URL remapping for android
Browse files Browse the repository at this point in the history
For developer experience, we remap localhost / 127.0.0.1 on android to the
canonical android emulator IP address for the host machine. This is best most
of the time but is quite unexpected if you run on a real device and try to do
adb reverse port mapping for the emulator port and use localhost, only to find
that you can't connect to the emulator because of this remapping

This PR adds a note to the docs and a console.log so developers are aware of
the behavior

Fixes #4810
  • Loading branch information
mikehardy committed Feb 3, 2021
1 parent 06c2a18 commit 73869e1
Show file tree
Hide file tree
Showing 7 changed files with 54 additions and 11 deletions.
4 changes: 2 additions & 2 deletions packages/auth/__tests__/auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ describe('Auth', function () {
it('useEmulator requires a well-formed url', function () {
// No http://
expect(() => auth().useEmulator('localhost:9099')).toThrow(
'firebase.auth().useEmulator() unable to parse host and port from url',
'firebase.auth().useEmulator() takes a non-empty string URL',
);
// No port
expect(() => auth().useEmulator('http://localhost')).toThrow(
'firebase.auth().useEmulator() unable to parse host and port from url',
'firebase.auth().useEmulator() unable to parse host and port from URL',
);
});

Expand Down
5 changes: 5 additions & 0 deletions packages/auth/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1646,6 +1646,11 @@ export namespace FirebaseAuthTypes {
* This must be called synchronously immediately following the first call to firebase.auth().
* Do not use with production credentials as emulator traffic is not encrypted.
*
* Note: on android, hosts 'localhost' and '127.0.0.1' are automatically remapped to '10.0.2.2' (the
* "host" computer IP address for android emulators) to make the standard development experience easy.
* If you want to use the emulator on a real android device, you will need to specify the actual host
* computer IP address.
*
* @param url: emulator URL, must have host and port (eg, 'http://localhost:9099')
*/
useEmulator(url: string): void;
Expand Down
22 changes: 18 additions & 4 deletions packages/auth/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
*
*/

import { isAndroid, isBoolean, isString, isNull } from '@react-native-firebase/app/lib/common';
import {
isAndroid,
isBoolean,
isString,
isNull,
isValidUrl,
} from '@react-native-firebase/app/lib/common';
import {
createModuleNamespace,
FirebaseModule,
Expand Down Expand Up @@ -336,25 +342,33 @@ class FirebaseAuthModule extends FirebaseModule {
}

useEmulator(url) {
if (!url || !isString(url) || url === '') {
throw new Error('firebase.auth().useEmulator() takes a non-empty string');
if (!url || !isString(url) || !isValidUrl(url)) {
throw new Error('firebase.auth().useEmulator() takes a non-empty string URL');
}

let _url = url;
if (isAndroid && _url) {
if (_url.startsWith('http://localhost')) {
_url = _url.replace('http://localhost', 'http://10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping auth host "localhost" to "10.0.2.2" for android emulators. Use real IP on real devices.',
);
}
if (_url.startsWith('http://127.0.0.1')) {
_url = _url.replace('http://127.0.0.1', 'http://10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping auth host "127.0.0.1" to "10.0.2.2" for android emulators. Use real IP on real devices.',
);
}
}

// Native calls take the host and port split out
const hostPortRegex = /^http:\/\/([\w\d.]+):(\d+)$/;
const urlMatches = _url.match(hostPortRegex);
if (!urlMatches) {
throw new Error('firebase.auth().useEmulator() unable to parse host and port from url');
throw new Error('firebase.auth().useEmulator() unable to parse host and port from URL');
}
const host = urlMatches[1];
const port = parseInt(urlMatches[2], 10);
Expand Down
5 changes: 5 additions & 0 deletions packages/firestore/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1407,6 +1407,11 @@ export namespace FirebaseFirestoreTypes {

/**
* The hostname to connect to.
*
* Note: on android, hosts 'localhost' and '127.0.0.1' are automatically remapped to '10.0.2.2' (the
* "host" computer IP address for android emulators) to make the standard development experience easy.
* If you want to use the emulator on a real android device, you will need to specify the actual host
* computer IP address.
*/
host: string;

Expand Down
8 changes: 8 additions & 0 deletions packages/firestore/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,17 @@ class FirebaseFirestoreModule extends FirebaseModule {
if (isAndroid) {
if (settings.host.startsWith('localhost')) {
settings.host = settings.host.replace('localhost', '10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping firestore host "localhost" to "10.0.2.2" for android emulators. Use real IP on real devices.',
);
}
if (settings.host.startsWith('127.0.0.1')) {
settings.host = settings.host.replace('127.0.0.1', '10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping firestore host "127.0.0.1" to "10.0.2.2" for android emulators. Use real IP on real devices.',
);
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions packages/functions/lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,21 +339,24 @@ export namespace FirebaseFunctionsTypes {
httpsCallable(name: string, options?: HttpsCallableOptions): HttpsCallable;

/**
* Changes this instance to point to a Cloud Functions emulator running
* locally.
* Changes this instance to point to a Cloud Functions emulator running locally.
*
* See https://firebase.google.com/docs/functions/local-emulator
*
* #### Example
*
* ```js
* if (__DEV__) {
* firebase.functions().useFunctionsEmulator('http://10.0.0.8:1337');
* firebase.functions().useFunctionsEmulator('http://localhost:5001');
* }
* ```
*
* @param origin The origin string of the local emulator started via firebase tools
* "http://10.0.0.8:1337".
* Note: on android, hosts 'localhost' and '127.0.0.1' are automatically remapped to '10.0.2.2' (the
* "host" computer IP address for android emulators) to make the standard development experience easy.
* If you want to use the emulator on a real android device, you will need to specify the actual host
* computer IP address.
*
* @param origin url of the local emulator started via firebase tools "http://localhost:5001"
*/
useFunctionsEmulator(origin: string): void;
}
Expand Down
8 changes: 8 additions & 0 deletions packages/functions/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,17 @@ class FirebaseFunctionsModule extends FirebaseModule {
if (isAndroid && _origin) {
if (_origin.startsWith('http://localhost')) {
_origin = _origin.replace('http://localhost', 'http://10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping functions host "localhost" to "10.0.2.2" for android emulators. Use real IP on real devices.',
);
}
if (_origin.startsWith('http://127.0.0.1')) {
_origin = _origin.replace('http://127.0.0.1', 'http://10.0.2.2');
// eslint-disable-next-line no-console
console.log(
'Mapping functions host "127.0.0.1" to "10.0.2.2" for android emulators. Use real IP on real devices.',
);
}
}
this._useFunctionsEmulatorOrigin = _origin || null;
Expand Down

1 comment on commit 73869e1

@vercel
Copy link

@vercel vercel bot commented on 73869e1 Feb 3, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.