quickjs-tart

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

request.c (13314B)


      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 #include "curl_setup.h"
     26 
     27 #include "urldata.h"
     28 #include "cfilters.h"
     29 #include "curlx/dynbuf.h"
     30 #include "doh.h"
     31 #include "multiif.h"
     32 #include "progress.h"
     33 #include "request.h"
     34 #include "sendf.h"
     35 #include "transfer.h"
     36 #include "url.h"
     37 #include "curlx/strparse.h"
     38 
     39 /* The last 3 #include files should be in this order */
     40 #include "curl_printf.h"
     41 #include "curl_memory.h"
     42 #include "memdebug.h"
     43 
     44 void Curl_req_init(struct SingleRequest *req)
     45 {
     46   memset(req, 0, sizeof(*req));
     47 }
     48 
     49 CURLcode Curl_req_soft_reset(struct SingleRequest *req,
     50                              struct Curl_easy *data)
     51 {
     52   CURLcode result;
     53 
     54   req->done = FALSE;
     55   req->upload_done = FALSE;
     56   req->upload_aborted = FALSE;
     57   req->download_done = FALSE;
     58   req->eos_written = FALSE;
     59   req->eos_read = FALSE;
     60   req->eos_sent = FALSE;
     61   req->ignorebody = FALSE;
     62   req->shutdown = FALSE;
     63   req->bytecount = 0;
     64   req->writebytecount = 0;
     65   req->header = TRUE; /* assume header */
     66   req->headerline = 0;
     67   req->headerbytecount = 0;
     68   req->allheadercount =  0;
     69   req->deductheadercount = 0;
     70   req->httpversion_sent = 0;
     71   req->httpversion = 0;
     72   req->sendbuf_hds_len = 0;
     73 
     74   result = Curl_client_start(data);
     75   if(result)
     76     return result;
     77 
     78   if(!req->sendbuf_init) {
     79     Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
     80                     BUFQ_OPT_SOFT_LIMIT);
     81     req->sendbuf_init = TRUE;
     82   }
     83   else {
     84     Curl_bufq_reset(&req->sendbuf);
     85     if(data->set.upload_buffer_size != req->sendbuf.chunk_size) {
     86       Curl_bufq_free(&req->sendbuf);
     87       Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
     88                       BUFQ_OPT_SOFT_LIMIT);
     89     }
     90   }
     91 
     92   return CURLE_OK;
     93 }
     94 
     95 CURLcode Curl_req_start(struct SingleRequest *req,
     96                         struct Curl_easy *data)
     97 {
     98   req->start = curlx_now();
     99   return Curl_req_soft_reset(req, data);
    100 }
    101 
    102 static CURLcode req_flush(struct Curl_easy *data);
    103 
    104 CURLcode Curl_req_done(struct SingleRequest *req,
    105                        struct Curl_easy *data, bool aborted)
    106 {
    107   (void)req;
    108   if(!aborted)
    109     (void)req_flush(data);
    110   Curl_client_reset(data);
    111 #ifndef CURL_DISABLE_DOH
    112   Curl_doh_close(data);
    113 #endif
    114   return CURLE_OK;
    115 }
    116 
    117 void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
    118 {
    119   struct curltime t0 = {0, 0};
    120 
    121   Curl_safefree(req->newurl);
    122   Curl_client_reset(data);
    123   if(req->sendbuf_init)
    124     Curl_bufq_reset(&req->sendbuf);
    125 
    126 #ifndef CURL_DISABLE_DOH
    127   Curl_doh_close(data);
    128 #endif
    129   /* Can no longer memset() this struct as we need to keep some state */
    130   req->size = -1;
    131   req->maxdownload = -1;
    132   req->bytecount = 0;
    133   req->writebytecount = 0;
    134   req->start = t0;
    135   req->headerbytecount = 0;
    136   req->allheadercount =  0;
    137   req->deductheadercount = 0;
    138   req->headerline = 0;
    139   req->offset = 0;
    140   req->httpcode = 0;
    141   req->keepon = 0;
    142   req->upgr101 = UPGR101_INIT;
    143   req->sendbuf_hds_len = 0;
    144   req->timeofdoc = 0;
    145   req->location = NULL;
    146   req->newurl = NULL;
    147 #ifndef CURL_DISABLE_COOKIES
    148   req->setcookies = 0;
    149 #endif
    150   req->header = FALSE;
    151   req->content_range = FALSE;
    152   req->download_done = FALSE;
    153   req->eos_written = FALSE;
    154   req->eos_read = FALSE;
    155   req->eos_sent = FALSE;
    156   req->upload_done = FALSE;
    157   req->upload_aborted = FALSE;
    158   req->ignorebody = FALSE;
    159   req->http_bodyless = FALSE;
    160   req->chunk = FALSE;
    161   req->ignore_cl = FALSE;
    162   req->upload_chunky = FALSE;
    163   req->getheader = FALSE;
    164   req->no_body = data->set.opt_no_body;
    165   req->authneg = FALSE;
    166   req->shutdown = FALSE;
    167 }
    168 
    169 void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
    170 {
    171   Curl_safefree(req->newurl);
    172   if(req->sendbuf_init)
    173     Curl_bufq_free(&req->sendbuf);
    174   Curl_client_cleanup(data);
    175 }
    176 
    177 static CURLcode xfer_send(struct Curl_easy *data,
    178                           const char *buf, size_t blen,
    179                           size_t hds_len, size_t *pnwritten)
    180 {
    181   CURLcode result = CURLE_OK;
    182   bool eos = FALSE;
    183 
    184   *pnwritten = 0;
    185   DEBUGASSERT(hds_len <= blen);
    186 #ifdef DEBUGBUILD
    187   {
    188     /* Allow debug builds to override this logic to force short initial
    189        sends */
    190     size_t body_len = blen - hds_len;
    191     if(body_len) {
    192       const char *p = getenv("CURL_SMALLREQSEND");
    193       if(p) {
    194         curl_off_t body_small;
    195         if(!curlx_str_number(&p, &body_small, body_len))
    196           blen = hds_len + (size_t)body_small;
    197       }
    198     }
    199   }
    200 #endif
    201   /* Make sure this does not send more body bytes than what the max send
    202      speed says. The headers do not count to the max speed. */
    203   if(data->set.max_send_speed) {
    204     size_t body_bytes = blen - hds_len;
    205     if((curl_off_t)body_bytes > data->set.max_send_speed)
    206       blen = hds_len + (size_t)data->set.max_send_speed;
    207   }
    208 
    209   if(data->req.eos_read &&
    210     (Curl_bufq_is_empty(&data->req.sendbuf) ||
    211      Curl_bufq_len(&data->req.sendbuf) == blen)) {
    212     DEBUGF(infof(data, "sending last upload chunk of %zu bytes", blen));
    213     eos = TRUE;
    214   }
    215   result = Curl_xfer_send(data, buf, blen, eos, pnwritten);
    216   if(!result) {
    217     if(eos && (blen == *pnwritten))
    218       data->req.eos_sent = TRUE;
    219     if(*pnwritten) {
    220       if(hds_len)
    221         Curl_debug(data, CURLINFO_HEADER_OUT, buf,
    222                    CURLMIN(hds_len, *pnwritten));
    223       if(*pnwritten > hds_len) {
    224         size_t body_len = *pnwritten - hds_len;
    225         Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len);
    226         data->req.writebytecount += body_len;
    227         Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
    228       }
    229     }
    230   }
    231   return result;
    232 }
    233 
    234 static CURLcode req_send_buffer_flush(struct Curl_easy *data)
    235 {
    236   CURLcode result = CURLE_OK;
    237   const unsigned char *buf;
    238   size_t blen;
    239 
    240   while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
    241     size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
    242     result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
    243     if(result)
    244       break;
    245 
    246     Curl_bufq_skip(&data->req.sendbuf, nwritten);
    247     if(hds_len) {
    248       data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
    249     }
    250     /* leave if we could not send all. Maybe network blocking or
    251      * speed limits on transfer */
    252     if(nwritten < blen)
    253       break;
    254   }
    255   return result;
    256 }
    257 
    258 static CURLcode req_set_upload_done(struct Curl_easy *data)
    259 {
    260   DEBUGASSERT(!data->req.upload_done);
    261   data->req.upload_done = TRUE;
    262   data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */
    263 
    264   Curl_pgrsTime(data, TIMER_POSTRANSFER);
    265   Curl_creader_done(data, data->req.upload_aborted);
    266 
    267   if(data->req.upload_aborted) {
    268     Curl_bufq_reset(&data->req.sendbuf);
    269     if(data->req.writebytecount)
    270       infof(data, "abort upload after having sent %" FMT_OFF_T " bytes",
    271             data->req.writebytecount);
    272     else
    273       infof(data, "abort upload");
    274   }
    275   else if(data->req.writebytecount)
    276     infof(data, "upload completely sent off: %" FMT_OFF_T " bytes",
    277           data->req.writebytecount);
    278   else if(!data->req.download_done) {
    279     DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
    280     infof(data, Curl_creader_total_length(data) ?
    281           "We are completely uploaded and fine" :
    282           "Request completely sent off");
    283   }
    284 
    285   return Curl_xfer_send_close(data);
    286 }
    287 
    288 static CURLcode req_flush(struct Curl_easy *data)
    289 {
    290   CURLcode result;
    291 
    292   if(!data || !data->conn)
    293     return CURLE_FAILED_INIT;
    294 
    295   if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
    296     result = req_send_buffer_flush(data);
    297     if(result)
    298       return result;
    299     if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
    300       DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN",
    301              Curl_bufq_len(&data->req.sendbuf)));
    302       return CURLE_AGAIN;
    303     }
    304   }
    305   else if(Curl_xfer_needs_flush(data)) {
    306     DEBUGF(infof(data, "Curl_req_flush(), xfer send_pending"));
    307     return Curl_xfer_flush(data);
    308   }
    309 
    310   if(data->req.eos_read && !data->req.eos_sent) {
    311     char tmp;
    312     size_t nwritten;
    313     result = xfer_send(data, &tmp, 0, 0, &nwritten);
    314     if(result)
    315       return result;
    316     DEBUGASSERT(data->req.eos_sent);
    317   }
    318 
    319   if(!data->req.upload_done && data->req.eos_read && data->req.eos_sent) {
    320     DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
    321     if(data->req.shutdown) {
    322       bool done;
    323       result = Curl_xfer_send_shutdown(data, &done);
    324       if(result && data->req.shutdown_err_ignore) {
    325         infof(data, "Shutdown send direction error: %d. Broken server? "
    326               "Proceeding as if everything is ok.", result);
    327         result = CURLE_OK;
    328         done = TRUE;
    329       }
    330 
    331       if(result)
    332         return result;
    333       if(!done)
    334         return CURLE_AGAIN;
    335     }
    336     return req_set_upload_done(data);
    337   }
    338   return CURLE_OK;
    339 }
    340 
    341 static CURLcode add_from_client(void *reader_ctx,
    342                                 unsigned char *buf, size_t buflen,
    343                                 size_t *pnread)
    344 {
    345   struct Curl_easy *data = reader_ctx;
    346   CURLcode result;
    347   bool eos;
    348 
    349   result = Curl_client_read(data, (char *)buf, buflen, pnread, &eos);
    350   if(!result && eos)
    351     data->req.eos_read = TRUE;
    352   return result;
    353 }
    354 
    355 static CURLcode req_send_buffer_add(struct Curl_easy *data,
    356                                     const char *buf, size_t blen,
    357                                     size_t hds_len)
    358 {
    359   CURLcode result = CURLE_OK;
    360   size_t n;
    361   result = Curl_bufq_cwrite(&data->req.sendbuf, buf, blen, &n);
    362   if(result)
    363     return result;
    364   /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
    365   DEBUGASSERT(n == blen);
    366   data->req.sendbuf_hds_len += hds_len;
    367   return CURLE_OK;
    368 }
    369 
    370 CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req,
    371                        unsigned char httpversion)
    372 {
    373   CURLcode result;
    374   const char *buf;
    375   size_t blen, nwritten;
    376 
    377   if(!data || !data->conn)
    378     return CURLE_FAILED_INIT;
    379 
    380   data->req.httpversion_sent = httpversion;
    381   buf = curlx_dyn_ptr(req);
    382   blen = curlx_dyn_len(req);
    383   if(!Curl_creader_total_length(data)) {
    384     /* Request without body. Try to send directly from the buf given. */
    385     data->req.eos_read = TRUE;
    386     result = xfer_send(data, buf, blen, blen, &nwritten);
    387     if(result)
    388       return result;
    389     buf += nwritten;
    390     blen -= nwritten;
    391   }
    392 
    393   if(blen) {
    394     /* Either we have a request body, or we could not send the complete
    395      * request in one go. Buffer the remainder and try to add as much
    396      * body bytes as room is left in the buffer. Then flush. */
    397     result = req_send_buffer_add(data, buf, blen, blen);
    398     if(result)
    399       return result;
    400 
    401     return Curl_req_send_more(data);
    402   }
    403   return CURLE_OK;
    404 }
    405 
    406 bool Curl_req_sendbuf_empty(struct Curl_easy *data)
    407 {
    408   return !data->req.sendbuf_init || Curl_bufq_is_empty(&data->req.sendbuf);
    409 }
    410 
    411 bool Curl_req_want_send(struct Curl_easy *data)
    412 {
    413   /* Not done and
    414    * - KEEP_SEND and not PAUSEd.
    415    * - or request has buffered data to send
    416    * - or transfer connection has pending data to send */
    417   return !data->req.done &&
    418          (((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) ||
    419            !Curl_req_sendbuf_empty(data) ||
    420            Curl_xfer_needs_flush(data));
    421 }
    422 
    423 bool Curl_req_done_sending(struct Curl_easy *data)
    424 {
    425   return data->req.upload_done && !Curl_req_want_send(data);
    426 }
    427 
    428 CURLcode Curl_req_send_more(struct Curl_easy *data)
    429 {
    430   CURLcode result;
    431 
    432   /* Fill our send buffer if more from client can be read. */
    433   if(!data->req.upload_aborted &&
    434      !data->req.eos_read &&
    435      !Curl_xfer_send_is_paused(data) &&
    436      !Curl_bufq_is_full(&data->req.sendbuf)) {
    437     size_t nread;
    438     result = Curl_bufq_sipn(&data->req.sendbuf, 0,
    439                             add_from_client, data, &nread);
    440     if(result && result != CURLE_AGAIN)
    441       return result;
    442   }
    443 
    444   result = req_flush(data);
    445   if(result == CURLE_AGAIN)
    446     result = CURLE_OK;
    447 
    448   return result;
    449 }
    450 
    451 CURLcode Curl_req_abort_sending(struct Curl_easy *data)
    452 {
    453   if(!data->req.upload_done) {
    454     Curl_bufq_reset(&data->req.sendbuf);
    455     data->req.upload_aborted = TRUE;
    456     /* no longer KEEP_SEND and KEEP_SEND_PAUSE */
    457     data->req.keepon &= ~KEEP_SENDBITS;
    458     return req_set_upload_done(data);
    459   }
    460   return CURLE_OK;
    461 }
    462 
    463 CURLcode Curl_req_stop_send_recv(struct Curl_easy *data)
    464 {
    465   /* stop receiving and ALL sending as well, including PAUSE and HOLD.
    466    * We might still be paused on receive client writes though, so
    467    * keep those bits around. */
    468   data->req.keepon &= ~(KEEP_RECV|KEEP_SENDBITS);
    469   return Curl_req_abort_sending(data);
    470 }