Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/docs/examples/evhiperfifo.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 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 } |
