Annotation of embedaddon/curl/docs/examples/hiperfifo.c, revision 1.1.1.1
1.1 misho 1: /***************************************************************************
2: * _ _ ____ _
3: * Project ___| | | | _ \| |
4: * / __| | | | |_) | |
5: * | (__| |_| | _ <| |___
6: * \___|\___/|_| \_\_____|
7: *
8: * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
9: *
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 https://curl.haxx.se/docs/copyright.html.
13: *
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.
17: *
18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19: * KIND, either express or implied.
20: *
21: ***************************************************************************/
22: /* <DESC>
23: * multi socket API usage with libevent 2
24: * </DESC>
25: */
26: /* Example application source code using the multi socket interface to
27: download many files at once.
28:
29: Written by Jeff Pohlmeyer
30:
31: Requires libevent version 2 and a (POSIX?) system that has mkfifo().
32:
33: This is an adaptation of libcurl's "hipev.c" and libevent's "event-test.c"
34: sample programs.
35:
36: When running, the program creates the named pipe "hiper.fifo"
37:
38: Whenever there is input into the fifo, the program reads the input as a list
39: of URL's and creates some new easy handles to fetch each URL via the
40: curl_multi "hiper" API.
41:
42:
43: Thus, you can try a single URL:
44: % echo http://www.yahoo.com > hiper.fifo
45:
46: Or a whole bunch of them:
47: % cat my-url-list > hiper.fifo
48:
49: The fifo buffer is handled almost instantly, so you can even add more URL's
50: while the previous requests are still being downloaded.
51:
52: Note:
53: For the sake of simplicity, URL length is limited to 1023 char's !
54:
55: This is purely a demo app, all retrieved data is simply discarded by the write
56: callback.
57:
58: */
59:
60: #include <stdio.h>
61: #include <string.h>
62: #include <stdlib.h>
63: #include <sys/time.h>
64: #include <time.h>
65: #include <unistd.h>
66: #include <sys/poll.h>
67: #include <curl/curl.h>
68: #include <event2/event.h>
69: #include <event2/event_struct.h>
70: #include <fcntl.h>
71: #include <sys/stat.h>
72: #include <errno.h>
73: #include <sys/cdefs.h>
74:
75: #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
76:
77:
78: /* Global information, common to all connections */
79: typedef struct _GlobalInfo
80: {
81: struct event_base *evbase;
82: struct event fifo_event;
83: struct event timer_event;
84: CURLM *multi;
85: int still_running;
86: FILE *input;
87: int stopped;
88: } GlobalInfo;
89:
90:
91: /* Information associated with a specific easy handle */
92: typedef struct _ConnInfo
93: {
94: CURL *easy;
95: char *url;
96: GlobalInfo *global;
97: char error[CURL_ERROR_SIZE];
98: } ConnInfo;
99:
100:
101: /* Information associated with a specific socket */
102: typedef struct _SockInfo
103: {
104: curl_socket_t sockfd;
105: CURL *easy;
106: int action;
107: long timeout;
108: struct event ev;
109: GlobalInfo *global;
110: } SockInfo;
111:
112: #define mycase(code) \
113: case code: s = __STRING(code)
114:
115: /* Die if we get a bad CURLMcode somewhere */
116: static void mcode_or_die(const char *where, CURLMcode code)
117: {
118: if(CURLM_OK != code) {
119: const char *s;
120: switch(code) {
121: mycase(CURLM_BAD_HANDLE); break;
122: mycase(CURLM_BAD_EASY_HANDLE); break;
123: mycase(CURLM_OUT_OF_MEMORY); break;
124: mycase(CURLM_INTERNAL_ERROR); break;
125: mycase(CURLM_UNKNOWN_OPTION); break;
126: mycase(CURLM_LAST); break;
127: default: s = "CURLM_unknown"; break;
128: mycase(CURLM_BAD_SOCKET);
129: fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
130: /* ignore this error */
131: return;
132: }
133: fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
134: exit(code);
135: }
136: }
137:
138:
139: /* Update the event timer after curl_multi library calls */
140: static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
141: {
142: struct timeval timeout;
143: (void)multi;
144:
145: timeout.tv_sec = timeout_ms/1000;
146: timeout.tv_usec = (timeout_ms%1000)*1000;
147: fprintf(MSG_OUT, "multi_timer_cb: Setting timeout to %ld ms\n", timeout_ms);
148:
149: /*
150: * if timeout_ms is -1, just delete the timer
151: *
152: * For all other values of timeout_ms, this should set or *update* the timer
153: * to the new value
154: */
155: if(timeout_ms == -1)
156: evtimer_del(&g->timer_event);
157: else /* includes timeout zero */
158: evtimer_add(&g->timer_event, &timeout);
159: return 0;
160: }
161:
162:
163: /* Check for completed transfers, and remove their easy handles */
164: static void check_multi_info(GlobalInfo *g)
165: {
166: char *eff_url;
167: CURLMsg *msg;
168: int msgs_left;
169: ConnInfo *conn;
170: CURL *easy;
171: CURLcode res;
172:
173: fprintf(MSG_OUT, "REMAINING: %d\n", g->still_running);
174: while((msg = curl_multi_info_read(g->multi, &msgs_left))) {
175: if(msg->msg == CURLMSG_DONE) {
176: easy = msg->easy_handle;
177: res = msg->data.result;
178: curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn);
179: curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url);
180: fprintf(MSG_OUT, "DONE: %s => (%d) %s\n", eff_url, res, conn->error);
181: curl_multi_remove_handle(g->multi, easy);
182: free(conn->url);
183: curl_easy_cleanup(easy);
184: free(conn);
185: }
186: }
187: if(g->still_running == 0 && g->stopped)
188: event_base_loopbreak(g->evbase);
189: }
190:
191:
192:
193: /* Called by libevent when we get action on a multi socket */
194: static void event_cb(int fd, short kind, void *userp)
195: {
196: GlobalInfo *g = (GlobalInfo*) userp;
197: CURLMcode rc;
198:
199: int action =
200: ((kind & EV_READ) ? CURL_CSELECT_IN : 0) |
201: ((kind & EV_WRITE) ? CURL_CSELECT_OUT : 0);
202:
203: rc = curl_multi_socket_action(g->multi, fd, action, &g->still_running);
204: mcode_or_die("event_cb: curl_multi_socket_action", rc);
205:
206: check_multi_info(g);
207: if(g->still_running <= 0) {
208: fprintf(MSG_OUT, "last transfer done, kill timeout\n");
209: if(evtimer_pending(&g->timer_event, NULL)) {
210: evtimer_del(&g->timer_event);
211: }
212: }
213: }
214:
215:
216:
217: /* Called by libevent when our timeout expires */
218: static void timer_cb(int fd, short kind, void *userp)
219: {
220: GlobalInfo *g = (GlobalInfo *)userp;
221: CURLMcode rc;
222: (void)fd;
223: (void)kind;
224:
225: rc = curl_multi_socket_action(g->multi,
226: CURL_SOCKET_TIMEOUT, 0, &g->still_running);
227: mcode_or_die("timer_cb: curl_multi_socket_action", rc);
228: check_multi_info(g);
229: }
230:
231:
232:
233: /* Clean up the SockInfo structure */
234: static void remsock(SockInfo *f)
235: {
236: if(f) {
237: event_del(&f->ev);
238: free(f);
239: }
240: }
241:
242:
243:
244: /* Assign information to a SockInfo structure */
245: static void setsock(SockInfo *f, curl_socket_t s, CURL *e, int act,
246: GlobalInfo *g)
247: {
248: int kind =
249: ((act & CURL_POLL_IN) ? EV_READ : 0) |
250: ((act & CURL_POLL_OUT) ? EV_WRITE : 0) | EV_PERSIST;
251:
252: f->sockfd = s;
253: f->action = act;
254: f->easy = e;
255: event_del(&f->ev);
256: event_assign(&f->ev, g->evbase, f->sockfd, kind, event_cb, g);
257: event_add(&f->ev, NULL);
258: }
259:
260:
261:
262: /* Initialize a new SockInfo structure */
263: static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g)
264: {
265: SockInfo *fdp = calloc(sizeof(SockInfo), 1);
266:
267: fdp->global = g;
268: setsock(fdp, s, easy, action, g);
269: curl_multi_assign(g->multi, s, fdp);
270: }
271:
272: /* CURLMOPT_SOCKETFUNCTION */
273: static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
274: {
275: GlobalInfo *g = (GlobalInfo*) cbp;
276: SockInfo *fdp = (SockInfo*) sockp;
277: const char *whatstr[]={ "none", "IN", "OUT", "INOUT", "REMOVE" };
278:
279: fprintf(MSG_OUT,
280: "socket callback: s=%d e=%p what=%s ", s, e, whatstr[what]);
281: if(what == CURL_POLL_REMOVE) {
282: fprintf(MSG_OUT, "\n");
283: remsock(fdp);
284: }
285: else {
286: if(!fdp) {
287: fprintf(MSG_OUT, "Adding data: %s\n", whatstr[what]);
288: addsock(s, e, what, g);
289: }
290: else {
291: fprintf(MSG_OUT,
292: "Changing action from %s to %s\n",
293: whatstr[fdp->action], whatstr[what]);
294: setsock(fdp, s, e, what, g);
295: }
296: }
297: return 0;
298: }
299:
300:
301:
302: /* CURLOPT_WRITEFUNCTION */
303: static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
304: {
305: (void)ptr;
306: (void)data;
307: return size * nmemb;
308: }
309:
310:
311: /* CURLOPT_PROGRESSFUNCTION */
312: static int prog_cb(void *p, double dltotal, double dlnow, double ult,
313: double uln)
314: {
315: ConnInfo *conn = (ConnInfo *)p;
316: (void)ult;
317: (void)uln;
318:
319: fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
320: return 0;
321: }
322:
323:
324: /* Create a new easy handle, and add it to the global curl_multi */
325: static void new_conn(char *url, GlobalInfo *g)
326: {
327: ConnInfo *conn;
328: CURLMcode rc;
329:
330: conn = calloc(1, sizeof(ConnInfo));
331: conn->error[0]='\0';
332:
333: conn->easy = curl_easy_init();
334: if(!conn->easy) {
335: fprintf(MSG_OUT, "curl_easy_init() failed, exiting!\n");
336: exit(2);
337: }
338: conn->global = g;
339: conn->url = strdup(url);
340: curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url);
341: curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb);
342: curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, conn);
343: curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L);
344: curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error);
345: curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn);
346: curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 0L);
347: curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb);
348: curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn);
349: curl_easy_setopt(conn->easy, CURLOPT_FOLLOWLOCATION, 1L);
350: fprintf(MSG_OUT,
351: "Adding easy %p to multi %p (%s)\n", conn->easy, g->multi, url);
352: rc = curl_multi_add_handle(g->multi, conn->easy);
353: mcode_or_die("new_conn: curl_multi_add_handle", rc);
354:
355: /* note that the add_handle() will set a time-out to trigger very soon so
356: that the necessary socket_action() call will be called by this app */
357: }
358:
359: /* This gets called whenever data is received from the fifo */
360: static void fifo_cb(int fd, short event, void *arg)
361: {
362: char s[1024];
363: long int rv = 0;
364: int n = 0;
365: GlobalInfo *g = (GlobalInfo *)arg;
366: (void)fd;
367: (void)event;
368:
369: do {
370: s[0]='\0';
371: rv = fscanf(g->input, "%1023s%n", s, &n);
372: s[n]='\0';
373: if(n && s[0]) {
374: if(!strcmp(s, "stop")) {
375: g->stopped = 1;
376: if(g->still_running == 0)
377: event_base_loopbreak(g->evbase);
378: }
379: else
380: new_conn(s, arg); /* if we read a URL, go get it! */
381: }
382: else
383: break;
384: } while(rv != EOF);
385: }
386:
387: /* Create a named pipe and tell libevent to monitor it */
388: static const char *fifo = "hiper.fifo";
389: static int init_fifo(GlobalInfo *g)
390: {
391: struct stat st;
392: curl_socket_t sockfd;
393:
394: fprintf(MSG_OUT, "Creating named pipe \"%s\"\n", fifo);
395: if(lstat (fifo, &st) == 0) {
396: if((st.st_mode & S_IFMT) == S_IFREG) {
397: errno = EEXIST;
398: perror("lstat");
399: exit(1);
400: }
401: }
402: unlink(fifo);
403: if(mkfifo (fifo, 0600) == -1) {
404: perror("mkfifo");
405: exit(1);
406: }
407: sockfd = open(fifo, O_RDWR | O_NONBLOCK, 0);
408: if(sockfd == -1) {
409: perror("open");
410: exit(1);
411: }
412: g->input = fdopen(sockfd, "r");
413:
414: fprintf(MSG_OUT, "Now, pipe some URL's into > %s\n", fifo);
415: event_assign(&g->fifo_event, g->evbase, sockfd, EV_READ|EV_PERSIST,
416: fifo_cb, g);
417: event_add(&g->fifo_event, NULL);
418: return (0);
419: }
420:
421: static void clean_fifo(GlobalInfo *g)
422: {
423: event_del(&g->fifo_event);
424: fclose(g->input);
425: unlink(fifo);
426: }
427:
428: int main(int argc, char **argv)
429: {
430: GlobalInfo g;
431: (void)argc;
432: (void)argv;
433:
434: memset(&g, 0, sizeof(GlobalInfo));
435: g.evbase = event_base_new();
436: init_fifo(&g);
437: g.multi = curl_multi_init();
438: evtimer_assign(&g.timer_event, g.evbase, timer_cb, &g);
439:
440: /* setup the generic multi interface options we want */
441: curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
442: curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
443: curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
444: curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
445:
446: /* we don't call any curl_multi_socket*() function yet as we have no handles
447: added! */
448:
449: event_base_dispatch(g.evbase);
450:
451: /* this, of course, won't get called since only way to stop this program is
452: via ctrl-C, but it is here to show how cleanup /would/ be done. */
453: clean_fifo(&g);
454: event_del(&g.timer_event);
455: event_base_free(g.evbase);
456: curl_multi_cleanup(g.multi);
457: return 0;
458: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>