Bug 1244760 - DownloadContentService: Use HttpUrlConnection instead of HttpClient. r=rnewman draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Mon, 01 Feb 2016 17:32:07 +0100
changeset 327756 46f5cdfac98ef92a973db81d8373ee9026fd41e6
parent 327074 0ae6c7e277af505c6b13f8ba97e5618b0e8ffdfc
child 513742 e29f96e90cb7735a2e0fbd4a03b5b52db61d1629
push id10285
push users.kaspari@gmail.com
push dateMon, 01 Feb 2016 19:57:22 +0000
reviewersrnewman
bugs1244760
milestone47.0a1
Bug 1244760 - DownloadContentService: Use HttpUrlConnection instead of HttpClient. r=rnewman
mobile/android/base/java/org/mozilla/gecko/dlc/DownloadAction.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestDownloadAction.java
--- a/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadAction.java
+++ b/mobile/android/base/java/org/mozilla/gecko/dlc/DownloadAction.java
@@ -21,35 +21,34 @@ import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
 import java.util.zip.GZIPInputStream;
 
-import ch.boye.httpclientandroidlib.HttpEntity;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.HttpStatus;
-import ch.boye.httpclientandroidlib.client.HttpClient;
-import ch.boye.httpclientandroidlib.client.methods.HttpGet;
-import ch.boye.httpclientandroidlib.impl.client.DefaultHttpRequestRetryHandler;
-import ch.boye.httpclientandroidlib.impl.client.HttpClientBuilder;
-
 /**
  * Download content that has been scheduled during "study" or "verify".
  */
 public class DownloadAction extends BaseAction {
     private static final String LOGTAG = "DLCDownloadAction";
 
     private static final String CACHE_DIRECTORY = "downloadContent";
+
     private static final String CDN_BASE_URL = "https://mobile.cdn.mozilla.net/";
 
+    private static final int STATUS_OK = 200;
+    private static final int STATUS_PARTIAL_CONTENT = 206;
+
     public interface Callback {
         void onContentDownloaded(DownloadContent content);
     }
 
     private Callback callback;
 
     public DownloadAction(Callback callback) {
         this.callback = callback;
@@ -65,18 +64,16 @@ public class DownloadAction extends Base
         }
 
         if (isActiveNetworkMetered(context)) {
             Log.d(LOGTAG, "Network is metered. Postponing download.");
             // TODO: Reschedule download (bug 1209498)
             return;
         }
 
-        final HttpClient client = buildHttpClient();
-
         for (DownloadContent content : catalog.getScheduledDownloads()) {
             Log.d(LOGTAG, "Downloading: " + content);
 
             File temporaryFile = null;
 
             try {
                 File destinationFile = getDestinationFile(context, content);
                 if (destinationFile.exists() && verify(destinationFile, content.getChecksum())) {
@@ -91,17 +88,17 @@ public class DownloadAction extends Base
                     Log.d(LOGTAG, "Not enough disk space to save content. Skipping download.");
                     continue;
                 }
 
                 // TODO: Check space on disk before downloading content (bug 1220145)
                 final String url = createDownloadURL(content);
 
                 if (!temporaryFile.exists() || temporaryFile.length() < content.getSize()) {
-                    download(client, url, temporaryFile);
+                    download(url, temporaryFile);
                 }
 
                 if (!verify(temporaryFile, content.getDownloadChecksum())) {
                     Log.w(LOGTAG, "Wrong checksum after download, content=" + content.getId());
                     temporaryFile.delete();
                     continue;
                 }
 
@@ -141,68 +138,68 @@ public class DownloadAction extends Base
                     temporaryFile.delete();
                 }
             }
         }
 
         Log.v(LOGTAG, "Done");
     }
 
-    protected void download(HttpClient client, String source, File temporaryFile)
+    protected void download(String source, File temporaryFile)
             throws RecoverableDownloadContentException, UnrecoverableDownloadContentException {
         InputStream inputStream = null;
         OutputStream outputStream = null;
 
-        final HttpGet request = new HttpGet(source);
-
-        final long offset = temporaryFile.exists() ? temporaryFile.length() : 0;
-        if (offset > 0) {
-            request.setHeader("Range", "bytes=" + offset + "-");
-        }
+        HttpURLConnection connection = null;
 
         try {
-            final HttpResponse response = client.execute(request);
-            final int status = response.getStatusLine().getStatusCode();
-            if (status != HttpStatus.SC_OK && status != HttpStatus.SC_PARTIAL_CONTENT) {
+            connection = buildHttpURLConnection(source);
+
+            final long offset = temporaryFile.exists() ? temporaryFile.length() : 0;
+            if (offset > 0) {
+                connection.setRequestProperty("Range", "bytes=" + offset + "-");
+            }
+
+            final int status = connection.getResponseCode();
+            if (status != STATUS_OK && status != STATUS_PARTIAL_CONTENT) {
                 // We are trying to be smart and only retry if this is an error that might resolve in the future.
                 // TODO: This is guesstimating at best. We want to implement failure counters (Bug 1215106).
                 if (status >= 500) {
                     // Recoverable: Server errors 5xx
                     throw new RecoverableDownloadContentException(RecoverableDownloadContentException.SERVER,
                                                                   "(Recoverable) Download failed. Status code: " + status);
                 } else if (status >= 400) {
                     // Unrecoverable: Client errors 4xx - Unlikely that this version of the client will ever succeed.
                     throw new UnrecoverableDownloadContentException("(Unrecoverable) Download failed. Status code: " + status);
                 } else {
+                    // HttpsUrlConnection: -1 (No valid response code)
                     // Informational 1xx: They have no meaning to us.
                     // Successful 2xx: We don't know how to handle anything but 200.
                     // Redirection 3xx: HttpClient should have followed redirects if possible. We should not see those errors here.
                     throw new UnrecoverableDownloadContentException("(Unrecoverable) Download failed. Status code: " + status);
                 }
             }
 
-            final HttpEntity entity = response.getEntity();
-            if (entity == null) {
-                // Recoverable: Should not happen for a valid asset
-                throw new RecoverableDownloadContentException(RecoverableDownloadContentException.SERVER, "Null entity");
-            }
-
-            inputStream = new BufferedInputStream(entity.getContent());
-            outputStream = openFile(temporaryFile, status == HttpStatus.SC_PARTIAL_CONTENT);
+            inputStream = new BufferedInputStream(connection.getInputStream());
+            outputStream = openFile(temporaryFile, status == STATUS_PARTIAL_CONTENT);
 
             IOUtils.copy(inputStream, outputStream);
 
             inputStream.close();
             outputStream.close();
         } catch (IOException e) {
             // Recoverable: Just I/O discontinuation
             throw new RecoverableDownloadContentException(RecoverableDownloadContentException.NETWORK, e);
         } finally {
             IOUtils.safeStreamClose(inputStream);
             IOUtils.safeStreamClose(outputStream);
+
+            if (connection != null) {
+                connection.disconnect();
+            }
         }
     }
 
     protected OutputStream openFile(File file, boolean append) throws FileNotFoundException {
         return new BufferedOutputStream(new FileOutputStream(file, append));
     }
 
     protected void extract(File sourceFile, File destinationFile, String checksum)
@@ -253,24 +250,29 @@ public class DownloadAction extends Base
         return networkInfo != null && networkInfo.isConnected();
     }
 
     protected boolean isActiveNetworkMetered(Context context) {
         return ConnectivityManagerCompat.isActiveNetworkMetered(
                 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
     }
 
-    protected HttpClient buildHttpClient() {
+    protected HttpURLConnection buildHttpURLConnection(String url)
+            throws UnrecoverableDownloadContentException, IOException {
         // TODO: Implement proxy support (Bug 1209496)
-        return HttpClientBuilder.create()
-                .setUserAgent(HardwareUtils.isTablet() ?
-                        AppConstants.USER_AGENT_FENNEC_TABLET :
-                        AppConstants.USER_AGENT_FENNEC_MOBILE)
-                .setRetryHandler(new DefaultHttpRequestRetryHandler())
-                .build();
+        try {
+            HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
+            connection.setRequestProperty("User-Agent", HardwareUtils.isTablet() ?
+                    AppConstants.USER_AGENT_FENNEC_TABLET :
+                    AppConstants.USER_AGENT_FENNEC_MOBILE);
+            connection.setRequestMethod("GET");
+            return connection;
+        } catch (MalformedURLException e) {
+            throw new UnrecoverableDownloadContentException(e);
+        }
     }
 
     protected String createDownloadURL(DownloadContent content) {
         return CDN_BASE_URL + content.getLocation();
     }
 
     protected File createTemporaryFile(Context context, DownloadContent content)
             throws RecoverableDownloadContentException {
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestDownloadAction.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/dlc/TestDownloadAction.java
@@ -1,85 +1,79 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.dlc;
 
+import android.content.Context;
+
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.mozilla.gecko.dlc.catalog.DownloadContent;
 import org.mozilla.gecko.dlc.catalog.DownloadContentCatalog;
-
-import android.content.Context;
-
 import org.robolectric.RuntimeEnvironment;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.IOException;
+import java.net.HttpURLConnection;
 import java.util.Arrays;
 import java.util.Collections;
 
-import ch.boye.httpclientandroidlib.HttpEntity;
-import ch.boye.httpclientandroidlib.HttpResponse;
-import ch.boye.httpclientandroidlib.HttpStatus;
-import ch.boye.httpclientandroidlib.StatusLine;
-import ch.boye.httpclientandroidlib.client.HttpClient;
-import ch.boye.httpclientandroidlib.client.methods.HttpGet;
-import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
-
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
 
 /**
  * DownloadAction: Download content that has been scheduled during "study" or "verify".
  */
 @RunWith(TestRunner.class)
 public class TestDownloadAction {
     private static final String TEST_URL = "http://example.org";
 
+    private static final int STATUS_OK = 200;
+    private static final int STATUS_PARTIAL_CONTENT = 206;
+
     /**
      * Scenario: The current network is metered.
      *
      * Verify that:
      *  * No download is performed on a metered network
      */
     @Test
     public void testNothingIsDoneOnMeteredNetwork() throws Exception {
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(true).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
 
         action.perform(RuntimeEnvironment.application, null);
 
-        verify(action, never()).buildHttpClient();
-        verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
+        verify(action, never()).buildHttpURLConnection(anyString());
+        verify(action, never()).download(anyString(), any(File.class));
     }
 
     /**
      * Scenario: No (connected) network is available.
      *
      * Verify that:
      *  * No download is performed
      */
     @Test
     public void testNothingIsDoneIfNoNetworkIsAvailable() throws Exception {
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(false).when(action).isConnectedToNetwork(RuntimeEnvironment.application);
 
         action.perform(RuntimeEnvironment.application, null);
 
         verify(action, never()).isActiveNetworkMetered(any(Context.class));
-        verify(action, never()).buildHttpClient();
-        verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
+        verify(action, never()).buildHttpURLConnection(anyString());
+        verify(action, never()).download(anyString(), any(File.class));
     }
 
     /**
      * Scenario: Content is scheduled for download but already exists locally (with correct checksum).
      *
      * Verify that:
      *  * No download is performed for existing file
      *  * Content is marked as downloaded in the catalog
@@ -97,56 +91,58 @@ public class TestDownloadAction {
         File file = mock(File.class);
         doReturn(true).when(file).exists();
         doReturn(file).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
         doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
         doReturn(true).when(action).verify(eq(file), anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
-        verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
+        verify(action, never()).download(anyString(), any(File.class));
         verify(catalog).markAsDownloaded(content);
     }
 
     /**
      * Scenario: Server returns a server error (HTTP 500).
      *
      * Verify that:
      *  * Situation is treated as recoverable (RecoverableDownloadContentException)
      */
     @Test(expected=BaseAction.RecoverableDownloadContentException.class)
     public void testServerErrorsAreRecoverable() throws Exception {
-        HttpClient client = mockHttpClient(500, "");
+        HttpURLConnection connection = mockHttpURLConnection(500, "");
 
         File temporaryFile = mock(File.class);
         doReturn(false).when(temporaryFile).exists();
 
         DownloadAction action = spy(new DownloadAction(null));
-        action.download(client, TEST_URL, temporaryFile);
+        doReturn(connection).when(action).buildHttpURLConnection(anyString());
+        action.download(TEST_URL, temporaryFile);
 
-        verify(client).execute(any(HttpUriRequest.class));
+        verify(connection).getInputStream();
     }
 
     /**
      * Scenario: Server returns a client error (HTTP 404).
      *
      * Verify that:
      *  * Situation is treated as unrecoverable (UnrecoverableDownloadContentException)
      */
     @Test(expected=BaseAction.UnrecoverableDownloadContentException.class)
     public void testClientErrorsAreUnrecoverable() throws Exception {
-        HttpClient client = mockHttpClient(404, "");
+        HttpURLConnection connection = mockHttpURLConnection(404, "");
 
         File temporaryFile = mock(File.class);
         doReturn(false).when(temporaryFile).exists();
 
         DownloadAction action = spy(new DownloadAction(null));
-        action.download(client, TEST_URL, temporaryFile);
+        doReturn(connection).when(action).buildHttpURLConnection(anyString());
+        action.download(TEST_URL, temporaryFile);
 
-        verify(client).execute(any(HttpUriRequest.class));
+        verify(connection).getInputStream();
     }
 
     /**
      * Scenario: A successful download has been performed.
      *
      * Verify that:
      *  * The content will be extracted to the destination
      *  * The content is marked as downloaded in the catalog
@@ -164,24 +160,23 @@ public class TestDownloadAction {
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
 
         File file = mockNotExistingFile();
         doReturn(file).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
         doReturn(file).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
         doReturn(false).when(action).verify(eq(file), anyString());
-        doNothing().when(action).download(any(HttpClient.class), anyString(), eq(file));
+        doNothing().when(action).download(anyString(), eq(file));
         doReturn(true).when(action).verify(eq(file), anyString());
         doNothing().when(action).extract(eq(file), eq(file), anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
-        verify(action).buildHttpClient();
-        verify(action).download(any(HttpClient.class), anyString(), eq(file));
+        verify(action).download(anyString(), eq(file));
         verify(action).extract(eq(file), eq(file), anyString());
         verify(catalog).markAsDownloaded(content);
     }
 
     /**
      * Scenario: Pretend a partially downloaded file already exists.
      *
      * Verify that:
@@ -204,33 +199,30 @@ public class TestDownloadAction {
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
 
         File temporaryFile = mockFileWithSize(1337L);
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         doReturn(outputStream).when(action).openFile(eq(temporaryFile), anyBoolean());
 
-        HttpClient client = mockHttpClient(HttpStatus.SC_PARTIAL_CONTENT, "HelloWorld");
-        doReturn(client).when(action).buildHttpClient();
+        HttpURLConnection connection = mockHttpURLConnection(STATUS_PARTIAL_CONTENT, "HelloWorld");
+        doReturn(connection).when(action).buildHttpURLConnection(anyString());
 
         File destinationFile = mockNotExistingFile();
         doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
         doReturn(true).when(action).verify(eq(temporaryFile), anyString());
         doNothing().when(action).extract(eq(temporaryFile), eq(destinationFile), anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
-        ArgumentCaptor<HttpGet> argument = ArgumentCaptor.forClass(HttpGet.class);
-        verify(client).execute(argument.capture());
+        verify(connection).getInputStream();
+        verify(connection).setRequestProperty("Range", "bytes=1337-");
 
-        HttpGet request = argument.getValue();
-        Assert.assertTrue(request.containsHeader("Range"));
-        Assert.assertEquals("bytes=1337-", request.getFirstHeader("Range").getValue());
         Assert.assertEquals("HelloWorld", new String(outputStream.toByteArray(), "UTF-8"));
 
         verify(action).openFile(eq(temporaryFile), eq(true));
         verify(catalog).markAsDownloaded(content);
         verify(temporaryFile).delete();
     }
 
     /**
@@ -256,18 +248,18 @@ public class TestDownloadAction {
 
         File temporaryFile = mockFileWithSize(1337L);
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         ByteArrayOutputStream outputStream = spy(new ByteArrayOutputStream());
         doReturn(outputStream).when(action).openFile(eq(temporaryFile), anyBoolean());
         doThrow(IOException.class).when(outputStream).write(any(byte[].class), anyInt(), anyInt());
 
-        HttpClient client = mockHttpClient(HttpStatus.SC_PARTIAL_CONTENT, "HelloWorld");
-        doReturn(client).when(action).buildHttpClient();
+        HttpURLConnection connection = mockHttpURLConnection(STATUS_PARTIAL_CONTENT, "HelloWorld");
+        doReturn(connection).when(action).buildHttpURLConnection(anyString());
 
         doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
         action.perform(RuntimeEnvironment.application, catalog);
 
         verify(catalog, never()).markAsDownloaded(content);
         verify(action, never()).verify(any(File.class), anyString());
         verify(temporaryFile, never()).delete();
@@ -301,17 +293,17 @@ public class TestDownloadAction {
         File destinationFile = mockNotExistingFile();
         doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
         doReturn(true).when(action).verify(eq(temporaryFile), anyString());
         doNothing().when(action).extract(eq(temporaryFile), eq(destinationFile), anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
-        verify(action, never()).download(any(HttpClient.class), anyString(), eq(temporaryFile));
+        verify(action, never()).download(anyString(), eq(temporaryFile));
         verify(action).verify(eq(temporaryFile), anyString());
         verify(action).extract(eq(temporaryFile), eq(destinationFile), anyString());
         verify(catalog).markAsDownloaded(content);
     }
 
     /**
      * Scenario: Download is completed but verification (checksum) failed.
      *
@@ -328,17 +320,17 @@ public class TestDownloadAction {
                 .setSize(1337L)
                 .build();
 
         DownloadContentCatalog catalog = mock(DownloadContentCatalog.class);
         doReturn(Collections.singletonList(content)).when(catalog).getScheduledDownloads();
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
-        doNothing().when(action).download(any(HttpClient.class), anyString(), any(File.class));
+        doNothing().when(action).download(anyString(), any(File.class));
         doReturn(false).when(action).verify(any(File.class), anyString());
 
         File temporaryFile = mockNotExistingFile();
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         File destinationFile = mockNotExistingFile();
         doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
@@ -367,18 +359,18 @@ public class TestDownloadAction {
         File temporaryFile = mockNotExistingFile();
         doReturn(temporaryFile).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
 
         File destinationFile = mockNotExistingFile();
         doReturn(destinationFile).when(action).getDestinationFile(RuntimeEnvironment.application, content);
 
         doReturn(true).when(action).hasEnoughDiskSpace(content, destinationFile, temporaryFile);
 
-        verify(action, never()).buildHttpClient();
-        verify(action, never()).download(any(HttpClient.class), anyString(), any(File.class));
+        verify(action, never()).buildHttpURLConnection(anyString());
+        verify(action, never()).download(anyString(), any(File.class));
         verify(action, never()).verify(any(File.class), anyString());
         verify(catalog, never()).markAsDownloaded(content);
     }
 
     /**
      * Scenario: Not enough storage space for temporary file available.
      *
      * Verify that:
@@ -439,19 +431,19 @@ public class TestDownloadAction {
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(true).when(action).isConnectedToNetwork(RuntimeEnvironment.application);
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
         doReturn(mockNotExistingFile()).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
         doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
         doReturn(true).when(action).hasEnoughDiskSpace(eq(content), any(File.class), any(File.class));
 
-        HttpClient client = mock(HttpClient.class);
-        doThrow(IOException.class).when(client).execute(any(HttpUriRequest.class));
-        doReturn(client).when(action).buildHttpClient();
+        HttpURLConnection connection = mockHttpURLConnection(STATUS_OK, "");
+        doThrow(IOException.class).when(connection).getInputStream();
+        doReturn(connection).when(action).buildHttpURLConnection(anyString());
 
         action.perform(RuntimeEnvironment.application, catalog);
 
         verify(catalog, never()).rememberFailure(eq(content), anyInt());
         verify(catalog, never()).markAsDownloaded(content);
     }
 
     /**
@@ -471,17 +463,17 @@ public class TestDownloadAction {
         Assert.assertEquals(DownloadContent.STATE_NONE, content.getState());
 
         DownloadAction action = spy(new DownloadAction(null));
         doReturn(true).when(action).isConnectedToNetwork(RuntimeEnvironment.application);
         doReturn(false).when(action).isActiveNetworkMetered(RuntimeEnvironment.application);
         doReturn(mockNotExistingFile()).when(action).createTemporaryFile(RuntimeEnvironment.application, content);
         doReturn(mockNotExistingFile()).when(action).getDestinationFile(RuntimeEnvironment.application, content);
         doReturn(true).when(action).hasEnoughDiskSpace(eq(content), any(File.class), any(File.class));
-        doNothing().when(action).download(any(HttpClient.class), anyString(), any(File.class));
+        doNothing().when(action).download(anyString(), any(File.class));
         doReturn(true).when(action).verify(any(File.class), anyString());
 
         File destinationFile = mock(File.class);
         doReturn(false).when(destinationFile).exists();
         File parentFile = mock(File.class);
         doReturn(false).when(parentFile).mkdirs();
         doReturn(false).when(parentFile).exists();
         doReturn(parentFile).when(destinationFile).getParentFile();
@@ -536,25 +528,17 @@ public class TestDownloadAction {
 
         File parentFile = mock(File.class);
         doReturn(usableSpace).when(parentFile).getUsableSpace();
         doReturn(parentFile).when(file).getParentFile();
 
         return file;
     }
 
-    private static HttpClient mockHttpClient(int statusCode, String content) throws Exception {
-        StatusLine status = mock(StatusLine.class);
-        doReturn(statusCode).when(status).getStatusCode();
-
-        HttpEntity entity = mock(HttpEntity.class);
-        doReturn(new ByteArrayInputStream(content.getBytes("UTF-8"))).when(entity).getContent();
+    private static HttpURLConnection mockHttpURLConnection(int statusCode, String content) throws Exception {
+        HttpURLConnection connection = mock(HttpURLConnection.class);
 
-        HttpResponse response = mock(HttpResponse.class);
-        doReturn(status).when(response).getStatusLine();
-        doReturn(entity).when(response).getEntity();
+        doReturn(statusCode).when(connection).getResponseCode();
+        doReturn(new ByteArrayInputStream(content.getBytes("UTF-8"))).when(connection).getInputStream();
 
-        HttpClient client = mock(HttpClient.class);
-        doReturn(response).when(client).execute(any(HttpUriRequest.class));
-
-        return client;
+        return connection;
     }
 }