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