Allow DownloadRequests to define a mirror that should be tried first

This commit is contained in:
Torsten Grote
2022-01-27 14:15:09 -03:00
parent decc2cc6e3
commit f40274d214
6 changed files with 47 additions and 13 deletions

View File

@@ -134,7 +134,7 @@ public class IndexUpdater {
try {
destFile = File.createTempFile("dl-", "", context.getCacheDir());
destFile.deleteOnExit(); // this probably does nothing, but maybe...
downloader = DownloaderFactory.createWithoutMirrors(repo, Uri.parse(indexUrl), destFile);
downloader = DownloaderFactory.createWithTryFirstMirror(repo, Uri.parse(indexUrl), destFile);
downloader.setCacheTag(repo.lastetag);
downloader.setListener(downloadListener);
downloader.download();

View File

@@ -115,7 +115,7 @@ public class IndexV1Updater extends IndexUpdater {
destFile = File.createTempFile("dl-", "", context.getCacheDir());
destFile.deleteOnExit(); // this probably does nothing, but maybe...
// read file name from file
downloader = DownloaderFactory.createWithoutMirrors(repo, Uri.parse(indexUrl), destFile);
downloader = DownloaderFactory.createWithTryFirstMirror(repo, Uri.parse(indexUrl), destFile);
downloader.setCacheTag(repo.lastetag);
downloader.setListener(downloadListener);
downloader.download();

View File

@@ -3,6 +3,8 @@ package org.fdroid.fdroid.net;
import android.content.ContentResolver;
import android.net.Uri;
import androidx.annotation.Nullable;
import org.fdroid.download.DownloadRequest;
import org.fdroid.download.Downloader;
import org.fdroid.download.HttpDownloader;
@@ -15,7 +17,6 @@ import org.fdroid.fdroid.data.Repo;
import java.io.File;
import java.io.IOException;
import java.net.Proxy;
import java.util.Collections;
import java.util.List;
import info.guardianproject.netcipher.NetCipher;
@@ -28,22 +29,24 @@ public class DownloaderFactory {
new HttpManager(Utils.getUserAgent(), FDroidApp.queryString, NetCipher.getProxy());
/**
* Same as {@link #create(Repo, Uri, File)}, but not using mirrors for download.
*
* Same as {@link #create(Repo, Uri, File)}, but trying canonical address first.
* <p>
* See https://gitlab.com/fdroid/fdroidclient/-/issues/1708 for why this is still needed.
*/
public static Downloader createWithoutMirrors(Repo repo, Uri uri, File destFile)
public static Downloader createWithTryFirstMirror(Repo repo, Uri uri, File destFile)
throws IOException {
List<Mirror> mirrors = Collections.singletonList(new Mirror(repo.address));
return create(repo, mirrors, uri, destFile);
Mirror tryFirst = new Mirror(repo.address);
List<Mirror> mirrors = Mirror.fromStrings(repo.getMirrorList());
return create(repo, mirrors, uri, destFile, tryFirst);
}
public static Downloader create(Repo repo, Uri uri, File destFile) throws IOException {
List<Mirror> mirrors = Mirror.fromStrings(repo.getMirrorList());
return create(repo, mirrors, uri, destFile);
return create(repo, mirrors, uri, destFile, null);
}
private static Downloader create(Repo repo, List<Mirror> mirrors, Uri uri, File destFile) throws IOException {
private static Downloader create(Repo repo, List<Mirror> mirrors, Uri uri, File destFile,
@Nullable Mirror tryFirst) throws IOException {
Downloader downloader;
String scheme = uri.getScheme();
@@ -57,7 +60,8 @@ public class DownloaderFactory {
String path = uri.toString().replace(repo.address, "");
Utils.debugLog(TAG, "Using suffix " + path + " with mirrors " + mirrors);
Proxy proxy = NetCipher.getProxy();
DownloadRequest request = new DownloadRequest(path, mirrors, proxy, repo.username, repo.password);
DownloadRequest request =
new DownloadRequest(path, mirrors, proxy, repo.username, repo.password, tryFirst);
downloader = new HttpDownloader(HTTP_MANAGER, request, destFile);
}
return downloader;

View File

@@ -9,4 +9,16 @@ public data class DownloadRequest @JvmOverloads constructor(
val proxy: ProxyConfig? = null,
val username: String? = null,
val password: String? = null,
)
/**
* Signals the [MirrorChooser] that this mirror should be tried before all other mirrors.
* This could be useful for index updates for repositories with mirrors that update infrequently,
* so that the official repository can be tried first to get updates fast.
*/
val tryFirstMirror: Mirror? = null,
) {
init {
require(tryFirstMirror == null || mirrors.contains(tryFirstMirror)) {
"$tryFirstMirror not in mirrors."
}
}
}

View File

@@ -58,7 +58,12 @@ internal class MirrorChooserRandom : MirrorChooserImpl() {
*/
override fun orderMirrors(downloadRequest: DownloadRequest): List<Mirror> {
// simple random selection for now
return downloadRequest.mirrors.toMutableList().apply { shuffle() }
return downloadRequest.mirrors.toMutableList().apply { shuffle() }.also { mirrors ->
// respect the mirror to try first, if set
if (downloadRequest.tryFirstMirror != null) {
mirrors.sortBy { if (it == downloadRequest.tryFirstMirror) 0 else 1 }
}
}
}
}

View File

@@ -34,4 +34,17 @@ class MirrorChooserTest {
assertEquals(mirrors.toSet(), orderedMirrors.toSet())
}
@Test
fun testMirrorChooserRandomRespectsTryFirstMirror() {
val mirrorChooser = MirrorChooserRandom()
val tryFirstRequest = downloadRequest.copy(tryFirstMirror = Mirror("42"))
val orderedMirrors = mirrorChooser.orderMirrors(tryFirstRequest)
// try-first mirror is first in list
assertEquals(tryFirstRequest.tryFirstMirror, orderedMirrors[0])
// set of input mirrors is equal to set of output mirrors
assertEquals(mirrors.toSet(), orderedMirrors.toSet())
}
}