diff --git a/analytics/src/main/java/com/segment/analytics/Analytics.java b/analytics/src/main/java/com/segment/analytics/Analytics.java index 4c560ab57..c01bb2145 100644 --- a/analytics/src/main/java/com/segment/analytics/Analytics.java +++ b/analytics/src/main/java/com/segment/analytics/Analytics.java @@ -27,6 +27,7 @@ import static com.segment.analytics.internal.Utils.assertNotNull; import static com.segment.analytics.internal.Utils.buffer; import static com.segment.analytics.internal.Utils.closeQuietly; +import static com.segment.analytics.internal.Utils.getInputStream; import static com.segment.analytics.internal.Utils.getResourceString; import static com.segment.analytics.internal.Utils.getSegmentSharedPreferences; import static com.segment.analytics.internal.Utils.hasPermission; @@ -360,7 +361,7 @@ void trackAttributionInformation() { // Read the response body. Map map = - cartographer.fromJson(buffer(connection.connection.getInputStream())); + cartographer.fromJson(buffer(getInputStream(connection.connection))); Properties properties = new Properties(map); track("Install Attributed", properties); diff --git a/analytics/src/main/java/com/segment/analytics/Client.java b/analytics/src/main/java/com/segment/analytics/Client.java index 4c50f69e7..bd369741e 100644 --- a/analytics/src/main/java/com/segment/analytics/Client.java +++ b/analytics/src/main/java/com/segment/analytics/Client.java @@ -25,6 +25,7 @@ package com.segment.analytics; import static com.segment.analytics.internal.Utils.readFully; +import static com.segment.analytics.internal.Utils.getInputStream; import static java.net.HttpURLConnection.HTTP_OK; import android.text.TextUtils; @@ -58,7 +59,7 @@ public void close() throws IOException { if (responseCode >= 300) { String responseBody; try { - responseBody = readFully(connection.getInputStream()); + responseBody = readFully(getInputStream(connection)); } catch (IOException e) { responseBody = "Could not read response body for rejected message: " + e.toString(); } @@ -73,7 +74,7 @@ public void close() throws IOException { } private static Connection createGetConnection(HttpURLConnection connection) throws IOException { - return new Connection(connection, connection.getInputStream(), null) { + return new Connection(connection, getInputStream(connection), null) { @Override public void close() throws IOException { super.close(); diff --git a/analytics/src/main/java/com/segment/analytics/internal/Utils.java b/analytics/src/main/java/com/segment/analytics/internal/Utils.java index 8adb8878d..98f6de3d4 100644 --- a/analytics/src/main/java/com/segment/analytics/internal/Utils.java +++ b/analytics/src/main/java/com/segment/analytics/internal/Utils.java @@ -52,6 +52,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Array; +import java.net.HttpURLConnection; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; @@ -351,6 +352,14 @@ public static String readFully(BufferedReader reader) throws IOException { return sb.toString(); } + public static InputStream getInputStream(HttpURLConnection connection) throws IOException { + try { + return connection.getInputStream(); + } catch (IOException ignored) { + return connection.getErrorStream(); + } + } + /** * Transforms the given map by replacing the keys mapped by {@code mapper}. Any keys not in the * mapper preserve their original keys. If a key in the mapper maps to null or a blank string, diff --git a/analytics/src/test/java/com/segment/analytics/ClientTest.java b/analytics/src/test/java/com/segment/analytics/ClientTest.java index e1d86500f..c797991cb 100644 --- a/analytics/src/test/java/com/segment/analytics/ClientTest.java +++ b/analytics/src/test/java/com/segment/analytics/ClientTest.java @@ -12,6 +12,8 @@ import com.squareup.okhttp.mockwebserver.MockResponse; import com.squareup.okhttp.mockwebserver.RecordedRequest; import com.squareup.okhttp.mockwebserver.rule.MockWebServerRule; + +import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -150,6 +152,34 @@ public void uploadFailureClosesStreamsAndThrowsException() throws Exception { verify(os).close(); } + @Test + public void uploadFailureWithErrorStreamClosesStreamsAndThrowsException() throws Exception { + OutputStream os = mock(OutputStream.class); + InputStream is = mock(InputStream.class); + when(mockConnection.getOutputStream()).thenReturn(os); + when(mockConnection.getResponseCode()).thenReturn(404); + when(mockConnection.getResponseMessage()).thenReturn("bar"); + when(mockConnection.getInputStream()).thenThrow(new FileNotFoundException()); + when(mockConnection.getErrorStream()).thenReturn(is); + + Client.Connection connection = mockClient.upload(); + verify(mockConnection).setDoOutput(true); + verify(mockConnection).setChunkedStreamingMode(0); + + try { + connection.close(); + fail(">= 300 return code should throw an exception"); + } catch (Client.HTTPException e) { + assertThat(e) + .hasMessage( + "HTTP 404: bar. " + + "Response: Could not read response body for rejected message: " + + "java.io.IOException: Underlying input stream returned zero bytes"); + } + verify(mockConnection).disconnect(); + verify(os).close(); + } + @Test public void fetchSettings() throws Exception { server.enqueue(new MockResponse());