summaryrefslogtreecommitdiffstats
path: root/Utilities/cmcurl/lib/transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'Utilities/cmcurl/lib/transfer.c')
-rw-r--r--Utilities/cmcurl/lib/transfer.c277
1 files changed, 123 insertions, 154 deletions
diff --git a/Utilities/cmcurl/lib/transfer.c b/Utilities/cmcurl/lib/transfer.c
index 6886764..96f1fde 100644
--- a/Utilities/cmcurl/lib/transfer.c
+++ b/Utilities/cmcurl/lib/transfer.c
@@ -163,9 +163,9 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
{
size_t buffersize = bytes;
size_t nread;
-
curl_read_callback readfunc = NULL;
void *extra_data = NULL;
+ int eof_index = 0;
#ifndef CURL_DISABLE_HTTP
if(data->state.trailers_state == TRAILERS_INITIALIZED) {
@@ -223,6 +223,7 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
*/
readfunc = trailers_read;
extra_data = (void *)data;
+ eof_index = 1;
}
else
#endif
@@ -231,10 +232,15 @@ CURLcode Curl_fillreadbuffer(struct Curl_easy *data, size_t bytes,
extra_data = data->state.in;
}
- Curl_set_in_callback(data, true);
- nread = readfunc(data->req.upload_fromhere, 1,
- buffersize, extra_data);
- Curl_set_in_callback(data, false);
+ if(!data->req.fread_eof[eof_index]) {
+ Curl_set_in_callback(data, true);
+ nread = readfunc(data->req.upload_fromhere, 1, buffersize, extra_data);
+ Curl_set_in_callback(data, false);
+ /* make sure the callback is not called again after EOF */
+ data->req.fread_eof[eof_index] = !nread;
+ }
+ else
+ nread = 0;
if(nread == CURL_READFUNC_ABORT) {
failf(data, "operation aborted by callback");
@@ -422,16 +428,15 @@ static CURLcode readwrite_data(struct Curl_easy *data,
bool *comeback)
{
CURLcode result = CURLE_OK;
- ssize_t nread; /* number of bytes read */
- size_t excess = 0; /* excess bytes read */
- bool readmore = FALSE; /* used by RTP to signal for more data */
+ char *buf;
+ size_t blen;
+ size_t consumed;
int maxloops = 100;
curl_off_t max_recv = data->set.max_recv_speed?
data->set.max_recv_speed : CURL_OFF_T_MAX;
- char *buf = data->state.buffer;
bool data_eof_handled = FALSE;
- DEBUGASSERT(buf);
+ DEBUGASSERT(data->state.buffer);
*done = FALSE;
*comeback = FALSE;
@@ -439,8 +444,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
read or we get a CURLE_AGAIN */
do {
bool is_empty_data = FALSE;
- size_t buffersize = data->set.buffer_size;
- size_t bytestoread = buffersize;
+ size_t bytestoread = data->set.buffer_size;
/* For HTTP/2 and HTTP/3, read data without caring about the content
length. This is safe because body in HTTP/2 is always segmented
thanks to its framing layer. Meanwhile, we have to call Curl_read
@@ -449,31 +453,38 @@ static CURLcode readwrite_data(struct Curl_easy *data,
bool is_http3 = Curl_conn_is_http3(data, conn, FIRSTSOCKET);
data_eof_handled = is_http3 || Curl_conn_is_http2(data, conn, FIRSTSOCKET);
- if(!data_eof_handled && k->size != -1 && !k->header) {
- /* make sure we don't read too much */
+ /* Each loop iteration starts with a fresh buffer and handles
+ * all data read into it. */
+ buf = data->state.buffer;
+ blen = 0;
+
+ /* If we are reading BODY data and the connection does NOT handle EOF
+ * and we know the size of the BODY data, limit the read amount */
+ if(!k->header && !data_eof_handled && k->size != -1) {
curl_off_t totalleft = k->size - k->bytecount;
- if(totalleft < (curl_off_t)bytestoread)
+ if(totalleft <= 0)
+ bytestoread = 0;
+ else if(totalleft < (curl_off_t)bytestoread)
bytestoread = (size_t)totalleft;
}
if(bytestoread) {
/* receive data from the network! */
+ ssize_t nread; /* number of bytes read */
result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread);
-
- /* read would've blocked */
if(CURLE_AGAIN == result) {
result = CURLE_OK;
break; /* get out of loop */
}
-
- if(result>0)
+ else if(result)
goto out;
+ DEBUGASSERT(nread >= 0);
+ blen = (size_t)nread;
}
else {
/* read nothing but since we wanted nothing we consider this an OK
situation to proceed from */
DEBUGF(infof(data, "readwrite_data: we're done"));
- nread = 0;
}
if(!k->bytecount) {
@@ -485,12 +496,17 @@ static CURLcode readwrite_data(struct Curl_easy *data,
*didwhat |= KEEP_RECV;
/* indicates data of zero size, i.e. empty file */
- is_empty_data = ((nread == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
-
- if(0 < nread || is_empty_data) {
- buf[nread] = 0;
+ is_empty_data = ((blen == 0) && (k->bodywrites == 0)) ? TRUE : FALSE;
+
+ if(0 < blen || is_empty_data) {
+ /* data->state.buffer is allocated 1 byte larger than
+ * data->set.buffer_size admits. *wink* */
+ /* TODO: we should really not rely on this being 0-terminated, since
+ * the actual data read might contain 0s. */
+ buf[blen] = 0;
}
- if(!nread) {
+
+ if(!blen) {
/* if we receive 0 or less here, either the data transfer is done or the
server closed the connection and we bail out from this! */
if(data_eof_handled)
@@ -502,48 +518,70 @@ static CURLcode readwrite_data(struct Curl_easy *data,
break;
}
- /* Default buffer to use when we write the buffer, it may be changed
- in the flow below before the actual storing is done. */
- k->str = buf;
-
if(conn->handler->readwrite) {
- result = conn->handler->readwrite(data, conn, &nread, &readmore);
+ bool readmore = FALSE; /* indicates data is incomplete, need more */
+ consumed = 0;
+ result = conn->handler->readwrite(data, conn, buf, blen,
+ &consumed, &readmore);
if(result)
goto out;
if(readmore)
break;
+ buf += consumed;
+ blen -= consumed;
+ if(k->download_done) {
+ /* We've stopped dealing with input, get out of the do-while loop */
+ if(blen > 0) {
+ infof(data,
+ "Excess found:"
+ " excess = %zu"
+ " url = %s (zero-length body)",
+ blen, data->state.up.path);
+ }
+
+ /* we make sure that this socket isn't read more now */
+ k->keepon &= ~KEEP_RECV;
+ break;
+ }
}
#ifndef CURL_DISABLE_HTTP
/* Since this is a two-state thing, we check if we are parsing
headers at the moment or not. */
if(k->header) {
- /* we are in parse-the-header-mode */
- bool stop_reading = FALSE;
- result = Curl_http_readwrite_headers(data, conn, &nread, &stop_reading);
+ consumed = 0;
+ result = Curl_http_readwrite_headers(data, conn, buf, blen, &consumed);
if(result)
goto out;
+ buf += consumed;
+ blen -= consumed;
if(conn->handler->readwrite &&
- (k->maxdownload <= 0 && nread > 0)) {
- result = conn->handler->readwrite(data, conn, &nread, &readmore);
+ (k->maxdownload <= 0 && blen > 0)) {
+ bool readmore = FALSE; /* indicates data is incomplete, need more */
+ consumed = 0;
+ result = conn->handler->readwrite(data, conn, buf, blen,
+ &consumed, &readmore);
if(result)
goto out;
if(readmore)
break;
+ buf += consumed;
+ blen -= consumed;
}
- if(stop_reading) {
+ if(k->download_done) {
/* We've stopped dealing with input, get out of the do-while loop */
-
- if(nread > 0) {
+ if(blen > 0) {
infof(data,
"Excess found:"
- " excess = %zd"
+ " excess = %zu"
" url = %s (zero-length body)",
- nread, data->state.up.path);
+ blen, data->state.up.path);
}
+ /* we make sure that this socket isn't read more now */
+ k->keepon &= ~KEEP_RECV;
break;
}
}
@@ -553,11 +591,13 @@ static CURLcode readwrite_data(struct Curl_easy *data,
/* This is not an 'else if' since it may be a rest from the header
parsing, where the beginning of the buffer is headers and the end
is non-headers. */
- if(!k->header && (nread > 0 || is_empty_data)) {
+ if(!k->header && (blen > 0 || is_empty_data)) {
- if(data->req.no_body) {
+ if(data->req.no_body && blen > 0) {
/* data arrives although we want none, bail out */
streamclose(conn, "ignoring body");
+ DEBUGF(infof(data, "did not want a BODY, but seeing %zu bytes",
+ blen));
*done = TRUE;
result = CURLE_WEIRD_SERVER_REPLY;
goto out;
@@ -576,34 +616,18 @@ static CURLcode readwrite_data(struct Curl_easy *data,
} /* this is the first time we write a body part */
#endif /* CURL_DISABLE_HTTP */
- k->bodywrites++;
-
- /* pass data to the debug function before it gets "dechunked" */
- if(data->set.verbose) {
- if(k->badheader) {
- Curl_debug(data, CURLINFO_DATA_IN,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
- if(k->badheader == HEADER_PARTHEADER)
- Curl_debug(data, CURLINFO_DATA_IN,
- k->str, (size_t)nread);
- }
- else
- Curl_debug(data, CURLINFO_DATA_IN,
- k->str, (size_t)nread);
- }
-
#ifndef CURL_DISABLE_HTTP
if(k->chunk) {
/*
* Here comes a chunked transfer flying and we need to decode this
* properly. While the name says read, this function both reads
- * and writes away the data. The returned 'nread' holds the number
- * of actual data it wrote to the client.
+ * and writes away the data.
*/
CURLcode extra;
- CHUNKcode res =
- Curl_httpchunk_read(data, k->str, nread, &nread, &extra);
+ CHUNKcode res;
+
+ consumed = 0;
+ res = Curl_httpchunk_read(data, buf, blen, &consumed, &extra);
if(CHUNKE_OK < res) {
if(CHUNKE_PASSTHRU_ERROR == res) {
@@ -615,9 +639,14 @@ static CURLcode readwrite_data(struct Curl_easy *data,
result = CURLE_RECV_ERROR;
goto out;
}
- if(CHUNKE_STOP == res) {
+
+ buf += consumed;
+ blen -= consumed;
+ if(CHUNKE_STOP == res) {
/* we're done reading chunks! */
k->keepon &= ~KEEP_RECV; /* read no more */
+ /* chunks read successfully, download is complete */
+ k->download_done = TRUE;
/* N number of bytes at the end of the str buffer that weren't
written to the client. */
@@ -631,117 +660,57 @@ static CURLcode readwrite_data(struct Curl_easy *data,
}
#endif /* CURL_DISABLE_HTTP */
- /* Account for body content stored in the header buffer */
- if((k->badheader == HEADER_PARTHEADER) && !k->ignorebody) {
- size_t headlen = Curl_dyn_len(&data->state.headerb);
- DEBUGF(infof(data, "Increasing bytecount by %zu", headlen));
- k->bytecount += headlen;
- }
-
- if((-1 != k->maxdownload) &&
- (k->bytecount + nread >= k->maxdownload)) {
+ max_recv -= blen;
- excess = (size_t)(k->bytecount + nread - k->maxdownload);
- if(excess > 0 && !k->ignorebody) {
- infof(data,
- "Excess found in a read:"
- " excess = %zu"
- ", size = %" CURL_FORMAT_CURL_OFF_T
- ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
- ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
- excess, k->size, k->maxdownload, k->bytecount);
- connclose(conn, "excess found in a read");
- }
-
- nread = (ssize_t) (k->maxdownload - k->bytecount);
- if(nread < 0) /* this should be unusual */
- nread = 0;
-
- /* HTTP/3 over QUIC should keep reading until QUIC connection
- is closed. In contrast to HTTP/2 which can stop reading
- from TCP connection, HTTP/3 over QUIC needs ACK from server
- to ensure stream closure. It should keep reading. */
- if(!is_http3) {
- k->keepon &= ~KEEP_RECV; /* we're done reading */
- }
- }
-
- k->bytecount += nread;
- max_recv -= nread;
-
- result = Curl_pgrsSetDownloadCounter(data, k->bytecount);
- if(result)
- goto out;
-
- if(!k->chunk && (nread || k->badheader || is_empty_data)) {
+ if(!k->chunk && (blen || k->badheader || is_empty_data)) {
/* If this is chunky transfer, it was already written */
- if(k->badheader && !k->ignorebody) {
+ if(k->badheader) {
/* we parsed a piece of data wrongly assuming it was a header
and now we output it as body instead */
size_t headlen = Curl_dyn_len(&data->state.headerb);
/* Don't let excess data pollute body writes */
- if(k->maxdownload == -1 || (curl_off_t)headlen <= k->maxdownload)
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&data->state.headerb),
- headlen);
- else
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&data->state.headerb),
- (size_t)k->maxdownload);
+ if(k->maxdownload != -1 && (curl_off_t)headlen > k->maxdownload)
+ headlen = (size_t)k->maxdownload;
+ result = Curl_client_write(data, CLIENTWRITE_BODY,
+ Curl_dyn_ptr(&data->state.headerb),
+ headlen);
if(result)
goto out;
}
- if(k->badheader < HEADER_ALLBAD) {
- /* This switch handles various content encodings. If there's an
- error here, be sure to check over the almost identical code
- in http_chunks.c.
- Make sure that ALL_CONTENT_ENCODINGS contains all the
- encodings handled here. */
- if(!k->ignorebody && nread) {
+
+ if(blen) {
#ifndef CURL_DISABLE_POP3
- if(conn->handler->protocol & PROTO_FAMILY_POP3)
- result = Curl_pop3_write(data, k->str, nread);
- else
-#endif /* CURL_DISABLE_POP3 */
- result = Curl_client_write(data, CLIENTWRITE_BODY, k->str,
- nread);
+ if(conn->handler->protocol & PROTO_FAMILY_POP3) {
+ result = k->ignorebody? CURLE_OK :
+ Curl_pop3_write(data, buf, blen);
}
+ else
+#endif /* CURL_DISABLE_POP3 */
+ result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
}
- k->badheader = HEADER_NORMAL; /* taken care of now */
+ k->badheader = FALSE; /* taken care of now */
if(result)
goto out;
}
- } /* if(!header and data to read) */
-
- if(conn->handler->readwrite && excess) {
- /* Parse the excess data */
- k->str += nread;
-
- if(&k->str[excess] > &buf[data->set.buffer_size]) {
- /* the excess amount was too excessive(!), make sure
- it doesn't read out of buffer */
- excess = &buf[data->set.buffer_size] - k->str;
+ if(k->download_done && !is_http3) {
+ /* HTTP/3 over QUIC should keep reading until QUIC connection
+ is closed. In contrast to HTTP/2 which can stop reading
+ from TCP connection, HTTP/3 over QUIC needs ACK from server
+ to ensure stream closure. It should keep reading. */
+ k->keepon &= ~KEEP_RECV; /* we're done reading */
}
- nread = (ssize_t)excess;
-
- result = conn->handler->readwrite(data, conn, &nread, &readmore);
- if(result)
- goto out;
-
- if(readmore)
- k->keepon |= KEEP_RECV; /* we're not done reading */
- break;
- }
+ } /* if(!header and data to read) */
if(is_empty_data) {
/* if we received nothing, the server closed the connection and we
are done */
k->keepon &= ~KEEP_RECV;
+ k->download_done = TRUE;
}
if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) {
@@ -764,6 +733,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
on from our side, we need to stop that immediately. */
infof(data, "we are done reading and this is set to close, stop send");
k->keepon &= ~KEEP_SEND; /* no writing anymore either */
+ k->keepon &= ~KEEP_SEND_PAUSE; /* no pausing anymore either */
}
out:
@@ -783,7 +753,7 @@ CURLcode Curl_done_sending(struct Curl_easy *data,
return CURLE_OK;
}
-#if defined(WIN32) && defined(USE_WINSOCK)
+#if defined(_WIN32) && defined(USE_WINSOCK)
#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
#endif
@@ -977,7 +947,7 @@ static CURLcode readwrite_upload(struct Curl_easy *data,
if(result)
return result;
-#if defined(WIN32) && defined(USE_WINSOCK)
+#if defined(_WIN32) && defined(USE_WINSOCK)
{
struct curltime n = Curl_now();
if(Curl_timediff(n, k->last_sndbuf_update) > 1000) {
@@ -1430,8 +1400,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
}
wc = data->wildcard;
- if((wc->state < CURLWC_INIT) ||
- (wc->state >= CURLWC_CLEAN)) {
+ if(wc->state < CURLWC_INIT) {
if(wc->ftpwc)
wc->dtor(wc->ftpwc);
Curl_safefree(wc->pattern);
@@ -1635,7 +1604,7 @@ CURLcode Curl_follow(struct Curl_easy *data,
return Curl_uc_to_curlcode(uc);
}
- p = Curl_builtin_scheme(scheme, CURL_ZERO_TERMINATED);
+ p = Curl_get_scheme_handler(scheme);
if(p && (p->protocol != data->info.conn_protocol)) {
infof(data, "Clear auth, redirects scheme from %s to %s",
data->info.conn_scheme, scheme);