nya.miku.wishmaster.http.streamer.HttpStreamer.java Source code

Java tutorial

Introduction

Here is the source code for nya.miku.wishmaster.http.streamer.HttpStreamer.java

Source

/*
 * Overchan Android (Meta Imageboard Client)
 * Copyright (C) 2014-2015  miku-nyan <https://github.com/miku-nyan>
 *     
 * 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 nya.miku.wishmaster.http.streamer;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.HashMap;

import nya.miku.wishmaster.api.interfaces.CancellableTask;
import nya.miku.wishmaster.api.interfaces.ProgressListener;
import nya.miku.wishmaster.common.IOUtils;
import nya.miku.wishmaster.common.Logger;
import nya.miku.wishmaster.http.client.ExtendedHttpClient;
import nya.miku.wishmaster.lib.org_json.JSONArray;
import nya.miku.wishmaster.lib.org_json.JSONException;
import nya.miku.wishmaster.lib.org_json.JSONObject;
import nya.miku.wishmaster.lib.org_json.JSONTokener;

import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.message.BasicHeader;

/**
 *  HTTP ?.
 * @author miku-nyan
 *
 */

/* Google  ? ??  ?  org.apache.http  "deprecated"  API 22 (Android 5.1)
 * ? ?  ??? ? ?? apache-hc httpclient 4.3.5.1-android
 * ?: https://issues.apache.org/jira/browse/HTTPCLIENT-1632 */
@SuppressWarnings("deprecation")

public class HttpStreamer {
    private static final String TAG = "HttpStreamer";

    private HttpStreamer() {
    }

    private static HttpStreamer instance = null;

    /**
     * ??  {@link android.app.Application#onCreate()}
     */
    public static void initInstance() {
        if (instance == null)
            instance = new HttpStreamer();
    }

    public static HttpStreamer getInstance() {
        if (instance == null) { //should never happens
            Logger.e(TAG, "HttpStreamer is not initialized");
            initInstance();
        }
        return instance;
    }

    /**  ?  If-Modified-Since */
    private final HashMap<String, String> ifModifiedMap = new HashMap<String, String>();

    /**
     *  url    ? If-Modified-Since
     * @param url
     * @return   ?,  NULL, ?  ?    
     */
    public String removeFromModifiedMap(String url) {
        String value = null;
        synchronized (ifModifiedMap) {
            value = ifModifiedMap.remove(url);
        }
        return value;
    }

    /**
     * HTTP ?  ?. ? ?  ? ?,    release()  !
     * ?   ? ?? ? ?  If-Modified-Since,  ?      
     *  ? ?? ?    Modified ({@link #removeFromModifiedMap(String)})!
     * @param url ? ?
     * @param requestModel  ? (  null,   GET   If-Modified)
     * @param httpClient HTTP , ?? ?
     * @param listener ? ?? ?? (  null)
     * @param task ,     (  null)
     * @return  ?     HTTP 
     * @throws HttpRequestException ?, ?  ?  ?
     */
    public HttpResponseModel getFromUrl(String url, HttpRequestModel requestModel, HttpClient httpClient,
            ProgressListener listener, CancellableTask task) throws HttpRequestException {
        if (requestModel == null)
            requestModel = HttpRequestModel.builder().setGET().build();

        // Request
        HttpUriRequest request = null;
        try {
            RequestConfig requestConfigBuilder = ExtendedHttpClient
                    .getDefaultRequestConfigBuilder(requestModel.timeoutValue)
                    .setRedirectsEnabled(!requestModel.noRedirect).build();

            RequestBuilder requestBuilder = null;
            switch (requestModel.method) {
            case HttpRequestModel.METHOD_GET:
                requestBuilder = RequestBuilder.get().setUri(url);
                break;
            case HttpRequestModel.METHOD_POST:
                requestBuilder = RequestBuilder.post().setUri(url).setEntity(requestModel.postEntity);
                break;
            default:
                throw new IllegalArgumentException("Incorrect type of HTTP Request");
            }
            if (requestModel.customHeaders != null) {
                for (Header header : requestModel.customHeaders) {
                    requestBuilder.addHeader(header);
                }
            }
            if (requestModel.checkIfModified && requestModel.method == HttpRequestModel.METHOD_GET) {
                synchronized (ifModifiedMap) {
                    if (ifModifiedMap.containsKey(url)) {
                        requestBuilder
                                .addHeader(new BasicHeader(HttpHeaders.IF_MODIFIED_SINCE, ifModifiedMap.get(url)));
                    }
                }
            }
            request = requestBuilder.setConfig(requestConfigBuilder).build();
        } catch (Exception e) {
            Logger.e(TAG, e);
            HttpResponseModel.release(request, null);
            throw new IllegalArgumentException(e);
        }
        // ?
        HttpResponseModel responseModel = new HttpResponseModel();
        HttpResponse response = null;
        try {
            IOException responseException = null;
            for (int i = 0; i < 5; ++i) {
                try {
                    if (task != null && task.isCancelled())
                        throw new InterruptedException();
                    response = httpClient.execute(request);
                    responseException = null;
                    break;
                } catch (IOException e) {
                    Logger.e(TAG, e);
                    responseException = e;
                    if (e.getMessage() == null)
                        break;
                    if (e.getMessage().indexOf("Connection reset by peer") != -1
                            || e.getMessage().indexOf("I/O error during system call, Broken pipe") != -1) {
                        continue;
                    } else {
                        break;
                    }
                }
            }
            if (responseException != null) {
                throw responseException;
            }
            if (task != null && task.isCancelled())
                throw new InterruptedException();
            //  ???? HTTP
            StatusLine status = response.getStatusLine();
            responseModel.statusCode = status.getStatusCode();
            responseModel.statusReason = status.getReasonPhrase();
            //   (headers)
            String lastModifiedValue = null;
            if (responseModel.statusCode == 200) {
                Header header = response.getFirstHeader(HttpHeaders.LAST_MODIFIED);
                if (header != null)
                    lastModifiedValue = header.getValue();
            }
            Header header = response.getFirstHeader(HttpHeaders.LOCATION);
            if (header != null)
                responseModel.locationHeader = header.getValue();
            responseModel.headers = response.getAllHeaders();
            //
            HttpEntity responseEntity = response.getEntity();
            if (responseEntity != null) {
                responseModel.contentLength = responseEntity.getContentLength();
                if (listener != null)
                    listener.setMaxValue(responseModel.contentLength);
                InputStream stream = responseEntity.getContent();
                responseModel.stream = IOUtils.modifyInputStream(stream, listener, task);
            }
            responseModel.request = request;
            responseModel.response = response;
            if (lastModifiedValue != null) {
                synchronized (ifModifiedMap) {
                    ifModifiedMap.put(url, lastModifiedValue);
                }
            }
        } catch (Exception e) {
            Logger.e(TAG, e);
            HttpResponseModel.release(request, response);
            throw new HttpRequestException(e);
        }

        return responseModel;
    }

    /**
     * HTTP ?  ?,  ?? 
     * @param url ? ?
     * @param requestModel  ? (  null,   GET   If-Modified)
     * @param httpClient HTTP , ?? ?
     * @param listener ? ?? ?? (  null)
     * @param task ,     (  null)
     * @param anyCode  ?,  ? ?   HTTP 200, , html ? 
     * (   ? {@link HttpWrongStatusCodeException})
     * @return ??  ,  NULL, ? ?    (HTTP 304)
     * @throws IOException  /,  .. ?     (?? {@link IOUtils.InterruptedStreamException})
     * @throws HttpRequestException ?  ?  ?,  ? ??
     * @throws HttpWrongStatusCodeException ? ?    200.  anycode==true  ?  HTML ? .
     */
    public byte[] getBytesFromUrl(String url, HttpRequestModel requestModel, HttpClient httpClient,
            ProgressListener listener, CancellableTask task, boolean anyCode)
            throws IOException, HttpRequestException, HttpWrongStatusCodeException {
        HttpResponseModel responseModel = null;
        try {
            responseModel = getFromUrl(url, requestModel, httpClient, listener, task);
            if (responseModel.statusCode == 200) {
                if (responseModel.stream == null)
                    throw new HttpRequestException(new NullPointerException());
                ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
                IOUtils.copyStream(responseModel.stream, output);
                return output.toByteArray();
            } else {
                if (responseModel.notModified())
                    return null;
                if (anyCode) {
                    byte[] html = null;
                    try {
                        ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
                        IOUtils.copyStream(responseModel.stream, output);
                        html = output.toByteArray();
                    } catch (Exception e) {
                        Logger.e(TAG, e);
                    }
                    throw new HttpWrongStatusCodeException(responseModel.statusCode,
                            responseModel.statusCode + " - " + responseModel.statusReason, html);
                } else {
                    throw new HttpWrongStatusCodeException(responseModel.statusCode,
                            responseModel.statusCode + " - " + responseModel.statusReason);
                }

            }
        } catch (Exception e) {
            if (responseModel != null)
                removeFromModifiedMap(url);
            // (responseModel != null) <=> ?   ? ?,    ? ? ? (.. ?  ?   ?)
            throw e;
        } finally {
            if (responseModel != null)
                responseModel.release();
        }
    }

    /**
     * HTTP ?  ?,  ?
     * @param url ? ?
     * @param requestModel  ? (  null,   GET   If-Modified)
     * @param httpClient HTTP , ?? ?
     * @param listener ? ?? ?? (  null)
     * @param task ,     (  null)
     * @param anyCode  ?,  ? ?   HTTP 200, , html ? 
     * (   ? {@link HttpWrongStatusCodeException})
     * @return ? ?,  NULL, ? ?    (HTTP 304)
     * @throws IOException  /,  .. ?     (?? {@link IOUtils.InterruptedStreamException})
     * @throws HttpRequestException ?  ?  ?,  ? ??
     * @throws HttpWrongStatusCodeException ? ?    200.  anycode==true  ?  HTML ? . 
     */
    public String getStringFromUrl(String url, HttpRequestModel requestModel, HttpClient httpClient,
            ProgressListener listener, CancellableTask task, boolean anyCode)
            throws IOException, HttpRequestException, HttpWrongStatusCodeException {
        byte[] bytes = getBytesFromUrl(url, requestModel, httpClient, listener, task, anyCode);
        if (bytes == null)
            return null;
        return new String(bytes);
    }

    /**
     * HTTP ?  ?,   JSON ({@link nya.miku.wishmaster.lib.org_json.JSONObject} ? ?? org.json)
     * @param url ? ?
     * @param requestModel  ? (  null,   GET   If-Modified)
     * @param httpClient HTTP , ?? ?
     * @param listener ? ?? ?? (  null)
     * @param task ,     (  null)
     * @param anyCode  ?,  ? ?   HTTP 200, , html ? 
     * (   ? {@link HttpWrongStatusCodeException})
     * @return  {@link JSONObject},  NULL, ? ?    (HTTP 304)
     * @throws IOException  /,  .. ?     (?? {@link IOUtils.InterruptedStreamException})
     * @throws HttpRequestException ?  ?  ?,  ? ??
     * @throws HttpWrongStatusCodeException ? ?    200.  anycode==true  ?  HTML ? .
     * @throws JSONException  ?   ? JSON
     */
    public JSONObject getJSONObjectFromUrl(String url, HttpRequestModel requestModel, HttpClient httpClient,
            ProgressListener listener, CancellableTask task, boolean anyCode)
            throws IOException, HttpRequestException, HttpWrongStatusCodeException, JSONException {
        return (JSONObject) getJSONFromUrl(url, requestModel, httpClient, listener, task, anyCode, false);
    }

    /**
     * HTTP ?  ?,  ?? JSON ({@link nya.miku.wishmaster.lib.org_json.JSONArray} ? ?? org.json.JSONArray)
     * @param url ? ?
     * @param requestModel  ? (  null,   GET   If-Modified)
     * @param httpClient HTTP , ?? ?
     * @param listener ? ?? ?? (  null)
     * @param task ,     (  null)
     * @param anyCode  ?,  ? ?   HTTP 200, , html ? 
     * (   ? {@link HttpWrongStatusCodeException})
     * @return  {@link JSONArray},  NULL, ? ?    (HTTP 304)
     * @throws IOException  /,  .. ?     (?? {@link IOUtils.InterruptedStreamException})
     * @throws HttpRequestException ?  ?  ?,  ? ??
     * @throws HttpWrongStatusCodeException ? ?    200.  anycode==true  ?  HTML ? .
     * @throws JSONException  ?   ? JSON
     */
    public JSONArray getJSONArrayFromUrl(String url, HttpRequestModel requestModel, HttpClient httpClient,
            ProgressListener listener, CancellableTask task, boolean anyCode)
            throws IOException, HttpRequestException, HttpWrongStatusCodeException, JSONException {
        return (JSONArray) getJSONFromUrl(url, requestModel, httpClient, listener, task, anyCode, true);
    }

    private Object getJSONFromUrl(String url, HttpRequestModel requestModel, HttpClient httpClient,
            ProgressListener listener, CancellableTask task, boolean anyCode, boolean isArray)
            throws IOException, HttpRequestException, HttpWrongStatusCodeException, JSONException {
        HttpResponseModel responseModel = null;
        BufferedReader in = null;
        try {
            responseModel = getFromUrl(url, requestModel, httpClient, listener, task);
            if (responseModel.statusCode == 200) {
                if (responseModel.stream == null)
                    throw new HttpRequestException(new NullPointerException());
                in = new BufferedReader(new InputStreamReader(responseModel.stream));
                return isArray ? new JSONArray(new JSONTokener(in)) : new JSONObject(new JSONTokener(in));
            } else {
                if (responseModel.notModified())
                    return null;
                if (anyCode) {
                    byte[] html = null;
                    try {
                        ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
                        IOUtils.copyStream(responseModel.stream, output);
                        html = output.toByteArray();
                    } catch (Exception e) {
                        Logger.e(TAG, e);
                    }
                    throw new HttpWrongStatusCodeException(responseModel.statusCode,
                            responseModel.statusCode + " - " + responseModel.statusReason, html);
                } else {
                    throw new HttpWrongStatusCodeException(responseModel.statusCode,
                            responseModel.statusCode + " - " + responseModel.statusReason);
                }

            }
        } catch (Exception e) {
            if (responseModel != null)
                removeFromModifiedMap(url);
            throw e;
        } finally {
            IOUtils.closeQuietly(in);
            if (responseModel != null)
                responseModel.release();
        }
    }

    /**
     *   (HTTP)   ? URL, ?   out.
     * @param url ?
     * @param out  
     * @param requestModel  ? (  null,   GET   If-Modified)
     * @param httpClient HTTP , ?? ?
     * @param listener ? ?? ?? (  null)
     * @param task ,     (  null)
     * @param anyCode  ?,  ? ?   HTTP 200, , html ? 
     * (   ? {@link HttpWrongStatusCodeException})
     * @throws IOException  /,  .. ?     (?? {@link IOUtils.InterruptedStreamException})
     * @throws HttpRequestException ?  ?  ?,  ? ??
     * @throws HttpWrongStatusCodeException ? ?    200.  anycode==true  ?  HTML ? .
     */
    public void downloadFileFromUrl(String url, OutputStream out, HttpRequestModel requestModel,
            HttpClient httpClient, ProgressListener listener, CancellableTask task, boolean anyCode)
            throws IOException, HttpRequestException, HttpWrongStatusCodeException {
        HttpResponseModel responseModel = null;
        try {
            responseModel = getFromUrl(url, requestModel, httpClient, listener, task);
            if (responseModel.statusCode == 200) {
                IOUtils.copyStream(responseModel.stream, out);
            } else {
                if (anyCode) {
                    byte[] html = null;
                    try {
                        ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);
                        IOUtils.copyStream(responseModel.stream, byteStream);
                        html = byteStream.toByteArray();
                    } catch (Exception e) {
                        Logger.e(TAG, e);
                    }
                    throw new HttpWrongStatusCodeException(responseModel.statusCode,
                            responseModel.statusCode + " - " + responseModel.statusReason, html);
                } else {
                    throw new HttpWrongStatusCodeException(responseModel.statusCode,
                            responseModel.statusCode + " - " + responseModel.statusReason);
                }
            }
        } catch (Exception e) {
            if (responseModel != null)
                removeFromModifiedMap(url);
            throw e;
        } finally {
            if (responseModel != null)
                responseModel.release();
        }
    }
}