1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2014, 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_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
137 case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
138 case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
139 case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
140 case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
141 case CURLM_LAST: s="CURLM_LAST"; break;
142 default: s="CURLM_unknown";
144 case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
145 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
146 /* ignore this error */
149 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
156 /* Check for completed transfers, and remove their easy handles */
157 static void check_multi_info(GlobalInfo *g)
166 fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
167 while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
168 if (msg->msg == CURLMSG_DONE) {
169 easy = msg->easy_handle;
170 res = msg->data.result;
171 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
172 curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
173 fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
174 curl_multi_remove_handle(g->multi, easy);
176 curl_easy_cleanup(easy);
184 /* Called by libevent when we get action on a multi socket */
185 static void event_cb(EV_P_ struct ev_io *w, int revents)
187 DPRINT("%s w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
188 GlobalInfo *g = (GlobalInfo*) w->data;
191 int action = (revents&EV_READ?CURL_POLL_IN:0)|
192 (revents&EV_WRITE?CURL_POLL_OUT:0);
193 rc = curl_multi_socket_action(g->multi, w->fd, action, &g->still_running);
194 mcode_or_die("event_cb: curl_multi_socket_action", rc);
196 if ( g->still_running <= 0 )
198 fprintf(MSG_OUT, "last transfer done, kill timeout\n");
199 ev_timer_stop(g->loop, &g->timer_event);
203 /* Called by libevent when our timeout expires */
204 static void timer_cb(EV_P_ struct ev_timer *w, int revents)
206 DPRINT("%s w %p revents %i\n", __PRETTY_FUNCTION__, w, revents);
208 GlobalInfo *g = (GlobalInfo *)w->data;
211 rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, &g->still_running);
212 mcode_or_die("timer_cb: curl_multi_socket_action", rc);
216 /* Clean up the SockInfo structure */
217 static void remsock(SockInfo *f, GlobalInfo *g)
219 printf("%s \n", __PRETTY_FUNCTION__);
223 ev_io_stop(g->loop, &f->ev);
230 /* Assign information to a SockInfo structure */
231 static void setsock(SockInfo*f, curl_socket_t s, CURL*e, int act, GlobalInfo*g)
233 printf("%s \n", __PRETTY_FUNCTION__);
235 int kind = (act&CURL_POLL_IN?EV_READ:0)|(act&CURL_POLL_OUT?EV_WRITE:0);
241 ev_io_stop(g->loop, &f->ev);
242 ev_io_init(&f->ev, event_cb, f->sockfd, kind);
245 ev_io_start(g->loop, &f->ev);
250 /* Initialize a new SockInfo structure */
251 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
253 SockInfo *fdp = calloc(sizeof(SockInfo), 1);
256 setsock(fdp, s, easy, action, g);
257 curl_multi_assign(g->multi, s, fdp);
260 /* CURLMOPT_SOCKETFUNCTION */
261 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
263 DPRINT("%s e %p s %i what %i cbp %p sockp %p\n",
264 __PRETTY_FUNCTION__, e, s, what, cbp, sockp);
266 GlobalInfo *g = (GlobalInfo*) cbp;
267 SockInfo *fdp = (SockInfo*) sockp;
268 const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE"};
271 "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
272 if ( what == CURL_POLL_REMOVE )
274 fprintf(MSG_OUT, "\n");
280 fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
281 addsock(s, e, what, g);
285 "Changing action from %s to %s\n",
286 whatstr[fdp->action], whatstr[what]);
287 setsock(fdp, s, e, what, g);
294 /* CURLOPT_WRITEFUNCTION */
295 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
297 size_t realsize = size * nmemb;
298 ConnInfo *conn = (ConnInfo*) data;
305 /* CURLOPT_PROGRESSFUNCTION */
306 static int prog_cb (void *p, double dltotal, double dlnow, double ult,
309 ConnInfo *conn = (ConnInfo *)p;
313 fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
318 /* Create a new easy handle, and add it to the global curl_multi */
319 static void new_conn(char *url, GlobalInfo *g )
324 conn = calloc(1, sizeof(ConnInfo));
325 memset(conn, 0, sizeof(ConnInfo));
328 conn->easy = curl_easy_init();
331 fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
335 conn->url = strdup(url);
336 curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
337 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
338 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn);
339 curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
340 curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
341 curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
342 curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
343 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
344 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
345 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L);
346 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L);
349 "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
350 rc = curl_multi_add_handle(g->multi, conn->easy);
351 mcode_or_die("new_conn: curl_multi_add_handle", rc);
353 /* note that the add_handle() will set a time-out to trigger very soon so
354 that the necessary socket_action() call will be called by this app */
357 /* This gets called whenever data is received from the fifo */
358 static void fifo_cb(EV_P_ struct ev_io *w, int revents)
363 GlobalInfo *g = (GlobalInfo *)w->data;
368 rv=fscanf(g->input, "%1023s%n", s, &n);
372 new_conn(s,g); /* if we read a URL, go get it! */
374 } while ( rv != EOF );
377 /* Create a named pipe and tell libevent to monitor it */
378 static int init_fifo (GlobalInfo *g)
381 static const char *fifo = "hiper.fifo";
382 curl_socket_t sockfd;
384 fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
385 if ( lstat (fifo, &st) == 0 )
387 if ( (st.st_mode & S_IFMT) == S_IFREG )
395 if ( mkfifo (fifo, 0600) == -1 )
400 sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
406 g->input = fdopen(sockfd, "r");
408 fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
409 ev_io_init(&g->fifo_event, fifo_cb, sockfd, EV_READ);
410 ev_io_start(g->loop, &g->fifo_event);
414 int main(int argc, char **argv)
421 memset(&g, 0, sizeof(GlobalInfo));
422 g.loop = ev_default_loop(0);
425 g.multi = curl_multi_init();
427 ev_timer_init(&g.timer_event, timer_cb, 0., 0.);
428 g.timer_event.data = &g;
429 g.fifo_event.data = &g;
430 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
431 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
432 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
433 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
435 /* we don't call any curl_multi_socket*() function yet as we have no handles
439 curl_multi_cleanup(g.multi);