Support POST requests with DefaultHttpDataSource/DataSpec

This commit is contained in:
Oliver Woodman 2015-07-23 13:09:52 +01:00
parent 6b03e6a17c
commit 9dc1bfbbe7
2 changed files with 54 additions and 13 deletions

View file

@ -20,6 +20,8 @@ import com.google.android.exoplayer.util.Assertions;
import android.net.Uri;
import java.util.Arrays;
/**
* Defines a region of media data.
*/
@ -42,6 +44,10 @@ public final class DataSpec {
* Identifies the source from which data should be read.
*/
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.
*/
@ -124,10 +130,28 @@ public final class DataSpec {
*/
public DataSpec(Uri uri, long absoluteStreamPosition, long position, long length, String key,
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(position >= 0);
Assertions.checkArgument(length > 0 || length == C.LENGTH_UNBOUNDED);
this.uri = uri;
this.postBody = postBody;
this.absoluteStreamPosition = absoluteStreamPosition;
this.position = position;
this.length = length;
@ -137,8 +161,8 @@ public final class DataSpec {
@Override
public String toString() {
return "DataSpec[" + uri + ", " + absoluteStreamPosition + ", " + position + ", " + length
+ ", " + key + ", " + flags + "]";
return "DataSpec[" + uri + ", " + Arrays.toString(postBody) + ", " + absoluteStreamPosition
+ ", " + position + ", " + length + ", " + key + ", " + flags + "]";
}
}

View file

@ -27,6 +27,7 @@ import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.NoRouteToHostException;
import java.net.ProtocolException;
@ -329,6 +330,7 @@ public class DefaultHttpDataSource implements HttpDataSource {
*/
private HttpURLConnection makeConnection(DataSpec dataSpec) throws IOException {
URL url = new URL(dataSpec.uri.toString());
byte[] postBody = dataSpec.postBody;
long position = dataSpec.position;
long length = dataSpec.length;
boolean allowGzip = (dataSpec.flags & DataSpec.FLAG_ALLOW_GZIP) != 0;
@ -336,24 +338,27 @@ public class DefaultHttpDataSource implements HttpDataSource {
if (!allowCrossProtocolRedirects) {
// HttpURLConnection disallows cross-protocol redirects, but otherwise performs redirection
// automatically. This is the behavior we want, so use it.
HttpURLConnection connection = configureConnection(url, position, length, allowGzip);
connection.connect();
HttpURLConnection connection = makeConnection(
url, postBody, position, length, allowGzip, true /* followRedirects */);
return connection;
}
// We need to handle redirects ourselves to allow cross-protocol redirects.
int redirectCount = 0;
while (redirectCount++ <= MAX_REDIRECTS) {
HttpURLConnection connection = configureConnection(url, position, length, allowGzip);
connection.setInstanceFollowRedirects(false);
connection.connect();
HttpURLConnection connection = makeConnection(
url, postBody, position, length, allowGzip, false /* followRedirects */);
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_MULT_CHOICE
|| responseCode == HttpURLConnection.HTTP_MOVED_PERM
|| responseCode == HttpURLConnection.HTTP_MOVED_TEMP
|| responseCode == HttpURLConnection.HTTP_SEE_OTHER
|| responseCode == 307 /* HTTP_TEMP_REDIRECT */
|| responseCode == 308 /* HTTP_PERM_REDIRECT */) {
|| (postBody == null
&& (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");
connection.disconnect();
url = handleRedirect(url, location);
@ -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 postBody The body data for a POST request.
* @param position The byte offset of the requested data.
* @param length The length of the requested data, or {@link C#LENGTH_UNBOUNDED}.
* @param allowGzip Whether to allow the use of gzip.
* @param followRedirects Whether to follow redirects.
*/
private HttpURLConnection configureConnection(URL url, long position, long length,
boolean allowGzip) throws IOException {
private HttpURLConnection makeConnection(URL url, byte[] postBody, long position,
long length, boolean allowGzip, boolean followRedirects) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(connectTimeoutMillis);
connection.setReadTimeout(readTimeoutMillis);
connection.setDoOutput(false);
synchronized (requestProperties) {
for (Map.Entry<String, String> property : requestProperties.entrySet()) {
connection.setRequestProperty(property.getKey(), property.getValue());
@ -396,6 +402,17 @@ public class DefaultHttpDataSource implements HttpDataSource {
if (!allowGzip) {
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;
}