aboutsummaryrefslogtreecommitdiff
path: root/lib/transfer.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/transfer.c')
-rw-r--r--lib/transfer.c574
1 files changed, 171 insertions, 403 deletions
diff --git a/lib/transfer.c b/lib/transfer.c
index 6886764f4..3ae4b61c0 100644
--- a/lib/transfer.c
+++ b/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");
@@ -407,363 +413,154 @@ bool Curl_meets_timecondition(struct Curl_easy *data, time_t timeofdoc)
return TRUE;
}
+/**
+ * Receive raw response data for the transfer.
+ * @param data the transfer
+ * @param buf buffer to keep response data received
+ * @param blen length of `buf`
+ * @param eos_reliable if EOS detection in underlying connection is reliable
+ * @param err error code in case of -1 return
+ * @return number of bytes read or -1 for error
+ */
+static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
+ char *buf, size_t blen,
+ bool eos_reliable,
+ CURLcode *err)
+{
+ ssize_t nread;
+
+ DEBUGASSERT(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(!eos_reliable && !data->req.header && data->req.size != -1) {
+ curl_off_t totalleft = data->req.size - data->req.bytecount;
+ if(totalleft <= 0)
+ blen = 0;
+ else if(totalleft < (curl_off_t)blen)
+ blen = (size_t)totalleft;
+ }
+
+ if(!blen) {
+ /* want nothing - continue as if read nothing. */
+ DEBUGF(infof(data, "readwrite_data: we're done"));
+ *err = CURLE_OK;
+ return 0;
+ }
+
+ *err = Curl_read(data, data->conn->sockfd, buf, blen, &nread);
+ if(*err)
+ return -1;
+ DEBUGASSERT(nread >= 0);
+ *err = CURLE_OK;
+ return nread;
+}
+
/*
* Go ahead and do a read if we have a readable socket or if
* the stream was rewound (in which case we have data in a
* buffer)
- *
- * return '*comeback' TRUE if we didn't properly drain the socket so this
- * function should get called again without select() or similar in between!
*/
static CURLcode readwrite_data(struct Curl_easy *data,
- struct connectdata *conn,
struct SingleRequest *k,
- int *didwhat, bool *done,
- bool *comeback)
+ int *didwhat, bool *done)
{
+ struct connectdata *conn = data->conn;
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 */
- 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);
+ char *buf;
+ size_t blen;
+ int maxloops = 10;
+ curl_off_t total_received = 0;
+ bool is_multiplex = FALSE;
+ DEBUGASSERT(data->state.buffer);
*done = FALSE;
- *comeback = FALSE;
/* This is where we loop until we have read everything there is to
read or we get a CURLE_AGAIN */
do {
- bool is_empty_data = FALSE;
- size_t buffersize = data->set.buffer_size;
- size_t bytestoread = buffersize;
- /* 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
- to ensure that http2_handle_stream_close is called when we read all
- incoming bytes for a particular stream. */
- 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 */
- curl_off_t totalleft = k->size - k->bytecount;
- if(totalleft < (curl_off_t)bytestoread)
- bytestoread = (size_t)totalleft;
+ bool is_eos = FALSE;
+ size_t bytestoread;
+ ssize_t nread;
+
+ if(!is_multiplex) {
+ /* Multiplexed connection have inherent handling of EOF and we do not
+ * have to carefully restrict the amount we try to read.
+ * Multiplexed changes only in one direction. */
+ is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
}
- if(bytestoread) {
- /* receive data from the network! */
- result = Curl_read(data, conn->sockfd, buf, bytestoread, &nread);
+ buf = data->state.buffer;
+ bytestoread = data->set.buffer_size;
+
+ /* Observe any imposed speed limit */
+ if(bytestoread && data->set.max_recv_speed) {
+ curl_off_t net_limit = data->set.max_recv_speed - total_received;
+ if(net_limit <= 0)
+ break;
+ if((size_t)net_limit < bytestoread)
+ bytestoread = (size_t)net_limit;
+ }
- /* read would've blocked */
+ nread = Curl_xfer_recv_resp(data, buf, bytestoread,
+ is_multiplex, &result);
+ if(nread < 0) {
if(CURLE_AGAIN == result) {
result = CURLE_OK;
break; /* get out of loop */
}
-
- if(result>0)
- goto out;
- }
- 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) {
- Curl_pgrsTime(data, TIMER_STARTTRANSFER);
- if(k->exp100 > EXP100_SEND_DATA)
- /* set time stamp to compare with when waiting for the 100 */
- k->start100 = Curl_now();
+ goto out; /* real error */
}
+ /* We only get a 0-length read on EndOfStream */
+ blen = (size_t)nread;
+ is_eos = (blen == 0);
*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;
- }
- 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)
+ if(is_multiplex)
DEBUGF(infof(data, "nread == 0, stream closed, bailing"));
else
DEBUGF(infof(data, "nread <= 0, server closed connection, bailing"));
- k->keepon = 0; /* stop sending as well */
- if(!is_empty_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);
- if(result)
- goto out;
- if(readmore)
- 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);
- if(result)
- goto out;
-
- if(conn->handler->readwrite &&
- (k->maxdownload <= 0 && nread > 0)) {
- result = conn->handler->readwrite(data, conn, &nread, &readmore);
- if(result)
- goto out;
- if(readmore)
- break;
- }
-
- if(stop_reading) {
- /* We've stopped dealing with input, get out of the do-while loop */
-
- if(nread > 0) {
- infof(data,
- "Excess found:"
- " excess = %zd"
- " url = %s (zero-length body)",
- nread, data->state.up.path);
- }
-
+ if(k->eos_written) { /* already did write this to client, leave */
+ k->keepon = 0; /* stop sending as well */
break;
}
}
-#endif /* CURL_DISABLE_HTTP */
-
-
- /* 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(data->req.no_body) {
- /* data arrives although we want none, bail out */
- streamclose(conn, "ignoring body");
- *done = TRUE;
- result = CURLE_WEIRD_SERVER_REPLY;
- goto out;
- }
-
-#ifndef CURL_DISABLE_HTTP
- if(0 == k->bodywrites && !is_empty_data) {
- /* These checks are only made the first time we are about to
- write a piece of the body */
- if(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)) {
- /* HTTP-only checks */
- result = Curl_http_firstwrite(data, conn, done);
- if(result || *done)
- goto out;
- }
- } /* 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.
- */
- CURLcode extra;
- CHUNKcode res =
- Curl_httpchunk_read(data, k->str, nread, &nread, &extra);
-
- if(CHUNKE_OK < res) {
- if(CHUNKE_PASSTHRU_ERROR == res) {
- failf(data, "Failed reading the chunked-encoded stream");
- result = extra;
- goto out;
- }
- failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
- result = CURLE_RECV_ERROR;
- goto out;
- }
- if(CHUNKE_STOP == res) {
- /* we're done reading chunks! */
- k->keepon &= ~KEEP_RECV; /* read no more */
-
- /* N number of bytes at the end of the str buffer that weren't
- written to the client. */
- if(conn->chunk.datasize) {
- infof(data, "Leftovers after chunking: % "
- CURL_FORMAT_CURL_OFF_T "u bytes",
- conn->chunk.datasize);
- }
- }
- /* If it returned OK, we just keep going */
- }
-#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)) {
-
- 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 this is chunky transfer, it was already written */
-
- if(k->badheader && !k->ignorebody) {
- /* 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(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) {
-#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);
- }
- }
- k->badheader = HEADER_NORMAL; /* 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;
+ total_received += blen;
- 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;
- }
- 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;
- }
+ result = Curl_xfer_write_resp(data, buf, blen, is_eos, done);
+ if(result || *done)
+ goto out;
- if(is_empty_data) {
- /* if we received nothing, the server closed the connection and we
- are done */
- k->keepon &= ~KEEP_RECV;
+ /* if we are done, we stop receiving. On multiplexed connections,
+ * we should read the EOS. Which may arrive as meta data after
+ * the bytes. Not taking it in might lead to RST of streams. */
+ if((!is_multiplex && data->req.download_done) || is_eos) {
+ data->req.keepon &= ~KEEP_RECV;
}
-
- if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV)) {
- /* this is a paused or stopped transfer */
+ /* if we are PAUSEd or stopped receiving, leave the loop */
+ if((k->keepon & KEEP_RECV_PAUSE) || !(k->keepon & KEEP_RECV))
break;
- }
- } while((max_recv > 0) && data_pending(data) && maxloops--);
+ } while(maxloops-- && data_pending(data));
- if(maxloops <= 0 || max_recv <= 0) {
- /* we mark it as read-again-please */
- data->state.dselect_bits = CURL_CSELECT_IN;
- *comeback = TRUE;
+ if(maxloops <= 0) {
+ /* did not read until EAGAIN, mark read-again-please */
+ data->state.select_bits = CURL_CSELECT_IN;
+ if((k->keepon & KEEP_SENDBITS) == KEEP_SEND)
+ data->state.select_bits |= CURL_CSELECT_OUT;
}
if(((k->keepon & (KEEP_RECV|KEEP_SEND)) == KEEP_SEND) &&
- (conn->bits.close || data_eof_handled)) {
+ (conn->bits.close || is_multiplex)) {
/* When we've read the entire thing and the close bit is set, the server
may now close the connection. If there's now any kind of sending going
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 +580,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 +774,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) {
@@ -1053,46 +850,41 @@ static int select_bits_paused(struct Curl_easy *data, int select_bits)
* of our state machine are handling PAUSED transfers correctly. So, we
* do not want to go there.
* NOTE: we are only interested in PAUSE, not HOLD. */
- return (((select_bits & CURL_CSELECT_IN) &&
- (data->req.keepon & KEEP_RECV_PAUSE)) ||
- ((select_bits & CURL_CSELECT_OUT) &&
- (data->req.keepon & KEEP_SEND_PAUSE)));
+
+ /* if there is data in a direction not paused, return false */
+ if(((select_bits & CURL_CSELECT_IN) &&
+ !(data->req.keepon & KEEP_RECV_PAUSE)) ||
+ ((select_bits & CURL_CSELECT_OUT) &&
+ !(data->req.keepon & KEEP_SEND_PAUSE)))
+ return FALSE;
+
+ return (data->req.keepon & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE));
}
/*
* Curl_readwrite() is the low-level function to be called when data is to
* be read and written to/from the connection.
- *
- * return '*comeback' TRUE if we didn't properly drain the socket so this
- * function should get called again without select() or similar in between!
*/
-CURLcode Curl_readwrite(struct connectdata *conn,
- struct Curl_easy *data,
- bool *done,
- bool *comeback)
+CURLcode Curl_readwrite(struct Curl_easy *data,
+ bool *done)
{
+ struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
CURLcode result;
struct curltime now;
int didwhat = 0;
int select_bits;
- if(data->state.dselect_bits) {
- if(select_bits_paused(data, data->state.dselect_bits)) {
+ if(data->state.select_bits) {
+ if(select_bits_paused(data, data->state.select_bits)) {
/* leave the bits unchanged, so they'll tell us what to do when
* this transfer gets unpaused. */
- DEBUGF(infof(data, "readwrite, dselect_bits, early return on PAUSED"));
+ DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED"));
result = CURLE_OK;
goto out;
}
- select_bits = data->state.dselect_bits;
- data->state.dselect_bits = 0;
- }
- else if(conn->cselect_bits) {
- /* CAVEAT: adding `select_bits_paused()` check here makes test640 hang
- * (among others). Which hints at strange state handling in FTP land... */
- select_bits = conn->cselect_bits;
- conn->cselect_bits = 0;
+ select_bits = data->state.select_bits;
+ data->state.select_bits = 0;
}
else {
curl_socket_t fd_read;
@@ -1130,7 +922,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
the stream was rewound (in which case we have data in a
buffer) */
if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
- result = readwrite_data(data, conn, k, &didwhat, done, comeback);
+ result = readwrite_data(data, k, &didwhat, done);
if(result || *done)
goto out;
}
@@ -1226,21 +1018,6 @@ CURLcode Curl_readwrite(struct connectdata *conn,
result = CURLE_PARTIAL_FILE;
goto out;
}
- if(!(data->req.no_body) && k->chunk &&
- (conn->chunk.state != CHUNK_STOP)) {
- /*
- * In chunked mode, return an error if the connection is closed prior to
- * the empty (terminating) chunk is read.
- *
- * The condition above used to check for
- * conn->proto.http->chunk.datasize != 0 which is true after reading
- * *any* chunk, not just the empty chunk.
- *
- */
- failf(data, "transfer closed with outstanding read data remaining");
- result = CURLE_PARTIAL_FILE;
- goto out;
- }
if(Curl_pgrsUpdate(data)) {
result = CURLE_ABORTED_BY_CALLBACK;
goto out;
@@ -1255,52 +1032,6 @@ out:
return result;
}
-/*
- * Curl_single_getsock() gets called by the multi interface code when the app
- * has requested to get the sockets for the current connection. This function
- * will then be called once for every connection that the multi interface
- * keeps track of. This function will only be called for connections that are
- * in the proper state to have this information available.
- */
-int Curl_single_getsock(struct Curl_easy *data,
- struct connectdata *conn,
- curl_socket_t *sock)
-{
- int bitmap = GETSOCK_BLANK;
- unsigned sockindex = 0;
-
- if(conn->handler->perform_getsock)
- return conn->handler->perform_getsock(data, conn, sock);
-
- /* don't include HOLD and PAUSE connections */
- if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
-
- DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
-
- bitmap |= GETSOCK_READSOCK(sockindex);
- sock[sockindex] = conn->sockfd;
- }
-
- /* don't include HOLD and PAUSE connections */
- if((data->req.keepon & KEEP_SENDBITS) == KEEP_SEND) {
- if((conn->sockfd != conn->writesockfd) ||
- bitmap == GETSOCK_BLANK) {
- /* only if they are not the same socket and we have a readable
- one, we increase index */
- if(bitmap != GETSOCK_BLANK)
- sockindex++; /* increase index if we need two entries */
-
- DEBUGASSERT(conn->writesockfd != CURL_SOCKET_BAD);
-
- sock[sockindex] = conn->writesockfd;
- }
-
- bitmap |= GETSOCK_WRITESOCK(sockindex);
- }
-
- return bitmap;
-}
-
/* Curl_init_CONNECT() gets called each time the handle switches to CONNECT
which means this gets called once for each subsequent redirect etc */
void Curl_init_CONNECT(struct Curl_easy *data)
@@ -1430,8 +1161,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 +1365,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);
@@ -1948,3 +1678,41 @@ Curl_setup_transfer(
} /* if(k->getheader || !data->req.no_body) */
}
+
+CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
+ char *buf, size_t blen,
+ bool is_eos, bool *done)
+{
+ CURLcode result = CURLE_OK;
+
+ if(data->conn->handler->write_resp) {
+ /* protocol handlers offering this function take full responsibility
+ * for writing all received download data to the client. */
+ result = data->conn->handler->write_resp(data, buf, blen, is_eos, done);
+ }
+ else {
+ /* No special handling by protocol handler, write all received data
+ * as BODY to the client. */
+ if(blen || is_eos) {
+ int cwtype = CLIENTWRITE_BODY;
+ if(is_eos)
+ cwtype |= CLIENTWRITE_EOS;
+
+#ifndef CURL_DISABLE_POP3
+ if(blen && data->conn->handler->protocol & PROTO_FAMILY_POP3) {
+ result = data->req.ignorebody? CURLE_OK :
+ Curl_pop3_write(data, buf, blen);
+ }
+ else
+#endif /* CURL_DISABLE_POP3 */
+ result = Curl_client_write(data, cwtype, buf, blen);
+ }
+ }
+
+ if(!result && is_eos) {
+ /* If we wrote the EOS, we are definitely done */
+ data->req.eos_written = TRUE;
+ data->req.download_done = TRUE;
+ }
+ return result;
+}