com.bt.download.android.core.HttpFetcher.java Source code

Java tutorial

Introduction

Here is the source code for com.bt.download.android.core.HttpFetcher.java

Source

/*
 * Created by Angel Leon (@gubatron), Alden Torres (aldenml)
 * Copyright (c) 2011, 2012, FrostWire(TM). All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.bt.download.android.core;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.GZIPInputStream;

import org.apache.commons.io.IOUtils;
import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseInterceptor;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.params.ConnManagerPNames;
import org.apache.http.conn.params.ConnPerRouteBean;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.HttpEntityWrapper;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.impl.cookie.DateUtils;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import android.util.Log;

import com.frostwire.util.UserAgentGenerator;

/**
 * A Blocking HttpClient.
 * Use fetch() to retrieve the byte[]
 * 
 * @author gubatron
 * @author aldenml
 *
 */
public class HttpFetcher {

    private static final String TAG = "FW.HttpFetcher";

    private static final int HTTP_RETRY_COUNT = 4;

    private static final String DEFAULT_USER_AGENT = UserAgentGenerator.getUserAgent();
    private static final int DEFAULT_TIMEOUT = 5000;

    private static HttpClient DEFAULT_HTTP_CLIENT;
    private static HttpClient DEFAULT_HTTP_CLIENT_GZIP;

    private final URI uri;
    private final String userAgent;
    private final int timeout;

    private byte[] body = null;

    static {
        setupHttpClients();
    }

    public HttpFetcher(URI uri, String userAgent, int timeout) {
        this.uri = uri;
        this.userAgent = userAgent;
        this.timeout = timeout;
    }

    public HttpFetcher(URI uri, String userAgent) {
        this(uri, userAgent, DEFAULT_TIMEOUT);
    }

    public HttpFetcher(URI uri, int timeout) {
        this(uri, DEFAULT_USER_AGENT, timeout);
    }

    public HttpFetcher(URI uri) {
        this(uri, DEFAULT_USER_AGENT);
    }

    public HttpFetcher(String uri) {
        this(convert(uri));
    }

    public HttpFetcher(String uri, int timeout) {
        this(convert(uri), timeout);
    }

    public Object[] fetch(boolean gzip) throws IOException {
        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort());
        HttpGet httpGet = new HttpGet(uri);
        httpGet.addHeader("Connection", "close");

        HttpParams params = httpGet.getParams();
        HttpConnectionParams.setConnectionTimeout(params, timeout);
        HttpConnectionParams.setSoTimeout(params, timeout);
        HttpConnectionParams.setStaleCheckingEnabled(params, true);
        HttpConnectionParams.setTcpNoDelay(params, true);
        HttpClientParams.setRedirecting(params, true);
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpProtocolParams.setUserAgent(params, userAgent);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {

            HttpResponse response = (gzip ? DEFAULT_HTTP_CLIENT_GZIP : DEFAULT_HTTP_CLIENT).execute(httpHost,
                    httpGet);

            if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() >= 300) {
                throw new IOException(
                        "bad status code, downloading file " + response.getStatusLine().getStatusCode());
            }

            Long date = Long.valueOf(0);

            Header[] headers = response.getAllHeaders();
            for (int i = 0; i < headers.length; i++) {
                if (headers[i].getName().startsWith("Last-Modified")) {
                    try {
                        date = DateUtils.parseDate(headers[i].getValue()).getTime();
                    } catch (Exception e) {
                    }
                    break;
                }
            }

            if (response.getEntity() != null) {
                if (gzip) {
                    String str = EntityUtils.toString(response.getEntity());
                    baos.write(str.getBytes());
                } else {
                    response.getEntity().writeTo(baos);
                }
            }

            body = baos.toByteArray();

            if (body == null || body.length == 0) {
                throw new IOException("invalid response");
            }

            return new Object[] { body, date };

        } finally {
            try {
                baos.close();
            } catch (IOException e) {
            }
        }
    }

    public byte[] fetch() {
        Object[] objArray = null;
        try {
            objArray = fetch(false);
        } catch (Throwable e) {
            Log.e(TAG, "Error performing http to " + uri + ", e: " + e.getMessage());
        }

        return objArray != null ? (byte[]) objArray[0] : null;
    }

    public byte[] fetchGzip() {
        Object[] objArray = null;
        try {
            objArray = fetch(true);
        } catch (Throwable e) {
            Log.e(TAG, "Error performing http to " + uri + ", e: " + e.getMessage());
        }

        return objArray != null ? (byte[]) objArray[0] : null;
    }

    public void save(File file) throws IOException {
        save(file, null);
    }

    public void save(File file, HttpFetcherListener listener) throws IOException {
        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort());
        HttpGet httpGet = new HttpGet(uri);
        httpGet.addHeader("Connection", "close");

        HttpParams params = httpGet.getParams();
        HttpConnectionParams.setConnectionTimeout(params, timeout);
        HttpConnectionParams.setSoTimeout(params, timeout);
        HttpConnectionParams.setStaleCheckingEnabled(params, true);
        HttpConnectionParams.setTcpNoDelay(params, true);
        HttpClientParams.setRedirecting(params, true);
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpProtocolParams.setUserAgent(params, userAgent);

        FileOutputStream out = null;

        int statusCode = -1;
        Map<String, String> headers = null;

        try {

            out = new FileOutputStream(file);

            HttpResponse response = DEFAULT_HTTP_CLIENT.execute(httpHost, httpGet);

            statusCode = response.getStatusLine().getStatusCode();
            headers = mapHeaders(response.headerIterator());

            if (statusCode < 200 || statusCode >= 300) {
                throw new IOException("bad status code, downloading file " + statusCode);
            }

            if (response.getEntity() != null) {
                writeEntity(response.getEntity(), out, listener);
            }

            if (listener != null) {
                listener.onSuccess(new byte[0]);
            }

        } catch (Throwable e) {
            if (listener != null) {
                listener.onError(e, statusCode, headers);
            }
            Log.e(TAG, "Error downloading from: " + uri + ", e: " + e.getMessage());
        } finally {
            IOUtils.closeQuietly(out);
        }
    }

    public byte[] post(String postBody, String contentType) throws IOException {
        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort());
        HttpPost httpPost = new HttpPost(uri);

        StringEntity stringEntity = new StringEntity(postBody);
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setEntity(stringEntity);

        HttpParams params = httpPost.getParams();
        HttpConnectionParams.setConnectionTimeout(params, timeout);
        HttpConnectionParams.setSoTimeout(params, timeout);
        HttpConnectionParams.setStaleCheckingEnabled(params, true);
        HttpConnectionParams.setTcpNoDelay(params, true);
        HttpClientParams.setRedirecting(params, true);
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpProtocolParams.setUserAgent(params, DEFAULT_USER_AGENT);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        try {

            HttpResponse response = DEFAULT_HTTP_CLIENT.execute(httpHost, httpPost);

            if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() >= 300)
                throw new IOException("bad status code, upload file " + response.getStatusLine().getStatusCode());

            if (response.getEntity() != null) {
                response.getEntity().writeTo(baos);
            }

            body = baos.toByteArray();

            if (body == null || body.length == 0) {
                throw new IOException("invalid response");
            }

            return body;
        } catch (IOException e) {
            throw e;
        } catch (Exception e) {
            throw new IOException("Http error: " + e.getMessage());
        } finally {
            try {
                baos.close();
            } catch (IOException e) {
            }
        }
    }

    public void post(File file) throws IOException {
        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort());
        HttpPost httpPost = new HttpPost(uri);
        FileEntity fileEntity = new FileEntity(file, "binary/octet-stream");
        fileEntity.setChunked(true);
        httpPost.setEntity(fileEntity);

        HttpParams params = httpPost.getParams();
        HttpConnectionParams.setConnectionTimeout(params, timeout);
        HttpConnectionParams.setSoTimeout(params, timeout);
        HttpConnectionParams.setStaleCheckingEnabled(params, true);
        HttpConnectionParams.setTcpNoDelay(params, true);
        HttpClientParams.setRedirecting(params, true);
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpProtocolParams.setUserAgent(params, DEFAULT_USER_AGENT);

        try {

            HttpResponse response = DEFAULT_HTTP_CLIENT.execute(httpHost, httpPost);

            if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() >= 300)
                throw new IOException("bad status code, upload file " + response.getStatusLine().getStatusCode());

        } catch (IOException e) {
            throw e;
        } catch (Exception e) {
            throw new IOException("Http error: " + e.getMessage());
        } finally {
            //
        }
    }

    public void post(FileEntity fileEntity) throws IOException {
        HttpHost httpHost = new HttpHost(uri.getHost(), uri.getPort());
        HttpPost httpPost = new HttpPost(uri);
        httpPost.setEntity(fileEntity);

        HttpParams params = httpPost.getParams();
        HttpConnectionParams.setConnectionTimeout(params, timeout);
        HttpConnectionParams.setSoTimeout(params, timeout);
        HttpConnectionParams.setStaleCheckingEnabled(params, true);
        HttpConnectionParams.setTcpNoDelay(params, true);
        HttpClientParams.setRedirecting(params, true);
        HttpProtocolParams.setUseExpectContinue(params, false);
        HttpProtocolParams.setUserAgent(params, DEFAULT_USER_AGENT);

        try {

            HttpResponse response = DEFAULT_HTTP_CLIENT.execute(httpHost, httpPost);

            if (response.getStatusLine().getStatusCode() < 200 || response.getStatusLine().getStatusCode() >= 300)
                throw new IOException("bad status code, upload file " + response.getStatusLine().getStatusCode());

        } catch (IOException e) {
            throw e;
        } catch (Exception e) {
            throw new IOException("Http error: " + e.getMessage());
        } finally {
            //
        }
    }

    private static void setupHttpClients() {
        DEFAULT_HTTP_CLIENT = setupHttpClient(false);
        DEFAULT_HTTP_CLIENT_GZIP = setupHttpClient(true);
    }

    private static HttpClient setupHttpClient(boolean gzip) {
        SSLSocketFactory.getSocketFactory().setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        SchemeRegistry schemeRegistry = new SchemeRegistry();
        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
        schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        BasicHttpParams params = new BasicHttpParams();
        params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(20));
        params.setIntParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 200);
        ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);

        DefaultHttpClient httpClient = new DefaultHttpClient(cm, new BasicHttpParams());
        httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(HTTP_RETRY_COUNT, true));

        if (gzip) {
            httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
                public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
                    if (!request.containsHeader("Accept-Encoding")) {
                        request.addHeader("Accept-Encoding", "gzip");
                    }
                }
            });

            httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
                public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
                    HttpEntity entity = response.getEntity();
                    Header ceheader = entity.getContentEncoding();
                    if (ceheader != null) {
                        HeaderElement[] codecs = ceheader.getElements();
                        for (int i = 0; i < codecs.length; i++) {
                            if (codecs[i].getName().equalsIgnoreCase("gzip")) {
                                response.setEntity(new GzipDecompressingEntity(response.getEntity()));
                                return;
                            }
                        }
                    }
                }
            });
        }

        return httpClient;
    }

    private static void writeEntity(final HttpEntity entity, OutputStream out, HttpFetcherListener listener)
            throws IOException {
        if (entity == null) {
            throw new IllegalArgumentException("HTTP entity may not be null");
        }
        InputStream in = entity.getContent();
        if (in == null) {
            return;
        }
        int i = (int) entity.getContentLength();
        if (i < 0) {
            i = 4096;
        }
        try {
            byte[] data = new byte[4096];
            int n;
            while ((n = in.read(data)) != -1) {
                if (listener != null) {
                    listener.onData(data, n);
                }
                out.write(data, 0, n);
                out.flush();
            }
        } finally {
            in.close();
        }
    }

    private static URI convert(String uri) {
        try {
            return new URI(uri);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException(e);
        }
    }

    private static Map<String, String> mapHeaders(HeaderIterator it) {
        Map<String, String> map = new HashMap<String, String>();

        while (it.hasNext()) {
            Header h = it.nextHeader();
            map.put(h.getName(), h.getValue());
        }

        return map;
    }

    private static final class GzipDecompressingEntity extends HttpEntityWrapper {

        public GzipDecompressingEntity(final HttpEntity entity) {
            super(entity);
        }

        @Override
        public InputStream getContent() throws IOException, IllegalStateException {
            return new GZIPInputStream(wrappedEntity.getContent());
        }

        @Override
        public long getContentLength() {
            return -1;
        }
    }
}