Skip to content

Commit 8a018b7

Browse files
authored
Add useEmulator support for Firebase Storage (#2442)
1 parent 9080b3e commit 8a018b7

31 files changed

+422
-91
lines changed

firebase-storage/api.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ package com.google.firebase.storage {
4343
method public void setMaxDownloadRetryTimeMillis(long);
4444
method public void setMaxOperationRetryTimeMillis(long);
4545
method public void setMaxUploadRetryTimeMillis(long);
46+
method public void useEmulator(@NonNull String, int);
4647
}
4748

4849
public final class ListResult {

firebase-storage/src/main/java/com/google/firebase/storage/DeleteStorageTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public DeleteStorageTask(
5454
@Override
5555
public void run() {
5656
final NetworkRequest request =
57-
new DeleteNetworkRequest(mStorageRef.getStorageUri(), mStorageRef.getApp());
57+
new DeleteNetworkRequest(mStorageRef.getStorageReferenceUri(), mStorageRef.getApp());
5858
mSender.sendWithExponentialBackoff(request);
5959
request.completeTask(mPendingResult, null);
6060
}

firebase-storage/src/main/java/com/google/firebase/storage/FileDownloadTask.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,8 @@ private boolean processResponse(final NetworkRequest request) throws IOException
195195
mException = null;
196196
mSender.reset();
197197
final NetworkRequest request =
198-
new GetNetworkRequest(mStorageRef.getStorageUri(), mStorageRef.getApp(), mResumeOffset);
198+
new GetNetworkRequest(
199+
mStorageRef.getStorageReferenceUri(), mStorageRef.getApp(), mResumeOffset);
199200

200201
mSender.sendWithExponentialBackoff(request, false);
201202
mResultCode = request.getResultCode();

firebase-storage/src/main/java/com/google/firebase/storage/FirebaseStorage.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.google.firebase.FirebaseApp;
2525
import com.google.firebase.FirebaseOptions;
2626
import com.google.firebase.auth.internal.InternalAuthProvider;
27+
import com.google.firebase.emulators.EmulatedServiceSettings;
2728
import com.google.firebase.inject.Provider;
2829
import com.google.firebase.storage.internal.Util;
2930
import java.io.UnsupportedEncodingException;
@@ -51,6 +52,8 @@ public class FirebaseStorage {
5152
private long sMaxDownloadRetry = 10 * DateUtils.MINUTE_IN_MILLIS; // 10 * 60 * 1000
5253
private long sMaxQueryRetry = 2 * DateUtils.MINUTE_IN_MILLIS; // 2 * 60 * 1000
5354

55+
@Nullable private EmulatedServiceSettings emulatorSettings;
56+
5457
FirebaseStorage(
5558
@Nullable String bucketName,
5659
@NonNull FirebaseApp app,
@@ -154,6 +157,18 @@ public static FirebaseStorage getInstance(@NonNull FirebaseApp app, @NonNull Str
154157
}
155158
}
156159

160+
/**
161+
* Modifies this FirebaseStorage instance to communicate with the Storage emulator.
162+
*
163+
* <p>Note: Call this method before using the instance to do any storage operations.
164+
*
165+
* @param host the emulator host (for example, 10.0.2.2)
166+
* @param port the emulator port (for example, 9000)
167+
*/
168+
public void useEmulator(@NonNull String host, int port) {
169+
this.emulatorSettings = new EmulatedServiceSettings(host, port);
170+
}
171+
157172
/**
158173
* Returns the maximum time to retry a download if a failure occurs.
159174
*
@@ -307,4 +322,9 @@ public FirebaseApp getApp() {
307322
InternalAuthProvider getAuthProvider() {
308323
return mAuthProvider != null ? mAuthProvider.get() : null;
309324
}
325+
326+
@Nullable
327+
EmulatedServiceSettings getEmulatorSettings() {
328+
return emulatorSettings;
329+
}
310330
}

firebase-storage/src/main/java/com/google/firebase/storage/GetDownloadUrlTask.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private Uri extractDownloadUrl(JSONObject response) {
5959

6060
if (!TextUtils.isEmpty(downloadTokens)) {
6161
String downloadToken = downloadTokens.split(",", -1)[0];
62-
Uri.Builder uriBuilder = NetworkRequest.getDefaultURL(storageRef.getStorageUri()).buildUpon();
62+
Uri.Builder uriBuilder = storageRef.getStorageReferenceUri().getHttpUri().buildUpon();
6363
uriBuilder.appendQueryParameter("alt", "media");
6464
uriBuilder.appendQueryParameter("token", downloadToken);
6565
return uriBuilder.build();
@@ -71,7 +71,7 @@ private Uri extractDownloadUrl(JSONObject response) {
7171
@Override
7272
public void run() {
7373
final NetworkRequest request =
74-
new GetMetadataNetworkRequest(storageRef.getStorageUri(), storageRef.getApp());
74+
new GetMetadataNetworkRequest(storageRef.getStorageReferenceUri(), storageRef.getApp());
7575

7676
sender.sendWithExponentialBackoff(request);
7777

firebase-storage/src/main/java/com/google/firebase/storage/GetMetadataTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class GetMetadataTask implements Runnable {
5656
@Override
5757
public void run() {
5858
final NetworkRequest request =
59-
new GetMetadataNetworkRequest(mStorageRef.getStorageUri(), mStorageRef.getApp());
59+
new GetMetadataNetworkRequest(mStorageRef.getStorageReferenceUri(), mStorageRef.getApp());
6060

6161
mSender.sendWithExponentialBackoff(request);
6262
if (request.isResultSuccess()) {

firebase-storage/src/main/java/com/google/firebase/storage/ListTask.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class ListTask implements Runnable {
5959
public void run() {
6060
final NetworkRequest request =
6161
new ListNetworkRequest(
62-
storageRef.getStorageUri(), storageRef.getApp(), maxResults, pageToken);
62+
storageRef.getStorageReferenceUri(), storageRef.getApp(), maxResults, pageToken);
6363

6464
sender.sendWithExponentialBackoff(request);
6565

firebase-storage/src/main/java/com/google/firebase/storage/StorageReference.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.google.android.gms.tasks.Tasks;
3030
import com.google.firebase.FirebaseApp;
3131
import com.google.firebase.storage.internal.Slashes;
32+
import com.google.firebase.storage.internal.StorageReferenceUri;
3233
import java.io.ByteArrayOutputStream;
3334
import java.io.File;
3435
import java.io.IOException;
@@ -686,4 +687,9 @@ public int compareTo(@NonNull StorageReference other) {
686687
// of this reference.
687688
return mStorageUri.compareTo(other.mStorageUri);
688689
}
690+
691+
@NonNull
692+
StorageReferenceUri getStorageReferenceUri() {
693+
return new StorageReferenceUri(mStorageUri, mFirebaseStorage.getEmulatorSettings());
694+
}
689695
}

firebase-storage/src/main/java/com/google/firebase/storage/StreamDownloadTask.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,8 @@ private InputStream createDownloadStream() throws Exception {
117117
}
118118

119119
request =
120-
new GetNetworkRequest(storageRef.getStorageUri(), storageRef.getApp(), bytesDownloaded);
120+
new GetNetworkRequest(
121+
storageRef.getStorageReferenceUri(), storageRef.getApp(), bytesDownloaded);
121122

122123
sender.sendWithExponentialBackoff(request, false);
123124
resultCode = request.getResultCode();

firebase-storage/src/main/java/com/google/firebase/storage/UpdateMetadataTask.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ public UpdateMetadataTask(
5252
public void run() {
5353
final NetworkRequest request =
5454
new UpdateMetadataNetworkRequest(
55-
mStorageRef.getStorageUri(), mStorageRef.getApp(), mNewMetadata.createJSONObject());
55+
mStorageRef.getStorageReferenceUri(),
56+
mStorageRef.getApp(),
57+
mNewMetadata.createJSONObject());
5658

5759
mSender.sendWithExponentialBackoff(request);
5860
if (request.isResultSuccess()) {

firebase-storage/src/main/java/com/google/firebase/storage/UploadTask.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ private void beginResumableUpload() {
260260
}
261261
NetworkRequest startRequest =
262262
new ResumableUploadStartRequest(
263-
mStorageRef.getStorageUri(),
263+
mStorageRef.getStorageReferenceUri(),
264264
mStorageRef.getApp(),
265265
mMetadata != null ? mMetadata.createJSONObject() : null,
266266
mimeType);
@@ -345,7 +345,7 @@ private boolean serverStateValid() {
345345
private boolean recoverStatus(boolean withRetry) {
346346
NetworkRequest queryRequest =
347347
new ResumableUploadQueryRequest(
348-
mStorageRef.getStorageUri(), mStorageRef.getApp(), mUploadUri);
348+
mStorageRef.getStorageReferenceUri(), mStorageRef.getApp(), mUploadUri);
349349

350350
if (RESUMABLE_FINAL_STATUS.equals(mServerStatus)) {
351351
return false;
@@ -409,7 +409,7 @@ private void uploadChunk() {
409409

410410
NetworkRequest uploadRequest =
411411
new ResumableUploadByteRequest(
412-
mStorageRef.getStorageUri(),
412+
mStorageRef.getStorageReferenceUri(),
413413
mStorageRef.getApp(),
414414
mUploadUri,
415415
mStreamBuffer.get(),
@@ -484,7 +484,7 @@ protected void onCanceled() {
484484
if (mUploadUri != null) {
485485
cancelRequest =
486486
new ResumableUploadCancelRequest(
487-
mStorageRef.getStorageUri(), mStorageRef.getApp(), mUploadUri);
487+
mStorageRef.getStorageReferenceUri(), mStorageRef.getApp(), mUploadUri);
488488
}
489489

490490
if (cancelRequest != null) {
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.storage.internal;
16+
17+
import android.net.Uri;
18+
import androidx.annotation.NonNull;
19+
import androidx.annotation.Nullable;
20+
import com.google.firebase.emulators.EmulatedServiceSettings;
21+
import com.google.firebase.storage.network.NetworkRequest;
22+
23+
/** Utility class to encapsulate the two "views" of a storage URI in a single object. */
24+
public class StorageReferenceUri {
25+
26+
private final Uri httpBaseUri;
27+
private final Uri httpUri;
28+
private final Uri gsUri;
29+
30+
public StorageReferenceUri(@NonNull Uri gsUri) {
31+
this(gsUri, null);
32+
}
33+
34+
public StorageReferenceUri(
35+
@NonNull Uri gsUri, @Nullable EmulatedServiceSettings emulatorSettings) {
36+
// We assume this has already come from Util.normalize()
37+
this.gsUri = gsUri;
38+
this.httpBaseUri =
39+
emulatorSettings == null
40+
? NetworkRequest.PROD_BASE_URL
41+
: Uri.parse(
42+
"http://" + emulatorSettings.getHost() + ":" + emulatorSettings.getPort() + "/v0");
43+
44+
// Add /b/bucket
45+
String bucket = gsUri.getAuthority();
46+
Uri.Builder httpBuilder = httpBaseUri.buildUpon().appendPath("b").appendEncodedPath(bucket);
47+
48+
// Add /o/path (if there is a path)
49+
String path = Slashes.normalizeSlashes(gsUri.getPath());
50+
if (path.length() > 0 && !"/".equals(path)) {
51+
httpBuilder = httpBuilder.appendPath("o").appendPath(path);
52+
}
53+
54+
this.httpUri = httpBuilder.build();
55+
}
56+
57+
@NonNull
58+
public Uri getHttpBaseUri() {
59+
return httpBaseUri;
60+
}
61+
62+
@NonNull
63+
public Uri getHttpUri() {
64+
return httpUri;
65+
}
66+
67+
@NonNull
68+
public Uri getGsUri() {
69+
return gsUri;
70+
}
71+
}

firebase-storage/src/main/java/com/google/firebase/storage/internal/Util.java

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
package com.google.firebase.storage.internal;
1616

1717
import android.net.Uri;
18-
import android.os.RemoteException;
1918
import android.text.TextUtils;
2019
import android.util.Log;
2120
import androidx.annotation.NonNull;
@@ -70,10 +69,6 @@ public static boolean equals(@Nullable Object a, @Nullable Object b) {
7069
return Objects.equal(a, b);
7170
}
7271

73-
private static String getAuthority() throws RemoteException {
74-
return NetworkRequest.getAuthority();
75-
}
76-
7772
/**
7873
* Normalizes a Firebase Storage uri into its "gs://" format and strips any trailing slash.
7974
*
@@ -91,6 +86,7 @@ public static Uri normalize(@NonNull FirebaseApp app, @Nullable String s)
9186
"Firebase Storage URLs must point to an object in your Storage Bucket. Please "
9287
+ "obtain a URL using the Firebase Console or getDownloadUrl().";
9388

89+
Uri baseUrl = NetworkRequest.PROD_BASE_URL;
9490
String trimmedInput = s.toLowerCase();
9591
String bucket;
9692
String encodedPath;
@@ -104,13 +100,7 @@ public static Uri normalize(@NonNull FirebaseApp app, @Nullable String s)
104100
if (scheme != null
105101
&& (equals(scheme.toLowerCase(), "http") || equals(scheme.toLowerCase(), "https"))) {
106102
String lowerAuthority = uri.getAuthority().toLowerCase();
107-
int indexOfAuth;
108-
try {
109-
indexOfAuth = lowerAuthority.indexOf(getAuthority());
110-
} catch (RemoteException e) {
111-
throw new UnsupportedEncodingException(
112-
"Could not parse Url because the Storage network layer did not load");
113-
}
103+
int indexOfAuth = lowerAuthority.indexOf(baseUrl.getAuthority());
114104
encodedPath = Slashes.slashize(uri.getEncodedPath());
115105
if (indexOfAuth == 0 && encodedPath.startsWith("/")) {
116106
int firstBSlash = encodedPath.indexOf("/b/", 0); // /v0/b/bucket.storage

firebase-storage/src/main/java/com/google/firebase/storage/network/DeleteNetworkRequest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414

1515
package com.google.firebase.storage.network;
1616

17-
import android.net.Uri;
1817
import androidx.annotation.NonNull;
1918
import com.google.firebase.FirebaseApp;
19+
import com.google.firebase.storage.internal.StorageReferenceUri;
2020

2121
/** A network request that deletes a gcs object. */
2222
public class DeleteNetworkRequest extends NetworkRequest {
23-
public DeleteNetworkRequest(@NonNull Uri gsUri, @NonNull FirebaseApp app) {
24-
super(gsUri, app);
23+
public DeleteNetworkRequest(
24+
@NonNull StorageReferenceUri storageReferenceUri, @NonNull FirebaseApp app) {
25+
super(storageReferenceUri, app);
2526
}
2627

2728
@Override

firebase-storage/src/main/java/com/google/firebase/storage/network/GetMetadataNetworkRequest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414

1515
package com.google.firebase.storage.network;
1616

17-
import android.net.Uri;
1817
import androidx.annotation.NonNull;
1918
import com.google.firebase.FirebaseApp;
19+
import com.google.firebase.storage.internal.StorageReferenceUri;
2020

2121
/** A network request that returns metadata on a gcs object. */
2222
public class GetMetadataNetworkRequest extends NetworkRequest {
23-
public GetMetadataNetworkRequest(@NonNull Uri gsUri, @NonNull FirebaseApp app) {
24-
super(gsUri, app);
23+
public GetMetadataNetworkRequest(
24+
@NonNull StorageReferenceUri storageReferenceUri, @NonNull FirebaseApp app) {
25+
super(storageReferenceUri, app);
2526
}
2627

2728
@Override

firebase-storage/src/main/java/com/google/firebase/storage/network/GetNetworkRequest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
package com.google.firebase.storage.network;
1616

17-
import android.net.Uri;
1817
import androidx.annotation.NonNull;
1918
import com.google.firebase.FirebaseApp;
19+
import com.google.firebase.storage.internal.StorageReferenceUri;
2020
import java.util.Collections;
2121
import java.util.Map;
2222

@@ -25,8 +25,9 @@ public class GetNetworkRequest extends NetworkRequest {
2525
@SuppressWarnings("unused")
2626
private static final String TAG = "GetNetworkRequest";
2727

28-
public GetNetworkRequest(@NonNull Uri gsUri, @NonNull FirebaseApp app, long startByte) {
29-
super(gsUri, app);
28+
public GetNetworkRequest(
29+
@NonNull StorageReferenceUri storageReferenceUri, @NonNull FirebaseApp app, long startByte) {
30+
super(storageReferenceUri, app);
3031
if (startByte != 0) {
3132
super.setCustomHeader("Range", "bytes=" + startByte + "-");
3233
}

firebase-storage/src/main/java/com/google/firebase/storage/network/ListNetworkRequest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import androidx.annotation.NonNull;
2020
import androidx.annotation.Nullable;
2121
import com.google.firebase.FirebaseApp;
22+
import com.google.firebase.storage.internal.StorageReferenceUri;
2223
import java.util.HashMap;
2324
import java.util.Map;
2425

@@ -28,11 +29,11 @@ public class ListNetworkRequest extends NetworkRequest {
2829
@Nullable private final String nextPageToken;
2930

3031
public ListNetworkRequest(
31-
@NonNull Uri gsUri,
32+
@NonNull StorageReferenceUri storageReferenceUri,
3233
@NonNull FirebaseApp app,
3334
@Nullable Integer maxPageSize,
3435
@Nullable String nextPageToken) {
35-
super(gsUri, app);
36+
super(storageReferenceUri, app);
3637
this.maxPageSize = maxPageSize;
3738
this.nextPageToken = nextPageToken;
3839
}
@@ -45,8 +46,9 @@ protected String getAction() {
4546

4647
@Override
4748
@NonNull
48-
protected Uri getURL() {
49-
return Uri.parse(sNetworkRequestUrl + "/b/" + mGsUri.getAuthority() + "/o");
49+
public Uri getURL() {
50+
String bucketName = getStorageReferenceUri().getGsUri().getAuthority();
51+
return Uri.parse(getStorageReferenceUri().getHttpBaseUri() + "/b/" + bucketName + "/o");
5052
}
5153

5254
@Override

0 commit comments

Comments
 (0)