123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479 |
- /***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
- #include "curl_setup.h"
- #include "urldata.h"
- #include "cfilters.h"
- #include "dynbuf.h"
- #include "doh.h"
- #include "multiif.h"
- #include "progress.h"
- #include "request.h"
- #include "sendf.h"
- #include "transfer.h"
- #include "url.h"
- /* The last 3 #include files should be in this order */
- #include "curl_printf.h"
- #include "curl_memory.h"
- #include "memdebug.h"
- void Curl_req_init(struct SingleRequest *req)
- {
- memset(req, 0, sizeof(*req));
- }
- CURLcode Curl_req_soft_reset(struct SingleRequest *req,
- struct Curl_easy *data)
- {
- CURLcode result;
- req->done = FALSE;
- req->upload_done = FALSE;
- req->upload_aborted = FALSE;
- req->download_done = FALSE;
- req->eos_written = FALSE;
- req->eos_read = FALSE;
- req->eos_sent = FALSE;
- req->ignorebody = FALSE;
- req->shutdown = FALSE;
- req->bytecount = 0;
- req->writebytecount = 0;
- req->header = TRUE; /* assume header */
- req->headerline = 0;
- req->headerbytecount = 0;
- req->allheadercount = 0;
- req->deductheadercount = 0;
- result = Curl_client_start(data);
- if(result)
- return result;
- if(!req->sendbuf_init) {
- Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
- BUFQ_OPT_SOFT_LIMIT);
- req->sendbuf_init = TRUE;
- }
- else {
- Curl_bufq_reset(&req->sendbuf);
- if(data->set.upload_buffer_size != req->sendbuf.chunk_size) {
- Curl_bufq_free(&req->sendbuf);
- Curl_bufq_init2(&req->sendbuf, data->set.upload_buffer_size, 1,
- BUFQ_OPT_SOFT_LIMIT);
- }
- }
- return CURLE_OK;
- }
- CURLcode Curl_req_start(struct SingleRequest *req,
- struct Curl_easy *data)
- {
- req->start = Curl_now();
- return Curl_req_soft_reset(req, data);
- }
- static CURLcode req_flush(struct Curl_easy *data);
- CURLcode Curl_req_done(struct SingleRequest *req,
- struct Curl_easy *data, bool aborted)
- {
- (void)req;
- if(!aborted)
- (void)req_flush(data);
- Curl_client_reset(data);
- #ifndef CURL_DISABLE_DOH
- Curl_doh_close(data);
- #endif
- return CURLE_OK;
- }
- void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
- {
- struct curltime t0 = {0, 0};
- /* This is a bit ugly. `req->p` is a union and we assume we can
- * free this safely without leaks. */
- Curl_safefree(req->p.ftp);
- Curl_safefree(req->newurl);
- Curl_client_reset(data);
- if(req->sendbuf_init)
- Curl_bufq_reset(&req->sendbuf);
- #ifndef CURL_DISABLE_DOH
- Curl_doh_close(data);
- #endif
- /* Can no longer memset() this struct as we need to keep some state */
- req->size = -1;
- req->maxdownload = -1;
- req->bytecount = 0;
- req->writebytecount = 0;
- req->start = t0;
- req->headerbytecount = 0;
- req->allheadercount = 0;
- req->deductheadercount = 0;
- req->headerline = 0;
- req->offset = 0;
- req->httpcode = 0;
- req->keepon = 0;
- req->upgr101 = UPGR101_INIT;
- req->timeofdoc = 0;
- req->location = NULL;
- req->newurl = NULL;
- #ifndef CURL_DISABLE_COOKIES
- req->setcookies = 0;
- #endif
- req->header = FALSE;
- req->content_range = FALSE;
- req->download_done = FALSE;
- req->eos_written = FALSE;
- req->eos_read = FALSE;
- req->eos_sent = FALSE;
- req->upload_done = FALSE;
- req->upload_aborted = FALSE;
- req->ignorebody = FALSE;
- req->http_bodyless = FALSE;
- req->chunk = FALSE;
- req->ignore_cl = FALSE;
- req->upload_chunky = FALSE;
- req->getheader = FALSE;
- req->no_body = data->set.opt_no_body;
- req->authneg = FALSE;
- req->shutdown = FALSE;
- #ifdef USE_HYPER
- req->bodywritten = FALSE;
- #endif
- }
- void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
- {
- /* This is a bit ugly. `req->p` is a union and we assume we can
- * free this safely without leaks. */
- Curl_safefree(req->p.ftp);
- Curl_safefree(req->newurl);
- if(req->sendbuf_init)
- Curl_bufq_free(&req->sendbuf);
- Curl_client_cleanup(data);
- #ifndef CURL_DISABLE_DOH
- Curl_doh_cleanup(data);
- #endif
- }
- static CURLcode xfer_send(struct Curl_easy *data,
- const char *buf, size_t blen,
- size_t hds_len, size_t *pnwritten)
- {
- CURLcode result = CURLE_OK;
- bool eos = FALSE;
- *pnwritten = 0;
- DEBUGASSERT(hds_len <= blen);
- #ifdef DEBUGBUILD
- {
- /* Allow debug builds to override this logic to force short initial
- sends */
- size_t body_len = blen - hds_len;
- char *p = getenv("CURL_SMALLREQSEND");
- if(p) {
- size_t body_small = (size_t)strtoul(p, NULL, 10);
- if(body_small && body_small < body_len)
- blen = hds_len + body_small;
- }
- }
- #endif
- /* Make sure this does not send more body bytes than what the max send
- speed says. The headers do not count to the max speed. */
- if(data->set.max_send_speed) {
- size_t body_bytes = blen - hds_len;
- if((curl_off_t)body_bytes > data->set.max_send_speed)
- blen = hds_len + (size_t)data->set.max_send_speed;
- }
- if(data->req.eos_read &&
- (Curl_bufq_is_empty(&data->req.sendbuf) ||
- Curl_bufq_len(&data->req.sendbuf) == blen)) {
- DEBUGF(infof(data, "sending last upload chunk of %zu bytes", blen));
- eos = TRUE;
- }
- result = Curl_xfer_send(data, buf, blen, eos, pnwritten);
- if(!result) {
- if(eos && (blen == *pnwritten))
- data->req.eos_sent = TRUE;
- if(*pnwritten) {
- if(hds_len)
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
- CURLMIN(hds_len, *pnwritten));
- if(*pnwritten > hds_len) {
- size_t body_len = *pnwritten - hds_len;
- Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len);
- data->req.writebytecount += body_len;
- Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
- }
- }
- }
- return result;
- }
- static CURLcode req_send_buffer_flush(struct Curl_easy *data)
- {
- CURLcode result = CURLE_OK;
- const unsigned char *buf;
- size_t blen;
- while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
- size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
- result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
- if(result)
- break;
- Curl_bufq_skip(&data->req.sendbuf, nwritten);
- if(hds_len) {
- data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
- }
- /* leave if we could not send all. Maybe network blocking or
- * speed limits on transfer */
- if(nwritten < blen)
- break;
- }
- return result;
- }
- CURLcode Curl_req_set_upload_done(struct Curl_easy *data)
- {
- DEBUGASSERT(!data->req.upload_done);
- data->req.upload_done = TRUE;
- data->req.keepon &= ~(KEEP_SEND|KEEP_SEND_TIMED); /* we are done sending */
- Curl_pgrsTime(data, TIMER_POSTRANSFER);
- Curl_creader_done(data, data->req.upload_aborted);
- if(data->req.upload_aborted) {
- Curl_bufq_reset(&data->req.sendbuf);
- if(data->req.writebytecount)
- infof(data, "abort upload after having sent %" FMT_OFF_T " bytes",
- data->req.writebytecount);
- else
- infof(data, "abort upload");
- }
- else if(data->req.writebytecount)
- infof(data, "upload completely sent off: %" FMT_OFF_T " bytes",
- data->req.writebytecount);
- else if(!data->req.download_done) {
- DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
- infof(data, Curl_creader_total_length(data) ?
- "We are completely uploaded and fine" :
- "Request completely sent off");
- }
- return Curl_xfer_send_close(data);
- }
- static CURLcode req_flush(struct Curl_easy *data)
- {
- CURLcode result;
- if(!data || !data->conn)
- return CURLE_FAILED_INIT;
- if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
- result = req_send_buffer_flush(data);
- if(result)
- return result;
- if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
- DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN",
- Curl_bufq_len(&data->req.sendbuf)));
- return CURLE_AGAIN;
- }
- }
- else if(Curl_xfer_needs_flush(data)) {
- DEBUGF(infof(data, "Curl_req_flush(), xfer send_pending"));
- return Curl_xfer_flush(data);
- }
- if(data->req.eos_read && !data->req.eos_sent) {
- char tmp;
- size_t nwritten;
- result = xfer_send(data, &tmp, 0, 0, &nwritten);
- if(result)
- return result;
- DEBUGASSERT(data->req.eos_sent);
- }
- if(!data->req.upload_done && data->req.eos_read && data->req.eos_sent) {
- DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
- if(data->req.shutdown) {
- bool done;
- result = Curl_xfer_send_shutdown(data, &done);
- if(result && data->req.shutdown_err_ignore) {
- infof(data, "Shutdown send direction error: %d. Broken server? "
- "Proceeding as if everything is ok.", result);
- result = CURLE_OK;
- done = TRUE;
- }
- if(result)
- return result;
- if(!done)
- return CURLE_AGAIN;
- }
- return Curl_req_set_upload_done(data);
- }
- return CURLE_OK;
- }
- static ssize_t add_from_client(void *reader_ctx,
- unsigned char *buf, size_t buflen,
- CURLcode *err)
- {
- struct Curl_easy *data = reader_ctx;
- size_t nread;
- bool eos;
- *err = Curl_client_read(data, (char *)buf, buflen, &nread, &eos);
- if(*err)
- return -1;
- if(eos)
- data->req.eos_read = TRUE;
- return (ssize_t)nread;
- }
- #ifndef USE_HYPER
- static CURLcode req_send_buffer_add(struct Curl_easy *data,
- const char *buf, size_t blen,
- size_t hds_len)
- {
- CURLcode result = CURLE_OK;
- ssize_t n;
- n = Curl_bufq_write(&data->req.sendbuf,
- (const unsigned char *)buf, blen, &result);
- if(n < 0)
- return result;
- /* We rely on a SOFTLIMIT on sendbuf, so it can take all data in */
- DEBUGASSERT((size_t)n == blen);
- data->req.sendbuf_hds_len += hds_len;
- return CURLE_OK;
- }
- CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
- {
- CURLcode result;
- const char *buf;
- size_t blen, nwritten;
- if(!data || !data->conn)
- return CURLE_FAILED_INIT;
- buf = Curl_dyn_ptr(req);
- blen = Curl_dyn_len(req);
- if(!Curl_creader_total_length(data)) {
- /* Request without body. Try to send directly from the buf given. */
- data->req.eos_read = TRUE;
- result = xfer_send(data, buf, blen, blen, &nwritten);
- if(result)
- return result;
- buf += nwritten;
- blen -= nwritten;
- }
- if(blen) {
- /* Either we have a request body, or we could not send the complete
- * request in one go. Buffer the remainder and try to add as much
- * body bytes as room is left in the buffer. Then flush. */
- result = req_send_buffer_add(data, buf, blen, blen);
- if(result)
- return result;
- return Curl_req_send_more(data);
- }
- return CURLE_OK;
- }
- #endif /* !USE_HYPER */
- bool Curl_req_sendbuf_empty(struct Curl_easy *data)
- {
- return !data->req.sendbuf_init || Curl_bufq_is_empty(&data->req.sendbuf);
- }
- bool Curl_req_want_send(struct Curl_easy *data)
- {
- /* Not done and
- * - KEEP_SEND and not PAUSEd.
- * - or request has buffered data to send
- * - or transfer connection has pending data to send */
- return !data->req.done &&
- (((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) ||
- !Curl_req_sendbuf_empty(data) ||
- Curl_xfer_needs_flush(data));
- }
- bool Curl_req_done_sending(struct Curl_easy *data)
- {
- return data->req.upload_done && !Curl_req_want_send(data);
- }
- CURLcode Curl_req_send_more(struct Curl_easy *data)
- {
- CURLcode result;
- /* Fill our send buffer if more from client can be read. */
- if(!data->req.upload_aborted &&
- !data->req.eos_read &&
- !(data->req.keepon & KEEP_SEND_PAUSE) &&
- !Curl_bufq_is_full(&data->req.sendbuf)) {
- ssize_t nread = Curl_bufq_sipn(&data->req.sendbuf, 0,
- add_from_client, data, &result);
- if(nread < 0 && result != CURLE_AGAIN)
- return result;
- }
- result = req_flush(data);
- if(result == CURLE_AGAIN)
- result = CURLE_OK;
- return result;
- }
- CURLcode Curl_req_abort_sending(struct Curl_easy *data)
- {
- if(!data->req.upload_done) {
- Curl_bufq_reset(&data->req.sendbuf);
- data->req.upload_aborted = TRUE;
- /* no longer KEEP_SEND and KEEP_SEND_PAUSE */
- data->req.keepon &= ~KEEP_SENDBITS;
- return Curl_req_set_upload_done(data);
- }
- return CURLE_OK;
- }
- CURLcode Curl_req_stop_send_recv(struct Curl_easy *data)
- {
- /* stop receiving and ALL sending as well, including PAUSE and HOLD.
- * We might still be paused on receive client writes though, so
- * keep those bits around. */
- data->req.keepon &= ~(KEEP_RECV|KEEP_SENDBITS);
- return Curl_req_abort_sending(data);
- }
|