![JAR search and dependency download from the Maven repository](/logo.png)
com.bumptech.glide.load.data.HttpUrlFetcher Maven / Gradle / Ivy
package com.bumptech.glide.load.data;
import android.text.TextUtils;
import android.util.Log;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.HttpException;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.util.ContentLengthInputStream;
import com.bumptech.glide.util.LogTime;
import com.bumptech.glide.util.Synthetic;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Map;
/**
* A DataFetcher that retrieves an {@link java.io.InputStream} for a Url.
*/
public class HttpUrlFetcher implements DataFetcher {
private static final String TAG = "HttpUrlFetcher";
private static final int MAXIMUM_REDIRECTS = 5;
// Visible for testing.
static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY =
new DefaultHttpUrlConnectionFactory();
private final GlideUrl glideUrl;
private final int timeout;
private final HttpUrlConnectionFactory connectionFactory;
private HttpURLConnection urlConnection;
private InputStream stream;
private volatile boolean isCancelled;
public HttpUrlFetcher(GlideUrl glideUrl, int timeout) {
this(glideUrl, timeout, DEFAULT_CONNECTION_FACTORY);
}
// Visible for testing.
HttpUrlFetcher(GlideUrl glideUrl, int timeout, HttpUrlConnectionFactory connectionFactory) {
this.glideUrl = glideUrl;
this.timeout = timeout;
this.connectionFactory = connectionFactory;
}
@Override
public void loadData(Priority priority, DataCallback super InputStream> callback) {
long startTime = LogTime.getLogTime();
final InputStream result;
try {
result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
glideUrl.getHeaders());
} catch (IOException e) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Failed to load data for url", e);
}
callback.onLoadFailed(e);
return;
}
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
+ " ms and loaded " + result);
}
callback.onDataReady(result);
}
private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
Map headers) throws IOException {
if (redirects >= MAXIMUM_REDIRECTS) {
throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
} else {
// Comparing the URLs using .equals performs additional network I/O and is generally broken.
// See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
try {
if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
throw new HttpException("In re-direct loop");
}
} catch (URISyntaxException e) {
// Do nothing, this is best effort.
}
}
urlConnection = connectionFactory.build(url);
for (Map.Entry headerEntry : headers.entrySet()) {
urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
}
urlConnection.setConnectTimeout(timeout);
urlConnection.setReadTimeout(timeout);
urlConnection.setUseCaches(false);
urlConnection.setDoInput(true);
// Stop the urlConnection instance of HttpUrlConnection from following redirects so that
// redirects will be handled by recursive calls to this method, loadDataWithRedirects.
urlConnection.setInstanceFollowRedirects(false);
// Connect explicitly to avoid errors in decoders if connection fails.
urlConnection.connect();
if (isCancelled) {
return null;
}
final int statusCode = urlConnection.getResponseCode();
if (statusCode / 100 == 2) {
return getStreamForSuccessfulRequest(urlConnection);
} else if (statusCode / 100 == 3) {
String redirectUrlString = urlConnection.getHeaderField("Location");
if (TextUtils.isEmpty(redirectUrlString)) {
throw new HttpException("Received empty or null redirect url");
}
URL redirectUrl = new URL(url, redirectUrlString);
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
} else if (statusCode == -1) {
throw new HttpException(statusCode);
} else {
throw new HttpException(urlConnection.getResponseMessage(), statusCode);
}
}
private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
throws IOException {
if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
int contentLength = urlConnection.getContentLength();
stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
} else {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
}
stream = urlConnection.getInputStream();
}
return stream;
}
@Override
public void cleanup() {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
// Ignore
}
}
if (urlConnection != null) {
urlConnection.disconnect();
}
}
@Override
public void cancel() {
// TODO: we should consider disconnecting the url connection here, but we can't do so
// directly because cancel is often called on the main thread.
isCancelled = true;
}
@Override
public Class getDataClass() {
return InputStream.class;
}
@Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
interface HttpUrlConnectionFactory {
HttpURLConnection build(URL url) throws IOException;
}
private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
@Synthetic
DefaultHttpUrlConnectionFactory() { }
@Override
public HttpURLConnection build(URL url) throws IOException {
return (HttpURLConnection) url.openConnection();
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy