Skip to content
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
1 change: 1 addition & 0 deletions firebase-messaging/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased

- [fixed] FCM registration error due to FID_ALREADY_USED (#8507)
- [fixed] StrictMode LeakedClosableViolation in TopicSubscriptionClient (#8534)

# 25.1.1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.firebase.FirebaseApp;
import com.google.firebase.installations.FirebaseInstallationsApi;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -106,11 +107,14 @@ private void performTopicOperation(String topic, String token, String fid, Strin
connection.setDoOutput(false);

int responseCode;
String responseMessage;
try {
responseCode = connection.getResponseCode();
responseMessage = connection.getResponseMessage();
} catch (IOException e) {
throw new IOException(ERROR_SERVICE_NOT_AVAILABLE, e);
} finally {
closeQuietly(connection);
connection.disconnect();
}

Expand All @@ -121,16 +125,35 @@ private void performTopicOperation(String topic, String token, String fid, Strin
}
} else if (responseCode == 404 || responseCode == 403) {
if (isDebugLogEnabled()) {
Log.d(TAG, "Topic " + operation + " failed: " + connection.getResponseMessage());
Log.d(TAG, "Topic " + operation + " failed: " + responseMessage);
}
throw new IOException("Topic " + operation + " failed: " + connection.getResponseMessage());
throw new IOException("Topic " + operation + " failed: " + responseMessage);
} else if (responseCode >= 500) {
throw new IOException(ERROR_INTERNAL_SERVER_ERROR);
} else {
throw new IOException("Topic " + operation + " failed with status: " + responseCode);
}
}

private static void closeQuietly(HttpURLConnection connection) {
try {
InputStream inputStream = connection.getInputStream();
if (inputStream != null) {
inputStream.close();
}
} catch (IOException ignored) {
// getInputStream() throws IOException for HTTP error status codes (for example, 4xx or 5xx)
}
try {
InputStream errorStream = connection.getErrorStream();
if (errorStream != null) {
errorStream.close();
}
} catch (IOException ignored) {
// Ignore exception while closing error stream
}
}

@VisibleForTesting
protected HttpURLConnection createConnection(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
Expand All @@ -29,6 +30,7 @@
import com.google.firebase.installations.FirebaseInstallationsApi;
import com.google.firebase.installations.InstallationTokenResult;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -160,6 +162,32 @@ public void testUnsubscribe_failure503_throwsUnknownStatus() throws Exception {
.isEqualTo(TopicSubscriptionClient.ERROR_INTERNAL_SERVER_ERROR);
}

@Test
public void testSubscribe_success_closesInputStreamAndDisconnects() throws Exception {
InputStream mockInputStream = mock(InputStream.class);
when(mockConnection.getResponseCode()).thenReturn(200);
when(mockConnection.getInputStream()).thenReturn(mockInputStream);

runOnBackground(() -> client.subscribe(TEST_TOPIC));

verify(mockInputStream).close();
verify(mockConnection).disconnect();
}

@Test
public void testSubscribe_failure404_closesErrorStreamAndDisconnects() throws Exception {
InputStream mockErrorStream = mock(InputStream.class);
when(mockConnection.getResponseCode()).thenReturn(404);
when(mockConnection.getResponseMessage()).thenReturn("Not Found");
when(mockConnection.getInputStream()).thenThrow(new IOException("Error"));
when(mockConnection.getErrorStream()).thenReturn(mockErrorStream);

assertThrows(IOException.class, () -> runOnBackground(() -> client.subscribe(TEST_TOPIC)));

verify(mockErrorStream).close();
verify(mockConnection).disconnect();
}

private void runOnBackground(ThrowingRunnable runnable) throws Exception {
Future<?> future =
Executors.newSingleThreadExecutor()
Expand Down
Loading