Repository: firebase/firebase-admin-java
Pull request #1059 (2025-01-20 21:14:42)

fix: `ApiClientUtils.getDefaultTransport()` should return a cached instance to an `ApacheHttp2Transport`
This fixes an unintentional change in `ApiClientUtils.getDefaultTransport()` which caused firebaseOptions to create a new instance of the default transport on `firebaseOptions` build. This only affected developers who made multiple calls to `FirebaseOptions.builder(...).build()` or initialized multiple app instances.


Patch:
diff --git a/src/main/java/com/google/firebase/internal/ApiClientUtils.java b/src/main/java/com/google/firebase/internal/ApiClientUtils.java
index 3397261..9d501fa 100644
--- a/src/main/java/com/google/firebase/internal/ApiClientUtils.java
+++ b/src/main/java/com/google/firebase/internal/ApiClientUtils.java
@@ -88,6 +88,10 @@ public class ApiClientUtils {
   }
 
   public static HttpTransport getDefaultTransport() {
-    return new ApacheHttp2Transport();
+    return TransportInstanceHolder.INSTANCE;
+  }
+
+  private static class TransportInstanceHolder {
+    static final HttpTransport INSTANCE = new ApacheHttp2Transport();
   }
 }


Test patch:
diff --git a/src/test/java/com/google/firebase/internal/ApacheHttp2TransportIT.java b/src/test/java/com/google/firebase/internal/ApacheHttp2TransportIT.java
index 047e1de..0beb1b3 100644
--- a/src/test/java/com/google/firebase/internal/ApacheHttp2TransportIT.java
+++ b/src/test/java/com/google/firebase/internal/ApacheHttp2TransportIT.java
@@ -198,9 +198,12 @@ public class ApacheHttp2TransportIT {
 
   @Test(timeout = 10_000L)
   public void testWriteTimeoutAuthorizedGet() throws FirebaseException {
+    // Use a fresh transport so that writeTimeout triggers while waiting for the transport to
+    // be ready to receive data.
     app = FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(MOCK_CREDENTIALS)
         .setWriteTimeout(100)
+        .setHttpTransport(new ApacheHttp2Transport())
         .build(), "test-app");
     ErrorHandlingHttpClient<FirebaseException> httpClient = getHttpClient(true, app);
     HttpRequestInfo request = HttpRequestInfo.buildGetRequest(GET_URL);
@@ -217,9 +220,12 @@ public class ApacheHttp2TransportIT {
 
   @Test(timeout = 10_000L)
   public void testWriteTimeoutAuthorizedPost() throws FirebaseException {
+    // Use a fresh transport so that writeTimeout triggers while waiting for the transport to
+    // be ready to receive data.
     app = FirebaseApp.initializeApp(FirebaseOptions.builder()
         .setCredentials(MOCK_CREDENTIALS)
         .setWriteTimeout(100)
+        .setHttpTransport(new ApacheHttp2Transport())
         .build(), "test-app");
     ErrorHandlingHttpClient<FirebaseException> httpClient = getHttpClient(true, app);
     HttpRequestInfo request = HttpRequestInfo.buildJsonPostRequest(POST_URL, payload);
@@ -292,6 +298,21 @@ public class ApacheHttp2TransportIT {
     }
   }
 
+  @Test
+  public void testVerifyDefaultTransportReused() {
+    FirebaseOptions o1 = FirebaseOptions.builder()
+        .setCredentials(MOCK_CREDENTIALS)
+        .build();
+    
+    FirebaseOptions o2 = FirebaseOptions.builder()
+        .setCredentials(MOCK_CREDENTIALS)
+        .build();
+
+    HttpTransport t1 = o1.getHttpTransport();
+    HttpTransport t2 = o2.getHttpTransport();
+    assertEquals(t1, t2);
+  }
+
   private static ErrorHandlingHttpClient<FirebaseException> getHttpClient(boolean authorized,
       FirebaseApp app) {
     HttpRequestFactory requestFactory;
diff --git a/src/test/java/com/google/firebase/internal/ApiClientUtilsTest.java b/src/test/java/com/google/firebase/internal/ApiClientUtilsTest.java
index f23c7a3..e0e619f 100644
--- a/src/test/java/com/google/firebase/internal/ApiClientUtilsTest.java
+++ b/src/test/java/com/google/firebase/internal/ApiClientUtilsTest.java
@@ -25,6 +25,7 @@ import com.google.api.client.http.GenericUrl;
 import com.google.api.client.http.HttpRequest;
 import com.google.api.client.http.HttpRequestFactory;
 import com.google.api.client.http.HttpResponse;
+import com.google.api.client.http.HttpTransport;
 import com.google.api.client.http.HttpUnsuccessfulResponseHandler;
 import com.google.api.client.testing.http.MockHttpTransport;
 import com.google.api.client.testing.http.MockLowLevelHttpResponse;
@@ -128,4 +129,12 @@ public class ApiClientUtilsTest {
 
     assertTrue(lowLevelResponse.isDisconnected());
   }
+
+  @Test
+  public void testVerifyDefaultTransportReused() {
+    HttpTransport t1 = ApiClientUtils.getDefaultTransport();
+    HttpTransport t2 = ApiClientUtils.getDefaultTransport();
+    assertEquals(t1, t2);
+  }
+
 }