mirror of
https://github.com/samsonjs/media.git
synced 2026-04-26 14:57:47 +00:00
Updating DefaultHttpDataSource to allow for http methods other than GET and POST,
as specified by DataSpec.httpMethod. ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=207769779
This commit is contained in:
parent
ca473c86c7
commit
d3686cf8a2
8 changed files with 209 additions and 57 deletions
|
|
@ -92,6 +92,8 @@
|
||||||
([#273](https://github.com/google/ExoPlayer/issues/273)).
|
([#273](https://github.com/google/ExoPlayer/issues/273)).
|
||||||
* Fix where transitions to clipped media sources happened too early
|
* Fix where transitions to clipped media sources happened too early
|
||||||
([#4583](https://github.com/google/ExoPlayer/issues/4583)).
|
([#4583](https://github.com/google/ExoPlayer/issues/4583)).
|
||||||
|
* Add `DataSpec.httpMethod` and update `HttpDataSource` implemenations to support
|
||||||
|
http HEAD method. Previously, only GET and POST were supported.
|
||||||
|
|
||||||
### 2.8.3 ###
|
### 2.8.3 ###
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -473,8 +473,8 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
isContentTypeHeaderSet = isContentTypeHeaderSet || CONTENT_TYPE.equals(key);
|
isContentTypeHeaderSet = isContentTypeHeaderSet || CONTENT_TYPE.equals(key);
|
||||||
requestBuilder.addHeader(key, headerEntry.getValue());
|
requestBuilder.addHeader(key, headerEntry.getValue());
|
||||||
}
|
}
|
||||||
if (dataSpec.postBody != null && dataSpec.postBody.length != 0 && !isContentTypeHeaderSet) {
|
if (dataSpec.httpBody != null && !isContentTypeHeaderSet) {
|
||||||
throw new IOException("POST request with non-empty body must set Content-Type");
|
throw new IOException("HTTP request with non-empty body must set Content-Type");
|
||||||
}
|
}
|
||||||
// Set the Range header.
|
// Set the Range header.
|
||||||
if (dataSpec.position != 0 || dataSpec.length != C.LENGTH_UNSET) {
|
if (dataSpec.position != 0 || dataSpec.length != C.LENGTH_UNSET) {
|
||||||
|
|
@ -494,12 +494,10 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
// requestBuilder.addHeader("Accept-Encoding", "identity");
|
// requestBuilder.addHeader("Accept-Encoding", "identity");
|
||||||
// }
|
// }
|
||||||
// Set the method and (if non-empty) the body.
|
// Set the method and (if non-empty) the body.
|
||||||
if (dataSpec.postBody != null) {
|
requestBuilder.setHttpMethod(dataSpec.getHttpMethodString());
|
||||||
requestBuilder.setHttpMethod("POST");
|
if (dataSpec.httpBody != null) {
|
||||||
if (dataSpec.postBody.length != 0) {
|
requestBuilder.setUploadDataProvider(
|
||||||
requestBuilder.setUploadDataProvider(new ByteArrayUploadDataProvider(dataSpec.postBody),
|
new ByteArrayUploadDataProvider(dataSpec.httpBody), executor);
|
||||||
executor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return requestBuilder;
|
return requestBuilder;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ public final class CronetDataSourceTest {
|
||||||
|
|
||||||
private DataSpec testDataSpec;
|
private DataSpec testDataSpec;
|
||||||
private DataSpec testPostDataSpec;
|
private DataSpec testPostDataSpec;
|
||||||
|
private DataSpec testHeadDataSpec;
|
||||||
private Map<String, String> testResponseHeader;
|
private Map<String, String> testResponseHeader;
|
||||||
private UrlResponseInfo testUrlResponseInfo;
|
private UrlResponseInfo testUrlResponseInfo;
|
||||||
|
|
||||||
|
|
@ -120,6 +121,9 @@ public final class CronetDataSourceTest {
|
||||||
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null);
|
testDataSpec = new DataSpec(Uri.parse(TEST_URL), 0, C.LENGTH_UNSET, null);
|
||||||
testPostDataSpec =
|
testPostDataSpec =
|
||||||
new DataSpec(Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0);
|
new DataSpec(Uri.parse(TEST_URL), TEST_POST_BODY, 0, 0, C.LENGTH_UNSET, null, 0);
|
||||||
|
testHeadDataSpec =
|
||||||
|
new DataSpec(
|
||||||
|
Uri.parse(TEST_URL), DataSpec.HTTP_METHOD_HEAD, null, 0, 0, C.LENGTH_UNSET, null, 0);
|
||||||
testResponseHeader = new HashMap<>();
|
testResponseHeader = new HashMap<>();
|
||||||
testResponseHeader.put("Content-Type", TEST_CONTENT_TYPE);
|
testResponseHeader.put("Content-Type", TEST_CONTENT_TYPE);
|
||||||
// This value can be anything since the DataSpec is unset.
|
// This value can be anything since the DataSpec is unset.
|
||||||
|
|
@ -332,6 +336,15 @@ public final class CronetDataSourceTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHeadRequestOpen() throws HttpDataSourceException {
|
||||||
|
mockResponseStartSuccess();
|
||||||
|
dataSourceUnderTest.open(testHeadDataSpec);
|
||||||
|
verify(mockTransferListener)
|
||||||
|
.onTransferStart(dataSourceUnderTest, testHeadDataSpec, /* isNetwork= */ true);
|
||||||
|
dataSourceUnderTest.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestReadTwice() throws HttpDataSourceException {
|
public void testRequestReadTwice() throws HttpDataSourceException {
|
||||||
mockResponseStartSuccess();
|
mockResponseStartSuccess();
|
||||||
|
|
|
||||||
|
|
@ -296,9 +296,14 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
if (!allowGzip) {
|
if (!allowGzip) {
|
||||||
builder.addHeader("Accept-Encoding", "identity");
|
builder.addHeader("Accept-Encoding", "identity");
|
||||||
}
|
}
|
||||||
if (dataSpec.postBody != null) {
|
RequestBody requestBody = null;
|
||||||
builder.post(RequestBody.create(null, dataSpec.postBody));
|
if (dataSpec.httpBody != null) {
|
||||||
|
requestBody = RequestBody.create(null, dataSpec.httpBody);
|
||||||
|
} else if (dataSpec.httpMethod == DataSpec.HTTP_METHOD_POST) {
|
||||||
|
// OkHttp requires a non-null body for POST requests.
|
||||||
|
requestBody = RequestBody.create(null, new byte[0]);
|
||||||
}
|
}
|
||||||
|
builder.method(dataSpec.getHttpMethodString(), requestBody);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,33 @@ public final class DataSpec {
|
||||||
*/
|
*/
|
||||||
public static final int FLAG_ALLOW_CACHING_UNKNOWN_LENGTH = 1 << 1; // 2
|
public static final int FLAG_ALLOW_CACHING_UNKNOWN_LENGTH = 1 << 1; // 2
|
||||||
|
|
||||||
|
/** The set of HTTP methods that are supported by ExoPlayer {@link HttpDataSource}s. */
|
||||||
|
@IntDef({HTTP_METHOD_GET, HTTP_METHOD_POST, HTTP_METHOD_HEAD})
|
||||||
|
public @interface HttpMethod {}
|
||||||
|
|
||||||
|
public static final int HTTP_METHOD_GET = 1;
|
||||||
|
public static final int HTTP_METHOD_POST = 2;
|
||||||
|
public static final int HTTP_METHOD_HEAD = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The source from which data should be read.
|
* The source from which data should be read.
|
||||||
*/
|
*/
|
||||||
public final Uri uri;
|
public final Uri uri;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Body for a POST request, null otherwise.
|
* The HTTP method, which will be used by {@link HttpDataSource} when requesting this DataSpec.
|
||||||
|
* This value will be ignored by non-http {@link DataSource}s.
|
||||||
*/
|
*/
|
||||||
public final @Nullable byte[] postBody;
|
public final @HttpMethod int httpMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HTTP body, null otherwise. If the body is non-null, then httpBody.length will be non-zero.
|
||||||
|
*/
|
||||||
|
public final @Nullable byte[] httpBody;
|
||||||
|
|
||||||
|
/** @deprecated Use {@link #httpBody} instead. */
|
||||||
|
@Deprecated public final @Nullable byte[] postBody;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The absolute position of the data in the full stream.
|
* The absolute position of the data in the full stream.
|
||||||
*/
|
*/
|
||||||
|
|
@ -155,11 +174,13 @@ public final class DataSpec {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a {@link DataSpec} where {@link #position} may differ from {@link
|
* Construct a {@link DataSpec} by inferring the {@link #httpMethod} based on the {@code postBody}
|
||||||
* #absoluteStreamPosition}.
|
* parameter. If postBody is non-null, then httpMethod is set to {@link #HTTP_METHOD_POST}. If
|
||||||
|
* postBody is null, then httpMethod is set to {@link #HTTP_METHOD_GET}.
|
||||||
*
|
*
|
||||||
* @param uri {@link #uri}.
|
* @param uri {@link #uri}.
|
||||||
* @param postBody {@link #postBody}.
|
* @param postBody {@link #httpBody} The body of the HTTP request, which is also used to infer the
|
||||||
|
* {@link #httpMethod}.
|
||||||
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
|
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
|
||||||
* @param position {@link #position}.
|
* @param position {@link #position}.
|
||||||
* @param length {@link #length}.
|
* @param length {@link #length}.
|
||||||
|
|
@ -174,11 +195,46 @@ public final class DataSpec {
|
||||||
long length,
|
long length,
|
||||||
@Nullable String key,
|
@Nullable String key,
|
||||||
@Flags int flags) {
|
@Flags int flags) {
|
||||||
|
this(
|
||||||
|
uri,
|
||||||
|
/* httpMethod= */ postBody != null ? HTTP_METHOD_POST : HTTP_METHOD_GET,
|
||||||
|
/* httpBody= */ postBody,
|
||||||
|
absoluteStreamPosition,
|
||||||
|
position,
|
||||||
|
length,
|
||||||
|
key,
|
||||||
|
flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct a {@link DataSpec} where {@link #position} may differ from {@link
|
||||||
|
* #absoluteStreamPosition}.
|
||||||
|
*
|
||||||
|
* @param uri {@link #uri}.
|
||||||
|
* @param httpMethod {@link #httpMethod}.
|
||||||
|
* @param httpBody {@link #httpBody}.
|
||||||
|
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
|
||||||
|
* @param position {@link #position}.
|
||||||
|
* @param length {@link #length}.
|
||||||
|
* @param key {@link #key}.
|
||||||
|
* @param flags {@link #flags}.
|
||||||
|
*/
|
||||||
|
public DataSpec(
|
||||||
|
Uri uri,
|
||||||
|
@HttpMethod int httpMethod,
|
||||||
|
@Nullable byte[] httpBody,
|
||||||
|
long absoluteStreamPosition,
|
||||||
|
long position,
|
||||||
|
long length,
|
||||||
|
@Nullable String key,
|
||||||
|
@Flags int flags) {
|
||||||
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
Assertions.checkArgument(absoluteStreamPosition >= 0);
|
||||||
Assertions.checkArgument(position >= 0);
|
Assertions.checkArgument(position >= 0);
|
||||||
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET);
|
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNSET);
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.postBody = postBody;
|
this.httpMethod = httpMethod;
|
||||||
|
this.httpBody = (httpBody != null && httpBody.length != 0) ? httpBody : null;
|
||||||
|
this.postBody = this.httpBody;
|
||||||
this.absoluteStreamPosition = absoluteStreamPosition;
|
this.absoluteStreamPosition = absoluteStreamPosition;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
|
|
@ -197,8 +253,48 @@ public final class DataSpec {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "DataSpec[" + uri + ", " + Arrays.toString(postBody) + ", " + absoluteStreamPosition
|
return "DataSpec["
|
||||||
+ ", " + position + ", " + length + ", " + key + ", " + flags + "]";
|
+ getHttpMethodString()
|
||||||
|
+ " "
|
||||||
|
+ uri
|
||||||
|
+ ", "
|
||||||
|
+ Arrays.toString(httpBody)
|
||||||
|
+ ", "
|
||||||
|
+ absoluteStreamPosition
|
||||||
|
+ ", "
|
||||||
|
+ position
|
||||||
|
+ ", "
|
||||||
|
+ length
|
||||||
|
+ ", "
|
||||||
|
+ key
|
||||||
|
+ ", "
|
||||||
|
+ flags
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an uppercase HTTP method name (e.g., "GET", "POST", "HEAD") corresponding to the {@link
|
||||||
|
* #httpMethod}.
|
||||||
|
*/
|
||||||
|
public final String getHttpMethodString() {
|
||||||
|
return getStringForHttpMethod(httpMethod);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an uppercase HTTP method name (e.g., "GET", "POST", "HEAD") corresponding to the {@code
|
||||||
|
* httpMethod}.
|
||||||
|
*/
|
||||||
|
public static String getStringForHttpMethod(@HttpMethod int httpMethod) {
|
||||||
|
switch (httpMethod) {
|
||||||
|
case HTTP_METHOD_GET:
|
||||||
|
return "GET";
|
||||||
|
case HTTP_METHOD_POST:
|
||||||
|
return "POST";
|
||||||
|
case HTTP_METHOD_HEAD:
|
||||||
|
return "HEAD";
|
||||||
|
default:
|
||||||
|
throw new AssertionError(httpMethod);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -223,8 +319,15 @@ public final class DataSpec {
|
||||||
if (offset == 0 && this.length == length) {
|
if (offset == 0 && this.length == length) {
|
||||||
return this;
|
return this;
|
||||||
} else {
|
} else {
|
||||||
return new DataSpec(uri, postBody, absoluteStreamPosition + offset, position + offset, length,
|
return new DataSpec(
|
||||||
key, flags);
|
uri,
|
||||||
|
httpMethod,
|
||||||
|
httpBody,
|
||||||
|
absoluteStreamPosition + offset,
|
||||||
|
position + offset,
|
||||||
|
length,
|
||||||
|
key,
|
||||||
|
flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,6 +338,7 @@ public final class DataSpec {
|
||||||
* @return The copied {@link DataSpec} with the specified Uri.
|
* @return The copied {@link DataSpec} with the specified Uri.
|
||||||
*/
|
*/
|
||||||
public DataSpec withUri(Uri uri) {
|
public DataSpec withUri(Uri uri) {
|
||||||
return new DataSpec(uri, postBody, absoluteStreamPosition, position, length, key, flags);
|
return new DataSpec(
|
||||||
|
uri, httpMethod, httpBody, absoluteStreamPosition, position, length, key, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Predicate;
|
import com.google.android.exoplayer2.util.Predicate;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
@ -61,6 +62,8 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
||||||
|
|
||||||
private static final String TAG = "DefaultHttpDataSource";
|
private static final String TAG = "DefaultHttpDataSource";
|
||||||
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
|
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
|
||||||
|
private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307;
|
||||||
|
private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308;
|
||||||
private static final long MAX_BYTES_TO_DRAIN = 2048;
|
private static final long MAX_BYTES_TO_DRAIN = 2048;
|
||||||
private static final Pattern CONTENT_RANGE_HEADER =
|
private static final Pattern CONTENT_RANGE_HEADER =
|
||||||
Pattern.compile("^bytes (\\d+)-(\\d+)/(\\d+)$");
|
Pattern.compile("^bytes (\\d+)-(\\d+)/(\\d+)$");
|
||||||
|
|
@ -417,7 +420,8 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
||||||
*/
|
*/
|
||||||
private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException {
|
private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException {
|
||||||
URL url = new URL(dataSpec.uri.toString());
|
URL url = new URL(dataSpec.uri.toString());
|
||||||
byte[] postBody = dataSpec.postBody;
|
@HttpMethod int httpMethod = dataSpec.httpMethod;
|
||||||
|
byte[] httpBody = dataSpec.httpBody;
|
||||||
long position = dataSpec.position;
|
long position = dataSpec.position;
|
||||||
long length = dataSpec.length;
|
long length = dataSpec.length;
|
||||||
boolean allowGzip = dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP);
|
boolean allowGzip = dataSpec.isFlagSet(DataSpec.FLAG_ALLOW_GZIP);
|
||||||
|
|
@ -425,28 +429,37 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
||||||
if (!allowCrossProtocolRedirects) {
|
if (!allowCrossProtocolRedirects) {
|
||||||
// HttpURLConnection disallows cross-protocol redirects, but otherwise performs redirection
|
// HttpURLConnection disallows cross-protocol redirects, but otherwise performs redirection
|
||||||
// automatically. This is the behavior we want, so use it.
|
// automatically. This is the behavior we want, so use it.
|
||||||
return makeConnection(url, postBody, position, length, allowGzip, true /* followRedirects */);
|
return makeConnection(
|
||||||
|
url, httpMethod, httpBody, position, length, allowGzip, true /* followRedirects */);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to handle redirects ourselves to allow cross-protocol redirects.
|
// We need to handle redirects ourselves to allow cross-protocol redirects.
|
||||||
int redirectCount = 0;
|
int redirectCount = 0;
|
||||||
while (redirectCount++ <= MAX_REDIRECTS) {
|
while (redirectCount++ <= MAX_REDIRECTS) {
|
||||||
HttpURLConnection connection = makeConnection(
|
HttpURLConnection connection =
|
||||||
url, postBody, position, length, allowGzip, false /* followRedirects */);
|
makeConnection(
|
||||||
|
url, httpMethod, httpBody, position, length, allowGzip, false /* followRedirects */);
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
if (responseCode == HttpURLConnection.HTTP_MULT_CHOICE
|
String location = connection.getHeaderField("Location");
|
||||||
|| responseCode == HttpURLConnection.HTTP_MOVED_PERM
|
if ((httpMethod == DataSpec.HTTP_METHOD_GET || httpMethod == DataSpec.HTTP_METHOD_HEAD)
|
||||||
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|
&& (responseCode == HttpURLConnection.HTTP_MULT_CHOICE
|
||||||
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
|
|| responseCode == HttpURLConnection.HTTP_MOVED_PERM
|
||||||
|| (postBody == null
|
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|
||||||
&& (responseCode == 307 /* HTTP_TEMP_REDIRECT */
|
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
|
||||||
|| responseCode == 308 /* HTTP_PERM_REDIRECT */))) {
|
|| responseCode == HTTP_STATUS_TEMPORARY_REDIRECT
|
||||||
// For 300, 301, 302, and 303 POST requests follow the redirect and are transformed into
|
|| responseCode == HTTP_STATUS_PERMANENT_REDIRECT)) {
|
||||||
// GET requests. For 307 and 308 POST requests are not redirected.
|
|
||||||
postBody = null;
|
|
||||||
String location = connection.getHeaderField("Location");
|
|
||||||
connection.disconnect();
|
connection.disconnect();
|
||||||
url = handleRedirect(url, location);
|
url = handleRedirect(url, location);
|
||||||
|
} else if (httpMethod == DataSpec.HTTP_METHOD_POST
|
||||||
|
&& (responseCode == HttpURLConnection.HTTP_MULT_CHOICE
|
||||||
|
|| responseCode == HttpURLConnection.HTTP_MOVED_PERM
|
||||||
|
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|
||||||
|
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER)) {
|
||||||
|
// POST request follows the redirect and is transformed into a GET request.
|
||||||
|
connection.disconnect();
|
||||||
|
httpMethod = DataSpec.HTTP_METHOD_GET;
|
||||||
|
httpBody = null;
|
||||||
|
url = handleRedirect(url, location);
|
||||||
} else {
|
} else {
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
@ -460,14 +473,22 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
||||||
* Configures a connection and opens it.
|
* Configures a connection and opens it.
|
||||||
*
|
*
|
||||||
* @param url The url to connect to.
|
* @param url The url to connect to.
|
||||||
* @param postBody The body data for a POST request.
|
* @param httpMethod The http method.
|
||||||
|
* @param httpBody The body data.
|
||||||
* @param position The byte offset of the requested data.
|
* @param position The byte offset of the requested data.
|
||||||
* @param length The length of the requested data, or {@link C#LENGTH_UNSET}.
|
* @param length The length of the requested data, or {@link C#LENGTH_UNSET}.
|
||||||
* @param allowGzip Whether to allow the use of gzip.
|
* @param allowGzip Whether to allow the use of gzip.
|
||||||
* @param followRedirects Whether to follow redirects.
|
* @param followRedirects Whether to follow redirects.
|
||||||
*/
|
*/
|
||||||
private HttpURLConnection makeConnection(URL url, byte[] postBody, long position,
|
private HttpURLConnection makeConnection(
|
||||||
long length, boolean allowGzip, boolean followRedirects) throws IOException {
|
URL url,
|
||||||
|
@HttpMethod int httpMethod,
|
||||||
|
byte[] httpBody,
|
||||||
|
long position,
|
||||||
|
long length,
|
||||||
|
boolean allowGzip,
|
||||||
|
boolean followRedirects)
|
||||||
|
throws IOException {
|
||||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||||
connection.setConnectTimeout(connectTimeoutMillis);
|
connection.setConnectTimeout(connectTimeoutMillis);
|
||||||
connection.setReadTimeout(readTimeoutMillis);
|
connection.setReadTimeout(readTimeoutMillis);
|
||||||
|
|
@ -491,18 +512,14 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
||||||
connection.setRequestProperty("Accept-Encoding", "identity");
|
connection.setRequestProperty("Accept-Encoding", "identity");
|
||||||
}
|
}
|
||||||
connection.setInstanceFollowRedirects(followRedirects);
|
connection.setInstanceFollowRedirects(followRedirects);
|
||||||
connection.setDoOutput(postBody != null);
|
connection.setDoOutput(httpBody != null);
|
||||||
if (postBody != null) {
|
connection.setRequestMethod(DataSpec.getStringForHttpMethod(httpMethod));
|
||||||
connection.setRequestMethod("POST");
|
if (httpBody != null) {
|
||||||
if (postBody.length == 0) {
|
connection.setFixedLengthStreamingMode(httpBody.length);
|
||||||
connection.connect();
|
connection.connect();
|
||||||
} else {
|
OutputStream os = connection.getOutputStream();
|
||||||
connection.setFixedLengthStreamingMode(postBody.length);
|
os.write(httpBody);
|
||||||
connection.connect();
|
os.close();
|
||||||
OutputStream os = connection.getOutputStream();
|
|
||||||
os.write(postBody);
|
|
||||||
os.close();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
connection.connect();
|
connection.connect();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import com.google.android.exoplayer2.upstream.DataSink;
|
||||||
import com.google.android.exoplayer2.upstream.DataSource;
|
import com.google.android.exoplayer2.upstream.DataSource;
|
||||||
import com.google.android.exoplayer2.upstream.DataSourceException;
|
import com.google.android.exoplayer2.upstream.DataSourceException;
|
||||||
import com.google.android.exoplayer2.upstream.DataSpec;
|
import com.google.android.exoplayer2.upstream.DataSpec;
|
||||||
|
import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod;
|
||||||
import com.google.android.exoplayer2.upstream.FileDataSource;
|
import com.google.android.exoplayer2.upstream.FileDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.TeeDataSource;
|
import com.google.android.exoplayer2.upstream.TeeDataSource;
|
||||||
import com.google.android.exoplayer2.upstream.TransferListener;
|
import com.google.android.exoplayer2.upstream.TransferListener;
|
||||||
|
|
@ -133,6 +134,7 @@ public final class CacheDataSource implements DataSource {
|
||||||
private boolean currentDataSpecLengthUnset;
|
private boolean currentDataSpecLengthUnset;
|
||||||
private @Nullable Uri uri;
|
private @Nullable Uri uri;
|
||||||
private @Nullable Uri actualUri;
|
private @Nullable Uri actualUri;
|
||||||
|
private @HttpMethod int httpMethod;
|
||||||
private int flags;
|
private int flags;
|
||||||
private @Nullable String key;
|
private @Nullable String key;
|
||||||
private long readPosition;
|
private long readPosition;
|
||||||
|
|
@ -269,6 +271,7 @@ public final class CacheDataSource implements DataSource {
|
||||||
key = cacheKeyFactory.buildCacheKey(dataSpec);
|
key = cacheKeyFactory.buildCacheKey(dataSpec);
|
||||||
uri = dataSpec.uri;
|
uri = dataSpec.uri;
|
||||||
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri);
|
actualUri = getRedirectedUriOrDefault(cache, key, /* defaultUri= */ uri);
|
||||||
|
httpMethod = dataSpec.httpMethod;
|
||||||
flags = dataSpec.flags;
|
flags = dataSpec.flags;
|
||||||
readPosition = dataSpec.position;
|
readPosition = dataSpec.position;
|
||||||
|
|
||||||
|
|
@ -353,6 +356,7 @@ public final class CacheDataSource implements DataSource {
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
uri = null;
|
uri = null;
|
||||||
actualUri = null;
|
actualUri = null;
|
||||||
|
httpMethod = DataSpec.HTTP_METHOD_GET;
|
||||||
notifyBytesRead();
|
notifyBytesRead();
|
||||||
try {
|
try {
|
||||||
closeCurrentSource();
|
closeCurrentSource();
|
||||||
|
|
@ -397,7 +401,9 @@ public final class CacheDataSource implements DataSource {
|
||||||
// The data is locked in the cache, or we're ignoring the cache. Bypass the cache and read
|
// The data is locked in the cache, or we're ignoring the cache. Bypass the cache and read
|
||||||
// from upstream.
|
// from upstream.
|
||||||
nextDataSource = upstreamDataSource;
|
nextDataSource = upstreamDataSource;
|
||||||
nextDataSpec = new DataSpec(uri, readPosition, bytesRemaining, key, flags);
|
nextDataSpec =
|
||||||
|
new DataSpec(
|
||||||
|
uri, httpMethod, null, readPosition, readPosition, bytesRemaining, key, flags);
|
||||||
} else if (nextSpan.isCached) {
|
} else if (nextSpan.isCached) {
|
||||||
// Data is cached, read from cache.
|
// Data is cached, read from cache.
|
||||||
Uri fileUri = Uri.fromFile(nextSpan.file);
|
Uri fileUri = Uri.fromFile(nextSpan.file);
|
||||||
|
|
@ -419,7 +425,8 @@ public final class CacheDataSource implements DataSource {
|
||||||
length = Math.min(length, bytesRemaining);
|
length = Math.min(length, bytesRemaining);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nextDataSpec = new DataSpec(uri, readPosition, length, key, flags);
|
nextDataSpec =
|
||||||
|
new DataSpec(uri, httpMethod, null, readPosition, readPosition, length, key, flags);
|
||||||
if (cacheWriteDataSource != null) {
|
if (cacheWriteDataSource != null) {
|
||||||
nextDataSource = cacheWriteDataSource;
|
nextDataSource = cacheWriteDataSource;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -264,10 +264,16 @@ public final class CacheUtil {
|
||||||
throwExceptionIfInterruptedOrCancelled(isCanceled);
|
throwExceptionIfInterruptedOrCancelled(isCanceled);
|
||||||
// Create a new dataSpec setting length to C.LENGTH_UNSET to prevent getting an error in
|
// Create a new dataSpec setting length to C.LENGTH_UNSET to prevent getting an error in
|
||||||
// case the given length exceeds the end of input.
|
// case the given length exceeds the end of input.
|
||||||
dataSpec = new DataSpec(dataSpec.uri, dataSpec.postBody, absoluteStreamPosition,
|
dataSpec =
|
||||||
dataSpec.position + absoluteStreamPosition - dataSpec.absoluteStreamPosition,
|
new DataSpec(
|
||||||
C.LENGTH_UNSET, dataSpec.key,
|
dataSpec.uri,
|
||||||
dataSpec.flags | DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH);
|
dataSpec.httpMethod,
|
||||||
|
dataSpec.httpBody,
|
||||||
|
absoluteStreamPosition,
|
||||||
|
dataSpec.position + absoluteStreamPosition - dataSpec.absoluteStreamPosition,
|
||||||
|
C.LENGTH_UNSET,
|
||||||
|
dataSpec.key,
|
||||||
|
dataSpec.flags | DataSpec.FLAG_ALLOW_CACHING_UNKNOWN_LENGTH);
|
||||||
long resolvedLength = dataSource.open(dataSpec);
|
long resolvedLength = dataSource.open(dataSpec);
|
||||||
if (counters.contentLength == C.LENGTH_UNSET && resolvedLength != C.LENGTH_UNSET) {
|
if (counters.contentLength == C.LENGTH_UNSET && resolvedLength != C.LENGTH_UNSET) {
|
||||||
counters.contentLength = dataSpec.absoluteStreamPosition + resolvedLength;
|
counters.contentLength = dataSpec.absoluteStreamPosition + resolvedLength;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue