#include <errno.h>
#include <stdlib.h>
+#include <sys/queue.h>
#include <curl/curl.h>
// The number of concurrent downloads
#define DEFAULT_MAX_PARALLEL 4
+struct pakfire_httpclient_xfer {
+ TAILQ_ENTRY(pakfire_httpclient_xfer) nodes;
+
+ struct pakfire_xfer* xfer;
+};
+
struct pakfire_httpclient {
struct pakfire_ctx* ctx;
int nrefs;
// cURL multi handle
CURLM* curl;
+ // Referenced xfers
+ TAILQ_HEAD(xfers, pakfire_httpclient_xfer) xfers;
+
// How many xfers do we have queued?
unsigned int total_xfers;
unsigned int total_downloadsize;
};
+static int pakfire_httpclient_xfer_create(
+ struct pakfire_httpclient_xfer** x, struct pakfire_xfer* xfer) {
+ struct pakfire_httpclient_xfer* e = NULL;
+
+ // Allocate some space
+ e = calloc(1, sizeof(*e));
+ if (!e)
+ return -errno;
+
+ // Store a reference to the xfer
+ e->xfer = pakfire_xfer_ref(xfer);
+
+ // Return the pointer
+ *x = e;
+
+ return 0;
+}
+
+static void pakfire_httpclient_xfer_free(struct pakfire_httpclient_xfer* x) {
+ if (x->xfer)
+ pakfire_xfer_unref(x->xfer);
+ free(x);
+}
+
+static struct pakfire_httpclient_xfer* pakfire_httpclient_xfer_find(
+ struct pakfire_httpclient* client, struct pakfire_xfer* xfer) {
+ struct pakfire_httpclient_xfer* e = NULL;
+
+ TAILQ_FOREACH(e, &client->xfers, nodes) {
+ if (e->xfer == xfer)
+ return e;
+ }
+
+ return NULL;
+}
+
static int pakfire_httpclient_check(struct pakfire_httpclient* client) {
struct pakfire_xfer* xfer = NULL;
int r;
}
static void pakfire_httpclient_free(struct pakfire_httpclient* client) {
+ struct pakfire_httpclient_xfer* e = NULL;
+
+ // Free any xfers that we still hold
+ for (;;) {
+ e = TAILQ_LAST(&client->xfers, xfers);
+ if (!e)
+ break;
+
+ TAILQ_REMOVE(&client->xfers, e, nodes);
+
+ pakfire_httpclient_xfer_free(e);
+ }
+
if (client->progress)
pakfire_progress_unref(client->progress);
if (client->curl)
// Set parallelism
c->max_parallel = DEFAULT_MAX_PARALLEL;
+ // Init the xfer queue
+ TAILQ_INIT(&c->xfers);
+
// Setup event loop
r = pakfire_httpclient_setup_loop(c, loop);
if (r)
int pakfire_httpclient_enqueue_xfer(struct pakfire_httpclient* client,
struct pakfire_xfer* xfer) {
+ struct pakfire_httpclient_xfer* e = NULL;
int r;
+ // Create a new object
+ r = pakfire_httpclient_xfer_create(&e, xfer);
+ if (r < 0)
+ return r;
+
// Prepare the xfer
r = pakfire_xfer_prepare(xfer, client->progress, 0);
- if (r)
- return r;
+ if (r < 0)
+ goto ERROR;
// Increment the xfer counter
client->total_xfers++;
r = curl_multi_add_handle(client->curl, pakfire_xfer_handle(xfer));
if (r) {
CTX_ERROR(client->ctx, "Adding handle failed: %s\n", curl_multi_strerror(r));
-
- return r;
+ goto ERROR;
}
+ // Keep a reference to the xfer
+ TAILQ_INSERT_TAIL(&client->xfers, e, nodes);
+
// Transfer enqueued
return pakfire_xfer_enqueued(xfer, client);
+
+ERROR:
+ if (e)
+ pakfire_httpclient_xfer_free(e);
+
+ return r;
}
int pakfire_httpclient_remove_xfer(struct pakfire_httpclient* client,
struct pakfire_xfer* xfer) {
+ struct pakfire_httpclient_xfer* e = NULL;
int r;
+ // Find reference
+ e = pakfire_httpclient_xfer_find(client, xfer);
+
// Decrement the xfers counter
client->total_xfers--;
r = curl_multi_remove_handle(client->curl, pakfire_xfer_handle(xfer));
if (r) {
CTX_ERROR(client->ctx, "Could not remove the handle: %s\n", curl_multi_strerror(r));
-
- return r;
+ goto ERROR;
}
- return 0;
+ERROR:
+ if (e)
+ pakfire_httpclient_xfer_free(e);
+
+ return r;
}
int pakfire_httpclient_run(struct pakfire_httpclient* client, const char* title) {
curl_easy_setopt(xfer->handle, CURLOPT_ACCEPT_ENCODING, "");
// Reference back to this xfer
- curl_easy_setopt(xfer->handle, CURLOPT_PRIVATE, pakfire_xfer_ref(xfer));
+ curl_easy_setopt(xfer->handle, CURLOPT_PRIVATE, xfer);
// Follow any redirects
curl_easy_setopt(xfer->handle, CURLOPT_FOLLOWLOCATION, 1);
goto ERROR;
// Return the reference
- *xfer = x;
-
- return 0;
+ *xfer = pakfire_xfer_ref(x);
ERROR:
if (x)
}
pakfire_xfer_error_code_t pakfire_xfer_done(struct pakfire_xfer* xfer, int code) {
- struct pakfire_xfer* private = NULL;
CURL* h = xfer->handle;
int r;
const char* scheme = NULL;
curl_off_t upload_size = 0;
curl_off_t upload_speed = 0;
- // Fetch the private pointer to this xfer
- r = curl_easy_getinfo(h, CURLINFO_PRIVATE, &private);
- if (r) {
- CTX_ERROR(xfer->ctx,
- "Could not fetch the private pointer: %s\n", curl_easy_strerror(r));
- goto ERROR;
- }
-
- // Check if the private pointer was set
- if (!private) {
- CTX_ERROR(xfer->ctx, "Private pointer not set\n");
- r = -EINVAL;
- goto ERROR;
- }
-
- // Reset the private pointer
- r = curl_easy_setopt(h, CURLOPT_PRIVATE, NULL);
- if (r) {
- CTX_ERROR(xfer->ctx,
- "Could not reset the private pointer: %s\n", curl_easy_strerror(r));
- goto ERROR;
- }
-
// Finish progress
r = pakfire_progress_finish(xfer->progress);
if (r)
// Remove the handle
if (xfer->client)
pakfire_httpclient_remove_xfer(xfer->client, xfer);
- if (private)
- pakfire_xfer_unref(private);
return r;
}