Home | History | Annotate | Download | only in data
      1 package com.bumptech.glide.load.data;
      2 
      3 import android.text.TextUtils;
      4 
      5 import com.bumptech.glide.Priority;
      6 import com.bumptech.glide.load.model.GlideUrl;
      7 
      8 import java.io.IOException;
      9 import java.io.InputStream;
     10 import java.net.HttpURLConnection;
     11 import java.net.URISyntaxException;
     12 import java.net.URL;
     13 
     14 /**
     15  * A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
     16  */
     17 public class HttpUrlFetcher implements DataFetcher<InputStream> {
     18     private static final int MAXIMUM_REDIRECTS = 5;
     19     private static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();
     20 
     21     private final GlideUrl glideUrl;
     22     private final HttpUrlConnectionFactory connectionFactory;
     23 
     24     private HttpURLConnection urlConnection;
     25     private InputStream stream;
     26     private volatile boolean isCancelled;
     27 
     28     public HttpUrlFetcher(GlideUrl glideUrl) {
     29         this(glideUrl, DEFAULT_CONNECTION_FACTORY);
     30     }
     31 
     32     // Visible for testing.
     33     HttpUrlFetcher(GlideUrl glideUrl, HttpUrlConnectionFactory connectionFactory) {
     34         this.glideUrl = glideUrl;
     35         this.connectionFactory = connectionFactory;
     36     }
     37 
     38     @Override
     39     public InputStream loadData(Priority priority) throws Exception {
     40         return loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/);
     41     }
     42 
     43     private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl) throws IOException {
     44         if (redirects >= MAXIMUM_REDIRECTS) {
     45             throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
     46         } else {
     47             // Comparing the URLs using .equals performs additional network I/O and is generally broken.
     48             // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
     49             try {
     50                 if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
     51                     throw new IOException("In re-direct loop");
     52                 }
     53             } catch (URISyntaxException e) {
     54                 // Do nothing, this is best effort.
     55             }
     56         }
     57         urlConnection = connectionFactory.build(url);
     58         urlConnection.setConnectTimeout(2500);
     59         urlConnection.setReadTimeout(2500);
     60         urlConnection.setUseCaches(false);
     61         urlConnection.setDoInput(true);
     62 
     63         // Connect explicitly to avoid errors in decoders if connection fails.
     64         urlConnection.connect();
     65         if (isCancelled) {
     66             return null;
     67         }
     68         final int statusCode = urlConnection.getResponseCode();
     69         if (statusCode / 100 == 2) {
     70             stream = urlConnection.getInputStream();
     71             return stream;
     72         } else if (statusCode / 100 == 3) {
     73             String redirectUrlString = urlConnection.getHeaderField("Location");
     74             if (TextUtils.isEmpty(redirectUrlString)) {
     75                 throw new IOException("Received empty or null redirect url");
     76             }
     77             URL redirectUrl = new URL(url, redirectUrlString);
     78             return loadDataWithRedirects(redirectUrl, redirects + 1, url);
     79         } else {
     80             if (statusCode == -1) {
     81                 throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
     82             }
     83             throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
     84         }
     85     }
     86 
     87     @Override
     88     public void cleanup() {
     89         if (stream != null) {
     90             try {
     91                 stream.close();
     92             } catch (IOException e) {
     93                 // Ignore
     94             }
     95         }
     96         if (urlConnection != null) {
     97             urlConnection.disconnect();
     98         }
     99     }
    100 
    101     @Override
    102     public String getId() {
    103         return glideUrl.toString();
    104     }
    105 
    106     @Override
    107     public void cancel() {
    108         // TODO: we should consider disconnecting the url connection here, but we can't do so directly because cancel is
    109         // often called on the main thread.
    110         isCancelled = true;
    111     }
    112 
    113     interface HttpUrlConnectionFactory {
    114         HttpURLConnection build(URL url) throws IOException;
    115     }
    116 
    117     private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
    118         @Override
    119         public HttpURLConnection build(URL url) throws IOException {
    120             return (HttpURLConnection) url.openConnection();
    121         }
    122     }
    123 }
    124