Initial commit of visual studio 9 git build superproject.
[git-build-vc9.git] / curl / docs / examples / hiperfifo.c
blob4dae7cae20bc17962dccff1500fab23d593dd199
1 /*****************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * $Id: hiperfifo.c,v 1.7 2008-05-22 21:20:09 danf 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"
18 sample programs.
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.
36 Note:
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
40 callback.
44 #include <stdio.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <sys/time.h>
48 #include <time.h>
49 #include <unistd.h>
50 #include <sys/poll.h>
51 #include <curl/curl.h>
52 #include <event.h>
53 #include <fcntl.h>
54 #include <sys/stat.h>
55 #include <errno.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;
65 CURLM *multi;
66 int prev_running;
67 int still_running;
68 FILE* input;
69 } GlobalInfo;
72 /* Information associated with a specific easy handle */
73 typedef struct _ConnInfo {
74 CURL *easy;
75 char *url;
76 GlobalInfo *global;
77 char error[CURL_ERROR_SIZE];
78 } ConnInfo;
81 /* Information associated with a specific socket */
82 typedef struct _SockInfo {
83 curl_socket_t sockfd;
84 CURL *easy;
85 int action;
86 long timeout;
87 struct event ev;
88 int evset;
89 GlobalInfo *global;
90 } SockInfo;
94 /* Update the event timer after curl_multi library calls */
95 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
97 struct timeval timeout;
98 (void)multi; /* unused */
100 timeout.tv_sec = timeout_ms/1000;
101 timeout.tv_usec = (timeout_ms%1000)*1000;
102 fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
103 evtimer_add(&g->timer_event, &timeout);
104 return 0;
107 /* Die if we get a bad CURLMcode somewhere */
108 static void mcode_or_die(const char *where, CURLMcode code)
110 if ( CURLM_OK != code ) {
111 const char *s;
112 switch (code) {
113 case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
114 case CURLM_OK: s="CURLM_OK"; break;
115 case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
116 case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
117 case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
118 case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
119 case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
120 case CURLM_LAST: s="CURLM_LAST"; break;
121 default: s="CURLM_unknown";
122 break;
123 case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
124 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
125 /* ignore this error */
126 return;
128 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
129 exit(code);
135 /* Check for completed transfers, and remove their easy handles */
136 static void check_run_count(GlobalInfo *g)
138 if (g->prev_running > g->still_running) {
139 char *eff_url=NULL;
140 CURLMsg *msg;
141 int msgs_left;
142 ConnInfo *conn=NULL;
143 CURL*easy;
144 CURLcode res;
146 fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
148 I am still uncertain whether it is safe to remove an easy handle
149 from inside the curl_multi_info_read loop, so here I will search
150 for completed transfers in the inner "while" loop, and then remove
151 them in the outer "do-while" loop...
153 do {
154 easy=NULL;
155 while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
156 if (msg->msg == CURLMSG_DONE) {
157 easy=msg->easy_handle;
158 res=msg->data.result;
159 break;
162 if (easy) {
163 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
164 curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
165 fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
166 curl_multi_remove_handle(g->multi, easy);
167 free(conn->url);
168 curl_easy_cleanup(easy);
169 free(conn);
171 } while ( easy );
173 g->prev_running = g->still_running;
178 /* Called by libevent when we get action on a multi socket */
179 static void event_cb(int fd, short kind, void *userp)
181 GlobalInfo *g = (GlobalInfo*) userp;
182 CURLMcode rc;
183 (void)kind; /* unused */
185 do {
186 rc = curl_multi_socket(g->multi, fd, &g->still_running);
187 } while (rc == CURLM_CALL_MULTI_PERFORM);
188 mcode_or_die("event_cb: curl_multi_socket", rc);
189 check_run_count(g);
190 if ( g->still_running <= 0 ) {
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)
203 GlobalInfo *g = (GlobalInfo *)userp;
204 CURLMcode rc;
205 (void)fd;
206 (void)kind;
208 do {
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);
212 check_run_count(g);
217 /* Clean up the SockInfo structure */
218 static void remsock(SockInfo *f)
220 if (f) {
221 if (f->evset)
222 event_del(&f->ev);
223 free(f);
229 /* Assign information to a SockInfo structure */
230 static void setsock(SockInfo*f, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
232 int kind =
233 (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0)|EV_PERSIST;
235 f->sockfd = s;
236 f->action = act;
237 f->easy = e;
238 if (f->evset)
239 event_del(&f->ev);
240 event_set(&f->ev, f->sockfd, kind, event_cb, g);
241 f->evset=1;
242 event_add(&f->ev, NULL);
247 /* Initialize a new SockInfo structure */
248 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g) {
249 SockInfo *fdp = calloc(sizeof(SockInfo), 1);
251 fdp->global = g;
252 setsock(fdp, s, easy, action, g);
253 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 const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
263 fprintf(MSG_OUT,
264 "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
265 if (what == CURL_POLL_REMOVE) {
266 fprintf(MSG_OUT, "\n");
267 remsock(fdp);
269 else {
270 if (!fdp) {
271 fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
272 addsock(s, e, what, g);
274 else {
275 fprintf(MSG_OUT,
276 "Changing action from %s to %s\n",
277 whatstr[fdp->action], whatstr[what]);
278 setsock(fdp, s, e, what, g);
281 return 0;
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;
291 (void)ptr;
292 (void)conn;
293 return realsize;
297 /* CURLOPT_PROGRESSFUNCTION */
298 static int prog_cb (void *p, double dltotal, double dlnow, double ult,
299 double uln)
301 ConnInfo *conn = (ConnInfo *)p;
302 (void)ult;
303 (void)uln;
305 fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
306 return 0;
310 /* Create a new easy handle, and add it to the global curl_multi */
311 static void new_conn(char *url, GlobalInfo *g )
313 ConnInfo *conn;
314 CURLMcode rc;
316 conn = calloc(1, sizeof(ConnInfo));
317 memset(conn, 0, sizeof(ConnInfo));
318 conn->error[0]='\0';
320 conn->easy = curl_easy_init();
321 if (!conn->easy) {
322 fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
323 exit(2);
325 conn->global = g;
326 conn->url = strdup(url);
327 curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
328 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
329 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn);
330 curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
331 curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
332 curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
333 curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
334 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
335 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
336 fprintf(MSG_OUT,
337 "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
338 rc =curl_multi_add_handle(g->multi, conn->easy);
339 mcode_or_die("new_conn: curl_multi_add_handle", rc);
340 do {
341 rc = curl_multi_socket_all(g->multi, &g->still_running);
342 } while (CURLM_CALL_MULTI_PERFORM == rc);
343 mcode_or_die("new_conn: curl_multi_socket_all", rc);
344 check_run_count(g);
347 /* This gets called whenever data is received from the fifo */
348 static void fifo_cb(int fd, short event, void *arg)
350 char s[1024];
351 long int rv=0;
352 int n=0;
353 GlobalInfo *g = (GlobalInfo *)arg;
354 (void)fd; /* unused */
355 (void)event; /* unused */
357 do {
358 s[0]='\0';
359 rv=fscanf(g->input, "%1023s%n", s, &n);
360 s[n]='\0';
361 if ( n && s[0] ) {
362 new_conn(s,arg); /* if we read a URL, go get it! */
363 } else break;
364 } while ( rv != EOF);
367 /* Create a named pipe and tell libevent to monitor it */
368 static int init_fifo (GlobalInfo *g)
370 struct stat st;
371 static const char *fifo = "hiper.fifo";
372 int sockfd;
374 fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
375 if (lstat (fifo, &st) == 0) {
376 if ((st.st_mode & S_IFMT) == S_IFREG) {
377 errno = EEXIST;
378 perror("lstat");
379 exit (1);
382 unlink(fifo);
383 if (mkfifo (fifo, 0600) == -1) {
384 perror("mkfifo");
385 exit (1);
387 sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
388 if (sockfd == -1) {
389 perror("open");
390 exit (1);
392 g->input = fdopen(sockfd, "r");
394 fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
395 event_set(&g->fifo_event, sockfd, EV_READ | EV_PERSIST, fifo_cb, g);
396 event_add(&g->fifo_event, NULL);
397 return (0);
400 int main(int argc, char **argv)
402 GlobalInfo g;
403 CURLMcode rc;
404 (void)argc;
405 (void)argv;
407 memset(&g, 0, sizeof(GlobalInfo));
408 event_init();
409 init_fifo(&g);
410 g.multi = curl_multi_init();
411 evtimer_set(&g.timer_event, timer_cb, &g);
412 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
413 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
414 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
415 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
416 do {
417 rc = curl_multi_socket_all(g.multi, &g.still_running);
418 } while (CURLM_CALL_MULTI_PERFORM == rc);
419 event_dispatch();
420 curl_multi_cleanup(g.multi);
421 return 0;