package com.rnfs;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.HttpURLConnection;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

import android.util.Log;

import android.os.AsyncTask;

import com.facebook.react.bridge.ReadableMapKeySetIterator;

public class Downloader extends AsyncTask<DownloadParams, int[], DownloadResult> {
  private DownloadParams mParam;
  private AtomicBoolean mAbort = new AtomicBoolean(false);

  protected DownloadResult doInBackground(DownloadParams... params) {
    mParam = params[0];

    DownloadResult res = new DownloadResult();

    try {
      this.download(mParam, res);
      mParam.onTaskCompleted.onTaskCompleted(res);
    } catch (Exception ex) {
      res.exception = ex;
      mParam.onTaskCompleted.onTaskCompleted(res);
      return res;
    }

    return res;
  }

  private void download(DownloadParams param, DownloadResult res) throws IOException {
    InputStream input = null;
    OutputStream output = null;
    HttpURLConnection connection = null;

    try {
      connection = (HttpURLConnection)param.src.openConnection();

      ReadableMapKeySetIterator iterator = param.headers.keySetIterator();

      while (iterator.hasNextKey()) {
        String key = iterator.nextKey();
        String value = param.headers.getString(key);
        connection.setRequestProperty(key, value);
      }

      connection.setConnectTimeout(5000);
      connection.connect();

      int statusCode = connection.getResponseCode();
      int lengthOfFile = connection.getContentLength();

      boolean isRedirect = (
        statusCode != HttpURLConnection.HTTP_OK &&
        (
          statusCode == HttpURLConnection.HTTP_MOVED_PERM ||
          statusCode == HttpURLConnection.HTTP_MOVED_TEMP
        )
      );

      if (isRedirect) {
        String redirectURL = connection.getHeaderField("Location");
        connection.disconnect();

        connection = (HttpURLConnection) new URL(redirectURL).openConnection();
        connection.setConnectTimeout(5000);
        connection.connect();

        statusCode = connection.getResponseCode();
        lengthOfFile = connection.getContentLength();
      }

      Map<String, List<String>> headers = connection.getHeaderFields();

      Map<String, String> headersFlat = new HashMap<String, String>();

      for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
        String headerKey = entry.getKey();
        String valueKey = entry.getValue().get(0);

        if (headerKey != null && valueKey != null) {
          headersFlat.put(headerKey, valueKey);
        }
      }

      mParam.onDownloadBegin.onDownloadBegin(statusCode, lengthOfFile, headersFlat);

      input = new BufferedInputStream(connection.getInputStream(), 8 * 1024);
      output = new FileOutputStream(param.dest);

      byte data[] = new byte[8 * 1024];
      int total = 0;
      int count;
      double lastProgressValue = 0;

      while ((count = input.read(data)) != -1) {
        if (mAbort.get()) {
          break;
        }

        total += count;
        if (param.progressDivider <= 0) {
            publishProgress(new int[]{lengthOfFile, total});
        } else {
            double progress = Math.round(((double) total * 100) / lengthOfFile);
            if (progress % param.progressDivider == 0) {
                if ((progress != lastProgressValue) || (total == lengthOfFile)) {
                    Log.d("Downloader", "EMIT: " + String.valueOf(progress) + ", TOTAL:" + String.valueOf(total));
                    lastProgressValue = progress;
                    publishProgress(new int[]{lengthOfFile, total});
                }
            }
        }
        output.write(data, 0, count);
      }

      output.flush();

      res.statusCode = statusCode;
      res.bytesWritten = total;
    } finally {
      if (output != null) output.close();
      if (input != null) input.close();
      if (connection != null) connection.disconnect();
    }
  }

  protected void stop() {
    mAbort.set(true);
  }

  @Override
  protected void onProgressUpdate(int[]... values) {
    super.onProgressUpdate(values);
    mParam.onDownloadProgress.onDownloadProgress(values[0][0], values[0][1]);
  }

  protected void onPostExecute(Exception ex) {

  }
}

@@ -1634,24 +1634,64 @@
 eout(5000);%0A
+      connection.setReadTimeout(15000);%0A
       connec

Add read timeout