diff --git a/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java b/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java index 2f65b574cf..bcaef38898 100644 --- a/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java +++ b/extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java @@ -550,19 +550,11 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { UrlResponseInfo responseInfo = Assertions.checkNotNull(this.responseInfo); int responseCode = responseInfo.getHttpStatusCode(); if (responseCode < 200 || responseCode > 299) { - byte[] responseBody = Util.EMPTY_BYTE_ARRAY; - ByteBuffer readBuffer = getOrCreateReadBuffer(); - while (!readBuffer.hasRemaining()) { - operation.close(); - readBuffer.clear(); - readInternal(readBuffer); - if (finished) { - break; - } - readBuffer.flip(); - int existingResponseBodyEnd = responseBody.length; - responseBody = Arrays.copyOf(responseBody, responseBody.length + readBuffer.remaining()); - readBuffer.get(responseBody, existingResponseBodyEnd, readBuffer.remaining()); + byte[] responseBody; + try { + responseBody = readResponseBody(); + } catch (HttpDataSourceException e) { + responseBody = Util.EMPTY_BYTE_ARRAY; } InvalidResponseCodeException exception = @@ -859,6 +851,29 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { currentConnectTimeoutMs = clock.elapsedRealtime() + connectTimeoutMs; } + /** + * Reads the whole response body. + * + * @return The response body. + * @throws HttpDataSourceException If an error occurs reading from the source. + */ + private byte[] readResponseBody() throws HttpDataSourceException { + byte[] responseBody = Util.EMPTY_BYTE_ARRAY; + ByteBuffer readBuffer = getOrCreateReadBuffer(); + while (!finished) { + operation.close(); + readBuffer.clear(); + readInternal(readBuffer); + readBuffer.flip(); + if (readBuffer.remaining() > 0) { + int existingResponseBodyEnd = responseBody.length; + responseBody = Arrays.copyOf(responseBody, responseBody.length + readBuffer.remaining()); + readBuffer.get(responseBody, existingResponseBodyEnd, readBuffer.remaining()); + } + } + return responseBody; + } + /** * Reads up to {@code buffer.remaining()} bytes of data from {@code currentUrlRequest} and stores * them into {@code buffer}. If there is an error and {@code buffer == readBuffer}, then it resets diff --git a/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java index 3faf133c85..9accd10228 100644 --- a/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java +++ b/extensions/cronet/src/test/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java @@ -41,6 +41,7 @@ import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource.HttpDataSourceException; +import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Util; import java.io.IOException; @@ -380,7 +381,8 @@ public final class CronetDataSourceTest { } @Test - public void requestOpenPropagatesFailureResponseBody() throws Exception { + public void requestOpen_withNon2xxResponseCode_throwsInvalidResponseCodeExceptionWithBody() + throws Exception { mockResponseStartSuccess(); // Use a size larger than CronetDataSource.READ_BUFFER_SIZE_BYTES int responseLength = 40 * 1024; @@ -389,8 +391,8 @@ public final class CronetDataSourceTest { try { dataSourceUnderTest.open(testDataSpec); - fail("HttpDataSource.InvalidResponseCodeException expected"); - } catch (HttpDataSource.InvalidResponseCodeException e) { + fail("InvalidResponseCodeException expected"); + } catch (InvalidResponseCodeException e) { assertThat(e.responseBody).isEqualTo(buildTestDataArray(0, responseLength)); // Check for connection not automatically closed. verify(mockUrlRequest, never()).cancel(); @@ -399,6 +401,26 @@ public final class CronetDataSourceTest { } } + @Test + public void + requestOpen_withNon2xxResponseCode_andRequestBodyReadFailure_throwsInvalidResponseCodeExceptionWithoutBody() + throws Exception { + mockResponseStartSuccess(); + mockReadFailure(); + testUrlResponseInfo = createUrlResponseInfo(/* statusCode= */ 500); + + try { + dataSourceUnderTest.open(testDataSpec); + fail("InvalidResponseCodeException expected"); + } catch (InvalidResponseCodeException e) { + assertThat(e.responseBody).isEmpty(); + // Check for connection not automatically closed. + verify(mockUrlRequest, never()).cancel(); + verify(mockTransferListener, never()) + .onTransferStart(dataSourceUnderTest, testDataSpec, /* isNetwork= */ true); + } + } + @Test public void requestOpenValidatesContentTypePredicate() { mockResponseStartSuccess(); diff --git a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java index 92bf8281f5..fe551bb15c 100644 --- a/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java +++ b/extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java @@ -308,8 +308,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource { try { errorResponseBody = Util.toByteArray(Assertions.checkNotNull(responseByteStream)); } catch (IOException e) { - throw new HttpDataSourceException( - "Error reading non-2xx response body", e, dataSpec, HttpDataSourceException.TYPE_OPEN); + errorResponseBody = Util.EMPTY_BYTE_ARRAY; } Map> headers = response.headers().toMultimap(); closeConnectionQuietly(); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java index 15bee2c118..c0893b85c0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java @@ -377,8 +377,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou errorResponseBody = errorStream != null ? Util.toByteArray(errorStream) : Util.EMPTY_BYTE_ARRAY; } catch (IOException e) { - throw new HttpDataSourceException( - "Error reading non-2xx response body", e, dataSpec, HttpDataSourceException.TYPE_OPEN); + errorResponseBody = Util.EMPTY_BYTE_ARRAY; } closeConnectionQuietly(); InvalidResponseCodeException exception =