com.none.tom.simplerssreader.net.FeedDownloader.java Source code

Java tutorial

Introduction

Here is the source code for com.none.tom.simplerssreader.net.FeedDownloader.java

Source

// Copyright (c) 2017-2018, Tom Geiselmann
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge, publish, distribute,
// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY,WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

package com.none.tom.simplerssreader.net;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;

import com.none.tom.simplerssreader.utils.DefaultSharedPrefUtils;
import com.none.tom.simplerssreader.utils.ErrorHandler;
import com.none.tom.simplerssreader.utils.LogUtils;
import com.none.tom.simplerssreader.utils.SharedPrefUtils;

import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;

import static com.none.tom.simplerssreader.utils.Constants.CURRENT_FEED_SIZE_LIMIT;

public class FeedDownloader {
    private static final String HTTP_PROXY_TOR_IP = "127.0.0.1";

    private static final int HTTP_TEMP_REDIRECT = 307;
    private static final int HTTP_URL_DEFAULT_TIMEOUT = 20000;
    private static final int HTTP_NR_REDIRECTS_MAX = 10;

    private static final short HTTP_PROXY_TOR_PORT = 8118;

    private static InputStream getInputStream(final InputStream in) throws IOException {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();

        if (IOUtils.copy(in, out) > CURRENT_FEED_SIZE_LIMIT) {
            ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_FILE_SIZE);
            LogUtils.logError("Feed size limit exceeded" + '(' + CURRENT_FEED_SIZE_LIMIT + ')');
            return null;
        }

        in.close();

        return new ByteArrayInputStream(out.toByteArray());
    }

    @SuppressWarnings("ConstantConditions")
    public static InputStream getInputStream(final Context context, final String feedUrl) {
        final ConnectivityManager manager = context.getSystemService(ConnectivityManager.class);
        final NetworkInfo info = manager.getActiveNetworkInfo();

        if (info == null || !info.isConnected()) {
            ErrorHandler.setErrno(ErrorHandler.ERROR_NETWORK);
            LogUtils.logError("No network connection");
            return null;
        }

        URL url;
        try {
            url = new URL(feedUrl);
        } catch (final MalformedURLException e) {
            ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_BAD_REQUEST, e);
            return null;
        }

        final boolean[] networkPrefs = DefaultSharedPrefUtils.getNetworkPreferences(context);
        final boolean useHttpTorProxy = networkPrefs[0];
        final boolean httpsOnly = networkPrefs[1];
        final boolean followRedir = networkPrefs[2];

        for (int nrRedirects = 0;; nrRedirects++) {
            if (nrRedirects >= HTTP_NR_REDIRECTS_MAX) {
                ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_TOO_MANY_REDIRECTS);
                LogUtils.logError("Too many redirects");
                return null;
            }

            HttpURLConnection conn = null;

            try {
                if (httpsOnly && url.getProtocol().equalsIgnoreCase("http")) {
                    ErrorHandler.setErrno(ErrorHandler.ERROR_HTTPS_ONLY);
                    LogUtils.logError("Attempting insecure connection");
                    return null;
                }

                if (useHttpTorProxy) {
                    conn = (HttpURLConnection) url.openConnection(new Proxy(Proxy.Type.HTTP,
                            new InetSocketAddress(InetAddress.getByName(HTTP_PROXY_TOR_IP), HTTP_PROXY_TOR_PORT)));
                } else {
                    conn = (HttpURLConnection) url.openConnection();
                }

                conn.setRequestProperty("Accept-Charset", StandardCharsets.UTF_8.name());

                conn.setConnectTimeout(HTTP_URL_DEFAULT_TIMEOUT);
                conn.setReadTimeout(HTTP_URL_DEFAULT_TIMEOUT);

                conn.connect();

                final int responseCode = conn.getResponseCode();

                switch (responseCode) {
                case HttpURLConnection.HTTP_OK:
                    return getInputStream(conn.getInputStream());
                case HttpURLConnection.HTTP_MOVED_PERM:
                case HttpURLConnection.HTTP_MOVED_TEMP:
                case HttpURLConnection.HTTP_SEE_OTHER:
                case HTTP_TEMP_REDIRECT:
                    if (followRedir) {
                        final String location = conn.getHeaderField("Location");
                        url = new URL(url, location);

                        if (responseCode == HttpURLConnection.HTTP_MOVED_PERM) {
                            SharedPrefUtils.updateSubscriptionUrl(context, url.toString());
                        }
                        continue;
                    }
                    ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_FOLLOW_REDIRECT_DENIED);
                    LogUtils.logError("Couldn't follow redirect");
                    return null;
                default:
                    if (responseCode < 0) {
                        ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_RESPONSE);
                        LogUtils.logError("No valid HTTP response");
                        return null;
                    } else if (responseCode >= 400 && responseCode <= 500) {
                        ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_CLIENT);
                    } else if (responseCode >= 500 && responseCode <= 600) {
                        ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_SERVER);
                    } else {
                        ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_UNHANDLED);
                    }
                    LogUtils.logError("Error " + responseCode + ": " + conn.getResponseMessage());
                    return null;
                }
            } catch (final IOException e) {
                if ((e instanceof ConnectException && e.getMessage().equals("Network is unreachable"))
                        || e instanceof SocketTimeoutException || e instanceof UnknownHostException) {
                    ErrorHandler.setErrno(ErrorHandler.ERROR_NETWORK, e);
                } else {
                    ErrorHandler.setErrno(ErrorHandler.ERROR_HTTP_UNHANDLED, e);
                }
                return null;
            } finally {
                if (conn != null) {
                    conn.disconnect();
                }
            }
        }
    }
}