1
/*****************************************************************************
3
* Project ___| | | | _ \| |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
8
* $Id: evhiperfifo.c,v 1.1 2008-11-21 10:10:33 bagder Exp $
10
* Example application source code using the multi socket interface to
11
* download many files at once.
13
* This example features the same basic functionality as hiperfifo.c does,
14
* but this uses libev instead of libevent.
16
* Written by Jeff Pohlmeyer, converted to use libev by Markus Koetter
18
Requires libev and a (POSIX?) system that has mkfifo().
20
This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
23
When running, the program creates the named pipe "hiper.fifo"
25
Whenever there is input into the fifo, the program reads the input as a list
26
of URL's and creates some new easy handles to fetch each URL via the
27
curl_multi "hiper" API.
30
Thus, you can try a single URL:
31
% echo http://www.yahoo.com > hiper.fifo
33
Or a whole bunch of them:
34
% cat my-url-list > hiper.fifo
36
The fifo buffer is handled almost instantly, so you can even add more URL's
37
while the previous requests are still being downloaded.
40
For the sake of simplicity, URL length is limited to 1023 char's !
42
This is purely a demo app, all retrieved data is simply discarded by the write
54
#include <curl/curl.h>
60
#define DPRINT(x...) printf(x)
62
#define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
65
/* Global information, common to all connections */
66
typedef struct _GlobalInfo
69
struct ev_io fifo_event;
70
struct ev_timer timer_event;
78
/* Information associated with a specific easy handle */
79
typedef struct _ConnInfo
84
char error[CURL_ERROR_SIZE];
88
/* Information associated with a specific socket */
89
typedef struct _SockInfo
100
static void timer_cb(EV_P_ struct ev_timer *w, int revents);
102
/* Update the event timer after curl_multi library calls */
103
static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
105
DPRINT("%s %li\n", __PRETTY_FUNCTION__, timeout_ms);
106
ev_timer_stop(g->loop, &g->timer_event);
109
double t = timeout_ms / 1000;
110
ev_timer_init(&g->timer_event, timer_cb, t, 0.);
111
ev_timer_start(g->loop, &g->timer_event);
113
timer_cb(g->loop, &g->timer_event, 0);
117
/* Die if we get a bad CURLMcode somewhere */
118
static void mcode_or_die(const char *where, CURLMcode code)
120
if ( CURLM_OK != code )
125
case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
126
case CURLM_OK: s="CURLM_OK"; break;
127
case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
128
case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
129
case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
130
case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
131
case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
132
case CURLM_LAST: s="CURLM_LAST"; break;
133
default: s="CURLM_unknown";
135
case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
136
fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
137
/* ignore this error */
140
fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
147
/* Check for completed transfers, and remove their easy handles */
148
static void check_run_count(GlobalInfo *g)
150
DPRINT("%s prev %i still %i\n", __PRETTY_FUNCTION__,
151
g->prev_running, g->still_running);
152
if ( g->prev_running > g->still_running )
161
fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
163
I am still uncertain whether it is safe to remove an easy
164
handle from inside the curl_multi_info_read loop, so here I
165
will search for completed transfers in the inner "while"
166
loop, and then remove them in the outer "do-while" loop...
171
while ( (msg = curl_multi_info_read(g->multi, &msgs_left)) )
173
if ( msg->msg == CURLMSG_DONE )
175
easy=msg->easy_handle;
176
res=msg->data.result;
181
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
182
curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
183
fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
184
curl_multi_remove_handle(g->multi, easy);
186
curl_easy_cleanup(easy);
192
g->prev_running = g->still_running;
196
/* Called by libevent when we get action on a multi socket */
197
static void event_cb(EV_P_ struct ev_io *w, int revents)
199
DPRINT("%s w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
200
GlobalInfo *g = (GlobalInfo*) w->data;
203
int action = (revents&EV_READ?CURL_POLL_IN:0)|
204
(revents&EV_WRITE?CURL_POLL_OUT:0);
207
rc = curl_multi_socket_action(g->multi, w->fd, action, &g->still_running);
208
} while ( rc == CURLM_CALL_MULTI_PERFORM );
209
mcode_or_die("event_cb: curl_multi_socket", rc);
211
if ( g->still_running <= 0 )
213
fprintf(MSG_OUT, "last transfer done, kill timeout\n");
214
ev_timer_stop(g->loop, &g->timer_event);
218
/* Called by libevent when our timeout expires */
219
static void timer_cb(EV_P_ struct ev_timer *w, int revents)
221
DPRINT("%s w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
223
GlobalInfo *g = (GlobalInfo *)w->data;
228
rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
229
} while ( rc == CURLM_CALL_MULTI_PERFORM );
230
mcode_or_die("timer_cb: curl_multi_socket", rc);
234
/* Clean up the SockInfo structure */
235
static void remsock(SockInfo *f, GlobalInfo *g)
237
printf("%s \n", __PRETTY_FUNCTION__);
241
ev_io_stop(g->loop, &f->ev);
248
/* Assign information to a SockInfo structure */
249
static void setsock(SockInfo*f, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
251
printf("%s \n", __PRETTY_FUNCTION__);
253
int kind = (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0);
259
ev_io_stop(g->loop, &f->ev);
260
ev_io_init(&f->ev, event_cb, f->sockfd, kind);
263
ev_io_start(g->loop, &f->ev);
268
/* Initialize a new SockInfo structure */
269
static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
271
SockInfo *fdp = calloc(sizeof(SockInfo), 1);
274
setsock(fdp, s, easy, action, g);
275
curl_multi_assign(g->multi, s, fdp);
278
/* CURLMOPT_SOCKETFUNCTION */
279
static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
281
DPRINT("%s e %p s %i what %i cbp %p sockp %p\n",
282
__PRETTY_FUNCTION__, e, s, what, cbp, sockp);
284
GlobalInfo *g = (GlobalInfo*) cbp;
285
SockInfo *fdp = (SockInfo*) sockp;
286
const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE"};
289
"socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
290
if ( what == CURL_POLL_REMOVE )
292
fprintf(MSG_OUT, "\n");
298
fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
299
addsock(s, e, what, g);
303
"Changing action from %s to %s\n",
304
whatstr[fdp->action], whatstr[what]);
305
setsock(fdp, s, e, what, g);
312
/* CURLOPT_WRITEFUNCTION */
313
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
315
size_t realsize = size * nmemb;
316
ConnInfo *conn = (ConnInfo*) data;
323
/* CURLOPT_PROGRESSFUNCTION */
324
static int prog_cb (void *p, double dltotal, double dlnow, double ult,
327
ConnInfo *conn = (ConnInfo *)p;
331
fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
336
/* Create a new easy handle, and add it to the global curl_multi */
337
static void new_conn(char *url, GlobalInfo *g )
342
conn = calloc(1, sizeof(ConnInfo));
343
memset(conn, 0, sizeof(ConnInfo));
346
conn->easy = curl_easy_init();
349
fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
353
conn->url = strdup(url);
354
curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
355
curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
356
curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
357
curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
358
curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
359
curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
360
curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
361
curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
362
curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
363
curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
364
curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
367
"Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
368
rc =curl_multi_add_handle(g->multi, conn->easy);
369
mcode_or_die("new_conn: curl_multi_add_handle", rc);
371
mcode_or_die("new_conn: curl_multi_socket_all", rc);
375
/* This gets called whenever data is received from the fifo */
376
static void fifo_cb(EV_P_ struct ev_io *w, int revents)
381
GlobalInfo *g = (GlobalInfo *)w->data;
386
rv=fscanf(g->input, "%1023s%n", s, &n);
390
new_conn(s,g); /* if we read a URL, go get it! */
392
} while ( rv != EOF );
395
/* Create a named pipe and tell libevent to monitor it */
396
static int init_fifo (GlobalInfo *g)
399
static const char *fifo = "hiper.fifo";
402
fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
403
if ( lstat (fifo, &st) == 0 )
405
if ( (st.st_mode & S_IFMT) == S_IFREG )
413
if ( mkfifo (fifo, 0600) == -1 )
418
sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
424
g->input = fdopen(sockfd, "r");
426
fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
427
ev_io_init(&g->fifo_event, fifo_cb, sockfd, EV_READ);
428
ev_io_start(g->loop, &g->fifo_event);
432
int main(int argc, char **argv)
439
memset(&g, 0, sizeof(GlobalInfo));
440
g.loop = ev_default_loop(0);
443
g.multi = curl_multi_init();
445
ev_timer_init(&g.timer_event, timer_cb, 0., 0.);
446
g.timer_event.data = &g;
447
g.fifo_event.data = &g;
448
curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
449
curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
450
curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
451
curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
454
rc = curl_multi_socket_all(g.multi, &g.still_running);
455
} while ( CURLM_CALL_MULTI_PERFORM == rc );
458
curl_multi_cleanup(g.multi);