Home | History | Annotate | Download | only in examples
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) 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  * multi_socket API using libuv
     25  * </DESC>
     26  */
     27 /* Example application code using the multi socket interface to download
     28    multiple files at once, but instead of using curl_multi_perform and
     29    curl_multi_wait, which uses select(), we use libuv.
     30    It supports epoll, kqueue, etc. on unixes and fast IO completion ports on
     31    Windows, which means, it should be very fast on all platforms..
     32 
     33    Written by Clemens Gruber, based on an outdated example from uvbook and
     34    some tests from libuv.
     35 
     36    Requires libuv and (of course) libcurl.
     37 
     38    See http://nikhilm.github.com/uvbook/ for more information on libuv.
     39 */
     40 
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 #include <uv.h>
     44 #include <curl/curl.h>
     45 
     46 uv_loop_t *loop;
     47 CURLM *curl_handle;
     48 uv_timer_t timeout;
     49 
     50 typedef struct curl_context_s {
     51   uv_poll_t poll_handle;
     52   curl_socket_t sockfd;
     53 } curl_context_t;
     54 
     55 curl_context_t* create_curl_context(curl_socket_t sockfd)
     56 {
     57   curl_context_t *context;
     58 
     59   context = (curl_context_t *) malloc(sizeof *context);
     60 
     61   context->sockfd = sockfd;
     62 
     63   uv_poll_init_socket(loop, &context->poll_handle, sockfd);
     64   context->poll_handle.data = context;
     65 
     66   return context;
     67 }
     68 
     69 void curl_close_cb(uv_handle_t *handle)
     70 {
     71   curl_context_t *context = (curl_context_t *) handle->data;
     72   free(context);
     73 }
     74 
     75 void destroy_curl_context(curl_context_t *context)
     76 {
     77   uv_close((uv_handle_t *) &context->poll_handle, curl_close_cb);
     78 }
     79 
     80 
     81 void add_download(const char *url, int num)
     82 {
     83   char filename[50];
     84   FILE *file;
     85   CURL *handle;
     86 
     87   snprintf(filename, 50, "%d.download", num);
     88 
     89   file = fopen(filename, "wb");
     90   if(!file) {
     91     fprintf(stderr, "Error opening %s\n", filename);
     92     return;
     93   }
     94 
     95   handle = curl_easy_init();
     96   curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
     97   curl_easy_setopt(handle, CURLOPT_PRIVATE, file);
     98   curl_easy_setopt(handle, CURLOPT_URL, url);
     99   curl_multi_add_handle(curl_handle, handle);
    100   fprintf(stderr, "Added download %s -> %s\n", url, filename);
    101 }
    102 
    103 static void check_multi_info(void)
    104 {
    105   int running_handles;
    106   char *done_url;
    107   CURLMsg *message;
    108   int pending;
    109   FILE *file;
    110 
    111   while((message = curl_multi_info_read(curl_handle, &pending))) {
    112     switch(message->msg) {
    113     case CURLMSG_DONE:
    114       curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL,
    115                         &done_url);
    116       curl_easy_getinfo(message->easy_handle, CURLINFO_PRIVATE, &file);
    117       printf("%s DONE\n", done_url);
    118 
    119       curl_multi_remove_handle(curl_handle, message->easy_handle);
    120       curl_easy_cleanup(message->easy_handle);
    121       if(file) {
    122         fclose(file);
    123       }
    124       break;
    125 
    126     default:
    127       fprintf(stderr, "CURLMSG default\n");
    128       break;
    129     }
    130   }
    131 }
    132 
    133 void curl_perform(uv_poll_t *req, int status, int events)
    134 {
    135   int running_handles;
    136   int flags = 0;
    137   curl_context_t *context;
    138   char *done_url;
    139   CURLMsg *message;
    140   int pending;
    141 
    142   uv_timer_stop(&timeout);
    143 
    144   if(events & UV_READABLE)
    145     flags |= CURL_CSELECT_IN;
    146   if(events & UV_WRITABLE)
    147     flags |= CURL_CSELECT_OUT;
    148 
    149   context = (curl_context_t *) req;
    150 
    151   curl_multi_socket_action(curl_handle, context->sockfd, flags,
    152                            &running_handles);
    153 
    154   check_multi_info();
    155 }
    156 
    157 void on_timeout(uv_timer_t *req, int status)
    158 {
    159   int running_handles;
    160   curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
    161                            &running_handles);
    162   check_multi_info();
    163 }
    164 
    165 void start_timeout(CURLM *multi, long timeout_ms, void *userp)
    166 {
    167   if(timeout_ms <= 0)
    168     timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in
    169                        a bit */
    170   uv_timer_start(&timeout, on_timeout, timeout_ms, 0);
    171 }
    172 
    173 int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp,
    174                   void *socketp)
    175 {
    176   curl_context_t *curl_context;
    177   if(action == CURL_POLL_IN || action == CURL_POLL_OUT) {
    178     if(socketp) {
    179       curl_context = (curl_context_t *) socketp;
    180     }
    181     else {
    182       curl_context = create_curl_context(s);
    183     }
    184     curl_multi_assign(curl_handle, s, (void *) curl_context);
    185   }
    186 
    187   switch(action) {
    188   case CURL_POLL_IN:
    189     uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform);
    190     break;
    191   case CURL_POLL_OUT:
    192     uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform);
    193     break;
    194   case CURL_POLL_REMOVE:
    195     if(socketp) {
    196       uv_poll_stop(&((curl_context_t*)socketp)->poll_handle);
    197       destroy_curl_context((curl_context_t*) socketp);
    198       curl_multi_assign(curl_handle, s, NULL);
    199     }
    200     break;
    201   default:
    202     abort();
    203   }
    204 
    205   return 0;
    206 }
    207 
    208 int main(int argc, char **argv)
    209 {
    210   loop = uv_default_loop();
    211 
    212   if(argc <= 1)
    213     return 0;
    214 
    215   if(curl_global_init(CURL_GLOBAL_ALL)) {
    216     fprintf(stderr, "Could not init cURL\n");
    217     return 1;
    218   }
    219 
    220   uv_timer_init(loop, &timeout);
    221 
    222   curl_handle = curl_multi_init();
    223   curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
    224   curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
    225 
    226   while(argc-- > 1) {
    227     add_download(argv[argc], argc);
    228   }
    229 
    230   uv_run(loop, UV_RUN_DEFAULT);
    231   curl_multi_cleanup(curl_handle);
    232 
    233   return 0;
    234 }
    235