#include <pakfire/types.h>
#include <pakfire/util.h>
+// Retry a mirror up to N times before marking it as broken
+#define MIRROR_RETRIES 3
+
// The number of concurrent downloads
#define MAX_PARALLEL 4
struct pakfire_mirror {
char url[PATH_MAX];
unsigned int priority;
+
+ unsigned int retries_left;
+ int broken;
};
struct pakfire_transfer {
FILE* f;
// Mirrors
- unsigned int current_mirror;
+ int current_mirror;
};
struct pakfire_downloader {
snprintf(mirror->url, sizeof(mirror->url) - 1, "%s", url);
mirror->priority = priority;
+ // Set retries
+ mirror->retries_left = MIRROR_RETRIES;
+
// Append it to the list
downloader->mirrors[downloader->num_mirrors++] = mirror;
if (!transfer)
return ENOMEM;
+ // Select no mirror
+ transfer->current_mirror = -1;
+
// Copy URL
snprintf(transfer->url, sizeof(transfer->url) - 1, "%s", url);
return 1;
}
+static int pakfire_transfer_select_mirror(struct pakfire_downloader* downloader,
+ struct pakfire_transfer* transfer) {
+ for (unsigned int next = transfer->current_mirror + 1;
+ next < downloader->num_mirrors; next++) {
+ struct pakfire_mirror* mirror = downloader->mirrors[next];
+
+ // Skip broken mirrors
+ if (mirror->broken)
+ continue;
+
+ DEBUG(downloader->pakfire, "Selected mirror %s\n", mirror->url);
+ transfer->current_mirror = next;
+
+ return 0;
+ }
+
+ ERROR(downloader->pakfire, "No mirrors left to try\n");
+
+ // No mirrors left
+ return ENOENT;
+}
+
static const char* curl_http_version(long v) {
switch (v) {
case CURL_HTTP_VERSION_2_0:
if (r)
return r;
+ // Did we use a mirror?
+ if (transfer->current_mirror >= 0) {
+ struct pakfire_mirror* mirror = downloader->mirrors[transfer->current_mirror];
+
+ // Decrease retries and potentially mark mirror as broken
+ if (!mirror->retries_left--) {
+ DEBUG(downloader->pakfire, "Consider mirror %s as broken\n", mirror->url);
+ mirror->broken = 1;
+ }
+
+ // Try again with another mirror
+ return EAGAIN;
+ }
+
return 0;
}
// Join path if we are using mirrors
} else {
- // XXX for now
+ int r = pakfire_transfer_select_mirror(downloader, transfer);
+ if (r)
+ return r;
+
struct pakfire_mirror* mirror = downloader->mirrors[transfer->current_mirror];
char* url = pakfire_url_join(mirror->url, transfer->url);
// Update reference to transfer
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &transfer);
+ curl_multi_remove_handle(downloader->curl, transfer->handle);
// Handle whatever comes after the transfer
r = pakfire_transfer_done(downloader, transfer, msg);
- if (r)
+ if (r) {
+ // We will try another mirror and therefore insert this
+ // transfer to be picked up again next
+ if (r == EAGAIN) {
+ TAILQ_INSERT_TAIL(&downloader->transfers, transfer, nodes);
+ continue;
+ }
+
success = r;
+ }
- // Remove and destroy the handle
- curl_multi_remove_handle(downloader->curl, transfer->handle);
+ // Destroy transfer
pakfire_transfer_free(transfer);
-
// Log any other messages
} else {
ERROR(downloader->pakfire, "cURL reported error %d\n", msg->msg);