1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
22 /* Example application source code using the multi socket interface to
23 * download many files at once.
25 * This example features the same basic functionality as hiperfifo.c does,
26 * but this uses libev instead of libevent.
28 * Written by Jeff Pohlmeyer, converted to use libev by Markus Koetter
30 Requires libev and a (POSIX?) system that has mkfifo().
32 This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
35 When running, the program creates the named pipe "hiper.fifo"
37 Whenever there is input into the fifo, the program reads the input as a list
38 of URL's and creates some new easy handles to fetch each URL via the
39 curl_multi "hiper" API.
42 Thus, you can try a single URL:
43 % echo http://www.yahoo.com > hiper.fifo
45 Or a whole bunch of them:
46 % cat my-url-list > hiper.fifo
48 The fifo buffer is handled almost instantly, so you can even add more URL's
49 while the previous requests are still being downloaded.
52 For the sake of simplicity, URL length is limited to 1023 char's !
54 This is purely a demo app, all retrieved data is simply discarded by the write
66 #include <curl/curl.h>
72 #define DPRINT(x...) printf(x)
74 #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
77 /* Global information, common to all connections */
78 typedef struct _GlobalInfo
81 struct ev_io fifo_event;
82 struct ev_timer timer_event;
89 /* Information associated with a specific easy handle */
90 typedef struct _ConnInfo
95 char error[CURL_ERROR_SIZE];
99 /* Information associated with a specific socket */
100 typedef struct _SockInfo
102 curl_socket_t sockfd;
111 static void timer_cb(EV_P_ struct ev_timer *w, int revents);
113 /* Update the event timer after curl_multi library calls */
114 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
116 DPRINT("%s %li\n", __PRETTY_FUNCTION__, timeout_ms);
117 ev_timer_stop(g->loop, &g->timer_event);
120 double t = timeout_ms / 1000;
121 ev_timer_init(&g->timer_event, timer_cb, t, 0.);
122 ev_timer_start(g->loop, &g->timer_event);
124 timer_cb(g->loop, &g->timer_event, 0);
128 /* Die if we get a bad CURLMcode somewhere */
129 static void mcode_or_die(const char *where, CURLMcode code)
131 if ( CURLM_OK != code )
136 case CURLM_CALL_MULTI_PERFORM: s="CURLM_CALL_MULTI_PERFORM"; break;
137 case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
138 case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
139 case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
140 case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
141 case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
142 case CURLM_LAST: s="CURLM_LAST"; break;
143 default: s="CURLM_unknown";
145 case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
146 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
147 /* ignore this error */
150 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
157 /* Check for completed transfers, and remove their easy handles */
158 static void check_multi_info(GlobalInfo *g)
167 fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
168 while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
169 if (msg->msg == CURLMSG_DONE) {
170 easy = msg->easy_handle;
171 res = msg->data.result;
172 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
173 curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
174 fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
175 curl_multi_remove_handle(g->multi, easy);
177 curl_easy_cleanup(easy);
185 /* Called by libevent when we get action on a multi socket */
186 static void event_cb(EV_P_ struct ev_io *w, int revents)
188 DPRINT("%s w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
189 GlobalInfo *g = (GlobalInfo*) w->data;
192 int action = (revents&EV_READ?CURL_POLL_IN:0)|
193 (revents&EV_WRITE?CURL_POLL_OUT:0);
194 rc = curl_multi_socket_action(g->multi, w->fd, action, &g->still_running);
195 mcode_or_die("event_cb: curl_multi_socket_action", rc);
197 if ( g->still_running <= 0 )
199 fprintf(MSG_OUT, "last transfer done, kill timeout\n");
200 ev_timer_stop(g->loop, &g->timer_event);
204 /* Called by libevent when our timeout expires */
205 static void timer_cb(EV_P_ struct ev_timer *w, int revents)
207 DPRINT("%s w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
209 GlobalInfo *g = (GlobalInfo *)w->data;
212 rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
213 mcode_or_die("timer_cb: curl_multi_socket_action", rc);
217 /* Clean up the SockInfo structure */
218 static void remsock(SockInfo *f, GlobalInfo *g)
220 printf("%s \n", __PRETTY_FUNCTION__);
224 ev_io_stop(g->loop, &f->ev);
231 /* Assign information to a SockInfo structure */
232 static void setsock(SockInfo*f, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
234 printf("%s \n", __PRETTY_FUNCTION__);
236 int kind = (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0);
242 ev_io_stop(g->loop, &f->ev);
243 ev_io_init(&f->ev, event_cb, f->sockfd, kind);
246 ev_io_start(g->loop, &f->ev);
251 /* Initialize a new SockInfo structure */
252 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
254 SockInfo *fdp = calloc(sizeof(SockInfo), 1);
257 setsock(fdp, s, easy, action, g);
258 curl_multi_assign(g->multi, s, fdp);
261 /* CURLMOPT_SOCKETFUNCTION */
262 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
264 DPRINT("%s e %p s %i what %i cbp %p sockp %p\n",
265 __PRETTY_FUNCTION__, e, s, what, cbp, sockp);
267 GlobalInfo *g = (GlobalInfo*) cbp;
268 SockInfo *fdp = (SockInfo*) sockp;
269 const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE"};
272 "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
273 if ( what == CURL_POLL_REMOVE )
275 fprintf(MSG_OUT, "\n");
281 fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
282 addsock(s, e, what, g);
286 "Changing action from %s to %s\n",
287 whatstr[fdp->action], whatstr[what]);
288 setsock(fdp, s, e, what, g);
295 /* CURLOPT_WRITEFUNCTION */
296 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
298 size_t realsize = size * nmemb;
299 ConnInfo *conn = (ConnInfo*) data;
306 /* CURLOPT_PROGRESSFUNCTION */
307 static int prog_cb (void *p, double dltotal, double dlnow, double ult,
310 ConnInfo *conn = (ConnInfo *)p;
314 fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
319 /* Create a new easy handle, and add it to the global curl_multi */
320 static void new_conn(char *url, GlobalInfo *g )
325 conn = calloc(1, sizeof(ConnInfo));
326 memset(conn, 0, sizeof(ConnInfo));
329 conn->easy = curl_easy_init();
332 fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
336 conn->url = strdup(url);
337 curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
338 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
339 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn);
340 curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
341 curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
342 curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
343 curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
344 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
345 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
346 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
347 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
350 "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
351 rc = curl_multi_add_handle(g->multi, conn->easy);
352 mcode_or_die("new_conn: curl_multi_add_handle", rc);
354 /* note that the add_handle() will set a time-out to trigger very soon so
355 that the necessary socket_action() call will be called by this app */
358 /* This gets called whenever data is received from the fifo */
359 static void fifo_cb(EV_P_ struct ev_io *w, int revents)
364 GlobalInfo *g = (GlobalInfo *)w->data;
369 rv=fscanf(g->input, "%1023s%n", s, &n);
373 new_conn(s,g); /* if we read a URL, go get it! */
375 } while ( rv != EOF );
378 /* Create a named pipe and tell libevent to monitor it */
379 static int init_fifo (GlobalInfo *g)
382 static const char *fifo = "hiper.fifo";
383 curl_socket_t sockfd;
385 fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
386 if ( lstat (fifo, &st) == 0 )
388 if ( (st.st_mode & S_IFMT) == S_IFREG )
396 if ( mkfifo (fifo, 0600) == -1 )
401 sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
407 g->input = fdopen(sockfd, "r");
409 fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
410 ev_io_init(&g->fifo_event, fifo_cb, sockfd, EV_READ);
411 ev_io_start(g->loop, &g->fifo_event);
415 int main(int argc, char **argv)
422 memset(&g, 0, sizeof(GlobalInfo));
423 g.loop = ev_default_loop(0);
426 g.multi = curl_multi_init();
428 ev_timer_init(&g.timer_event, timer_cb, 0., 0.);
429 g.timer_event.data = &g;
430 g.fifo_event.data = &g;
431 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
432 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
433 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
434 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
436 /* we don't call any curl_multi_socket*() function yet as we have no handles
440 curl_multi_cleanup(g.multi);