Annotation of embedaddon/curl/docs/examples/hiperfifo.c, revision 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>