quickjs-tart

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

lib530.c (11243B)


      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 /*
     26  * The purpose of this test is to make sure that if CURLMOPT_SOCKETFUNCTION or
     27  * CURLMOPT_TIMERFUNCTION returns error, the associated transfer should be
     28  * aborted correctly.
     29  */
     30 
     31 #include "first.h"
     32 
     33 #include "memdebug.h"
     34 
     35 
     36 static struct t530_ctx {
     37   int socket_calls;
     38   int max_socket_calls;
     39   int timer_calls;
     40   int max_timer_calls;
     41   char buf[1024];
     42 } t530_ctx;
     43 
     44 static const char *t530_tag(void)
     45 {
     46   curl_msnprintf(t530_ctx.buf, sizeof(t530_ctx.buf),
     47                 "[T530-%d-%d] [%d/%d]",
     48                 t530_ctx.max_socket_calls, t530_ctx.max_timer_calls,
     49                 t530_ctx.socket_calls, t530_ctx.timer_calls);
     50   return t530_ctx.buf;
     51 }
     52 
     53 static void t530_msg(const char *msg)
     54 {
     55   curl_mfprintf(stderr, "%s %s\n", t530_tag(), msg);
     56 }
     57 
     58 
     59 struct t530_Sockets {
     60   curl_socket_t *sockets;
     61   int count;      /* number of sockets actually stored in array */
     62   int max_count;  /* max number of sockets that fit in allocated array */
     63 };
     64 
     65 struct t530_ReadWriteSockets {
     66   struct t530_Sockets read, write;
     67 };
     68 
     69 /**
     70  * Remove a file descriptor from a sockets array.
     71  */
     72 static void t530_removeFd(struct t530_Sockets *sockets, curl_socket_t fd,
     73                           int mention)
     74 {
     75   int i;
     76 
     77   if(mention)
     78     curl_mfprintf(stderr, "%s remove socket fd %d\n", t530_tag(), (int)fd);
     79 
     80   for(i = 0; i < sockets->count; ++i) {
     81     if(sockets->sockets[i] == fd) {
     82       if(i < sockets->count - 1)
     83         memmove(&sockets->sockets[i], &sockets->sockets[i + 1],
     84                 sizeof(curl_socket_t) * (sockets->count - (i + 1)));
     85       --sockets->count;
     86     }
     87   }
     88 }
     89 
     90 /**
     91  * Add a file descriptor to a sockets array.
     92  * Return 0 on success, 1 on error.
     93  */
     94 static int t530_addFd(struct t530_Sockets *sockets, curl_socket_t fd,
     95                       const char *what)
     96 {
     97   /**
     98    * To ensure we only have each file descriptor once, we remove it then add
     99    * it again.
    100    */
    101   curl_mfprintf(stderr, "%s add socket fd %d for %s\n",
    102                 t530_tag(), (int)fd, what);
    103   t530_removeFd(sockets, fd, 0);
    104   /*
    105    * Allocate array storage when required.
    106    */
    107   if(!sockets->sockets) {
    108     sockets->sockets = malloc(sizeof(curl_socket_t) * 20U);
    109     if(!sockets->sockets)
    110       return 1;
    111     sockets->max_count = 20;
    112   }
    113   else if(sockets->count + 1 > sockets->max_count) {
    114     curl_socket_t *ptr = realloc(sockets->sockets, sizeof(curl_socket_t) *
    115                                  (sockets->max_count + 20));
    116     if(!ptr)
    117       /* cleanup in test_cleanup */
    118       return 1;
    119     sockets->sockets = ptr;
    120     sockets->max_count += 20;
    121   }
    122   /*
    123    * Add file descriptor to array.
    124    */
    125   sockets->sockets[sockets->count] = fd;
    126   ++sockets->count;
    127   return 0;
    128 }
    129 
    130 /**
    131  * Callback invoked by curl to poll reading / writing of a socket.
    132  */
    133 static int t530_curlSocketCallback(CURL *easy, curl_socket_t s, int action,
    134                                    void *userp, void *socketp)
    135 {
    136   struct t530_ReadWriteSockets *sockets = userp;
    137 
    138   (void)easy; /* unused */
    139   (void)socketp; /* unused */
    140 
    141   t530_ctx.socket_calls++;
    142   t530_msg("-> CURLMOPT_SOCKETFUNCTION");
    143   if(t530_ctx.socket_calls == t530_ctx.max_socket_calls) {
    144     t530_msg("<- CURLMOPT_SOCKETFUNCTION returns error");
    145     return -1;
    146   }
    147 
    148   if(action == CURL_POLL_IN || action == CURL_POLL_INOUT)
    149     if(t530_addFd(&sockets->read, s, "read"))
    150       return -1; /* bail out */
    151 
    152   if(action == CURL_POLL_OUT || action == CURL_POLL_INOUT)
    153     if(t530_addFd(&sockets->write, s, "write"))
    154       return -1;
    155 
    156   if(action == CURL_POLL_REMOVE) {
    157     t530_removeFd(&sockets->read, s, 1);
    158     t530_removeFd(&sockets->write, s, 0);
    159   }
    160 
    161   return 0;
    162 }
    163 
    164 /**
    165  * Callback invoked by curl to set a timeout.
    166  */
    167 static int t530_curlTimerCallback(CURLM *multi, long timeout_ms, void *userp)
    168 {
    169   struct curltime *timeout = userp;
    170 
    171   (void)multi; /* unused */
    172   t530_ctx.timer_calls++;
    173   t530_msg("-> CURLMOPT_TIMERFUNCTION");
    174   if(t530_ctx.timer_calls == t530_ctx.max_timer_calls) {
    175     t530_msg("<- CURLMOPT_TIMERFUNCTION returns error");
    176     return -1;
    177   }
    178   if(timeout_ms != -1) {
    179     *timeout = curlx_now();
    180     timeout->tv_usec += (int)timeout_ms * 1000;
    181   }
    182   else {
    183     timeout->tv_sec = -1;
    184   }
    185   return 0;
    186 }
    187 
    188 /**
    189  * Check for curl completion.
    190  */
    191 static int t530_checkForCompletion(CURLM *curl, int *success)
    192 {
    193   int result = 0;
    194   *success = 0;
    195   while(1) {
    196     int numMessages;
    197     CURLMsg *message = curl_multi_info_read(curl, &numMessages);
    198     if(!message)
    199       break;
    200     if(message->msg == CURLMSG_DONE) {
    201       result = 1;
    202       if(message->data.result == CURLE_OK)
    203         *success = 1;
    204       else
    205         *success = 0;
    206     }
    207     else {
    208       curl_mfprintf(stderr, "%s got an unexpected message from curl: %i\n",
    209                     t530_tag(), (int)message->msg);
    210       result = 1;
    211       *success = 0;
    212     }
    213   }
    214   return result;
    215 }
    216 
    217 static int t530_getMicroSecondTimeout(struct curltime *timeout)
    218 {
    219   struct curltime now;
    220   ssize_t result;
    221   now = curlx_now();
    222   result = (ssize_t)((timeout->tv_sec - now.tv_sec) * 1000000 +
    223     timeout->tv_usec - now.tv_usec);
    224   if(result < 0)
    225     result = 0;
    226 
    227   return curlx_sztosi(result);
    228 }
    229 
    230 /**
    231  * Update a fd_set with all of the sockets in use.
    232  */
    233 static void t530_updateFdSet(struct t530_Sockets *sockets, fd_set* fdset,
    234                              curl_socket_t *maxFd)
    235 {
    236   int i;
    237   for(i = 0; i < sockets->count; ++i) {
    238 #if defined(__DJGPP__)
    239 #pragma GCC diagnostic push
    240 #pragma GCC diagnostic ignored "-Warith-conversion"
    241 #endif
    242     FD_SET(sockets->sockets[i], fdset);
    243 #if defined(__DJGPP__)
    244 #pragma GCC diagnostic pop
    245 #endif
    246     if(*maxFd < sockets->sockets[i] + 1) {
    247       *maxFd = sockets->sockets[i] + 1;
    248     }
    249   }
    250 }
    251 
    252 static int socket_action(CURLM *curl, curl_socket_t s, int evBitmask,
    253                          const char *info)
    254 {
    255   int numhandles = 0;
    256   CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles);
    257   if(result != CURLM_OK) {
    258     curl_mfprintf(stderr, "%s Curl error on %s (%i) %s\n",
    259                   t530_tag(), info, result, curl_multi_strerror(result));
    260   }
    261   return (int)result;
    262 }
    263 
    264 /**
    265  * Invoke curl when a file descriptor is set.
    266  */
    267 static int t530_checkFdSet(CURLM *curl, struct t530_Sockets *sockets,
    268                            fd_set *fdset, int evBitmask, const char *name)
    269 {
    270   int i;
    271   int result = 0;
    272   for(i = 0; i < sockets->count; ++i) {
    273     if(FD_ISSET(sockets->sockets[i], fdset)) {
    274       result = socket_action(curl, sockets->sockets[i], evBitmask, name);
    275       if(result)
    276         break;
    277     }
    278   }
    279   return result;
    280 }
    281 
    282 static CURLcode testone(char *URL, int timer_fail_at, int socket_fail_at)
    283 {
    284   CURLcode res = CURLE_OK;
    285   CURL *curl = NULL;  CURLM *m = NULL;
    286   struct t530_ReadWriteSockets sockets = {{NULL, 0, 0}, {NULL, 0, 0}};
    287   int success = 0;
    288   struct curltime timeout = {0};
    289   timeout.tv_sec = (time_t)-1;
    290 
    291   /* set the limits */
    292   memset(&t530_ctx, 0, sizeof(t530_ctx));
    293   t530_ctx.max_timer_calls = timer_fail_at;
    294   t530_ctx.max_socket_calls = socket_fail_at;
    295 
    296   t530_msg("start");
    297   start_test_timing();
    298 
    299   res_global_init(CURL_GLOBAL_ALL);
    300   if(res != CURLE_OK)
    301     return res;
    302 
    303   easy_init(curl);
    304 
    305   /* specify target */
    306   easy_setopt(curl, CURLOPT_URL, URL);
    307 
    308   /* go verbose */
    309   easy_setopt(curl, CURLOPT_VERBOSE, 1L);
    310 
    311   multi_init(m);
    312 
    313   multi_setopt(m, CURLMOPT_SOCKETFUNCTION, t530_curlSocketCallback);
    314   multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets);
    315 
    316   multi_setopt(m, CURLMOPT_TIMERFUNCTION, t530_curlTimerCallback);
    317   multi_setopt(m, CURLMOPT_TIMERDATA, &timeout);
    318 
    319   multi_add_handle(m, curl);
    320 
    321   if(socket_action(m, CURL_SOCKET_TIMEOUT, 0, "timeout")) {
    322     res = TEST_ERR_MAJOR_BAD;
    323     goto test_cleanup;
    324   }
    325 
    326   while(!t530_checkForCompletion(m, &success)) {
    327     fd_set readSet, writeSet;
    328     curl_socket_t maxFd = 0;
    329     struct timeval tv = {0};
    330     tv.tv_sec = 10;
    331 
    332     FD_ZERO(&readSet);
    333     FD_ZERO(&writeSet);
    334     t530_updateFdSet(&sockets.read, &readSet, &maxFd);
    335     t530_updateFdSet(&sockets.write, &writeSet, &maxFd);
    336 
    337     if(timeout.tv_sec != (time_t)-1) {
    338       int usTimeout = t530_getMicroSecondTimeout(&timeout);
    339       tv.tv_sec = usTimeout / 1000000;
    340       tv.tv_usec = usTimeout % 1000000;
    341     }
    342     else if(maxFd <= 0) {
    343       tv.tv_sec = 0;
    344       tv.tv_usec = 100000;
    345     }
    346 
    347     assert(maxFd);
    348     select_test((int)maxFd, &readSet, &writeSet, NULL, &tv);
    349 
    350     /* Check the sockets for reading / writing */
    351     if(t530_checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN,
    352                        "read")) {
    353       res = TEST_ERR_MAJOR_BAD;
    354       goto test_cleanup;
    355     }
    356     if(t530_checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT,
    357                        "write")) {
    358       res = TEST_ERR_MAJOR_BAD;
    359       goto test_cleanup;
    360     }
    361 
    362     if(timeout.tv_sec != (time_t)-1 &&
    363        t530_getMicroSecondTimeout(&timeout) == 0) {
    364       /* Curl's timer has elapsed. */
    365       if(socket_action(m, CURL_SOCKET_TIMEOUT, 0, "timeout")) {
    366         res = TEST_ERR_BAD_TIMEOUT;
    367         goto test_cleanup;
    368       }
    369     }
    370 
    371     abort_on_test_timeout();
    372   }
    373 
    374   if(!success) {
    375     t530_msg("Error getting file.");
    376     res = TEST_ERR_MAJOR_BAD;
    377   }
    378 
    379 test_cleanup:
    380 
    381   /* proper cleanup sequence */
    382   t530_msg("cleanup");
    383   curl_multi_remove_handle(m, curl);
    384   curl_easy_cleanup(curl);
    385   curl_multi_cleanup(m);
    386   curl_global_cleanup();
    387 
    388   /* free local memory */
    389   free(sockets.read.sockets);
    390   free(sockets.write.sockets);
    391   t530_msg("done");
    392 
    393   return res;
    394 }
    395 
    396 static CURLcode test_lib530(char *URL)
    397 {
    398   CURLcode rc;
    399   /* rerun the same transfer multiple times and make it fail in different
    400      callback calls */
    401   rc = testone(URL, 0, 0); /* no callback fails */
    402   if(rc)
    403     curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), rc);
    404 
    405   rc = testone(URL, 1, 0); /* fail 1st call to timer callback */
    406   if(!rc)
    407     curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), rc);
    408 
    409   rc = testone(URL, 2, 0); /* fail 2nd call to timer callback */
    410   if(!rc)
    411     curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), rc);
    412 
    413   rc = testone(URL, 0, 1); /* fail 1st call to socket callback */
    414   if(!rc)
    415     curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), rc);
    416 
    417   rc = testone(URL, 0, 2); /* fail 2nd call to socket callback */
    418   if(!rc)
    419     curl_mfprintf(stderr, "%s FAILED: %d\n", t530_tag(), rc);
    420 
    421   return CURLE_OK;
    422 }