Follow 307/308 POST redirects for license requests

I was considering putting this directly in DefaultHttpDataSource, however:

- We'd need to modify at least OkHttpDataSource as well. I'm not sure whether
  Cronet follows this type of redirect automatically or not.
- HttpDataSource instances don't know how they're going to be used, so it's
  probably correct that they behave like the underlying network stack.

Issue: #4108

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=192745408
This commit is contained in:
olly 2018-04-13 03:25:04 -07:00 committed by Oliver Woodman
parent 22b8ab5c09
commit 0ee3963789
2 changed files with 47 additions and 9 deletions

View file

@ -51,13 +51,15 @@
* Add release method to Cache interface. * Add release method to Cache interface.
* Prevent multiple instances of SimpleCache in the same folder. * Prevent multiple instances of SimpleCache in the same folder.
Previous instance must be released. Previous instance must be released.
* Store redirected URI * Cache redirect URLs
([#2360](https://github.com/google/ExoPlayer/issues/2360)). ([#2360](https://github.com/google/ExoPlayer/issues/2360)).
* DRM: * DRM:
* Allow multiple listeners for `DefaultDrmSessionManager`. * Allow multiple listeners for `DefaultDrmSessionManager`.
* Pass `DrmSessionManager` to `ExoPlayerFactory` instead of `RendererFactory`. * Pass `DrmSessionManager` to `ExoPlayerFactory` instead of `RendererFactory`.
* Change minimum API requirement for CBC and pattern encryption from 24 to 25 * Change minimum API requirement for CBC and pattern encryption from 24 to 25
([#4022][https://github.com/google/ExoPlayer/issues/4022]). ([#4022][https://github.com/google/ExoPlayer/issues/4022]).
* Fix handling of 307/308 redirects when making license requests
([#4108](https://github.com/google/ExoPlayer/issues/4108)).
* Removed default renderer time offset of 60000000 from internal player. The * Removed default renderer time offset of 60000000 from internal player. The
actual renderer timestamp offset can be obtained by listening to actual renderer timestamp offset can be obtained by listening to
`BaseRenderer.onStreamChanged`. `BaseRenderer.onStreamChanged`.

View file

@ -24,10 +24,12 @@ import com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest;
import com.google.android.exoplayer2.upstream.DataSourceInputStream; import com.google.android.exoplayer2.upstream.DataSourceInputStream;
import com.google.android.exoplayer2.upstream.DataSpec; import com.google.android.exoplayer2.upstream.DataSpec;
import com.google.android.exoplayer2.upstream.HttpDataSource; import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.InvalidResponseCodeException;
import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.util.Util;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@ -37,6 +39,8 @@ import java.util.UUID;
@TargetApi(18) @TargetApi(18)
public final class HttpMediaDrmCallback implements MediaDrmCallback { public final class HttpMediaDrmCallback implements MediaDrmCallback {
private static final int MAX_MANUAL_REDIRECTS = 5;
private final HttpDataSource.Factory dataSourceFactory; private final HttpDataSource.Factory dataSourceFactory;
private final String defaultLicenseUrl; private final String defaultLicenseUrl;
private final boolean forceDefaultLicenseUrl; private final boolean forceDefaultLicenseUrl;
@ -138,14 +142,46 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
dataSource.setRequestProperty(requestProperty.getKey(), requestProperty.getValue()); dataSource.setRequestProperty(requestProperty.getKey(), requestProperty.getValue());
} }
} }
DataSpec dataSpec = new DataSpec(Uri.parse(url), data, 0, 0, C.LENGTH_UNSET, null,
int manualRedirectCount = 0;
while (true) {
DataSpec dataSpec =
new DataSpec(
Uri.parse(url),
data,
/* absoluteStreamPosition= */ 0,
/* position= */ 0,
/* length= */ C.LENGTH_UNSET,
/* key= */ null,
DataSpec.FLAG_ALLOW_GZIP); DataSpec.FLAG_ALLOW_GZIP);
DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, dataSpec); DataSourceInputStream inputStream = new DataSourceInputStream(dataSource, dataSpec);
try { try {
return Util.toByteArray(inputStream); return Util.toByteArray(inputStream);
} catch (InvalidResponseCodeException e) {
// For POST requests, the underlying network stack will not normally follow 307 or 308
// redirects automatically. Do so manually here.
boolean manuallyRedirect =
(e.responseCode == 307 || e.responseCode == 308)
&& manualRedirectCount++ < MAX_MANUAL_REDIRECTS;
url = manuallyRedirect ? getRedirectUrl(e) : null;
if (url == null) {
throw e;
}
} finally { } finally {
Util.closeQuietly(inputStream); Util.closeQuietly(inputStream);
} }
} }
}
private static String getRedirectUrl(InvalidResponseCodeException exception) {
Map<String, List<String>> headerFields = exception.headerFields;
if (headerFields != null) {
List<String> locationHeaders = headerFields.get("Location");
if (locationHeaders != null && !locationHeaders.isEmpty()) {
return locationHeaders.get(0);
}
}
return null;
}
} }