Fix calling to getInputStream on errors
MODIFY
analytics/src/main/java/com/segment/analytics/Analytics.java
analytics/src/main/java/com/segment/analytics/Analytics.java
@@ -27,6 +27,7 @@ package com.segment.analytics;
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 @@ public class Analytics {
// Read the response body.
Map<String, Object> map =
- cartographer.fromJson(buffer(connection.connection.getInputStream()));
+ cartographer.fromJson(buffer(getInputStream(connection.connection)));
Properties properties = new Properties(map);
track("Install Attributed", properties);

MODIFY
analytics/src/main/java/com/segment/analytics/Client.java
analytics/src/main/java/com/segment/analytics/Client.java
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 @@ class Client {
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 @@ class Client {
}
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();

MODIFY
analytics/src/main/java/com/segment/analytics/internal/Utils.java
analytics/src/main/java/com/segment/analytics/internal/Utils.java
@@ -52,6 +52,7 @@ import java.io.IOException;
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 final class Utils {
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,

MODIFY
analytics/src/test/java/com/segment/analytics/ClientTest.java
analytics/src/test/java/com/segment/analytics/ClientTest.java
@@ -12,6 +12,8 @@ import com.segment.analytics.internal.Private;
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 class ClientTest {
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());

Fix calling to getInputStream on errors