Return to asiohiper.cpp 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) 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: }