You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2013/03/27 22:01:09 UTC

android commit: [CB-1517] Properly report download progress for GZIP-encoded resources

Updated Branches:
  refs/heads/master 36c33a588 -> 282367c6d


[CB-1517] Properly report download progress for GZIP-encoded resources


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/282367c6
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/282367c6
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/282367c6

Branch: refs/heads/master
Commit: 282367c6d59bf7a74d2455c7ff7b478f2157f0a1
Parents: 36c33a5
Author: Ian Clelland <ic...@chromium.org>
Authored: Tue Mar 19 22:39:11 2013 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed Mar 27 17:00:22 2013 -0400

----------------------------------------------------------------------
 framework/src/org/apache/cordova/FileTransfer.java |  101 +++++++++++++--
 1 files changed, 91 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/282367c6/framework/src/org/apache/cordova/FileTransfer.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java
index 75bed2c..c1ca15c 100644
--- a/framework/src/org/apache/cordova/FileTransfer.java
+++ b/framework/src/org/apache/cordova/FileTransfer.java
@@ -41,6 +41,8 @@ import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
 
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
@@ -100,10 +102,83 @@ public class FileTransfer extends CordovaPlugin {
     }
 
     /**
+     * Adds an interface method to an InputStream to return the number of bytes
+     * read from the raw stream. This is used to track total progress against
+     * the HTTP Content-Length header value from the server.
+     */
+    private static abstract class TrackingInputStream extends FilterInputStream {
+    	public TrackingInputStream(final InputStream in) {
+    		super(in);
+    	}
+        public abstract long getTotalRawBytesRead();
+	}
+
+    private static class ExposedGZIPInputStream extends GZIPInputStream {
+	    public ExposedGZIPInputStream(final InputStream in) throws IOException {
+	    	super(in);
+	    }
+	    public Inflater getInflater() {
+	    	return inf;
+	    }
+	}
+
+    /**
+     * Provides raw bytes-read tracking for a GZIP input stream. Reports the
+     * total number of compressed bytes read from the input, rather than the
+     * number of uncompressed bytes.
+     */
+    private static class TrackingGZIPInputStream extends TrackingInputStream {
+    	private ExposedGZIPInputStream gzin;
+	    public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
+	    	super(gzin);
+	    	this.gzin = gzin;
+	    }
+	    public long getTotalRawBytesRead() {
+	    	return gzin.getInflater().getBytesRead();
+	    }
+	}
+
+    /**
+     * Provides simple total-bytes-read tracking for an existing InputStream
+     */
+    private static class TrackingHTTPInputStream extends TrackingInputStream {
+        private long bytesRead = 0;
+        public TrackingHTTPInputStream(InputStream stream) {
+            super(stream);
+        }
+
+        private int updateBytesRead(int newBytesRead) {
+        	if (newBytesRead != -1) {
+        		bytesRead += newBytesRead;
+        	}
+        	return newBytesRead;
+        }
+
+        @Override
+        public int read() throws IOException {
+            return updateBytesRead(super.read());
+        }
+
+        @Override
+        public int read(byte[] buffer) throws IOException {
+            return updateBytesRead(super.read(buffer));
+        }
+
+        @Override
+        public int read(byte[] bytes, int offset, int count) throws IOException {
+            return updateBytesRead(super.read(bytes, offset, count));
+        }
+
+        public long getTotalRawBytesRead() {
+        	return bytesRead;
+        }
+    }
+
+    /**
      * Works around a bug on Android 2.3.
      * http://code.google.com/p/android/issues/detail?id=14562
      */
-    private static final class DoneHandlerInputStream extends FilterInputStream {
+    private static final class DoneHandlerInputStream extends TrackingHTTPInputStream {
         private boolean done;
         
         public DoneHandlerInputStream(InputStream stream) {
@@ -409,7 +484,7 @@ public class FileTransfer extends CordovaPlugin {
                     int responseCode = conn.getResponseCode();
                     Log.d(LOG_TAG, "response code: " + responseCode);
                     Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
-                    InputStream inStream = null;
+                    TrackingInputStream inStream = null;
                     try {
                         inStream = getInputStream(conn);
                         synchronized (context) {
@@ -485,11 +560,15 @@ public class FileTransfer extends CordovaPlugin {
         }
     }
 
-    private static InputStream getInputStream(URLConnection conn) throws IOException {
+    private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
             return new DoneHandlerInputStream(conn.getInputStream());
         }
-        return conn.getInputStream();
+        String encoding = conn.getContentEncoding();
+        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
+        	return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
+        }
+        return new TrackingHTTPInputStream(conn.getInputStream());
     }
 
     // always verify the host - don't check for certificate
@@ -700,6 +779,9 @@ public class FileTransfer extends CordovaPlugin {
                     {
                         connection.setRequestProperty("cookie", cookie);
                     }
+                    
+                    // This must be explicitly set for gzip progress tracking to work.
+                    connection.setRequestProperty("Accept-Encoding", "gzip");
 
                     // Handle the other headers
                     if (headers != null) {
@@ -711,14 +793,15 @@ public class FileTransfer extends CordovaPlugin {
                     Log.d(LOG_TAG, "Download file:" + url);
 
                     FileProgressResult progress = new FileProgressResult();
-                    if (connection.getContentEncoding() == null) {
-                        // Only trust content-length header if no gzip etc
+                    if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
+                        // Only trust content-length header if we understand
+                        // the encoding -- identity or gzip
                         progress.setLengthComputable(true);
                         progress.setTotal(connection.getContentLength());
                     }
                     
                     FileOutputStream outputStream = null;
-                    InputStream inputStream = null;
+                    TrackingInputStream inputStream = null;
                     
                     try {
                         inputStream = getInputStream(connection);
@@ -733,12 +816,10 @@ public class FileTransfer extends CordovaPlugin {
                         // write bytes to file
                         byte[] buffer = new byte[MAX_BUFFER_SIZE];
                         int bytesRead = 0;
-                        long totalBytes = 0;
                         while ((bytesRead = inputStream.read(buffer)) > 0) {
                             outputStream.write(buffer, 0, bytesRead);
-                            totalBytes += bytesRead;
                             // Send a progress event.
-                            progress.setLoaded(totalBytes);
+                            progress.setLoaded(inputStream.getTotalRawBytesRead());
                             PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
                             progressResult.setKeepCallback(true);
                             context.sendPluginResult(progressResult);