From b8ec05aea19be5eb3ef317a7daf10ed1d36154b4 Mon Sep 17 00:00:00 2001 From: tonihei Date: Thu, 30 May 2019 11:42:20 +0100 Subject: [PATCH] Handle gzip in DefaultHttpDataSource. Setting the requested encoding in all cases ensures we receive the relevant response headers indicating whether gzip was used. Doing that allows to detect the content length in cases where gzip was requested, but the server replied with uncompressed content. PiperOrigin-RevId: 250660890 --- .../ext/cronet/CronetDataSource.java | 6 +++--- .../upstream/DefaultHttpDataSource.java | 20 ++++++++++++------- 2 files changed, 16 insertions(+), 10 deletions(-) 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 dd10e5bb66..a1ee80767d 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 @@ -466,7 +466,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { bytesToSkip = responseCode == 200 && dataSpec.position != 0 ? dataSpec.position : 0; // Calculate the content length. - if (!getIsCompressed(responseInfo)) { + if (!isCompressed(responseInfo)) { if (dataSpec.length != C.LENGTH_UNSET) { bytesRemaining = dataSpec.length; } else { @@ -626,7 +626,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { } requestBuilder.addHeader("Range", rangeValue.toString()); } - // TODO: Uncomment when https://bugs.chromium.org/p/chromium/issues/detail?id=767025 is fixed + // TODO: Uncomment when https://bugs.chromium.org/p/chromium/issues/detail?id=711810 is fixed // (adjusting the code as necessary). // Force identity encoding unless gzip is allowed. // if (!dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP)) { @@ -655,7 +655,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource { currentConnectTimeoutMs = clock.elapsedRealtime() + connectTimeoutMs; } - private static boolean getIsCompressed(UrlResponseInfo info) { + private static boolean isCompressed(UrlResponseInfo info) { for (Map.Entry entry : info.getAllHeadersAsList()) { if (entry.getKey().equalsIgnoreCase("Content-Encoding")) { return !entry.getValue().equalsIgnoreCase("identity"); 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 0d16a3f20e..3ee1ef7564 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 @@ -41,6 +41,7 @@ import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.GZIPInputStream; /** * An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}. @@ -305,7 +306,8 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou bytesToSkip = responseCode == 200 && dataSpec.position != 0 ? dataSpec.position : 0; // Determine the length of the data to be read, after skipping. - if (!dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP)) { + boolean isCompressed = isCompressed(connection); + if (!isCompressed) { if (dataSpec.length != C.LENGTH_UNSET) { bytesToRead = dataSpec.length; } else { @@ -315,14 +317,16 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou } } else { // Gzip is enabled. If the server opts to use gzip then the content length in the response - // will be that of the compressed data, which isn't what we want. Furthermore, there isn't a - // reliable way to determine whether the gzip was used or not. Always use the dataSpec length - // in this case. + // will be that of the compressed data, which isn't what we want. Always use the dataSpec + // length in this case. bytesToRead = dataSpec.length; } try { inputStream = connection.getInputStream(); + if (isCompressed) { + inputStream = new GZIPInputStream(inputStream); + } } catch (IOException e) { closeConnectionQuietly(); throw new HttpDataSourceException(e, dataSpec, HttpDataSourceException.TYPE_OPEN); @@ -516,9 +520,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou connection.setRequestProperty("Range", rangeRequest); } connection.setRequestProperty("User-Agent", userAgent); - if (!allowGzip) { - connection.setRequestProperty("Accept-Encoding", "identity"); - } + connection.setRequestProperty("Accept-Encoding", allowGzip ? "gzip" : "identity"); if (allowIcyMetadata) { connection.setRequestProperty( IcyHeaders.REQUEST_HEADER_ENABLE_METADATA_NAME, @@ -747,4 +749,8 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou } } + private static boolean isCompressed(HttpURLConnection connection) { + String contentEncoding = connection.getHeaderField("Content-Encoding"); + return "gzip".equalsIgnoreCase(contentEncoding); + } }