Propagate updates of default header fields of the HttpDataSource.BaseFactory to HttpDataSource instances.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=149780233
This commit is contained in:
bachinger 2017-03-10 11:38:51 -08:00 committed by Oliver Woodman
parent 952bde700b
commit aede0f894d
9 changed files with 204 additions and 100 deletions

View File

@ -118,7 +118,8 @@ public final class CronetDataSourceTest {
TEST_CONNECT_TIMEOUT_MS, TEST_CONNECT_TIMEOUT_MS,
TEST_READ_TIMEOUT_MS, TEST_READ_TIMEOUT_MS,
true, // resetTimeoutOnRedirects true, // resetTimeoutOnRedirects
mockClock)); mockClock,
null));
when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true); when(mockContentTypePredicate.evaluate(anyString())).thenReturn(true);
when(mockCronetEngine.newUrlRequestBuilder( when(mockCronetEngine.newUrlRequestBuilder(
anyString(), any(UrlRequest.Callback.class), any(Executor.class))) anyString(), any(UrlRequest.Callback.class), any(Executor.class)))

View File

@ -32,7 +32,6 @@ import java.io.IOException;
import java.net.SocketTimeoutException; import java.net.SocketTimeoutException;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -98,7 +97,8 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
private final int connectTimeoutMs; private final int connectTimeoutMs;
private final int readTimeoutMs; private final int readTimeoutMs;
private final boolean resetTimeoutOnRedirects; private final boolean resetTimeoutOnRedirects;
private final Map<String, String> requestProperties; private final RequestProperties defaultRequestProperties;
private final RequestProperties requestProperties;
private final ConditionVariable operation; private final ConditionVariable operation;
private final Clock clock; private final Clock clock;
@ -136,7 +136,7 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
public CronetDataSource(CronetEngine cronetEngine, Executor executor, public CronetDataSource(CronetEngine cronetEngine, Executor executor,
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener) { Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener) {
this(cronetEngine, executor, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS, this(cronetEngine, executor, contentTypePredicate, listener, DEFAULT_CONNECT_TIMEOUT_MILLIS,
DEFAULT_READ_TIMEOUT_MILLIS, false); DEFAULT_READ_TIMEOUT_MILLIS, false, null);
} }
/** /**
@ -149,17 +149,20 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
* @param connectTimeoutMs The connection timeout, in milliseconds. * @param connectTimeoutMs The connection timeout, in milliseconds.
* @param readTimeoutMs The read timeout, in milliseconds. * @param readTimeoutMs The read timeout, in milliseconds.
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs. * @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
* @param defaultRequestProperties The default request properties to be used.
*/ */
public CronetDataSource(CronetEngine cronetEngine, Executor executor, public CronetDataSource(CronetEngine cronetEngine, Executor executor,
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener, Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects) { int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects,
RequestProperties defaultRequestProperties) {
this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs, this(cronetEngine, executor, contentTypePredicate, listener, connectTimeoutMs,
readTimeoutMs, resetTimeoutOnRedirects, new SystemClock()); readTimeoutMs, resetTimeoutOnRedirects, new SystemClock(), defaultRequestProperties);
} }
/* package */ CronetDataSource(CronetEngine cronetEngine, Executor executor, /* package */ CronetDataSource(CronetEngine cronetEngine, Executor executor,
Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener, Predicate<String> contentTypePredicate, TransferListener<? super CronetDataSource> listener,
int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects, Clock clock) { int connectTimeoutMs, int readTimeoutMs, boolean resetTimeoutOnRedirects, Clock clock,
RequestProperties defaultRequestProperties) {
this.cronetEngine = Assertions.checkNotNull(cronetEngine); this.cronetEngine = Assertions.checkNotNull(cronetEngine);
this.executor = Assertions.checkNotNull(executor); this.executor = Assertions.checkNotNull(executor);
this.contentTypePredicate = contentTypePredicate; this.contentTypePredicate = contentTypePredicate;
@ -168,7 +171,8 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
this.readTimeoutMs = readTimeoutMs; this.readTimeoutMs = readTimeoutMs;
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects; this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
this.clock = Assertions.checkNotNull(clock); this.clock = Assertions.checkNotNull(clock);
requestProperties = new HashMap<>(); this.defaultRequestProperties = defaultRequestProperties;
requestProperties = new RequestProperties();
operation = new ConditionVariable(); operation = new ConditionVariable();
} }
@ -176,24 +180,18 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
@Override @Override
public void setRequestProperty(String name, String value) { public void setRequestProperty(String name, String value) {
synchronized (requestProperties) { requestProperties.set(name, value);
requestProperties.put(name, value);
}
} }
@Override @Override
public void clearRequestProperty(String name) { public void clearRequestProperty(String name) {
synchronized (requestProperties) {
requestProperties.remove(name); requestProperties.remove(name);
} }
}
@Override @Override
public void clearAllRequestProperties() { public void clearAllRequestProperties() {
synchronized (requestProperties) {
requestProperties.clear(); requestProperties.clear();
} }
}
@Override @Override
public Map<String, List<String>> getResponseHeaders() { public Map<String, List<String>> getResponseHeaders() {
@ -421,16 +419,24 @@ public class CronetDataSource extends UrlRequest.Callback implements HttpDataSou
UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(dataSpec.uri.toString(), UrlRequest.Builder requestBuilder = cronetEngine.newUrlRequestBuilder(dataSpec.uri.toString(),
this, executor); this, executor);
// Set the headers. // Set the headers.
synchronized (requestProperties) { boolean isContentTypeHeaderSet = false;
if (dataSpec.postBody != null && dataSpec.postBody.length != 0 if (defaultRequestProperties != null) {
&& !requestProperties.containsKey(CONTENT_TYPE)) { for (Entry<String, String> headerEntry : defaultRequestProperties.getSnapshot().entrySet()) {
String key = headerEntry.getKey();
isContentTypeHeaderSet = isContentTypeHeaderSet || CONTENT_TYPE.equals(key);
requestBuilder.addHeader(key, headerEntry.getValue());
}
}
Map<String, String> requestPropertiesSnapshot = requestProperties.getSnapshot();
for (Entry<String, String> headerEntry : requestPropertiesSnapshot.entrySet()) {
String key = headerEntry.getKey();
isContentTypeHeaderSet = isContentTypeHeaderSet || CONTENT_TYPE.equals(key);
requestBuilder.addHeader(key, headerEntry.getValue());
}
if (dataSpec.postBody != null && dataSpec.postBody.length != 0 && !isContentTypeHeaderSet) {
throw new OpenException("POST request with non-empty body must set Content-Type", dataSpec, throw new OpenException("POST request with non-empty body must set Content-Type", dataSpec,
Status.IDLE); Status.IDLE);
} }
for (Entry<String, String> headerEntry : requestProperties.entrySet()) {
requestBuilder.addHeader(headerEntry.getKey(), headerEntry.getValue());
}
}
// Set the Range header. // Set the Range header.
if (currentDataSpec.position != 0 || currentDataSpec.length != C.LENGTH_UNSET) { if (currentDataSpec.position != 0 || currentDataSpec.length != C.LENGTH_UNSET) {
StringBuilder rangeValue = new StringBuilder(); StringBuilder rangeValue = new StringBuilder();

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.ext.cronet; package com.google.android.exoplayer2.ext.cronet;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory; import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
@ -68,9 +69,10 @@ public final class CronetDataSourceFactory extends BaseFactory {
} }
@Override @Override
protected CronetDataSource createDataSourceInternal() { protected CronetDataSource createDataSourceInternal(HttpDataSource.RequestProperties
defaultRequestProperties) {
return new CronetDataSource(cronetEngine, executor, contentTypePredicate, transferListener, return new CronetDataSource(cronetEngine, executor, contentTypePredicate, transferListener,
connectTimeoutMs, readTimeoutMs, resetTimeoutOnRedirects); connectTimeoutMs, readTimeoutMs, resetTimeoutOnRedirects, null, defaultRequestProperties);
} }
} }

View File

@ -27,7 +27,6 @@ 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.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -51,7 +50,8 @@ public class OkHttpDataSource implements HttpDataSource {
private final Predicate<String> contentTypePredicate; private final Predicate<String> contentTypePredicate;
private final TransferListener<? super OkHttpDataSource> listener; private final TransferListener<? super OkHttpDataSource> listener;
private final CacheControl cacheControl; private final CacheControl cacheControl;
private final HashMap<String, String> requestProperties; private final RequestProperties defaultRequestProperties;
private final RequestProperties requestProperties;
private DataSpec dataSpec; private DataSpec dataSpec;
private Response response; private Response response;
@ -87,7 +87,7 @@ public class OkHttpDataSource implements HttpDataSource {
*/ */
public OkHttpDataSource(Call.Factory callFactory, String userAgent, public OkHttpDataSource(Call.Factory callFactory, String userAgent,
Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener) { Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener) {
this(callFactory, userAgent, contentTypePredicate, listener, null); this(callFactory, userAgent, contentTypePredicate, listener, null, null);
} }
/** /**
@ -99,16 +99,19 @@ public class OkHttpDataSource implements HttpDataSource {
* {@link #open(DataSpec)}. * {@link #open(DataSpec)}.
* @param listener An optional listener. * @param listener An optional listener.
* @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header. * @param cacheControl An optional {@link CacheControl} for setting the Cache-Control header.
* @param defaultRequestProperties The optional default {@link RequestProperties} to be sent to
* the server as HTTP headers on every request.
*/ */
public OkHttpDataSource(Call.Factory callFactory, String userAgent, public OkHttpDataSource(Call.Factory callFactory, String userAgent,
Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener, Predicate<String> contentTypePredicate, TransferListener<? super OkHttpDataSource> listener,
CacheControl cacheControl) { CacheControl cacheControl, RequestProperties defaultRequestProperties) {
this.callFactory = Assertions.checkNotNull(callFactory); this.callFactory = Assertions.checkNotNull(callFactory);
this.userAgent = Assertions.checkNotEmpty(userAgent); this.userAgent = Assertions.checkNotEmpty(userAgent);
this.contentTypePredicate = contentTypePredicate; this.contentTypePredicate = contentTypePredicate;
this.listener = listener; this.listener = listener;
this.cacheControl = cacheControl; this.cacheControl = cacheControl;
this.requestProperties = new HashMap<>(); this.defaultRequestProperties = defaultRequestProperties;
this.requestProperties = new RequestProperties();
} }
@Override @Override
@ -125,25 +128,19 @@ public class OkHttpDataSource implements HttpDataSource {
public void setRequestProperty(String name, String value) { public void setRequestProperty(String name, String value) {
Assertions.checkNotNull(name); Assertions.checkNotNull(name);
Assertions.checkNotNull(value); Assertions.checkNotNull(value);
synchronized (requestProperties) { requestProperties.set(name, value);
requestProperties.put(name, value);
}
} }
@Override @Override
public void clearRequestProperty(String name) { public void clearRequestProperty(String name) {
Assertions.checkNotNull(name); Assertions.checkNotNull(name);
synchronized (requestProperties) {
requestProperties.remove(name); requestProperties.remove(name);
} }
}
@Override @Override
public void clearAllRequestProperties() { public void clearAllRequestProperties() {
synchronized (requestProperties) {
requestProperties.clear(); requestProperties.clear();
} }
}
@Override @Override
public long open(DataSpec dataSpec) throws HttpDataSourceException { public long open(DataSpec dataSpec) throws HttpDataSourceException {
@ -268,11 +265,14 @@ public class OkHttpDataSource implements HttpDataSource {
if (cacheControl != null) { if (cacheControl != null) {
builder.cacheControl(cacheControl); builder.cacheControl(cacheControl);
} }
synchronized (requestProperties) { if (defaultRequestProperties != null) {
for (Map.Entry<String, String> property : requestProperties.entrySet()) { for (Map.Entry<String, String> property : defaultRequestProperties.getSnapshot().entrySet()) {
builder.addHeader(property.getKey(), property.getValue()); builder.header(property.getKey(), property.getValue());
} }
} }
for (Map.Entry<String, String> property : requestProperties.getSnapshot().entrySet()) {
builder.header(property.getKey(), property.getValue());
}
if (!(position == 0 && length == C.LENGTH_UNSET)) { if (!(position == 0 && length == C.LENGTH_UNSET)) {
String rangeRequest = "bytes=" + position + "-"; String rangeRequest = "bytes=" + position + "-";
if (length != C.LENGTH_UNSET) { if (length != C.LENGTH_UNSET) {

View File

@ -16,6 +16,7 @@
package com.google.android.exoplayer2.ext.okhttp; package com.google.android.exoplayer2.ext.okhttp;
import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource;
import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory; import com.google.android.exoplayer2.upstream.HttpDataSource.BaseFactory;
import com.google.android.exoplayer2.upstream.HttpDataSource.Factory; import com.google.android.exoplayer2.upstream.HttpDataSource.Factory;
import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.TransferListener;
@ -59,8 +60,10 @@ public final class OkHttpDataSourceFactory extends BaseFactory {
} }
@Override @Override
protected OkHttpDataSource createDataSourceInternal() { protected OkHttpDataSource createDataSourceInternal(
return new OkHttpDataSource(callFactory, userAgent, null, listener, cacheControl); HttpDataSource.RequestProperties defaultRequestProperties) {
return new OkHttpDataSource(callFactory, userAgent, null, listener, cacheControl,
defaultRequestProperties);
} }
} }

View File

@ -81,7 +81,7 @@ public final class DefaultDataSource implements DataSource {
boolean allowCrossProtocolRedirects) { boolean allowCrossProtocolRedirects) {
this(context, listener, this(context, listener,
new DefaultHttpDataSource(userAgent, null, listener, connectTimeoutMillis, new DefaultHttpDataSource(userAgent, null, listener, connectTimeoutMillis,
readTimeoutMillis, allowCrossProtocolRedirects)); readTimeoutMillis, allowCrossProtocolRedirects, null));
} }
/** /**

View File

@ -32,7 +32,6 @@ import java.net.HttpURLConnection;
import java.net.NoRouteToHostException; import java.net.NoRouteToHostException;
import java.net.ProtocolException; import java.net.ProtocolException;
import java.net.URL; import java.net.URL;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
@ -44,8 +43,8 @@ import java.util.regex.Pattern;
* <p> * <p>
* By default this implementation will not follow cross-protocol redirects (i.e. redirects from * By default this implementation will not follow cross-protocol redirects (i.e. redirects from
* HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by using the * HTTP to HTTPS or vice versa). Cross-protocol redirects can be enabled by using the
* {@link #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean)} * {@link #DefaultHttpDataSource(String, Predicate, TransferListener, int, int, boolean,
* constructor and passing {@code true} as the final argument. * RequestProperties)} constructor and passing {@code true} as the second last argument.
*/ */
public class DefaultHttpDataSource implements HttpDataSource { public class DefaultHttpDataSource implements HttpDataSource {
@ -70,7 +69,8 @@ public class DefaultHttpDataSource implements HttpDataSource {
private final int readTimeoutMillis; private final int readTimeoutMillis;
private final String userAgent; private final String userAgent;
private final Predicate<String> contentTypePredicate; private final Predicate<String> contentTypePredicate;
private final HashMap<String, String> requestProperties; private final RequestProperties defaultRequestProperties;
private final RequestProperties requestProperties;
private final TransferListener<? super DefaultHttpDataSource> listener; private final TransferListener<? super DefaultHttpDataSource> listener;
private DataSpec dataSpec; private DataSpec dataSpec;
@ -121,7 +121,8 @@ public class DefaultHttpDataSource implements HttpDataSource {
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate, public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis, TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
int readTimeoutMillis) { int readTimeoutMillis) {
this(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, false); this(userAgent, contentTypePredicate, listener, connectTimeoutMillis, readTimeoutMillis, false,
null);
} }
/** /**
@ -137,17 +138,21 @@ public class DefaultHttpDataSource implements HttpDataSource {
* as an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value. * as an infinite timeout. Pass {@link #DEFAULT_READ_TIMEOUT_MILLIS} to use the default value.
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP * @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled. * to HTTPS and vice versa) are enabled.
* @param defaultRequestProperties The default request properties to be sent to the server as
* HTTP headers or {@code null} if not required.
*/ */
public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate, public DefaultHttpDataSource(String userAgent, Predicate<String> contentTypePredicate,
TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis, TransferListener<? super DefaultHttpDataSource> listener, int connectTimeoutMillis,
int readTimeoutMillis, boolean allowCrossProtocolRedirects) { int readTimeoutMillis, boolean allowCrossProtocolRedirects,
RequestProperties defaultRequestProperties) {
this.userAgent = Assertions.checkNotEmpty(userAgent); this.userAgent = Assertions.checkNotEmpty(userAgent);
this.contentTypePredicate = contentTypePredicate; this.contentTypePredicate = contentTypePredicate;
this.listener = listener; this.listener = listener;
this.requestProperties = new HashMap<>(); this.requestProperties = new RequestProperties();
this.connectTimeoutMillis = connectTimeoutMillis; this.connectTimeoutMillis = connectTimeoutMillis;
this.readTimeoutMillis = readTimeoutMillis; this.readTimeoutMillis = readTimeoutMillis;
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
this.defaultRequestProperties = defaultRequestProperties;
} }
@Override @Override
@ -164,25 +169,19 @@ public class DefaultHttpDataSource implements HttpDataSource {
public void setRequestProperty(String name, String value) { public void setRequestProperty(String name, String value) {
Assertions.checkNotNull(name); Assertions.checkNotNull(name);
Assertions.checkNotNull(value); Assertions.checkNotNull(value);
synchronized (requestProperties) { requestProperties.set(name, value);
requestProperties.put(name, value);
}
} }
@Override @Override
public void clearRequestProperty(String name) { public void clearRequestProperty(String name) {
Assertions.checkNotNull(name); Assertions.checkNotNull(name);
synchronized (requestProperties) {
requestProperties.remove(name); requestProperties.remove(name);
} }
}
@Override @Override
public void clearAllRequestProperties() { public void clearAllRequestProperties() {
synchronized (requestProperties) {
requestProperties.clear(); requestProperties.clear();
} }
}
@Override @Override
public long open(DataSpec dataSpec) throws HttpDataSourceException { public long open(DataSpec dataSpec) throws HttpDataSourceException {
@ -394,11 +393,14 @@ public class DefaultHttpDataSource implements HttpDataSource {
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(connectTimeoutMillis); connection.setConnectTimeout(connectTimeoutMillis);
connection.setReadTimeout(readTimeoutMillis); connection.setReadTimeout(readTimeoutMillis);
synchronized (requestProperties) { if (defaultRequestProperties != null) {
for (Map.Entry<String, String> property : requestProperties.entrySet()) { for (Map.Entry<String, String> property : defaultRequestProperties.getSnapshot().entrySet()) {
connection.setRequestProperty(property.getKey(), property.getValue()); connection.setRequestProperty(property.getKey(), property.getValue());
} }
} }
for (Map.Entry<String, String> property : requestProperties.getSnapshot().entrySet()) {
connection.setRequestProperty(property.getKey(), property.getValue());
}
if (!(position == 0 && length == C.LENGTH_UNSET)) { if (!(position == 0 && length == C.LENGTH_UNSET)) {
String rangeRequest = "bytes=" + position + "-"; String rangeRequest = "bytes=" + position + "-";
if (length != C.LENGTH_UNSET) { if (length != C.LENGTH_UNSET) {

View File

@ -76,9 +76,10 @@ public final class DefaultHttpDataSourceFactory extends BaseFactory {
} }
@Override @Override
protected DefaultHttpDataSource createDataSourceInternal() { protected DefaultHttpDataSource createDataSourceInternal(
HttpDataSource.RequestProperties defaultRequestProperties) {
return new DefaultHttpDataSource(userAgent, null, listener, connectTimeoutMillis, return new DefaultHttpDataSource(userAgent, null, listener, connectTimeoutMillis,
readTimeoutMillis, allowCrossProtocolRedirects); readTimeoutMillis, allowCrossProtocolRedirects, getDefaultRequestProperties());
} }
} }

View File

@ -17,12 +17,12 @@ package com.google.android.exoplayer2.upstream;
import android.support.annotation.IntDef; import android.support.annotation.IntDef;
import android.text.TextUtils; import android.text.TextUtils;
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;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -41,84 +41,173 @@ public interface HttpDataSource extends DataSource {
HttpDataSource createDataSource(); HttpDataSource createDataSource();
/** /**
* Sets a default request header for {@link HttpDataSource} instances subsequently created by * Gets the default request properties used by all {@link HttpDataSource}s created by the
* the factory. Previously created instances are not affected. * factory. Changes to the properties will be reflected in any future requests made by
* {@link HttpDataSource}s created by the factory.
* *
* @return The default request properties of the factory.
*/
RequestProperties getDefaultRequestProperties();
/**
* Sets a default request header for {@link HttpDataSource} instances created by the factory.
*
* @deprecated Use {@link #getDefaultRequestProperties} instead.
* @param name The name of the header field. * @param name The name of the header field.
* @param value The value of the field. * @param value The value of the field.
*/ */
@Deprecated
void setDefaultRequestProperty(String name, String value); void setDefaultRequestProperty(String name, String value);
/** /**
* Clears a default request header for {@link HttpDataSource} instances subsequently created by * Clears a default request header for {@link HttpDataSource} instances created by the factory.
* the factory. Previously created instances are not affected.
* *
* @deprecated Use {@link #getDefaultRequestProperties} instead.
* @param name The name of the header field. * @param name The name of the header field.
*/ */
@Deprecated
void clearDefaultRequestProperty(String name); void clearDefaultRequestProperty(String name);
/** /**
* Clears all default request header for all {@link HttpDataSource} instances subsequently * Clears all default request headers for all {@link HttpDataSource} instances created by the
* created by the factory. Previously created instances are not affected. * factory.
*
* @deprecated Use {@link #getDefaultRequestProperties} instead.
*/ */
@Deprecated
void clearAllDefaultRequestProperties(); void clearAllDefaultRequestProperties();
} }
/**
* Stores HTTP request properties (aka HTTP headers) and provides methods to modify the headers
* in a thread safe way to avoid the potential of creating snapshots of an inconsistent or
* unintended state.
*/
final class RequestProperties {
private final Map<String, String> requestProperties;
private Map<String, String> requestPropertiesSnapshot;
public RequestProperties() {
requestProperties = new HashMap<>();
}
/**
* Sets the specified property {@code value} for the specified {@code name}. If a property for
* this name previously existed, the old value is replaced by the specified value.
*
* @param name The name of the request property.
* @param value The value of the request property.
*/
public synchronized void set(String name, String value) {
requestPropertiesSnapshot = null;
requestProperties.put(name, value);
}
/**
* Sets the keys and values contained in the map. If a property previously existed, the old
* value is replaced by the specified value. If a property previously existed and is not in the
* map, the property is left unchanged.
*
* @param properties The request properties.
*/
public synchronized void set(Map<String, String> properties) {
requestPropertiesSnapshot = null;
requestProperties.putAll(properties);
}
/**
* Removes all properties previously existing and sets the keys and values of the map.
*
* @param properties The request properties.
*/
public synchronized void clearAndSet(Map<String, String> properties) {
requestPropertiesSnapshot = null;
requestProperties.clear();
requestProperties.putAll(properties);
}
/**
* Removes a request property by name.
*
* @param name The name of the request property to remove.
*/
public synchronized void remove(String name) {
requestPropertiesSnapshot = null;
requestProperties.remove(name);
}
/**
* Clears all request properties.
*/
public synchronized void clear() {
requestPropertiesSnapshot = null;
requestProperties.clear();
}
/**
* Gets a snapshot of the request properties.
*
* @return A snapshot of the request properties.
*/
public synchronized Map<String, String> getSnapshot() {
if (requestPropertiesSnapshot == null) {
requestPropertiesSnapshot = Collections.unmodifiableMap(new HashMap<>(requestProperties));
}
return requestPropertiesSnapshot;
}
}
/** /**
* Base implementation of {@link Factory} that sets default request properties. * Base implementation of {@link Factory} that sets default request properties.
*/ */
abstract class BaseFactory implements Factory { abstract class BaseFactory implements Factory {
private final HashMap<String, String> requestProperties; private final RequestProperties defaultRequestProperties;
public BaseFactory() { public BaseFactory() {
requestProperties = new HashMap<>(); defaultRequestProperties = new RequestProperties();
} }
@Override @Override
public final HttpDataSource createDataSource() { public final HttpDataSource createDataSource() {
HttpDataSource dataSource = createDataSourceInternal(); return createDataSourceInternal(defaultRequestProperties);
synchronized (requestProperties) {
for (Map.Entry<String, String> property : requestProperties.entrySet()) {
dataSource.setRequestProperty(property.getKey(), property.getValue());
}
}
return dataSource;
} }
@Override
public RequestProperties getDefaultRequestProperties() {
return defaultRequestProperties;
}
@Deprecated
@Override @Override
public final void setDefaultRequestProperty(String name, String value) { public final void setDefaultRequestProperty(String name, String value) {
Assertions.checkNotNull(name); defaultRequestProperties.set(name, value);
Assertions.checkNotNull(value);
synchronized (requestProperties) {
requestProperties.put(name, value);
}
} }
@Deprecated
@Override @Override
public final void clearDefaultRequestProperty(String name) { public final void clearDefaultRequestProperty(String name) {
Assertions.checkNotNull(name); defaultRequestProperties.remove(name);
synchronized (requestProperties) {
requestProperties.remove(name);
}
} }
@Deprecated
@Override @Override
public final void clearAllDefaultRequestProperties() { public final void clearAllDefaultRequestProperties() {
synchronized (requestProperties) { defaultRequestProperties.clear();
requestProperties.clear();
}
} }
/** /**
* Called by {@link #createDataSource()} to create a {@link HttpDataSource} instance without * Called by {@link #createDataSource()} to create a {@link HttpDataSource} instance.
* default request properties set. Default request properties will be set by
* {@link #createDataSource()} before the instance is returned.
* *
* @return A {@link HttpDataSource} instance without default request properties set. * @param defaultRequestProperties The default {@code RequestProperties} to be used by the
* {@link HttpDataSource} instance.
* @return A {@link HttpDataSource} instance.
*/ */
protected abstract HttpDataSource createDataSourceInternal(); protected abstract HttpDataSource createDataSourceInternal(RequestProperties
defaultRequestProperties);
} }