Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/docs/examples/asiohiper.cpp @ 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) 2012 - 2018, 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 | |
| 23 /* <DESC> | |
| 24 * demonstrate the use of multi socket interface with boost::asio | |
| 25 * </DESC> | |
| 26 */ | |
| 27 /* | |
| 28 * This program is in c++ and uses boost::asio instead of libevent/libev. | |
| 29 * Requires boost::asio, boost::bind and boost::system | |
| 30 * | |
| 31 * This is an adaptation of libcurl's "hiperfifo.c" and "evhiperfifo.c" | |
| 32 * sample programs. This example implements a subset of the functionality from | |
| 33 * hiperfifo.c, for full functionality refer hiperfifo.c or evhiperfifo.c | |
| 34 * | |
| 35 * Written by Lijo Antony based on hiperfifo.c by Jeff Pohlmeyer | |
| 36 * | |
| 37 * When running, the program creates an easy handle for a URL and | |
| 38 * uses the curl_multi API to fetch it. | |
| 39 * | |
| 40 * Note: | |
| 41 * For the sake of simplicity, URL is hard coded to "www.google.com" | |
| 42 * | |
| 43 * This is purely a demo app, all retrieved data is simply discarded by the | |
| 44 * write callback. | |
| 45 * | |
| 46 * =========================================================================== | |
| 47 * WARNING: This example program is known to have flaws: | |
| 48 * https://github.com/curl/curl/issues/2407 | |
| 49 * | |
| 50 * It still kept in the example repository with the hope that it might be | |
| 51 * useful, and maybe some day someone who knows enough about boost::asio will | |
| 52 * read this text, accept the challenge and make the example code work | |
| 53 * correctly. Until then: expect this example program to fail occasionally. | |
| 54 * =========================================================================== | |
| 55 */ | |
| 56 | |
| 57 | |
| 58 #include <curl/curl.h> | |
| 59 #include <boost/asio.hpp> | |
| 60 #include <boost/bind.hpp> | |
| 61 #include <iostream> | |
| 62 | |
| 63 #define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */ | |
| 64 | |
| 65 /* boost::asio related objects | |
| 66 * using global variables for simplicity | |
| 67 */ | |
| 68 boost::asio::io_service io_service; | |
| 69 boost::asio::deadline_timer timer(io_service); | |
| 70 std::map<curl_socket_t, boost::asio::ip::tcp::socket *> socket_map; | |
| 71 | |
| 72 /* Global information, common to all connections */ | |
| 73 typedef struct _GlobalInfo | |
| 74 { | |
| 75 CURLM *multi; | |
| 76 int still_running; | |
| 77 } GlobalInfo; | |
| 78 | |
| 79 /* Information associated with a specific easy handle */ | |
| 80 typedef struct _ConnInfo | |
| 81 { | |
| 82 CURL *easy; | |
| 83 char *url; | |
| 84 GlobalInfo *global; | |
| 85 char error[CURL_ERROR_SIZE]; | |
| 86 } ConnInfo; | |
| 87 | |
| 88 static void timer_cb(const boost::system::error_code & error, GlobalInfo *g); | |
| 89 | |
| 90 /* Update the event timer after curl_multi library calls */ | |
| 91 static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g) | |
| 92 { | |
| 93 fprintf(MSG_OUT, "\nmulti_timer_cb: timeout_ms %ld", timeout_ms); | |
| 94 | |
| 95 /* cancel running timer */ | |
| 96 timer.cancel(); | |
| 97 | |
| 98 if(timeout_ms > 0) { | |
| 99 /* update timer */ | |
| 100 timer.expires_from_now(boost::posix_time::millisec(timeout_ms)); | |
| 101 timer.async_wait(boost::bind(&timer_cb, _1, g)); | |
| 102 } | |
| 103 else if(timeout_ms == 0) { | |
| 104 /* call timeout function immediately */ | |
| 105 boost::system::error_code error; /*success*/ | |
| 106 timer_cb(error, g); | |
| 107 } | |
| 108 | |
| 109 return 0; | |
| 110 } | |
| 111 | |
| 112 /* Die if we get a bad CURLMcode somewhere */ | |
| 113 static void mcode_or_die(const char *where, CURLMcode code) | |
| 114 { | |
| 115 if(CURLM_OK != code) { | |
| 116 const char *s; | |
| 117 switch(code) { | |
| 118 case CURLM_CALL_MULTI_PERFORM: | |
| 119 s = "CURLM_CALL_MULTI_PERFORM"; | |
| 120 break; | |
| 121 case CURLM_BAD_HANDLE: | |
| 122 s = "CURLM_BAD_HANDLE"; | |
| 123 break; | |
| 124 case CURLM_BAD_EASY_HANDLE: | |
| 125 s = "CURLM_BAD_EASY_HANDLE"; | |
| 126 break; | |
| 127 case CURLM_OUT_OF_MEMORY: | |
| 128 s = "CURLM_OUT_OF_MEMORY"; | |
| 129 break; | |
| 130 case CURLM_INTERNAL_ERROR: | |
| 131 s = "CURLM_INTERNAL_ERROR"; | |
| 132 break; | |
| 133 case CURLM_UNKNOWN_OPTION: | |
| 134 s = "CURLM_UNKNOWN_OPTION"; | |
| 135 break; | |
| 136 case CURLM_LAST: | |
| 137 s = "CURLM_LAST"; | |
| 138 break; | |
| 139 default: | |
| 140 s = "CURLM_unknown"; | |
| 141 break; | |
| 142 case CURLM_BAD_SOCKET: | |
| 143 s = "CURLM_BAD_SOCKET"; | |
| 144 fprintf(MSG_OUT, "\nERROR: %s returns %s", where, s); | |
| 145 /* ignore this error */ | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 fprintf(MSG_OUT, "\nERROR: %s returns %s", where, s); | |
| 150 | |
| 151 exit(code); | |
| 152 } | |
| 153 } | |
| 154 | |
| 155 /* Check for completed transfers, and remove their easy handles */ | |
| 156 static void check_multi_info(GlobalInfo *g) | |
| 157 { | |
| 158 char *eff_url; | |
| 159 CURLMsg *msg; | |
| 160 int msgs_left; | |
| 161 ConnInfo *conn; | |
| 162 CURL *easy; | |
| 163 CURLcode res; | |
| 164 | |
| 165 fprintf(MSG_OUT, "\nREMAINING: %d", g->still_running); | |
| 166 | |
| 167 while((msg = curl_multi_info_read(g->multi, &msgs_left))) { | |
| 168 if(msg->msg == CURLMSG_DONE) { | |
| 169 easy = msg->easy_handle; | |
| 170 res = msg->data.result; | |
| 171 curl_easy_getinfo(easy, CURLINFO_PRIVATE, &conn); | |
| 172 curl_easy_getinfo(easy, CURLINFO_EFFECTIVE_URL, &eff_url); | |
| 173 fprintf(MSG_OUT, "\nDONE: %s => (%d) %s", eff_url, res, conn->error); | |
| 174 curl_multi_remove_handle(g->multi, easy); | |
| 175 free(conn->url); | |
| 176 curl_easy_cleanup(easy); | |
| 177 free(conn); | |
| 178 } | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 /* Called by asio when there is an action on a socket */ | |
| 183 static void event_cb(GlobalInfo *g, curl_socket_t s, | |
| 184 int action, const boost::system::error_code & error, | |
| 185 int *fdp) | |
| 186 { | |
| 187 fprintf(MSG_OUT, "\nevent_cb: action=%d", action); | |
| 188 | |
| 189 if(socket_map.find(s) == socket_map.end()) { | |
| 190 fprintf(MSG_OUT, "\nevent_cb: socket already closed"); | |
| 191 return; | |
| 192 } | |
| 193 | |
| 194 /* make sure the event matches what are wanted */ | |
| 195 if(*fdp == action || *fdp == CURL_POLL_INOUT) { | |
| 196 CURLMcode rc; | |
| 197 if(error) | |
| 198 action = CURL_CSELECT_ERR; | |
| 199 rc = curl_multi_socket_action(g->multi, s, action, &g->still_running); | |
| 200 | |
| 201 mcode_or_die("event_cb: curl_multi_socket_action", rc); | |
| 202 check_multi_info(g); | |
| 203 | |
| 204 if(g->still_running <= 0) { | |
| 205 fprintf(MSG_OUT, "\nlast transfer done, kill timeout"); | |
| 206 timer.cancel(); | |
| 207 } | |
| 208 | |
| 209 /* keep on watching. | |
| 210 * the socket may have been closed and/or fdp may have been changed | |
| 211 * in curl_multi_socket_action(), so check them both */ | |
| 212 if(!error && socket_map.find(s) != socket_map.end() && | |
| 213 (*fdp == action || *fdp == CURL_POLL_INOUT)) { | |
| 214 boost::asio::ip::tcp::socket *tcp_socket = socket_map.find(s)->second; | |
| 215 | |
| 216 if(action == CURL_POLL_IN) { | |
| 217 tcp_socket->async_read_some(boost::asio::null_buffers(), | |
| 218 boost::bind(&event_cb, g, s, | |
| 219 action, _1, fdp)); | |
| 220 } | |
| 221 if(action == CURL_POLL_OUT) { | |
| 222 tcp_socket->async_write_some(boost::asio::null_buffers(), | |
| 223 boost::bind(&event_cb, g, s, | |
| 224 action, _1, fdp)); | |
| 225 } | |
| 226 } | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 /* Called by asio when our timeout expires */ | |
| 231 static void timer_cb(const boost::system::error_code & error, GlobalInfo *g) | |
| 232 { | |
| 233 if(!error) { | |
| 234 fprintf(MSG_OUT, "\ntimer_cb: "); | |
| 235 | |
| 236 CURLMcode rc; | |
| 237 rc = curl_multi_socket_action(g->multi, CURL_SOCKET_TIMEOUT, 0, | |
| 238 &g->still_running); | |
| 239 | |
| 240 mcode_or_die("timer_cb: curl_multi_socket_action", rc); | |
| 241 check_multi_info(g); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 /* Clean up any data */ | |
| 246 static void remsock(int *f, GlobalInfo *g) | |
| 247 { | |
| 248 fprintf(MSG_OUT, "\nremsock: "); | |
| 249 | |
| 250 if(f) { | |
| 251 free(f); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 static void setsock(int *fdp, curl_socket_t s, CURL *e, int act, int oldact, | |
| 256 GlobalInfo *g) | |
| 257 { | |
| 258 fprintf(MSG_OUT, "\nsetsock: socket=%d, act=%d, fdp=%p", s, act, fdp); | |
| 259 | |
| 260 std::map<curl_socket_t, boost::asio::ip::tcp::socket *>::iterator it = | |
| 261 socket_map.find(s); | |
| 262 | |
| 263 if(it == socket_map.end()) { | |
| 264 fprintf(MSG_OUT, "\nsocket %d is a c-ares socket, ignoring", s); | |
| 265 return; | |
| 266 } | |
| 267 | |
| 268 boost::asio::ip::tcp::socket * tcp_socket = it->second; | |
| 269 | |
| 270 *fdp = act; | |
| 271 | |
| 272 if(act == CURL_POLL_IN) { | |
| 273 fprintf(MSG_OUT, "\nwatching for socket to become readable"); | |
| 274 if(oldact != CURL_POLL_IN && oldact != CURL_POLL_INOUT) { | |
| 275 tcp_socket->async_read_some(boost::asio::null_buffers(), | |
| 276 boost::bind(&event_cb, g, s, | |
| 277 CURL_POLL_IN, _1, fdp)); | |
| 278 } | |
| 279 } | |
| 280 else if(act == CURL_POLL_OUT) { | |
| 281 fprintf(MSG_OUT, "\nwatching for socket to become writable"); | |
| 282 if(oldact != CURL_POLL_OUT && oldact != CURL_POLL_INOUT) { | |
| 283 tcp_socket->async_write_some(boost::asio::null_buffers(), | |
| 284 boost::bind(&event_cb, g, s, | |
| 285 CURL_POLL_OUT, _1, fdp)); | |
| 286 } | |
| 287 } | |
| 288 else if(act == CURL_POLL_INOUT) { | |
| 289 fprintf(MSG_OUT, "\nwatching for socket to become readable & writable"); | |
| 290 if(oldact != CURL_POLL_IN && oldact != CURL_POLL_INOUT) { | |
| 291 tcp_socket->async_read_some(boost::asio::null_buffers(), | |
| 292 boost::bind(&event_cb, g, s, | |
| 293 CURL_POLL_IN, _1, fdp)); | |
| 294 } | |
| 295 if(oldact != CURL_POLL_OUT && oldact != CURL_POLL_INOUT) { | |
| 296 tcp_socket->async_write_some(boost::asio::null_buffers(), | |
| 297 boost::bind(&event_cb, g, s, | |
| 298 CURL_POLL_OUT, _1, fdp)); | |
| 299 } | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 static void addsock(curl_socket_t s, CURL *easy, int action, GlobalInfo *g) | |
| 304 { | |
| 305 /* fdp is used to store current action */ | |
| 306 int *fdp = (int *) calloc(sizeof(int), 1); | |
| 307 | |
| 308 setsock(fdp, s, easy, action, 0, g); | |
| 309 curl_multi_assign(g->multi, s, fdp); | |
| 310 } | |
| 311 | |
| 312 /* CURLMOPT_SOCKETFUNCTION */ | |
| 313 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp) | |
| 314 { | |
| 315 fprintf(MSG_OUT, "\nsock_cb: socket=%d, what=%d, sockp=%p", s, what, sockp); | |
| 316 | |
| 317 GlobalInfo *g = (GlobalInfo*) cbp; | |
| 318 int *actionp = (int *) sockp; | |
| 319 const char *whatstr[] = { "none", "IN", "OUT", "INOUT", "REMOVE"}; | |
| 320 | |
| 321 fprintf(MSG_OUT, | |
| 322 "\nsocket callback: s=%d e=%p what=%s ", s, e, whatstr[what]); | |
| 323 | |
| 324 if(what == CURL_POLL_REMOVE) { | |
| 325 fprintf(MSG_OUT, "\n"); | |
| 326 remsock(actionp, g); | |
| 327 } | |
| 328 else { | |
| 329 if(!actionp) { | |
| 330 fprintf(MSG_OUT, "\nAdding data: %s", whatstr[what]); | |
| 331 addsock(s, e, what, g); | |
| 332 } | |
| 333 else { | |
| 334 fprintf(MSG_OUT, | |
| 335 "\nChanging action from %s to %s", | |
| 336 whatstr[*actionp], whatstr[what]); | |
| 337 setsock(actionp, s, e, what, *actionp, g); | |
| 338 } | |
| 339 } | |
| 340 | |
| 341 return 0; | |
| 342 } | |
| 343 | |
| 344 /* CURLOPT_WRITEFUNCTION */ | |
| 345 static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data) | |
| 346 { | |
| 347 size_t written = size * nmemb; | |
| 348 char *pBuffer = (char *)malloc(written + 1); | |
| 349 | |
| 350 strncpy(pBuffer, (const char *)ptr, written); | |
| 351 pBuffer[written] = '\0'; | |
| 352 | |
| 353 fprintf(MSG_OUT, "%s", pBuffer); | |
| 354 | |
| 355 free(pBuffer); | |
| 356 | |
| 357 return written; | |
| 358 } | |
| 359 | |
| 360 /* CURLOPT_PROGRESSFUNCTION */ | |
| 361 static int prog_cb(void *p, double dltotal, double dlnow, double ult, | |
| 362 double uln) | |
| 363 { | |
| 364 ConnInfo *conn = (ConnInfo *)p; | |
| 365 | |
| 366 (void)ult; | |
| 367 (void)uln; | |
| 368 | |
| 369 fprintf(MSG_OUT, "\nProgress: %s (%g/%g)", conn->url, dlnow, dltotal); | |
| 370 fprintf(MSG_OUT, "\nProgress: %s (%g)", conn->url, ult); | |
| 371 | |
| 372 return 0; | |
| 373 } | |
| 374 | |
| 375 /* CURLOPT_OPENSOCKETFUNCTION */ | |
| 376 static curl_socket_t opensocket(void *clientp, curlsocktype purpose, | |
| 377 struct curl_sockaddr *address) | |
| 378 { | |
| 379 fprintf(MSG_OUT, "\nopensocket :"); | |
| 380 | |
| 381 curl_socket_t sockfd = CURL_SOCKET_BAD; | |
| 382 | |
| 383 /* restrict to IPv4 */ | |
| 384 if(purpose == CURLSOCKTYPE_IPCXN && address->family == AF_INET) { | |
| 385 /* create a tcp socket object */ | |
| 386 boost::asio::ip::tcp::socket *tcp_socket = | |
| 387 new boost::asio::ip::tcp::socket(io_service); | |
| 388 | |
| 389 /* open it and get the native handle*/ | |
| 390 boost::system::error_code ec; | |
| 391 tcp_socket->open(boost::asio::ip::tcp::v4(), ec); | |
| 392 | |
| 393 if(ec) { | |
| 394 /* An error occurred */ | |
| 395 std::cout << std::endl << "Couldn't open socket [" << ec << "][" << | |
| 396 ec.message() << "]"; | |
| 397 fprintf(MSG_OUT, "\nERROR: Returning CURL_SOCKET_BAD to signal error"); | |
| 398 } | |
| 399 else { | |
| 400 sockfd = tcp_socket->native_handle(); | |
| 401 fprintf(MSG_OUT, "\nOpened socket %d", sockfd); | |
| 402 | |
| 403 /* save it for monitoring */ | |
| 404 socket_map.insert(std::pair<curl_socket_t, | |
| 405 boost::asio::ip::tcp::socket *>(sockfd, tcp_socket)); | |
| 406 } | |
| 407 } | |
| 408 | |
| 409 return sockfd; | |
| 410 } | |
| 411 | |
| 412 /* CURLOPT_CLOSESOCKETFUNCTION */ | |
| 413 static int close_socket(void *clientp, curl_socket_t item) | |
| 414 { | |
| 415 fprintf(MSG_OUT, "\nclose_socket : %d", item); | |
| 416 | |
| 417 std::map<curl_socket_t, boost::asio::ip::tcp::socket *>::iterator it = | |
| 418 socket_map.find(item); | |
| 419 | |
| 420 if(it != socket_map.end()) { | |
| 421 delete it->second; | |
| 422 socket_map.erase(it); | |
| 423 } | |
| 424 | |
| 425 return 0; | |
| 426 } | |
| 427 | |
| 428 /* Create a new easy handle, and add it to the global curl_multi */ | |
| 429 static void new_conn(char *url, GlobalInfo *g) | |
| 430 { | |
| 431 ConnInfo *conn; | |
| 432 CURLMcode rc; | |
| 433 | |
| 434 conn = (ConnInfo *) calloc(1, sizeof(ConnInfo)); | |
| 435 | |
| 436 conn->easy = curl_easy_init(); | |
| 437 if(!conn->easy) { | |
| 438 fprintf(MSG_OUT, "\ncurl_easy_init() failed, exiting!"); | |
| 439 exit(2); | |
| 440 } | |
| 441 | |
| 442 conn->global = g; | |
| 443 conn->url = strdup(url); | |
| 444 curl_easy_setopt(conn->easy, CURLOPT_URL, conn->url); | |
| 445 curl_easy_setopt(conn->easy, CURLOPT_WRITEFUNCTION, write_cb); | |
| 446 curl_easy_setopt(conn->easy, CURLOPT_WRITEDATA, &conn); | |
| 447 curl_easy_setopt(conn->easy, CURLOPT_VERBOSE, 1L); | |
| 448 curl_easy_setopt(conn->easy, CURLOPT_ERRORBUFFER, conn->error); | |
| 449 curl_easy_setopt(conn->easy, CURLOPT_PRIVATE, conn); | |
| 450 curl_easy_setopt(conn->easy, CURLOPT_NOPROGRESS, 1L); | |
| 451 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSFUNCTION, prog_cb); | |
| 452 curl_easy_setopt(conn->easy, CURLOPT_PROGRESSDATA, conn); | |
| 453 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_TIME, 3L); | |
| 454 curl_easy_setopt(conn->easy, CURLOPT_LOW_SPEED_LIMIT, 10L); | |
| 455 | |
| 456 /* call this function to get a socket */ | |
| 457 curl_easy_setopt(conn->easy, CURLOPT_OPENSOCKETFUNCTION, opensocket); | |
| 458 | |
| 459 /* call this function to close a socket */ | |
| 460 curl_easy_setopt(conn->easy, CURLOPT_CLOSESOCKETFUNCTION, close_socket); | |
| 461 | |
| 462 fprintf(MSG_OUT, | |
| 463 "\nAdding easy %p to multi %p (%s)", conn->easy, g->multi, url); | |
| 464 rc = curl_multi_add_handle(g->multi, conn->easy); | |
| 465 mcode_or_die("new_conn: curl_multi_add_handle", rc); | |
| 466 | |
| 467 /* note that the add_handle() will set a time-out to trigger very soon so | |
| 468 that the necessary socket_action() call will be called by this app */ | |
| 469 } | |
| 470 | |
| 471 int main(int argc, char **argv) | |
| 472 { | |
| 473 GlobalInfo g; | |
| 474 | |
| 475 (void)argc; | |
| 476 (void)argv; | |
| 477 | |
| 478 memset(&g, 0, sizeof(GlobalInfo)); | |
| 479 g.multi = curl_multi_init(); | |
| 480 | |
| 481 curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb); | |
| 482 curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g); | |
| 483 curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb); | |
| 484 curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g); | |
| 485 | |
| 486 new_conn((char *)"www.google.com", &g); /* add a URL */ | |
| 487 | |
| 488 /* enter io_service run loop */ | |
| 489 io_service.run(); | |
| 490 | |
| 491 curl_multi_cleanup(g.multi); | |
| 492 | |
| 493 fprintf(MSG_OUT, "\ndone.\n"); | |
| 494 | |
| 495 return 0; | |
| 496 } |
