From 7f34ce37336709dd5ca4fca9a4111fb95944060c Mon Sep 17 00:00:00 2001 From: qdpham Date: Fri, 12 Apr 2024 18:01:22 +0000 Subject: [PATCH 01/10] Add strict closure for httpurlconnection --- .../internal/ConfigAutoFetch.java | 14 ++- .../internal/ConfigRealtimeHttpClient.java | 92 +++++++++++++++---- 2 files changed, 84 insertions(+), 22 deletions(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java index 81016602532..9e7402845cc 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java @@ -54,6 +54,7 @@ public class ConfigAutoFetch { private final ConfigUpdateListener retryCallback; private final ScheduledExecutorService scheduledExecutorService; private final Random random; + private volatile Boolean isInBackground; public ConfigAutoFetch( HttpURLConnection httpURLConnection, @@ -69,6 +70,7 @@ public ConfigAutoFetch( this.retryCallback = retryCallback; this.scheduledExecutorService = scheduledExecutorService; this.random = new Random(); + this.isInBackground = false; } private synchronized void propagateErrors(FirebaseRemoteConfigException exception) { @@ -87,6 +89,10 @@ private synchronized boolean isEventListenersEmpty() { return this.eventListeners.isEmpty(); } + public void setBackgroundState(boolean backgroundState) { + isInBackground = backgroundState; + } + private String parseAndValidateConfigUpdateMessage(String message) { int left = message.indexOf('{'); int right = message.lastIndexOf('}'); @@ -110,10 +116,10 @@ public void listenForNotifications() { handleNotifications(inputStream); inputStream.close(); } catch (IOException ex) { - // Stream was interrupted due to a transient issue and the system will retry the connection. - Log.d(TAG, "Stream was cancelled due to an exception. Retrying the connection...", ex); - } finally { - httpURLConnection.disconnect(); + if (!isInBackground) { + // Stream was interrupted due to a transient issue and the system will retry the connection. + Log.d(TAG, "Stream was cancelled due to an exception. Retrying the connection...", ex); + } } } diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index ef0ef2defc5..1bf2955d24f 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -98,8 +98,10 @@ public class ConfigRealtimeHttpClient { private boolean isRealtimeDisabled; /** Flag to indicate whether or not the app is in the background or not. */ - private boolean isInBackground; - + private volatile Boolean isInBackground; + // The HttpUrlConnection and auto-fetcher for this client. Only one of each exist at a time. + private HttpURLConnection httpURLConnection; + private ConfigAutoFetch configAutoFetch; private final int ORIGINAL_RETRIES = 8; private final ScheduledExecutorService scheduledExecutorService; private final ConfigFetchHandler configFetchHandler; @@ -392,13 +394,37 @@ public void run() { } void setRealtimeBackgroundState(boolean backgroundState) { - isInBackground = backgroundState; + synchronized (isInBackground) { + isInBackground = backgroundState; + if (configAutoFetch != null) { + configAutoFetch.setBackgroundState(backgroundState); + } + if (isInBackground) { + if (httpURLConnection != null) { + httpURLConnection.disconnect(); + } + } + } } private synchronized void resetRetryCount() { httpRetriesRemaining = ORIGINAL_RETRIES; } + /** + * The check and set http connection method are combined so that when canMakeHttpStreamConnection + * returns true, the same thread can mark isHttpConnectionIsRunning as true to prevent a race + * condition with another thread. + */ + private synchronized boolean checkAndSetHttpConnectionFlagIfNotRunning() { + boolean canMakeConnection = canMakeHttpStreamConnection(); + if (canMakeConnection) { + setIsHttpConnectionRunning(true); + } + + return canMakeConnection; + } + private synchronized void setIsHttpConnectionRunning(boolean connectionRunning) { isHttpConnectionRunning = connectionRunning; } @@ -469,7 +495,7 @@ private String parseForbiddenErrorResponseMessage(InputStream inputStream) { */ @SuppressLint({"VisibleForTests", "DefaultLocale"}) public void beginRealtimeHttpStream() { - if (!canMakeHttpStreamConnection()) { + if (!checkAndSetHttpConnectionFlagIfNotRunning()) { return; } @@ -489,17 +515,19 @@ public void beginRealtimeHttpStream() { this.scheduledExecutorService, (completedHttpUrlConnectionTask) -> { Integer responseCode = null; - HttpURLConnection httpURLConnection = null; + InputStream inputStream = null; + InputStream errorStream = null; try { // If HTTP connection task failed throw exception to move to the catch block. if (!httpURLConnectionTask.isSuccessful()) { throw new IOException(httpURLConnectionTask.getException()); } - setIsHttpConnectionRunning(true); // Get HTTP connection and check response code. httpURLConnection = httpURLConnectionTask.getResult(); + inputStream = httpURLConnection.getInputStream(); + errorStream = httpURLConnection.getErrorStream(); responseCode = httpURLConnection.getResponseCode(); // If the connection returned a 200 response code, start listening for messages. @@ -509,23 +537,36 @@ public void beginRealtimeHttpStream() { metadataClient.resetRealtimeBackoff(); // Start listening for realtime notifications. - ConfigAutoFetch configAutoFetch = startAutoFetch(httpURLConnection); + configAutoFetch = startAutoFetch(httpURLConnection); configAutoFetch.listenForNotifications(); } } catch (IOException e) { - // Stream could not be open due to a transient issue and the system will retry the - // connection - // without user intervention. - Log.d( - TAG, - "Exception connecting to real-time RC backend. Retrying the connection...", - e); + if (isInBackground) { + // It's possible the app was backgrounded while the connection was open, which + // threw an exception trying to read the response. No real error here, so treat + // this as a success, even if we haven't read a 200 response code yet. + resetRetryCount(); + } else { + // If it's not in the background, there might have been a transient error so the + // client will retry the connection. + Log.d( + TAG, + "Exception connecting to real-time RC backend. Retrying the connection...", + e); + } } finally { - closeRealtimeHttpStream(httpURLConnection); + if (httpURLConnection != null) { + httpURLConnection.disconnect(); + } + closeHttpConnectionStreams(inputStream); + closeHttpConnectionStreams(errorStream); + + // Either way indicate the HTTP connection is closed. setIsHttpConnectionRunning(false); boolean connectionFailed = - responseCode == null || isStatusCodeRetryable(responseCode); + !isInBackground + && (responseCode == null || isStatusCodeRetryable(responseCode)); if (connectionFailed) { updateBackoffMetadataWithLastFailedStreamConnectionTime( new Date(clock.currentTimeMillis())); @@ -542,8 +583,7 @@ public void beginRealtimeHttpStream() { "Unable to connect to the server. Try again in a few minutes. HTTP status code: %d", responseCode); // Return server message for when the Firebase Remote Config Realtime API is - // disabled and - // the server returns a 403 + // disabled and the server returns a 403 if (responseCode == 403) { errorMessage = parseForbiddenErrorResponseMessage(httpURLConnection.getErrorStream()); @@ -556,10 +596,26 @@ public void beginRealtimeHttpStream() { } } + // Reset parameters. + httpURLConnection = null; + configAutoFetch = null; + return Tasks.forResult(null); }); } + private void closeHttpConnectionStreams(InputStream stream) { + if (stream == null) { + return; + } + + try { + stream.close(); + } catch (IOException ex) { + Log.d(TAG, "Exception thrown when closing connection stream. Retrying connection...", ex); + } + } + // Pauses Http stream listening public void closeRealtimeHttpStream(HttpURLConnection httpURLConnection) { if (httpURLConnection != null) { From a62df829d9c3caa670705483a8e57c7f594886dd Mon Sep 17 00:00:00 2001 From: qdpham Date: Fri, 12 Apr 2024 18:10:14 +0000 Subject: [PATCH 02/10] Fix tests and refactor connection closing method --- .../internal/ConfigRealtimeHttpClient.java | 26 +++++------------ .../FirebaseRemoteConfigTest.java | 28 +++++++++++++------ 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index 1bf2955d24f..d525bed7513 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -555,13 +555,8 @@ public void beginRealtimeHttpStream() { e); } } finally { - if (httpURLConnection != null) { - httpURLConnection.disconnect(); - } - closeHttpConnectionStreams(inputStream); - closeHttpConnectionStreams(errorStream); - - // Either way indicate the HTTP connection is closed. + // Close HTTP connection and associated streams. + closeRealtimeHttpStream(httpURLConnection, inputStream, errorStream); setIsHttpConnectionRunning(false); boolean connectionFailed = @@ -604,7 +599,7 @@ public void beginRealtimeHttpStream() { }); } - private void closeHttpConnectionStreams(InputStream stream) { + private void closeHttpConnectionInputStream(InputStream stream) { if (stream == null) { return; } @@ -617,19 +612,12 @@ private void closeHttpConnectionStreams(InputStream stream) { } // Pauses Http stream listening - public void closeRealtimeHttpStream(HttpURLConnection httpURLConnection) { + public void closeRealtimeHttpStream( + HttpURLConnection httpURLConnection, InputStream inputStream, InputStream errorStream) { if (httpURLConnection != null) { httpURLConnection.disconnect(); - - // Explicitly close the input stream due to a bug in the Android okhttp implementation. - // See github.com/firebase/firebase-android-sdk/pull/808. - try { - httpURLConnection.getInputStream().close(); - if (httpURLConnection.getErrorStream() != null) { - httpURLConnection.getErrorStream().close(); - } - } catch (IOException e) { - } } + closeHttpConnectionInputStream(inputStream); + closeHttpConnectionInputStream(errorStream); } } diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index 74ea6c98c3b..b0477728405 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -80,6 +80,7 @@ import com.google.firebase.remoteconfig.internal.rollouts.RolloutsStateSubscriptionsHandler; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; @@ -1306,7 +1307,8 @@ public void realtime_redirectStatusCode_noRetries() throws Exception { .createRealtimeConnection(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(301); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1327,7 +1329,8 @@ public void realtime_okStatusCode_startAutofetchAndRetries() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1346,7 +1349,8 @@ public void realtime_badGatewayStatusCode_noAutofetchButRetries() throws Excepti doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1365,7 +1369,8 @@ public void realtime_retryableStatusCode_increasesConfigMetadataFailedStreams() doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); int failedStreams = configRealtimeHttpClientSpy.getNumberOfFailedStreams(); @@ -1384,7 +1389,8 @@ public void realtime_retryableStatusCode_increasesConfigMetadataBackoffDate() th doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); Date backoffDate = configRealtimeHttpClientSpy.getBackoffEndTime(); @@ -1405,7 +1411,8 @@ public void realtime_successfulStatusCode_doesNotIncreaseConfigMetadataFailedStr doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); int failedStreams = configRealtimeHttpClientSpy.getNumberOfFailedStreams(); @@ -1426,7 +1433,8 @@ public void realtime_successfulStatusCode_doesNotIncreaseConfigMetadataBackoffDa doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); Date backoffDate = configRealtimeHttpClientSpy.getBackoffEndTime(); @@ -1444,7 +1452,8 @@ public void realtime_forbiddenStatusCode_returnsStreamError() throws Exception { .createRealtimeConnection(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getErrorStream()) .thenReturn( new ByteArrayInputStream(FORBIDDEN_ERROR_MESSAGE.getBytes(StandardCharsets.UTF_8))); @@ -1467,7 +1476,8 @@ public void realtime_exceptionThrown_noAutofetchButRetries() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream(any(HttpURLConnection.class)); + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); flushScheduledTasks(); From d347ec45086f65ea2196fe3114d61c62335bbe10 Mon Sep 17 00:00:00 2001 From: qdpham Date: Fri, 12 Apr 2024 18:18:50 +0000 Subject: [PATCH 03/10] Add test --- .../FirebaseRemoteConfigTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index b0477728405..f463321fd60 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -350,6 +350,7 @@ public void onError(@NonNull FirebaseRemoteConfigException error) { listeners, mockRetryListener, scheduledExecutorService); + configAutoFetch.setBackgroundState(false); realtimeMetadataClient = new ConfigMetadataClient(context.getSharedPreferences("test_file", Context.MODE_PRIVATE)); configRealtimeHttpClient = @@ -1519,6 +1520,25 @@ public void realtime_stream_listen_and_failsafe_disabled() throws Exception { verify(mockFetchHandler).getTemplateVersionNumber(); } + @Test + public void realtime_stream_listen_backgrounded_disconnects() throws Exception { + when(mockHttpURLConnection.getResponseCode()).thenReturn(200); + when(mockHttpURLConnection.getInputStream()) + .thenReturn( + new ByteArrayInputStream( + "{ \"featureDisabled\": false, \"latestTemplateVersionNumber\": 2 } }" + .getBytes(StandardCharsets.UTF_8))); + when(mockFetchHandler.getTemplateVersionNumber()).thenReturn(1L); + when(mockFetchHandler.fetchNowWithTypeAndAttemptNumber( + ConfigFetchHandler.FetchType.REALTIME, 1)) + .thenReturn(Tasks.forResult(realtimeFetchedContainerResponse)); + + configAutoFetch.listenForNotifications(); + frc.setConfigUpdateBackgroundState(true); + + verify(mockHttpURLConnection, times(1)).disconnect(); + } + @Test public void realtimeStreamListen_andUnableToParseMessage() throws Exception { when(mockHttpURLConnection.getResponseCode()).thenReturn(200); From 6011c94a1b8514b55e6b3a8bd408a1521d8db665 Mon Sep 17 00:00:00 2001 From: qdpham Date: Fri, 12 Apr 2024 20:52:54 +0000 Subject: [PATCH 04/10] Fix test --- .../internal/ConfigAutoFetch.java | 15 ++++--- .../internal/ConfigRealtimeHttpClient.java | 4 +- .../FirebaseRemoteConfigTest.java | 43 +++++++++++-------- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java index 9e7402845cc..9fb5543d844 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java @@ -111,15 +111,23 @@ public void listenForNotifications() { return; } + InputStream inputStream = null; try { - InputStream inputStream = httpURLConnection.getInputStream(); + inputStream = httpURLConnection.getInputStream(); handleNotifications(inputStream); - inputStream.close(); } catch (IOException ex) { if (!isInBackground) { // Stream was interrupted due to a transient issue and the system will retry the connection. Log.d(TAG, "Stream was cancelled due to an exception. Retrying the connection...", ex); } + } finally { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException ex) { + Log.d(TAG, "Exception thrown when closing connection stream. Retrying connection...", ex); + } + } } } @@ -190,9 +198,6 @@ private void handleNotifications(InputStream inputStream) throws IOException { currentConfigUpdateMessage = ""; } } - - reader.close(); - inputStream.close(); } private void autoFetch(int remainingAttempts, long targetVersion) { diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index d525bed7513..1968fbd73bc 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -393,7 +393,7 @@ public void run() { } } - void setRealtimeBackgroundState(boolean backgroundState) { + public void setRealtimeBackgroundState(boolean backgroundState) { synchronized (isInBackground) { isInBackground = backgroundState; if (configAutoFetch != null) { @@ -614,7 +614,7 @@ private void closeHttpConnectionInputStream(InputStream stream) { // Pauses Http stream listening public void closeRealtimeHttpStream( HttpURLConnection httpURLConnection, InputStream inputStream, InputStream errorStream) { - if (httpURLConnection != null) { + if (httpURLConnection != null && !isInBackground) { httpURLConnection.disconnect(); } closeHttpConnectionInputStream(inputStream); diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index f463321fd60..80b405b3d35 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -37,6 +37,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -1274,17 +1275,18 @@ public void realtime_client_removeListener_success() { @Test public void realtime_stream_listen_and_end_connection() throws Exception { - when(mockHttpURLConnection.getInputStream()) - .thenReturn( - new ByteArrayInputStream( - "{ \"latestTemplateVersionNumber\": 1 }".getBytes(StandardCharsets.UTF_8))); + InputStream inputStream = + new ByteArrayInputStream( + "{ \"latestTemplateVersionNumber\": 1 }".getBytes(StandardCharsets.UTF_8)); + InputStream inputStreamSpy = spy(inputStream); + when(mockHttpURLConnection.getInputStream()).thenReturn(inputStreamSpy); when(mockFetchHandler.getTemplateVersionNumber()).thenReturn(1L); when(mockFetchHandler.fetchNowWithTypeAndAttemptNumber( ConfigFetchHandler.FetchType.REALTIME, 1)) .thenReturn(Tasks.forResult(realtimeFetchedContainerResponse)); configAutoFetch.listenForNotifications(); - verify(mockHttpURLConnection).disconnect(); + verify(inputStreamSpy, times(1)).close(); } @Test @@ -1522,19 +1524,20 @@ public void realtime_stream_listen_and_failsafe_disabled() throws Exception { @Test public void realtime_stream_listen_backgrounded_disconnects() throws Exception { + ConfigRealtimeHttpClient configRealtimeHttpClientSpy = spy(configRealtimeHttpClient); + doReturn(Tasks.forResult(mockHttpURLConnection)) + .when(configRealtimeHttpClientSpy) + .createRealtimeConnection(); + doReturn(mockConfigAutoFetch).when(configRealtimeHttpClientSpy).startAutoFetch(any()); + doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); + doNothing() + .when(configRealtimeHttpClientSpy) + .closeRealtimeHttpStream( + any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); - when(mockHttpURLConnection.getInputStream()) - .thenReturn( - new ByteArrayInputStream( - "{ \"featureDisabled\": false, \"latestTemplateVersionNumber\": 2 } }" - .getBytes(StandardCharsets.UTF_8))); - when(mockFetchHandler.getTemplateVersionNumber()).thenReturn(1L); - when(mockFetchHandler.fetchNowWithTypeAndAttemptNumber( - ConfigFetchHandler.FetchType.REALTIME, 1)) - .thenReturn(Tasks.forResult(realtimeFetchedContainerResponse)); - - configAutoFetch.listenForNotifications(); - frc.setConfigUpdateBackgroundState(true); + configRealtimeHttpClientSpy.beginRealtimeHttpStream(); + configRealtimeHttpClientSpy.setRealtimeBackgroundState(true); + flushScheduledTasks(); verify(mockHttpURLConnection, times(1)).disconnect(); } @@ -1558,15 +1561,17 @@ public void realtimeStreamListen_andUnableToParseMessage() throws Exception { @Test public void realtime_stream_listen_get_inputstream_fail() throws Exception { + InputStream inputStream = mock(InputStream.class); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); - when(mockHttpURLConnection.getInputStream()).thenThrow(IOException.class); + when(mockHttpURLConnection.getInputStream()).thenReturn(inputStream); + when(inputStream.read()).thenThrow(IOException.class); when(mockFetchHandler.getTemplateVersionNumber()).thenReturn(1L); when(mockFetchHandler.fetchNowWithTypeAndAttemptNumber( ConfigFetchHandler.FetchType.REALTIME, 1)) .thenReturn(Tasks.forResult(realtimeFetchedContainerResponse)); configAutoFetch.listenForNotifications(); - verify(mockHttpURLConnection).disconnect(); + verify(inputStream).close(); } @Test From e616edef21fe22f81f220fb65dd9c8b11c4a9f99 Mon Sep 17 00:00:00 2001 From: qdpham Date: Mon, 15 Apr 2024 15:02:10 +0000 Subject: [PATCH 05/10] Add helpful comments --- .../firebase/remoteconfig/internal/ConfigAutoFetch.java | 4 ++++ .../remoteconfig/internal/ConfigRealtimeHttpClient.java | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java index 9fb5543d844..22d6774ce3d 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java @@ -111,6 +111,8 @@ public void listenForNotifications() { return; } + // Keep reference to InputStream so it can be closed when the connection is finished or threw an + // exception. InputStream inputStream = null; try { inputStream = httpURLConnection.getInputStream(); @@ -123,6 +125,8 @@ public void listenForNotifications() { } finally { if (inputStream != null) { try { + // Only need to close the InputStream, ConfigRealtimeHttpClient will disconnect + // HttpUrlConnection inputStream.close(); } catch (IOException ex) { Log.d(TAG, "Exception thrown when closing connection stream. Retrying connection...", ex); diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index 1968fbd73bc..4cbd8a221c7 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -394,11 +394,15 @@ public void run() { } public void setRealtimeBackgroundState(boolean backgroundState) { + // Make changes in synchronized block so that everything is updated in a single atomic + // transaction. synchronized (isInBackground) { isInBackground = backgroundState; if (configAutoFetch != null) { configAutoFetch.setBackgroundState(backgroundState); } + // Close the connection if the app is in the background and their is an active + // HttpUrlConnection. if (isInBackground) { if (httpURLConnection != null) { httpURLConnection.disconnect(); @@ -515,6 +519,8 @@ public void beginRealtimeHttpStream() { this.scheduledExecutorService, (completedHttpUrlConnectionTask) -> { Integer responseCode = null; + // Get references to InputStream and ErrorStream before listening on the stream so + // that they can be closed without getting them from HttpUrlConnection. InputStream inputStream = null; InputStream errorStream = null; @@ -611,7 +617,8 @@ private void closeHttpConnectionInputStream(InputStream stream) { } } - // Pauses Http stream listening + // Pauses Http stream listening by disconnecting the HttpUrlConnection and underlying InputStream + // and ErrorStream if they exist. public void closeRealtimeHttpStream( HttpURLConnection httpURLConnection, InputStream inputStream, InputStream errorStream) { if (httpURLConnection != null && !isInBackground) { From 70ac1fae3255bb9f5cdd38ed73167abe1d164a71 Mon Sep 17 00:00:00 2001 From: qdpham Date: Mon, 15 Apr 2024 19:35:27 +0000 Subject: [PATCH 06/10] update test --- .../google/firebase/remoteconfig/FirebaseRemoteConfigTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index 80b405b3d35..abb0db12d96 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -1539,7 +1539,7 @@ public void realtime_stream_listen_backgrounded_disconnects() throws Exception { configRealtimeHttpClientSpy.setRealtimeBackgroundState(true); flushScheduledTasks(); - verify(mockHttpURLConnection, times(1)).disconnect(); + verify(mockHttpURLConnection).disconnect(); } @Test From 448ba8837804e6538fd4ea0c2a9bc373e4b22d63 Mon Sep 17 00:00:00 2001 From: qdpham Date: Mon, 15 Apr 2024 19:47:50 +0000 Subject: [PATCH 07/10] fix test in PR --- .../google/firebase/remoteconfig/FirebaseRemoteConfigTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index abb0db12d96..00d2c4f73fd 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -1536,8 +1536,8 @@ public void realtime_stream_listen_backgrounded_disconnects() throws Exception { any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); - configRealtimeHttpClientSpy.setRealtimeBackgroundState(true); flushScheduledTasks(); + configRealtimeHttpClientSpy.setRealtimeBackgroundState(true); verify(mockHttpURLConnection).disconnect(); } From d58223e07fa8ee970701eef3c8d6fdb0ba629316 Mon Sep 17 00:00:00 2001 From: qdpham Date: Tue, 16 Apr 2024 14:44:06 +0000 Subject: [PATCH 08/10] Remove unnecessary volatile descriptor --- .../google/firebase/remoteconfig/internal/ConfigAutoFetch.java | 2 +- .../remoteconfig/internal/ConfigRealtimeHttpClient.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java index 22d6774ce3d..3a049b05079 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigAutoFetch.java @@ -54,7 +54,7 @@ public class ConfigAutoFetch { private final ConfigUpdateListener retryCallback; private final ScheduledExecutorService scheduledExecutorService; private final Random random; - private volatile Boolean isInBackground; + private Boolean isInBackground; public ConfigAutoFetch( HttpURLConnection httpURLConnection, diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index 4cbd8a221c7..a6ce41eda0b 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -98,7 +98,7 @@ public class ConfigRealtimeHttpClient { private boolean isRealtimeDisabled; /** Flag to indicate whether or not the app is in the background or not. */ - private volatile Boolean isInBackground; + private Boolean isInBackground; // The HttpUrlConnection and auto-fetcher for this client. Only one of each exist at a time. private HttpURLConnection httpURLConnection; private ConfigAutoFetch configAutoFetch; From f47a400f7658c32c9cbf882097543b0bda856af2 Mon Sep 17 00:00:00 2001 From: qdpham Date: Thu, 18 Apr 2024 15:16:07 +0000 Subject: [PATCH 09/10] Refactor stream closing method --- .../internal/ConfigRealtimeHttpClient.java | 6 ++-- .../FirebaseRemoteConfigTest.java | 30 +++++++------------ 2 files changed, 13 insertions(+), 23 deletions(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index a6ce41eda0b..ac301b04be5 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -562,7 +562,7 @@ public void beginRealtimeHttpStream() { } } finally { // Close HTTP connection and associated streams. - closeRealtimeHttpStream(httpURLConnection, inputStream, errorStream); + closeAllRealtimeHttpStreams(inputStream, errorStream); setIsHttpConnectionRunning(false); boolean connectionFailed = @@ -619,8 +619,8 @@ private void closeHttpConnectionInputStream(InputStream stream) { // Pauses Http stream listening by disconnecting the HttpUrlConnection and underlying InputStream // and ErrorStream if they exist. - public void closeRealtimeHttpStream( - HttpURLConnection httpURLConnection, InputStream inputStream, InputStream errorStream) { + @VisibleForTesting + public void closeAllRealtimeHttpStreams(InputStream inputStream, InputStream errorStream) { if (httpURLConnection != null && !isInBackground) { httpURLConnection.disconnect(); } diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index 00d2c4f73fd..e569a02b687 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -1310,8 +1310,7 @@ public void realtime_redirectStatusCode_noRetries() throws Exception { .createRealtimeConnection(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(301); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1332,8 +1331,7 @@ public void realtime_okStatusCode_startAutofetchAndRetries() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1352,8 +1350,7 @@ public void realtime_badGatewayStatusCode_noAutofetchButRetries() throws Excepti doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1372,8 +1369,7 @@ public void realtime_retryableStatusCode_increasesConfigMetadataFailedStreams() doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); int failedStreams = configRealtimeHttpClientSpy.getNumberOfFailedStreams(); @@ -1392,8 +1388,7 @@ public void realtime_retryableStatusCode_increasesConfigMetadataBackoffDate() th doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); Date backoffDate = configRealtimeHttpClientSpy.getBackoffEndTime(); @@ -1414,8 +1409,7 @@ public void realtime_successfulStatusCode_doesNotIncreaseConfigMetadataFailedStr doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); int failedStreams = configRealtimeHttpClientSpy.getNumberOfFailedStreams(); @@ -1436,8 +1430,7 @@ public void realtime_successfulStatusCode_doesNotIncreaseConfigMetadataBackoffDa doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); Date backoffDate = configRealtimeHttpClientSpy.getBackoffEndTime(); @@ -1455,8 +1448,7 @@ public void realtime_forbiddenStatusCode_returnsStreamError() throws Exception { .createRealtimeConnection(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getErrorStream()) .thenReturn( new ByteArrayInputStream(FORBIDDEN_ERROR_MESSAGE.getBytes(StandardCharsets.UTF_8))); @@ -1479,8 +1471,7 @@ public void realtime_exceptionThrown_noAutofetchButRetries() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); flushScheduledTasks(); @@ -1532,8 +1523,7 @@ public void realtime_stream_listen_backgrounded_disconnects() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeRealtimeHttpStream( - any(HttpURLConnection.class), any(InputStream.class), any(InputStream.class)); + .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); flushScheduledTasks(); From 2a4b6eeb0a5ed64ac3d774189755fc1f6f4bd750 Mon Sep 17 00:00:00 2001 From: qdpham Date: Tue, 23 Apr 2024 19:18:41 +0000 Subject: [PATCH 10/10] Address PR comments --- .../internal/ConfigRealtimeHttpClient.java | 14 ++++++------- .../FirebaseRemoteConfigTest.java | 20 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java index ac301b04be5..b79d44b9d61 100644 --- a/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java +++ b/firebase-config/src/main/java/com/google/firebase/remoteconfig/internal/ConfigRealtimeHttpClient.java @@ -394,8 +394,8 @@ public void run() { } public void setRealtimeBackgroundState(boolean backgroundState) { - // Make changes in synchronized block so that everything is updated in a single atomic - // transaction. + // Make changes in synchronized block so only one thread sets the background state and calls + // disconnect. synchronized (isInBackground) { isInBackground = backgroundState; if (configAutoFetch != null) { @@ -562,7 +562,7 @@ public void beginRealtimeHttpStream() { } } finally { // Close HTTP connection and associated streams. - closeAllRealtimeHttpStreams(inputStream, errorStream); + closeRealtimeHttpConnection(inputStream, errorStream); setIsHttpConnectionRunning(false); boolean connectionFailed = @@ -605,13 +605,13 @@ public void beginRealtimeHttpStream() { }); } - private void closeHttpConnectionInputStream(InputStream stream) { - if (stream == null) { + private void closeHttpConnectionInputStream(InputStream inputStream) { + if (inputStream == null) { return; } try { - stream.close(); + inputStream.close(); } catch (IOException ex) { Log.d(TAG, "Exception thrown when closing connection stream. Retrying connection...", ex); } @@ -620,7 +620,7 @@ private void closeHttpConnectionInputStream(InputStream stream) { // Pauses Http stream listening by disconnecting the HttpUrlConnection and underlying InputStream // and ErrorStream if they exist. @VisibleForTesting - public void closeAllRealtimeHttpStreams(InputStream inputStream, InputStream errorStream) { + public void closeRealtimeHttpConnection(InputStream inputStream, InputStream errorStream) { if (httpURLConnection != null && !isInBackground) { httpURLConnection.disconnect(); } diff --git a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java index e569a02b687..fc78b070f11 100644 --- a/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java +++ b/firebase-config/src/test/java/com/google/firebase/remoteconfig/FirebaseRemoteConfigTest.java @@ -1310,7 +1310,7 @@ public void realtime_redirectStatusCode_noRetries() throws Exception { .createRealtimeConnection(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(301); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1331,7 +1331,7 @@ public void realtime_okStatusCode_startAutofetchAndRetries() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1350,7 +1350,7 @@ public void realtime_badGatewayStatusCode_noAutofetchButRetries() throws Excepti doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); @@ -1369,7 +1369,7 @@ public void realtime_retryableStatusCode_increasesConfigMetadataFailedStreams() doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); int failedStreams = configRealtimeHttpClientSpy.getNumberOfFailedStreams(); @@ -1388,7 +1388,7 @@ public void realtime_retryableStatusCode_increasesConfigMetadataBackoffDate() th doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(502); Date backoffDate = configRealtimeHttpClientSpy.getBackoffEndTime(); @@ -1409,7 +1409,7 @@ public void realtime_successfulStatusCode_doesNotIncreaseConfigMetadataFailedStr doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); int failedStreams = configRealtimeHttpClientSpy.getNumberOfFailedStreams(); @@ -1430,7 +1430,7 @@ public void realtime_successfulStatusCode_doesNotIncreaseConfigMetadataBackoffDa doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); Date backoffDate = configRealtimeHttpClientSpy.getBackoffEndTime(); @@ -1448,7 +1448,7 @@ public void realtime_forbiddenStatusCode_returnsStreamError() throws Exception { .createRealtimeConnection(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getErrorStream()) .thenReturn( new ByteArrayInputStream(FORBIDDEN_ERROR_MESSAGE.getBytes(StandardCharsets.UTF_8))); @@ -1471,7 +1471,7 @@ public void realtime_exceptionThrown_noAutofetchButRetries() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); flushScheduledTasks(); @@ -1523,7 +1523,7 @@ public void realtime_stream_listen_backgrounded_disconnects() throws Exception { doNothing().when(configRealtimeHttpClientSpy).retryHttpConnectionWhenBackoffEnds(); doNothing() .when(configRealtimeHttpClientSpy) - .closeAllRealtimeHttpStreams(any(InputStream.class), any(InputStream.class)); + .closeRealtimeHttpConnection(any(InputStream.class), any(InputStream.class)); when(mockHttpURLConnection.getResponseCode()).thenReturn(200); configRealtimeHttpClientSpy.beginRealtimeHttpStream(); flushScheduledTasks();