1
/*****************************************************************************
3
* Project ___| | | | _ \| |
5
* | (__| |_| | _ <| |___
6
* \___|\___/|_| \_\_____|
8
* $Id: hiperfifo.c,v 1.2 2006-10-08 22:19:25 bagder Exp $
10
* Example application source code using the multi socket interface to
11
* download many files at once.
13
* Written by Jeff Pohlmeyer
15
Requires libevent and a (POSIX?) system that has mkfifo().
17
This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
20
When running, the program creates the named pipe "hiper.fifo"
22
Whenever there is input into the fifo, the program reads the input as a list
23
of URL's and creates some new easy handles to fetch each URL via the
24
curl_multi "hiper" API.
27
Thus, you can try a single URL:
28
% echo http://www.yahoo.com > hiper.fifo
30
Or a whole bunch of them:
31
% cat my-url-list > hiper.fifo
33
The fifo buffer is handled almost instantly, so you can even add more URL's
34
while the previous requests are still being downloaded.
37
For the sake of simplicity, URL length is limited to 1023 char's !
39
This is purely a demo app, all retrieved data is simply discarded by the write
51
#include <curl/curl.h>
58
#define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
61
/* Global information, common to all connections */
62
typedef struct _GlobalInfo {
63
struct event fifo_event;
64
struct event timer_event;
72
/* Information associated with a specific easy handle */
73
typedef struct _ConnInfo {
77
char error[CURL_ERROR_SIZE];
81
/* Information associated with a specific socket */
82
typedef struct _SockInfo {
94
/* Update the event timer after curl_multi library calls */
95
static void update_timeout(GlobalInfo *g)
98
struct timeval timeout;
100
curl_multi_timeout(g->multi, &timeout_ms);
104
timeout.tv_sec = timeout_ms/1000;
105
timeout.tv_usec = (timeout_ms%1000)*1000;
106
evtimer_add(&g->timer_event, &timeout);
111
/* Die if we get a bad CURLMcode somewhere */
112
void mcode_or_die(char *where, CURLMcode code) {
113
if ( CURLM_OK != code ) {
116
case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
117
case CURLM_OK: s="CURLM_OK"; break;
118
case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
119
case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
120
case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
121
case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
122
case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET"; break;
123
case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
124
case CURLM_LAST: s="CURLM_LAST"; break;
125
default: s="CURLM_unknown";
127
fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
134
/* Check for completed transfers, and remove their easy handles */
135
static void check_run_count(GlobalInfo *g)
137
if (g->prev_running > g->still_running) {
145
fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
147
I am still uncertain whether it is safe to remove an easy handle
148
from inside the curl_multi_info_read loop, so here I will search
149
for completed transfers in the inner "while" loop, and then remove
150
them in the outer "do-while" loop...
154
while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
155
if (msg->msg == CURLMSG_DONE) {
156
easy=msg->easy_handle;
157
res=msg->data.result;
162
curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
163
curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
164
fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
165
curl_multi_remove_handle(g->multi, easy);
167
curl_easy_cleanup(easy);
172
g->prev_running = g->still_running;
177
/* Called by libevent when we get action on a multi socket */
178
static void event_cb(int fd, short kind, void *userp)
180
GlobalInfo *g = (GlobalInfo*) userp;
184
rc = curl_multi_socket(g->multi, fd, &g->still_running);
185
} while (rc == CURLM_CALL_MULTI_PERFORM);
186
mcode_or_die("event_cb: curl_multi_socket", rc);
188
if(g->still_running) {
191
fprintf(MSG_OUT, "last transfer done, kill timeout\n");
192
if (evtimer_pending(&g->timer_event, NULL)) {
193
evtimer_del(&g->timer_event);
200
/* Called by libevent when our timeout expires */
201
static void timer_cb(int fd, short kind, void *userp)
205
GlobalInfo *g = (GlobalInfo *)userp;
209
rc = curl_multi_socket(g->multi, CURL_SOCKET_TIMEOUT, &g->still_running);
210
} while (rc == CURLM_CALL_MULTI_PERFORM);
211
mcode_or_die("timer_cb: curl_multi_socket", rc);
213
if ( g->still_running ) { update_timeout(g); }
218
/* Clean up the SockInfo structure */
219
static void remsock(SockInfo *f)
222
if (f->evset) { event_del(&f->ev); }
228
/* Assign information to a SockInfo structure */
229
static void setsock(SockInfo*f, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
232
(act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0)|EV_PERSIST;
237
if (f->evset) { event_del(&f->ev); }
238
event_set( &f->ev, f->sockfd, kind, event_cb, g);
240
event_add(&f->ev, NULL);
245
/* Initialize a new SockInfo structure */
246
static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g) {
247
SockInfo *fdp = calloc(sizeof(SockInfo), 1);
250
setsock(fdp, s, easy, action, g);
251
curl_multi_assign(g->multi, s, fdp);
256
/* CURLMOPT_SOCKETFUNCTION */
257
static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
259
GlobalInfo *g = (GlobalInfo*) cbp;
260
SockInfo *fdp = (SockInfo*) sockp;
261
char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
264
"socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
265
if (what == CURL_POLL_REMOVE) {
266
fprintf(MSG_OUT, "\n");
270
fprintf(MSG_OUT, "Adding data: %s%s\n",
271
what&CURL_POLL_IN?"READ":"",
272
what&CURL_POLL_OUT?"WRITE":"" );
273
addsock(s, e, what, g);
277
"Changing action from %d to %d\n", fdp->action, what);
278
setsock(fdp, s, e, what, g);
286
/* CURLOPT_WRITEFUNCTION */
287
static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
289
size_t realsize = size * nmemb;
290
ConnInfo *conn = (ConnInfo*) data;
297
/* CURLOPT_PROGRESSFUNCTION */
298
int prog_cb (void *p, double dltotal, double dlnow, double ult, double uln)
300
ConnInfo *conn = (ConnInfo *)p;
301
fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
306
/* Create a new easy handle, and add it to the global curl_multi */
307
void new_conn(char *url, GlobalInfo *g ) {
311
conn = calloc(1, sizeof(ConnInfo));
312
memset(conn, 0, sizeof(ConnInfo));
315
conn->easy = curl_easy_init();
317
fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
321
conn->url = strdup(url);
322
curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
323
curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
324
curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
325
curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 0);
326
curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
327
curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
328
curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0);
329
curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
330
curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
332
"Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
333
rc =curl_multi_add_handle(g->multi, conn->easy);
334
mcode_or_die("new_conn: curl_multi_add_handle", rc);
336
rc = curl_multi_socket_all(g->multi, &g->still_running);
337
} while (CURLM_CALL_MULTI_PERFORM == rc);
338
mcode_or_die("new_conn: curl_multi_socket_all", rc);
344
/* This gets called whenever data is received from the fifo */
345
void fifo_cb(int fd, short event, void *arg) {
349
GlobalInfo *g = (GlobalInfo *)arg;
353
rv=fscanf(g->input, "%1023s%n", s, &n);
356
new_conn(s,arg); /* if we read a URL, go get it! */
358
} while ( rv != EOF);
363
/* Create a named pipe and tell libevent to monitor it */
364
int init_fifo (GlobalInfo *g) {
366
char *fifo = "hiper.fifo";
369
fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
370
if (lstat (fifo, &st) == 0) {
371
if ((st.st_mode & S_IFMT) == S_IFREG) {
378
if (mkfifo (fifo, 0600) == -1) {
382
socket = open(fifo, O_RDWR | O_NONBLOCK, 0);
387
g->input = fdopen(socket, "r");
389
fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
390
event_set(&g->fifo_event, socket, EV_READ | EV_PERSIST, fifo_cb, g);
391
event_add(&g->fifo_event, NULL);
397
int main(int argc, char **argv)
402
memset(&g, 0, sizeof(GlobalInfo));
405
g.multi = curl_multi_init();
406
evtimer_set(&g.timer_event, timer_cb, &g);
407
curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
408
curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
410
rc = curl_multi_socket_all(g.multi, &g.still_running);
411
} while (CURLM_CALL_MULTI_PERFORM == rc);
414
curl_multi_cleanup(g.multi);