Commit 9dc1bfbb by Oliver Woodman

Support POST requests with DefaultHttpDataSource/DataSpec

parent 6b03e6a1
...@@ -20,6 +20,8 @@ import com.google.android.exoplayer.util.Assertions; ...@@ -20,6 +20,8 @@ import com.google.android.exoplayer.util.Assertions;
import android.net.Uri; import android.net.Uri;
import java.util.Arrays;
/** /**
* Defines a region of media data. * Defines a region of media data.
*/ */
...@@ -43,6 +45,10 @@ public final class DataSpec { ...@@ -43,6 +45,10 @@ public final class DataSpec {
*/ */
public final Uri uri; public final Uri uri;
/** /**
* Body for a POST request, null otherwise.
*/
public final byte[] postBody;
/**
* The absolute position of the data in the full stream. * The absolute position of the data in the full stream.
*/ */
public final long absoluteStreamPosition; public final long absoluteStreamPosition;
...@@ -124,10 +130,28 @@ public final class DataSpec { ...@@ -124,10 +130,28 @@ public final class DataSpec {
*/ */
public DataSpec(Uri uri, long absoluteStreamPosition, long position, long length, String key, public DataSpec(Uri uri, long absoluteStreamPosition, long position, long length, String key,
int flags) { int flags) {
this(uri, null, absoluteStreamPosition, position, length, key, flags);
}
/**
* Construct a {@link DataSpec} where {@link #position} may differ from
* {@link #absoluteStreamPosition}.
*
* @param uri {@link #uri}.
* @param postBody {@link #postBody}.
* @param absoluteStreamPosition {@link #absoluteStreamPosition}.
* @param position {@link #position}.
* @param length {@link #length}.
* @param key {@link #key}.
* @param flags {@link #flags}.
*/
public DataSpec(Uri uri, byte[] postBody, long absoluteStreamPosition, long position, long length,
String key, 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_UNBOUNDED); Assertions.checkArgument(length > 0 || length == C.LENGTH_UNBOUNDED);
this.uri = uri; this.uri = uri;
this.postBody = postBody;
this.absoluteStreamPosition = absoluteStreamPosition; this.absoluteStreamPosition = absoluteStreamPosition;
this.position = position; this.position = position;
this.length = length; this.length = length;
...@@ -137,8 +161,8 @@ public final class DataSpec { ...@@ -137,8 +161,8 @@ public final class DataSpec {
@Override @Override
public String toString() { public String toString() {
return "DataSpec[" + uri + ", " + absoluteStreamPosition + ", " + position + ", " + length return "DataSpec[" + uri + ", " + Arrays.toString(postBody) + ", " + absoluteStreamPosition
+ ", " + key + ", " + flags + "]"; + ", " + position + ", " + length + ", " + key + ", " + flags + "]";
} }
} }
...@@ -27,6 +27,7 @@ import java.io.EOFException; ...@@ -27,6 +27,7 @@ import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InterruptedIOException; import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.NoRouteToHostException; import java.net.NoRouteToHostException;
import java.net.ProtocolException; import java.net.ProtocolException;
...@@ -329,6 +330,7 @@ public class DefaultHttpDataSource implements HttpDataSource { ...@@ -329,6 +330,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
*/ */
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;
long position = dataSpec.position; long position = dataSpec.position;
long length = dataSpec.length; long length = dataSpec.length;
boolean allowGzip = (dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) != 0; boolean allowGzip = (dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) != 0;
...@@ -336,24 +338,27 @@ public class DefaultHttpDataSource implements HttpDataSource { ...@@ -336,24 +338,27 @@ public class DefaultHttpDataSource implements HttpDataSource {
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.
HttpURLConnection connection = configureConnection(url, position, length, allowGzip); HttpURLConnection connection = makeConnection(
connection.connect(); url, postBody, position, length, allowGzip, true /* followRedirects */);
return connection; return connection;
} }
// 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 = configureConnection(url, position, length, allowGzip); HttpURLConnection connection = makeConnection(
connection.setInstanceFollowRedirects(false); url, postBody, position, length, allowGzip, false /* followRedirects */);
connection.connect();
int responseCode = connection.getResponseCode(); int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MULT_CHOICE if (responseCode == HttpURLConnection.HTTP_MULT_CHOICE
|| responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER || responseCode == HttpURLConnection.HTTP_SEE_OTHER
|| responseCode == 307 /* HTTP_TEMP_REDIRECT */ || (postBody == null
|| responseCode == 308 /* HTTP_PERM_REDIRECT */) { && (responseCode == 307 /* HTTP_TEMP_REDIRECT */
|| responseCode == 308 /* HTTP_PERM_REDIRECT */))) {
// For 300, 301, 302, and 303 POST requests follow the redirect and are transformed into
// GET requests. For 307 and 308 POST requests are not redirected.
postBody = null;
String location = connection.getHeaderField("Location"); String location = connection.getHeaderField("Location");
connection.disconnect(); connection.disconnect();
url = handleRedirect(url, location); url = handleRedirect(url, location);
...@@ -367,19 +372,20 @@ public class DefaultHttpDataSource implements HttpDataSource { ...@@ -367,19 +372,20 @@ public class DefaultHttpDataSource implements HttpDataSource {
} }
/** /**
* Configures a connection, but does not open 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 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_UNBOUNDED}. * @param length The length of the requested data, or {@link C#LENGTH_UNBOUNDED}.
* @param allowGzip Whether to allow the use of gzip. * @param allowGzip Whether to allow the use of gzip.
* @param followRedirects Whether to follow redirects.
*/ */
private HttpURLConnection configureConnection(URL url, long position, long length, private HttpURLConnection makeConnection(URL url, byte[] postBody, long position,
boolean allowGzip) throws IOException { 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);
connection.setDoOutput(false);
synchronized (requestProperties) { synchronized (requestProperties) {
for (Map.Entry<String, String> property : requestProperties.entrySet()) { for (Map.Entry<String, String> property : requestProperties.entrySet()) {
connection.setRequestProperty(property.getKey(), property.getValue()); connection.setRequestProperty(property.getKey(), property.getValue());
...@@ -396,6 +402,17 @@ public class DefaultHttpDataSource implements HttpDataSource { ...@@ -396,6 +402,17 @@ public class DefaultHttpDataSource implements HttpDataSource {
if (!allowGzip) { if (!allowGzip) {
connection.setRequestProperty("Accept-Encoding", "identity"); connection.setRequestProperty("Accept-Encoding", "identity");
} }
connection.setInstanceFollowRedirects(followRedirects);
connection.setDoOutput(postBody != null);
if (postBody != null) {
connection.setFixedLengthStreamingMode(postBody.length);
connection.connect();
OutputStream os = connection.getOutputStream();
os.write(postBody);
os.close();
} else {
connection.connect();
}
return connection; return connection;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment