• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.upstream;
17 
18 import android.text.TextUtils;
19 import androidx.annotation.IntDef;
20 import androidx.annotation.Nullable;
21 import com.google.android.exoplayer2.util.Predicate;
22 import com.google.android.exoplayer2.util.Util;
23 import java.io.IOException;
24 import java.lang.annotation.Documented;
25 import java.lang.annotation.Retention;
26 import java.lang.annotation.RetentionPolicy;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 
32 /**
33  * An HTTP {@link DataSource}.
34  */
35 public interface HttpDataSource extends DataSource {
36 
37   /**
38    * A factory for {@link HttpDataSource} instances.
39    */
40   interface Factory extends DataSource.Factory {
41 
42     @Override
createDataSource()43     HttpDataSource createDataSource();
44 
45     /**
46      * Gets the default request properties used by all {@link HttpDataSource}s created by the
47      * factory. Changes to the properties will be reflected in any future requests made by
48      * {@link HttpDataSource}s created by the factory.
49      *
50      * @return The default request properties of the factory.
51      */
getDefaultRequestProperties()52     RequestProperties getDefaultRequestProperties();
53 
54     /**
55      * Sets a default request header for {@link HttpDataSource} instances created by the factory.
56      *
57      * @deprecated Use {@link #getDefaultRequestProperties} instead.
58      * @param name The name of the header field.
59      * @param value The value of the field.
60      */
61     @Deprecated
setDefaultRequestProperty(String name, String value)62     void setDefaultRequestProperty(String name, String value);
63 
64     /**
65      * Clears a default request header for {@link HttpDataSource} instances created by the factory.
66      *
67      * @deprecated Use {@link #getDefaultRequestProperties} instead.
68      * @param name The name of the header field.
69      */
70     @Deprecated
clearDefaultRequestProperty(String name)71     void clearDefaultRequestProperty(String name);
72 
73     /**
74      * Clears all default request headers for all {@link HttpDataSource} instances created by the
75      * factory.
76      *
77      * @deprecated Use {@link #getDefaultRequestProperties} instead.
78      */
79     @Deprecated
clearAllDefaultRequestProperties()80     void clearAllDefaultRequestProperties();
81 
82   }
83 
84   /**
85    * Stores HTTP request properties (aka HTTP headers) and provides methods to modify the headers
86    * in a thread safe way to avoid the potential of creating snapshots of an inconsistent or
87    * unintended state.
88    */
89   final class RequestProperties {
90 
91     private final Map<String, String> requestProperties;
92     @Nullable private Map<String, String> requestPropertiesSnapshot;
93 
RequestProperties()94     public RequestProperties() {
95       requestProperties = new HashMap<>();
96     }
97 
98     /**
99      * Sets the specified property {@code value} for the specified {@code name}. If a property for
100      * this name previously existed, the old value is replaced by the specified value.
101      *
102      * @param name The name of the request property.
103      * @param value The value of the request property.
104      */
set(String name, String value)105     public synchronized void set(String name, String value) {
106       requestPropertiesSnapshot = null;
107       requestProperties.put(name, value);
108     }
109 
110     /**
111      * Sets the keys and values contained in the map. If a property previously existed, the old
112      * value is replaced by the specified value. If a property previously existed and is not in the
113      * map, the property is left unchanged.
114      *
115      * @param properties The request properties.
116      */
set(Map<String, String> properties)117     public synchronized void set(Map<String, String> properties) {
118       requestPropertiesSnapshot = null;
119       requestProperties.putAll(properties);
120     }
121 
122     /**
123      * Removes all properties previously existing and sets the keys and values of the map.
124      *
125      * @param properties The request properties.
126      */
clearAndSet(Map<String, String> properties)127     public synchronized void clearAndSet(Map<String, String> properties) {
128       requestPropertiesSnapshot = null;
129       requestProperties.clear();
130       requestProperties.putAll(properties);
131     }
132 
133     /**
134      * Removes a request property by name.
135      *
136      * @param name The name of the request property to remove.
137      */
remove(String name)138     public synchronized void remove(String name) {
139       requestPropertiesSnapshot = null;
140       requestProperties.remove(name);
141     }
142 
143     /**
144      * Clears all request properties.
145      */
clear()146     public synchronized void clear() {
147       requestPropertiesSnapshot = null;
148       requestProperties.clear();
149     }
150 
151     /**
152      * Gets a snapshot of the request properties.
153      *
154      * @return A snapshot of the request properties.
155      */
getSnapshot()156     public synchronized Map<String, String> getSnapshot() {
157       if (requestPropertiesSnapshot == null) {
158         requestPropertiesSnapshot = Collections.unmodifiableMap(new HashMap<>(requestProperties));
159       }
160       return requestPropertiesSnapshot;
161     }
162 
163   }
164 
165   /**
166    * Base implementation of {@link Factory} that sets default request properties.
167    */
168   abstract class BaseFactory implements Factory {
169 
170     private final RequestProperties defaultRequestProperties;
171 
BaseFactory()172     public BaseFactory() {
173       defaultRequestProperties = new RequestProperties();
174     }
175 
176     @Override
createDataSource()177     public final HttpDataSource createDataSource() {
178       return createDataSourceInternal(defaultRequestProperties);
179     }
180 
181     @Override
getDefaultRequestProperties()182     public final RequestProperties getDefaultRequestProperties() {
183       return defaultRequestProperties;
184     }
185 
186     /** @deprecated Use {@link #getDefaultRequestProperties} instead. */
187     @Deprecated
188     @Override
setDefaultRequestProperty(String name, String value)189     public final void setDefaultRequestProperty(String name, String value) {
190       defaultRequestProperties.set(name, value);
191     }
192 
193     /** @deprecated Use {@link #getDefaultRequestProperties} instead. */
194     @Deprecated
195     @Override
clearDefaultRequestProperty(String name)196     public final void clearDefaultRequestProperty(String name) {
197       defaultRequestProperties.remove(name);
198     }
199 
200     /** @deprecated Use {@link #getDefaultRequestProperties} instead. */
201     @Deprecated
202     @Override
clearAllDefaultRequestProperties()203     public final void clearAllDefaultRequestProperties() {
204       defaultRequestProperties.clear();
205     }
206 
207     /**
208      * Called by {@link #createDataSource()} to create a {@link HttpDataSource} instance.
209      *
210      * @param defaultRequestProperties The default {@code RequestProperties} to be used by the
211      *     {@link HttpDataSource} instance.
212      * @return A {@link HttpDataSource} instance.
213      */
createDataSourceInternal(RequestProperties defaultRequestProperties)214     protected abstract HttpDataSource createDataSourceInternal(RequestProperties
215         defaultRequestProperties);
216 
217   }
218 
219   /** A {@link Predicate} that rejects content types often used for pay-walls. */
220   Predicate<String> REJECT_PAYWALL_TYPES =
221       contentType -> {
222         contentType = Util.toLowerInvariant(contentType);
223         return !TextUtils.isEmpty(contentType)
224             && (!contentType.contains("text") || contentType.contains("text/vtt"))
225             && !contentType.contains("html")
226             && !contentType.contains("xml");
227       };
228 
229   /**
230    * Thrown when an error is encountered when trying to read from a {@link HttpDataSource}.
231    */
232   class HttpDataSourceException extends IOException {
233 
234     @Documented
235     @Retention(RetentionPolicy.SOURCE)
236     @IntDef({TYPE_OPEN, TYPE_READ, TYPE_CLOSE})
237     public @interface Type {}
238 
239     public static final int TYPE_OPEN = 1;
240     public static final int TYPE_READ = 2;
241     public static final int TYPE_CLOSE = 3;
242 
243     @Type public final int type;
244 
245     /**
246      * The {@link DataSpec} associated with the current connection.
247      */
248     public final DataSpec dataSpec;
249 
HttpDataSourceException(DataSpec dataSpec, @Type int type)250     public HttpDataSourceException(DataSpec dataSpec, @Type int type) {
251       super();
252       this.dataSpec = dataSpec;
253       this.type = type;
254     }
255 
HttpDataSourceException(String message, DataSpec dataSpec, @Type int type)256     public HttpDataSourceException(String message, DataSpec dataSpec, @Type int type) {
257       super(message);
258       this.dataSpec = dataSpec;
259       this.type = type;
260     }
261 
HttpDataSourceException(IOException cause, DataSpec dataSpec, @Type int type)262     public HttpDataSourceException(IOException cause, DataSpec dataSpec, @Type int type) {
263       super(cause);
264       this.dataSpec = dataSpec;
265       this.type = type;
266     }
267 
HttpDataSourceException(String message, IOException cause, DataSpec dataSpec, @Type int type)268     public HttpDataSourceException(String message, IOException cause, DataSpec dataSpec,
269         @Type int type) {
270       super(message, cause);
271       this.dataSpec = dataSpec;
272       this.type = type;
273     }
274 
275   }
276 
277   /**
278    * Thrown when the content type is invalid.
279    */
280   final class InvalidContentTypeException extends HttpDataSourceException {
281 
282     public final String contentType;
283 
InvalidContentTypeException(String contentType, DataSpec dataSpec)284     public InvalidContentTypeException(String contentType, DataSpec dataSpec) {
285       super("Invalid content type: " + contentType, dataSpec, TYPE_OPEN);
286       this.contentType = contentType;
287     }
288 
289   }
290 
291   /**
292    * Thrown when an attempt to open a connection results in a response code not in the 2xx range.
293    */
294   final class InvalidResponseCodeException extends HttpDataSourceException {
295 
296     /**
297      * The response code that was outside of the 2xx range.
298      */
299     public final int responseCode;
300 
301     /** The http status message. */
302     @Nullable public final String responseMessage;
303 
304     /**
305      * An unmodifiable map of the response header fields and values.
306      */
307     public final Map<String, List<String>> headerFields;
308 
309     /** @deprecated Use {@link #InvalidResponseCodeException(int, String, Map, DataSpec)}. */
310     @Deprecated
InvalidResponseCodeException( int responseCode, Map<String, List<String>> headerFields, DataSpec dataSpec)311     public InvalidResponseCodeException(
312         int responseCode, Map<String, List<String>> headerFields, DataSpec dataSpec) {
313       this(responseCode, /* responseMessage= */ null, headerFields, dataSpec);
314     }
315 
InvalidResponseCodeException( int responseCode, @Nullable String responseMessage, Map<String, List<String>> headerFields, DataSpec dataSpec)316     public InvalidResponseCodeException(
317         int responseCode,
318         @Nullable String responseMessage,
319         Map<String, List<String>> headerFields,
320         DataSpec dataSpec) {
321       super("Response code: " + responseCode, dataSpec, TYPE_OPEN);
322       this.responseCode = responseCode;
323       this.responseMessage = responseMessage;
324       this.headerFields = headerFields;
325     }
326 
327   }
328 
329   /**
330    * Opens the source to read the specified data.
331    *
332    * <p>Note: {@link HttpDataSource} implementations are advised to set request headers passed via
333    * (in order of decreasing priority) the {@code dataSpec}, {@link #setRequestProperty} and the
334    * default parameters set in the {@link Factory}.
335    */
336   @Override
open(DataSpec dataSpec)337   long open(DataSpec dataSpec) throws HttpDataSourceException;
338 
339   @Override
close()340   void close() throws HttpDataSourceException;
341 
342   @Override
read(byte[] buffer, int offset, int readLength)343   int read(byte[] buffer, int offset, int readLength) throws HttpDataSourceException;
344 
345   /**
346    * Sets the value of a request header. The value will be used for subsequent connections
347    * established by the source.
348    *
349    * <p>Note: If the same header is set as a default parameter in the {@link Factory}, then the
350    * header value set with this method should be preferred when connecting with the data source. See
351    * {@link #open}.
352    *
353    * @param name The name of the header field.
354    * @param value The value of the field.
355    */
setRequestProperty(String name, String value)356   void setRequestProperty(String name, String value);
357 
358   /**
359    * Clears the value of a request header. The change will apply to subsequent connections
360    * established by the source.
361    *
362    * @param name The name of the header field.
363    */
clearRequestProperty(String name)364   void clearRequestProperty(String name);
365 
366   /**
367    * Clears all request headers that were set by {@link #setRequestProperty(String, String)}.
368    */
clearAllRequestProperties()369   void clearAllRequestProperties();
370 
371   /**
372    * When the source is open, returns the HTTP response status code associated with the last {@link
373    * #open} call. Otherwise, returns a negative value.
374    */
getResponseCode()375   int getResponseCode();
376 
377   @Override
getResponseHeaders()378   Map<String, List<String>> getResponseHeaders();
379 }
380