-/*****************************************************************************
+/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * $Id$
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
- * Example application source code using the multi socket interface to
- * download many files at once.
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
*
- * Written by Jeff Pohlmeyer
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+/* Example application source code using the multi socket interface to
+ download many files at once.
+
+Written by Jeff Pohlmeyer
-Requires libevent and a (POSIX?) system that has mkfifo().
+Requires libevent version 2 and a (POSIX?) system that has mkfifo().
This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
sample programs.
#include <unistd.h>
#include <sys/poll.h>
#include <curl/curl.h>
-#include <event.h>
+#include <event2/event.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>
/* Global information, common to all connections */
-typedef struct _GlobalInfo {
- struct event fifo_event;
- struct event timer_event;
+typedef struct _GlobalInfo
+{
+ struct event_base *evbase;
+ struct event *fifo_event;
+ struct event *timer_event;
CURLM *multi;
- int prev_running;
int still_running;
FILE* input;
} GlobalInfo;
/* Information associated with a specific easy handle */
-typedef struct _ConnInfo {
+typedef struct _ConnInfo
+{
CURL *easy;
char *url;
GlobalInfo *global;
/* Information associated with a specific socket */
-typedef struct _SockInfo {
+typedef struct _SockInfo
+{
curl_socket_t sockfd;
CURL *easy;
int action;
long timeout;
- struct event ev;
+ struct event *ev;
int evset;
GlobalInfo *global;
} SockInfo;
static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
{
struct timeval timeout;
+ (void)multi; /* unused */
timeout.tv_sec = timeout_ms/1000;
timeout.tv_usec = (timeout_ms%1000)*1000;
fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
- evtimer_add(&g->timer_event, &timeout);
+ evtimer_add(g->timer_event, &timeout);
return 0;
}
-
-
/* Die if we get a bad CURLMcode somewhere */
-void mcode_or_die(char *where, CURLMcode code) {
+static void mcode_or_die(const char *where, CURLMcode code)
+{
if ( CURLM_OK != code ) {
- char *s;
+ const char *s;
switch (code) {
- case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
- case CURLM_OK: s="CURLM_OK"; break;
case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
- case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET"; break;
case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
case CURLM_LAST: s="CURLM_LAST"; break;
default: s="CURLM_unknown";
+ break;
+ case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
+ fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
+ /* ignore this error */
+ return;
}
fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
exit(code);
/* Check for completed transfers, and remove their easy handles */
-static void check_run_count(GlobalInfo *g)
+static void check_multi_info(GlobalInfo *g)
{
- if (g->prev_running > g->still_running) {
- char *eff_url=NULL;
- CURLMsg *msg;
- int msgs_left;
- ConnInfo *conn=NULL;
- CURL*easy;
- CURLcode res;
-
- fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
- /*
- I am still uncertain whether it is safe to remove an easy handle
- from inside the curl_multi_info_read loop, so here I will search
- for completed transfers in the inner "while" loop, and then remove
- them in the outer "do-while" loop...
- */
- do {
- easy=NULL;
- while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
- if (msg->msg == CURLMSG_DONE) {
- easy=msg->easy_handle;
- res=msg->data.result;
- break;
- }
- }
- if (easy) {
- curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
- curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
- fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
- curl_multi_remove_handle(g->multi, easy);
- free(conn->url);
- curl_easy_cleanup(easy);
- free(conn);
- }
- } while ( easy );
+ char *eff_url;
+ CURLMsg *msg;
+ int msgs_left;
+ ConnInfo *conn;
+ CURL *easy;
+ CURLcode res;
+
+ fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
+ while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
+ if (msg->msg == CURLMSG_DONE) {
+ easy = msg->easy_handle;
+ res = msg->data.result;
+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
+ curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
+ fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
+ curl_multi_remove_handle(g->multi, easy);
+ free(conn->url);
+ curl_easy_cleanup(easy);
+ free(conn);
+ }
}
- g->prev_running = g->still_running;
}
GlobalInfo *g = (GlobalInfo*) userp;
CURLMcode rc;
- do {
- rc = curl_multi_socket(g->multi, fd, &g->still_running);
- } while (rc == CURLM_CALL_MULTI_PERFORM);
- mcode_or_die("event_cb: curl_multi_socket", rc);
- check_run_count(g);
+ int action =
+ (kind & EV_READ ? CURL_CSELECT_IN : 0) |
+ (kind & EV_WRITE ? CURL_CSELECT_OUT : 0);
+
+ rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
+ mcode_or_die("event_cb: curl_multi_socket_action", rc);
+
+ check_multi_info(g);
if ( g->still_running <= 0 ) {
fprintf(MSG_OUT, "last transfer done, kill timeout\n");
- if (evtimer_pending(&g->timer_event, NULL)) {
- evtimer_del(&g->timer_event);
+ if (evtimer_pending(g->timer_event, NULL)) {
+ evtimer_del(g->timer_event);
}
}
}
/* Called by libevent when our timeout expires */
static void timer_cb(int fd, short kind, void *userp)
{
- (void)fd;
- (void)kind;
GlobalInfo *g = (GlobalInfo *)userp;
CURLMcode rc;
+ (void)fd;
+ (void)kind;
- do {
- rc = curl_multi_socket(g->multi, CURL_SOCKET_TIMEOUT, &g->still_running);
- } while (rc == CURLM_CALL_MULTI_PERFORM);
- mcode_or_die("timer_cb: curl_multi_socket", rc);
- check_run_count(g);
+ rc = curl_multi_socket_action(g->multi,
+ CURL_SOCKET_TIMEOUT, 0, &g->still_running);
+ mcode_or_die("timer_cb: curl_multi_socket_action", rc);
+ check_multi_info(g);
}
/* Clean up the SockInfo structure */
static void remsock(SockInfo *f)
{
- if (!f) { return; }
- if (f->evset) { event_del(&f->ev); }
- free(f);
+ if (f) {
+ if (f->evset)
+ event_free(f->ev);
+ free(f);
+ }
}
f->sockfd = s;
f->action = act;
f->easy = e;
- if (f->evset) { event_del(&f->ev); }
- event_set( &f->ev, f->sockfd, kind, event_cb, g);
- f->evset=1;
- event_add(&f->ev, NULL);
+ if (f->evset)
+ event_free(f->ev);
+ f->ev = event_new(g->evbase, f->sockfd, kind, event_cb, g);
+ f->evset = 1;
+ event_add(f->ev, NULL);
}
/* Initialize a new SockInfo structure */
-static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g) {
+static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
+{
SockInfo *fdp = calloc(sizeof(SockInfo), 1);
fdp->global = g;
curl_multi_assign(g->multi, s, fdp);
}
-
-
/* CURLMOPT_SOCKETFUNCTION */
static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
{
GlobalInfo *g = (GlobalInfo*) cbp;
SockInfo *fdp = (SockInfo*) sockp;
- char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
+ const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
fprintf(MSG_OUT,
"socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
if (what == CURL_POLL_REMOVE) {
fprintf(MSG_OUT, "\n");
remsock(fdp);
- } else {
+ }
+ else {
if (!fdp) {
- fprintf(MSG_OUT, "Adding data: %s%s\n",
- what&CURL_POLL_IN?"READ":"",
- what&CURL_POLL_OUT?"WRITE":"" );
+ fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
addsock(s, e, what, g);
}
else {
fprintf(MSG_OUT,
- "Changing action from %d to %d\n", fdp->action, what);
+ "Changing action from %s to %s\n",
+ whatstr[fdp->action], whatstr[what]);
setsock(fdp, s, e, what, g);
}
}
/* CURLOPT_PROGRESSFUNCTION */
-int prog_cb (void *p, double dltotal, double dlnow, double ult, double uln)
+static int prog_cb (void *p, double dltotal, double dlnow, double ult,
+ double uln)
{
ConnInfo *conn = (ConnInfo *)p;
+ (void)ult;
+ (void)uln;
+
fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
return 0;
}
/* Create a new easy handle, and add it to the global curl_multi */
-void new_conn(char *url, GlobalInfo *g ) {
+static void new_conn(char *url, GlobalInfo *g )
+{
ConnInfo *conn;
CURLMcode rc;
curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
- curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 0);
+ curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
- curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0);
+ curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
fprintf(MSG_OUT,
"Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
- rc =curl_multi_add_handle(g->multi, conn->easy);
+ rc = curl_multi_add_handle(g->multi, conn->easy);
mcode_or_die("new_conn: curl_multi_add_handle", rc);
- do {
- rc = curl_multi_socket_all(g->multi, &g->still_running);
- } while (CURLM_CALL_MULTI_PERFORM == rc);
- mcode_or_die("new_conn: curl_multi_socket_all", rc);
- check_run_count(g);
-}
-
+ /* note that the add_handle() will set a time-out to trigger very soon so
+ that the necessary socket_action() call will be called by this app */
+}
/* This gets called whenever data is received from the fifo */
-void fifo_cb(int fd, short event, void *arg) {
+static void fifo_cb(int fd, short event, void *arg)
+{
char s[1024];
long int rv=0;
int n=0;
GlobalInfo *g = (GlobalInfo *)arg;
+ (void)fd; /* unused */
+ (void)event; /* unused */
do {
s[0]='\0';
} while ( rv != EOF);
}
-
-
/* Create a named pipe and tell libevent to monitor it */
-int init_fifo (GlobalInfo *g) {
+static const char *fifo = "hiper.fifo";
+static int init_fifo (GlobalInfo *g)
+{
struct stat st;
- char *fifo = "hiper.fifo";
- int socket;
+ curl_socket_t sockfd;
fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
if (lstat (fifo, &st) == 0) {
perror("mkfifo");
exit (1);
}
- socket = open(fifo, O_RDWR | O_NONBLOCK, 0);
- if (socket == -1) {
- perror("open");
- exit (1);
+ sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
+ if (sockfd == -1) {
+ perror("open");
+ exit (1);
}
- g->input = fdopen(socket, "r");
+ g->input = fdopen(sockfd, "r");
fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
- event_set(&g->fifo_event, socket, EV_READ | EV_PERSIST, fifo_cb, g);
- event_add(&g->fifo_event, NULL);
+ g->fifo_event = event_new(g->evbase, sockfd, EV_READ|EV_PERSIST, fifo_cb, g);
+ event_add(g->fifo_event, NULL);
return (0);
}
-
+static void clean_fifo(GlobalInfo *g)
+{
+ event_free(g->fifo_event);
+ fclose(g->input);
+ unlink(fifo);
+}
int main(int argc, char **argv)
{
GlobalInfo g;
- CURLMcode rc;
+ (void)argc;
+ (void)argv;
memset(&g, 0, sizeof(GlobalInfo));
- event_init();
+ g.evbase = event_base_new();
init_fifo(&g);
g.multi = curl_multi_init();
- evtimer_set(&g.timer_event, timer_cb, &g);
+ g.timer_event = evtimer_new(g.evbase, timer_cb, &g);
+
+ /* setup the generic multi interface options we want */
curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
- do {
- rc = curl_multi_socket_all(g.multi, &g.still_running);
- } while (CURLM_CALL_MULTI_PERFORM == rc);
- event_dispatch();
+
+ /* we don't call any curl_multi_socket*() function yet as we have no handles
+ added! */
+
+ event_base_dispatch(g.evbase);
+
+ /* this, of course, won't get called since only way to stop this program is
+ via ctrl-C, but it is here to show how cleanup /would/ be done. */
+ clean_fifo(&g);
+ event_free(g.timer_event);
+ event_base_free(g.evbase);
curl_multi_cleanup(g.multi);
return 0;
}