quickjs-tart

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

hx_upload.c (15071B)


      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 #include "first.h"
     25 
     26 static int verbose_u = 1;
     27 
     28 struct transfer_u {
     29   int idx;
     30   CURL *easy;
     31   const char *method;
     32   char filename[128];
     33   FILE *out;
     34   curl_off_t send_total;
     35   curl_off_t recv_size;
     36   curl_off_t send_size;
     37   curl_off_t fail_at;
     38   curl_off_t pause_at;
     39   curl_off_t abort_at;
     40   int started;
     41   int paused;
     42   int resumed;
     43   int done;
     44 };
     45 
     46 static size_t transfer_count_u = 1;
     47 static struct transfer_u *transfer_u;
     48 static int forbid_reuse_u = 0;
     49 
     50 static struct transfer_u *get_transfer_for_easy_u(CURL *easy)
     51 {
     52   size_t i;
     53   for(i = 0; i < transfer_count_u; ++i) {
     54     if(easy == transfer_u[i].easy)
     55       return &transfer_u[i];
     56   }
     57   return NULL;
     58 }
     59 
     60 static size_t my_write_u_cb(char *buf, size_t nitems, size_t buflen,
     61                             void *userdata)
     62 {
     63   struct transfer_u *t = userdata;
     64   size_t blen = (nitems * buflen);
     65   size_t nwritten;
     66 
     67   curl_mfprintf(stderr, "[t-%d] RECV %ld bytes, total=%ld, pause_at=%ld\n",
     68                 t->idx, (long)blen, (long)t->recv_size, (long)t->pause_at);
     69   if(!t->out) {
     70     curl_msnprintf(t->filename, sizeof(t->filename)-1, "download_%u.data",
     71                    t->idx);
     72     t->out = fopen(t->filename, "wb");
     73     if(!t->out)
     74       return 0;
     75   }
     76 
     77   nwritten = fwrite(buf, nitems, buflen, t->out);
     78   if(nwritten < blen) {
     79     curl_mfprintf(stderr, "[t-%d] write failure\n", t->idx);
     80     return 0;
     81   }
     82   t->recv_size += (curl_off_t)nwritten;
     83   return (size_t)nwritten;
     84 }
     85 
     86 static size_t my_read_cb(char *buf, size_t nitems, size_t buflen,
     87                          void *userdata)
     88 {
     89   struct transfer_u *t = userdata;
     90   size_t blen = (nitems * buflen);
     91   size_t nread;
     92 
     93   if(t->send_total <= t->send_size)
     94     nread = 0;
     95   else if((t->send_total - t->send_size) < (curl_off_t)blen)
     96     nread = (size_t)(t->send_total - t->send_size);
     97   else
     98     nread = blen;
     99 
    100   curl_mfprintf(stderr, "[t-%d] SEND %ld bytes, total=%ld, pause_at=%ld\n",
    101                 t->idx, (long)nread, (long)t->send_total, (long)t->pause_at);
    102 
    103   if(!t->resumed &&
    104      t->send_size < t->pause_at &&
    105      ((t->send_size + (curl_off_t)blen) >= t->pause_at)) {
    106     curl_mfprintf(stderr, "[t-%d] PAUSE\n", t->idx);
    107     t->paused = 1;
    108     return CURL_READFUNC_PAUSE;
    109   }
    110 
    111   memset(buf, 'x', nread);
    112   t->send_size += (curl_off_t)nread;
    113   if(t->fail_at > 0 && t->send_size >= t->fail_at) {
    114     curl_mfprintf(stderr, "[t-%d] ABORT by read callback at %ld bytes\n",
    115                   t->idx, (long)t->send_size);
    116     return CURL_READFUNC_ABORT;
    117   }
    118   return (size_t)nread;
    119 }
    120 
    121 static int my_progress_u_cb(void *userdata,
    122                             curl_off_t dltotal, curl_off_t dlnow,
    123                             curl_off_t ultotal, curl_off_t ulnow)
    124 {
    125   struct transfer_u *t = userdata;
    126   (void)ultotal;
    127   (void)dlnow;
    128   (void)dltotal;
    129   if(t->abort_at > 0 && ulnow >= t->abort_at) {
    130     curl_mfprintf(stderr, "[t-%d] ABORT by progress_cb at %ld bytes sent\n",
    131                   t->idx, (long)ulnow);
    132     return 1;
    133   }
    134   return 0;
    135 }
    136 
    137 static int setup_hx_upload(CURL *hnd, const char *url, struct transfer_u *t,
    138                            long http_version, struct curl_slist *host,
    139                            CURLSH *share, int use_earlydata,
    140                            int announce_length)
    141 {
    142   curl_easy_setopt(hnd, CURLOPT_SHARE, share);
    143   curl_easy_setopt(hnd, CURLOPT_URL, url);
    144   curl_easy_setopt(hnd, CURLOPT_HTTP_VERSION, http_version);
    145   curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYPEER, 0L);
    146   curl_easy_setopt(hnd, CURLOPT_SSL_VERIFYHOST, 0L);
    147   curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, (long)(128 * 1024));
    148   curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, CURLFOLLOW_OBEYCODE);
    149   curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, my_write_u_cb);
    150   curl_easy_setopt(hnd, CURLOPT_WRITEDATA, t);
    151   if(use_earlydata)
    152     curl_easy_setopt(hnd, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_EARLYDATA);
    153 
    154   if(!t->method || !strcmp("PUT", t->method))
    155     curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
    156   else if(!strcmp("POST", t->method))
    157     curl_easy_setopt(hnd, CURLOPT_POST, 1L);
    158   else {
    159     curl_mfprintf(stderr, "unsupported method '%s'\n", t->method);
    160     return 1;
    161   }
    162   curl_easy_setopt(hnd, CURLOPT_READFUNCTION, my_read_cb);
    163   curl_easy_setopt(hnd, CURLOPT_READDATA, t);
    164   if(announce_length)
    165     curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, t->send_total);
    166 
    167   curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L);
    168   curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, my_progress_u_cb);
    169   curl_easy_setopt(hnd, CURLOPT_XFERINFODATA, t);
    170   if(forbid_reuse_u)
    171     curl_easy_setopt(hnd, CURLOPT_FORBID_REUSE, 1L);
    172   if(host)
    173     curl_easy_setopt(hnd, CURLOPT_RESOLVE, host);
    174 
    175   /* please be verbose */
    176   if(verbose_u) {
    177     curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
    178     curl_easy_setopt(hnd, CURLOPT_DEBUGFUNCTION, debug_cb);
    179   }
    180 
    181   /* wait for pipe connection to confirm */
    182   curl_easy_setopt(hnd, CURLOPT_PIPEWAIT, 1L);
    183 
    184   return 0; /* all is good */
    185 }
    186 
    187 static void usage_hx_upload(const char *msg)
    188 {
    189   if(msg)
    190     curl_mfprintf(stderr, "%s\n", msg);
    191   curl_mfprintf(stderr,
    192     "usage: [options] url\n"
    193     "  upload to a url with following options:\n"
    194     "  -a         abort paused transfer\n"
    195     "  -e         use TLS earlydata\n"
    196     "  -m number  max parallel uploads\n"
    197     "  -n number  total uploads\n"
    198     "  -A number  abort transfer after `number` request body bytes\n"
    199     "  -F number  fail reading request body after `number` of bytes\n"
    200     "  -P number  pause transfer after `number` request body bytes\n"
    201     "  -r <host>:<port>:<addr>  resolve information\n"
    202     "  -S number  size to upload\n"
    203     "  -V http_version (http/1.1, h2, h3) http version to use\n"
    204   );
    205 }
    206 
    207 /*
    208  * Download a file over HTTP/2, take care of server push.
    209  */
    210 static int test_hx_upload(int argc, char *argv[])
    211 {
    212   CURLM *multi_handle;
    213   CURLSH *share;
    214   const char *url;
    215   const char *method = "PUT";
    216   size_t i, n, max_parallel = 1;
    217   size_t active_transfers;
    218   size_t pause_offset = 0;
    219   size_t abort_offset = 0;
    220   size_t fail_offset = 0;
    221   size_t send_total = (128 * 1024);
    222   int abort_paused = 0;
    223   int reuse_easy = 0;
    224   int use_earlydata = 0;
    225   int announce_length = 0;
    226   struct transfer_u *t;
    227   int http_version = CURL_HTTP_VERSION_2_0;
    228   struct curl_slist *host = NULL;
    229   const char *resolve = NULL;
    230   int ch;
    231 
    232   while((ch = cgetopt(argc, argv, "aefhlm:n:A:F:M:P:r:RS:V:")) != -1) {
    233     switch(ch) {
    234     case 'h':
    235       usage_hx_upload(NULL);
    236       return 2;
    237     case 'a':
    238       abort_paused = 1;
    239       break;
    240     case 'e':
    241       use_earlydata = 1;
    242       break;
    243     case 'f':
    244       forbid_reuse_u = 1;
    245       break;
    246     case 'l':
    247       announce_length = 1;
    248       break;
    249     case 'm':
    250       max_parallel = (size_t)strtol(coptarg, NULL, 10);
    251       break;
    252     case 'n':
    253       transfer_count_u = (size_t)strtol(coptarg, NULL, 10);
    254       break;
    255     case 'A':
    256       abort_offset = (size_t)strtol(coptarg, NULL, 10);
    257       break;
    258     case 'F':
    259       fail_offset = (size_t)strtol(coptarg, NULL, 10);
    260       break;
    261     case 'M':
    262       method = coptarg;
    263       break;
    264     case 'P':
    265       pause_offset = (size_t)strtol(coptarg, NULL, 10);
    266       break;
    267     case 'r':
    268       resolve = coptarg;
    269       break;
    270     case 'R':
    271       reuse_easy = 1;
    272       break;
    273     case 'S':
    274       send_total = (size_t)strtol(coptarg, NULL, 10);
    275       break;
    276     case 'V': {
    277       if(!strcmp("http/1.1", coptarg))
    278         http_version = CURL_HTTP_VERSION_1_1;
    279       else if(!strcmp("h2", coptarg))
    280         http_version = CURL_HTTP_VERSION_2_0;
    281       else if(!strcmp("h3", coptarg))
    282         http_version = CURL_HTTP_VERSION_3ONLY;
    283       else {
    284         usage_hx_upload("invalid http version");
    285         return 1;
    286       }
    287       break;
    288     }
    289     default:
    290       usage_hx_upload("invalid option");
    291       return 1;
    292     }
    293   }
    294   argc -= coptind;
    295   argv += coptind;
    296 
    297   if(max_parallel > 1 && reuse_easy) {
    298     usage_hx_upload("cannot mix -R and -P");
    299     return 2;
    300   }
    301 
    302   curl_global_init(CURL_GLOBAL_DEFAULT);
    303   curl_global_trace("ids,time,http/2,http/3");
    304 
    305   if(argc != 1) {
    306     usage_hx_upload("not enough arguments");
    307     return 2;
    308   }
    309   url = argv[0];
    310 
    311   if(resolve)
    312     host = curl_slist_append(NULL, resolve);
    313 
    314   share = curl_share_init();
    315   if(!share) {
    316     curl_mfprintf(stderr, "error allocating share\n");
    317     return 1;
    318   }
    319   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
    320   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
    321   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
    322   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
    323   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
    324   curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_HSTS);
    325 
    326   transfer_u = calloc(transfer_count_u, sizeof(*transfer_u));
    327   if(!transfer_u) {
    328     curl_mfprintf(stderr, "error allocating transfer structs\n");
    329     return 1;
    330   }
    331 
    332   active_transfers = 0;
    333   for(i = 0; i < transfer_count_u; ++i) {
    334     t = &transfer_u[i];
    335     t->idx = (int)i;
    336     t->method = method;
    337     t->send_total = (curl_off_t)send_total;
    338     t->abort_at = (curl_off_t)abort_offset;
    339     t->fail_at = (curl_off_t)fail_offset;
    340     t->pause_at = (curl_off_t)pause_offset;
    341   }
    342 
    343   if(reuse_easy) {
    344     CURL *easy = curl_easy_init();
    345     CURLcode rc = CURLE_OK;
    346     if(!easy) {
    347       curl_mfprintf(stderr, "failed to init easy handle\n");
    348       return 1;
    349     }
    350     for(i = 0; i < transfer_count_u; ++i) {
    351       t = &transfer_u[i];
    352       t->easy = easy;
    353       if(setup_hx_upload(t->easy, url, t, http_version, host, share,
    354                          use_earlydata, announce_length)) {
    355         curl_mfprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
    356         return 1;
    357       }
    358 
    359       curl_mfprintf(stderr, "[t-%d] STARTING\n", t->idx);
    360       rc = curl_easy_perform(easy);
    361       curl_mfprintf(stderr, "[t-%d] DONE -> %d\n", t->idx, rc);
    362       t->easy = NULL;
    363       curl_easy_reset(easy);
    364     }
    365     curl_easy_cleanup(easy);
    366   }
    367   else {
    368     multi_handle = curl_multi_init();
    369     curl_multi_setopt(multi_handle, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);
    370 
    371     n = (max_parallel < transfer_count_u) ? max_parallel : transfer_count_u;
    372     for(i = 0; i < n; ++i) {
    373       t = &transfer_u[i];
    374       t->easy = curl_easy_init();
    375       if(!t->easy || setup_hx_upload(t->easy, url, t, http_version, host,
    376                                      share, use_earlydata, announce_length)) {
    377         curl_mfprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
    378         return 1;
    379       }
    380       curl_multi_add_handle(multi_handle, t->easy);
    381       t->started = 1;
    382       ++active_transfers;
    383       curl_mfprintf(stderr, "[t-%d] STARTED\n", t->idx);
    384     }
    385 
    386     do {
    387       int still_running; /* keep number of running handles */
    388       CURLMcode mc = curl_multi_perform(multi_handle, &still_running);
    389       struct CURLMsg *m;
    390 
    391       if(still_running) {
    392         /* wait for activity, timeout or "nothing" */
    393         mc = curl_multi_poll(multi_handle, NULL, 0, 1000, NULL);
    394       }
    395 
    396       if(mc)
    397         break;
    398 
    399       do {
    400         int msgq = 0;
    401         m = curl_multi_info_read(multi_handle, &msgq);
    402         if(m && (m->msg == CURLMSG_DONE)) {
    403           CURL *e = m->easy_handle;
    404           --active_transfers;
    405           curl_multi_remove_handle(multi_handle, e);
    406           t = get_transfer_for_easy_u(e);
    407           if(t) {
    408             long res_status;
    409             curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE, &res_status);
    410             t->done = 1;
    411             curl_mfprintf(stderr, "[t-%d] FINISHED, result=%d, response=%ld\n",
    412                           t->idx, m->data.result, res_status);
    413             if(use_earlydata) {
    414               curl_off_t sent;
    415               curl_easy_getinfo(e, CURLINFO_EARLYDATA_SENT_T, &sent);
    416               curl_mfprintf(stderr, "[t-%d] EarlyData: %ld\n", t->idx,
    417                             (long)sent);
    418             }
    419           }
    420           else {
    421             curl_easy_cleanup(e);
    422             curl_mfprintf(stderr, "unknown FINISHED???\n");
    423           }
    424         }
    425 
    426 
    427         /* nothing happening, maintenance */
    428         if(abort_paused) {
    429           /* abort paused transfers */
    430           for(i = 0; i < transfer_count_u; ++i) {
    431             t = &transfer_u[i];
    432             if(!t->done && t->paused && t->easy) {
    433               curl_multi_remove_handle(multi_handle, t->easy);
    434               t->done = 1;
    435               active_transfers--;
    436               curl_mfprintf(stderr, "[t-%d] ABORTED\n", t->idx);
    437             }
    438           }
    439         }
    440         else {
    441           /* resume one paused transfer */
    442           for(i = 0; i < transfer_count_u; ++i) {
    443             t = &transfer_u[i];
    444             if(!t->done && t->paused) {
    445               t->resumed = 1;
    446               t->paused = 0;
    447               curl_easy_pause(t->easy, CURLPAUSE_CONT);
    448               curl_mfprintf(stderr, "[t-%d] RESUMED\n", t->idx);
    449               break;
    450             }
    451           }
    452         }
    453 
    454         while(active_transfers < max_parallel) {
    455           for(i = 0; i < transfer_count_u; ++i) {
    456             t = &transfer_u[i];
    457             if(!t->started) {
    458               t->easy = curl_easy_init();
    459               if(!t->easy || setup_hx_upload(t->easy, url, t, http_version,
    460                                              host, share, use_earlydata,
    461                                              announce_length)) {
    462                 curl_mfprintf(stderr, "[t-%d] FAILED setup\n", (int)i);
    463                 return 1;
    464               }
    465               curl_multi_add_handle(multi_handle, t->easy);
    466               t->started = 1;
    467               ++active_transfers;
    468               curl_mfprintf(stderr, "[t-%d] STARTED\n", t->idx);
    469               break;
    470             }
    471           }
    472           /* all started */
    473           if(i == transfer_count_u)
    474             break;
    475         }
    476       } while(m);
    477 
    478     } while(active_transfers); /* as long as we have transfers going */
    479 
    480     curl_multi_cleanup(multi_handle);
    481   }
    482 
    483   for(i = 0; i < transfer_count_u; ++i) {
    484     t = &transfer_u[i];
    485     if(t->out) {
    486       fclose(t->out);
    487       t->out = NULL;
    488     }
    489     if(t->easy) {
    490       curl_easy_cleanup(t->easy);
    491       t->easy = NULL;
    492     }
    493   }
    494   free(transfer_u);
    495   curl_share_cleanup(share);
    496 
    497   return 0;
    498 }