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 Written by Jeff Pohlmeyer
27 Requires libevent version 2 and a (POSIX?) system that has mkfifo().
29 This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
32 When running, the program creates the named pipe "hiper.fifo"
34 Whenever there is input into the fifo, the program reads the input as a list
35 of URL's and creates some new easy handles to fetch each URL via the
36 curl_multi "hiper" API.
39 Thus, you can try a single URL:
40 % echo http://www.yahoo.com > hiper.fifo
42 Or a whole bunch of them:
43 % cat my-url-list > hiper.fifo
45 The fifo buffer is handled almost instantly, so you can even add more URL's
46 while the previous requests are still being downloaded.
49 For the sake of simplicity, URL length is limited to 1023 char's !
51 This is purely a demo app, all retrieved data is simply discarded by the write
63 #include <curl/curl.h>
64 #include <event2/event.h>
70 #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
73 /* Global information, common to all connections */
74 typedef struct _GlobalInfo
76 struct event_base *evbase;
77 struct event *fifo_event;
78 struct event *timer_event;
85 /* Information associated with a specific easy handle */
86 typedef struct _ConnInfo
91 char error[CURL_ERROR_SIZE];
95 /* Information associated with a specific socket */
96 typedef struct _SockInfo
109 /* Update the event timer after curl_multi library calls */
110 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
112 struct timeval timeout;
113 (void)multi; /* unused */
115 timeout.tv_sec = timeout_ms/1000;
116 timeout.tv_usec = (timeout_ms%1000)*1000;
117 fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
118 evtimer_add(g->timer_event, &timeout);
122 /* Die if we get a bad CURLMcode somewhere */
123 static void mcode_or_die(const char *where, CURLMcode code)
125 if ( CURLM_OK != code ) {
128 case CURLM_BAD_HANDLE: s="CURLM_BAD_HANDLE"; break;
129 case CURLM_BAD_EASY_HANDLE: s="CURLM_BAD_EASY_HANDLE"; break;
130 case CURLM_OUT_OF_MEMORY: s="CURLM_OUT_OF_MEMORY"; break;
131 case CURLM_INTERNAL_ERROR: s="CURLM_INTERNAL_ERROR"; break;
132 case CURLM_UNKNOWN_OPTION: s="CURLM_UNKNOWN_OPTION"; break;
133 case CURLM_LAST: s="CURLM_LAST"; break;
134 default: s="CURLM_unknown";
136 case CURLM_BAD_SOCKET: s="CURLM_BAD_SOCKET";
137 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
138 /* ignore this error */
141 fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
148 /* Check for completed transfers, and remove their easy handles */
149 static void check_multi_info(GlobalInfo *g)
158 fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
159 while ((msg = curl_multi_info_read(g->multi, &msgs_left))) {
160 if (msg->msg == CURLMSG_DONE) {
161 easy = msg->easy_handle;
162 res = msg->data.result;
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);
168 curl_easy_cleanup(easy);
176 /* Called by libevent when we get action on a multi socket */
177 static void event_cb(int fd, short kind, void *userp)
179 GlobalInfo *g = (GlobalInfo*) userp;
183 (kind & EV_READ ? CURL_CSELECT_IN : 0) |
184 (kind & EV_WRITE ? CURL_CSELECT_OUT : 0);
186 rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
187 mcode_or_die("event_cb: curl_multi_socket_action", rc);
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;
208 rc = curl_multi_socket_action(g->multi,
209 CURL_SOCKET_TIMEOUT, 0, &g->still_running);
210 mcode_or_die("timer_cb: curl_multi_socket_action", rc);
216 /* Clean up the SockInfo structure */
217 static void remsock(SockInfo *f)
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;
239 f->ev = event_new(g->evbase, f->sockfd, kind, event_cb, g);
241 event_add(f->ev, NULL);
246 /* Initialize a new SockInfo structure */
247 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
249 SockInfo *fdp = calloc(sizeof(SockInfo), 1);
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" };
264 "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
265 if (what == CURL_POLL_REMOVE) {
266 fprintf(MSG_OUT, "\n");
271 fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
272 addsock(s, e, what, g);
276 "Changing action from %s to %s\n",
277 whatstr[fdp->action], whatstr[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 static int prog_cb (void *p, double dltotal, double dlnow, double ult,
301 ConnInfo *conn = (ConnInfo *)p;
305 fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
310 /* Create a new easy handle, and add it to the global curl_multi */
311 static void new_conn(char *url, GlobalInfo *g )
316 conn = calloc(1, sizeof(ConnInfo));
317 memset(conn, 0, sizeof(ConnInfo));
320 conn->easy = curl_easy_init();
322 fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
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);
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);
341 /* note that the add_handle() will set a time-out to trigger very soon so
342 that the necessary socket_action() call will be called by this app */
345 /* This gets called whenever data is received from the fifo */
346 static void fifo_cb(int fd, short event, void *arg)
351 GlobalInfo *g = (GlobalInfo *)arg;
352 (void)fd; /* unused */
353 (void)event; /* unused */
357 rv=fscanf(g->input, "%1023s%n", s, &n);
360 new_conn(s,arg); /* if we read a URL, go get it! */
362 } while ( rv != EOF);
365 /* Create a named pipe and tell libevent to monitor it */
366 static const char *fifo = "hiper.fifo";
367 static int init_fifo (GlobalInfo *g)
370 curl_socket_t sockfd;
372 fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
373 if (lstat (fifo, &st) == 0) {
374 if ((st.st_mode & S_IFMT) == S_IFREG) {
381 if (mkfifo (fifo, 0600) == -1) {
385 sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
390 g->input = fdopen(sockfd, "r");
392 fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
393 g->fifo_event = event_new(g->evbase, sockfd, EV_READ|EV_PERSIST, fifo_cb, g);
394 event_add(g->fifo_event, NULL);
398 static void clean_fifo(GlobalInfo *g)
400 event_free(g->fifo_event);
405 int main(int argc, char **argv)
411 memset(&g, 0, sizeof(GlobalInfo));
412 g.evbase = event_base_new();
414 g.multi = curl_multi_init();
415 g.timer_event = evtimer_new(g.evbase, timer_cb, &g);
417 /* setup the generic multi interface options we want */
418 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
419 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
420 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
421 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
423 /* we don't call any curl_multi_socket*() function yet as we have no handles
426 event_base_dispatch(g.evbase);
428 /* this, of course, won't get called since only way to stop this program is
429 via ctrl-C, but it is here to show how cleanup /would/ be done. */
431 event_free(g.timer_event);
432 event_base_free(g.evbase);
433 curl_multi_cleanup(g.multi);