quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

multi-event.c (6297B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 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.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  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 /* <DESC>
     26  * multi_socket API using libevent
     27  * </DESC>
     28  */
     29 
     30 #include <stdio.h>
     31 #include <stdlib.h>
     32 #include <event2/event.h>
     33 #include <curl/curl.h>
     34 
     35 static struct event_base *base;
     36 static CURLM *curl_handle;
     37 static struct event *timeout;
     38 
     39 struct curl_context {
     40   struct event *event;
     41   curl_socket_t sockfd;
     42 };
     43 
     44 static void curl_perform(int fd, short event, void *arg);
     45 
     46 static struct curl_context *create_curl_context(curl_socket_t sockfd)
     47 {
     48   struct curl_context *context;
     49 
     50   context = (struct curl_context *) malloc(sizeof(*context));
     51 
     52   context->sockfd = sockfd;
     53 
     54   context->event = event_new(base, sockfd, 0, curl_perform, context);
     55 
     56   return context;
     57 }
     58 
     59 static void destroy_curl_context(struct curl_context *context)
     60 {
     61   event_del(context->event);
     62   event_free(context->event);
     63   free(context);
     64 }
     65 
     66 static void add_download(const char *url, int num)
     67 {
     68   char filename[50];
     69   FILE *file;
     70   CURL *handle;
     71 
     72   snprintf(filename, 50, "%d.download", num);
     73 
     74   file = fopen(filename, "wb");
     75   if(!file) {
     76     fprintf(stderr, "Error opening %s\n", filename);
     77     return;
     78   }
     79 
     80   handle = curl_easy_init();
     81   curl_easy_setopt(handle, CURLOPT_WRITEDATA, file);
     82   curl_easy_setopt(handle, CURLOPT_PRIVATE, file);
     83   curl_easy_setopt(handle, CURLOPT_URL, url);
     84   curl_multi_add_handle(curl_handle, handle);
     85   fprintf(stderr, "Added download %s -> %s\n", url, filename);
     86 }
     87 
     88 static void check_multi_info(void)
     89 {
     90   char *done_url;
     91   CURLMsg *message;
     92   int pending;
     93   CURL *easy_handle;
     94   FILE *file;
     95 
     96   while((message = curl_multi_info_read(curl_handle, &pending))) {
     97     switch(message->msg) {
     98     case CURLMSG_DONE:
     99       /* Do not use message data after calling curl_multi_remove_handle() and
    100          curl_easy_cleanup(). As per curl_multi_info_read() docs:
    101          "WARNING: The data the returned pointer points to does not survive
    102          calling curl_multi_cleanup, curl_multi_remove_handle or
    103          curl_easy_cleanup." */
    104       easy_handle = message->easy_handle;
    105 
    106       curl_easy_getinfo(easy_handle, CURLINFO_EFFECTIVE_URL, &done_url);
    107       curl_easy_getinfo(easy_handle, CURLINFO_PRIVATE, &file);
    108       printf("%s DONE\n", done_url);
    109 
    110       curl_multi_remove_handle(curl_handle, easy_handle);
    111       curl_easy_cleanup(easy_handle);
    112       if(file) {
    113         fclose(file);
    114       }
    115       break;
    116 
    117     default:
    118       fprintf(stderr, "CURLMSG default\n");
    119       break;
    120     }
    121   }
    122 }
    123 
    124 static void curl_perform(int fd, short event, void *arg)
    125 {
    126   int running_handles;
    127   int flags = 0;
    128   struct curl_context *context;
    129 
    130   (void)fd;
    131 
    132   if(event & EV_READ)
    133     flags |= CURL_CSELECT_IN;
    134   if(event & EV_WRITE)
    135     flags |= CURL_CSELECT_OUT;
    136 
    137   context = (struct curl_context *) arg;
    138 
    139   curl_multi_socket_action(curl_handle, context->sockfd, flags,
    140                            &running_handles);
    141 
    142   check_multi_info();
    143 }
    144 
    145 static void on_timeout(evutil_socket_t fd, short events, void *arg)
    146 {
    147   int running_handles;
    148   (void)fd;
    149   (void)events;
    150   (void)arg;
    151   curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0,
    152                            &running_handles);
    153   check_multi_info();
    154 }
    155 
    156 static int start_timeout(CURLM *multi, long timeout_ms, void *userp)
    157 {
    158   (void)multi;
    159   (void)userp;
    160   if(timeout_ms < 0) {
    161     evtimer_del(timeout);
    162   }
    163   else {
    164     struct timeval tv;
    165     if(timeout_ms == 0)
    166       timeout_ms = 1; /* 0 means call socket_action asap */
    167     tv.tv_sec = timeout_ms / 1000;
    168     tv.tv_usec = (timeout_ms % 1000) * 1000;
    169     evtimer_del(timeout);
    170     evtimer_add(timeout, &tv);
    171   }
    172   return 0;
    173 }
    174 
    175 static int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp,
    176                          void *socketp)
    177 {
    178   struct curl_context *curl_context;
    179   int events = 0;
    180 
    181   (void)easy;
    182   (void)userp;
    183 
    184   switch(action) {
    185   case CURL_POLL_IN:
    186   case CURL_POLL_OUT:
    187   case CURL_POLL_INOUT:
    188     curl_context = socketp ?
    189       (struct curl_context *) socketp : create_curl_context(s);
    190 
    191     curl_multi_assign(curl_handle, s, (void *) curl_context);
    192 
    193     if(action != CURL_POLL_IN)
    194       events |= EV_WRITE;
    195     if(action != CURL_POLL_OUT)
    196       events |= EV_READ;
    197 
    198     events |= EV_PERSIST;
    199 
    200     event_del(curl_context->event);
    201     event_assign(curl_context->event, base, curl_context->sockfd,
    202       (short)events, curl_perform, curl_context);
    203     event_add(curl_context->event, NULL);
    204 
    205     break;
    206   case CURL_POLL_REMOVE:
    207     if(socketp) {
    208       event_del(((struct curl_context*) socketp)->event);
    209       destroy_curl_context((struct curl_context*) socketp);
    210       curl_multi_assign(curl_handle, s, NULL);
    211     }
    212     break;
    213   default:
    214     abort();
    215   }
    216 
    217   return 0;
    218 }
    219 
    220 int main(int argc, char **argv)
    221 {
    222   if(argc <= 1)
    223     return 0;
    224 
    225   if(curl_global_init(CURL_GLOBAL_ALL)) {
    226     fprintf(stderr, "Could not init curl\n");
    227     return 1;
    228   }
    229 
    230   base = event_base_new();
    231   timeout = evtimer_new(base, on_timeout, NULL);
    232 
    233   curl_handle = curl_multi_init();
    234   curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket);
    235   curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout);
    236 
    237   while(argc-- > 1) {
    238     add_download(argv[argc], argc);
    239   }
    240 
    241   event_base_dispatch(base);
    242 
    243   curl_multi_cleanup(curl_handle);
    244   event_free(timeout);
    245   event_base_free(base);
    246 
    247   libevent_global_shutdown();
    248   curl_global_cleanup();
    249 
    250   return 0;
    251 }