aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/curl/lib/sendf.c
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2024-10-16 12:11:24 +0000
committerAlexander Smirnov <alex@ydb.tech>2024-10-16 12:11:24 +0000
commit40811e93f3fdf9342a9295369994012420fac548 (patch)
treea8d85e094a9c21e10aa250f537c101fc2016a049 /contrib/libs/curl/lib/sendf.c
parent30ebe5357bb143648c6be4d151ecd4944af81ada (diff)
parent28a0c4a9f297064538a018c512cd9bbd00a1a35d (diff)
downloadydb-40811e93f3fdf9342a9295369994012420fac548.tar.gz
Merge branch 'rightlib' into mergelibs-241016-1210
Diffstat (limited to 'contrib/libs/curl/lib/sendf.c')
-rw-r--r--contrib/libs/curl/lib/sendf.c1583
1 files changed, 471 insertions, 1112 deletions
diff --git a/contrib/libs/curl/lib/sendf.c b/contrib/libs/curl/lib/sendf.c
index 6f566622fd..a2fac0c4e9 100644
--- a/contrib/libs/curl/lib/sendf.c
+++ b/contrib/libs/curl/lib/sendf.c
@@ -41,7 +41,6 @@
#include "cfilters.h"
#include "connect.h"
#include "content_encoding.h"
-#include "cw-out.h"
#include "vtls/vtls.h"
#include "vssh/ssh.h"
#include "easyif.h"
@@ -50,8 +49,8 @@
#include "select.h"
#include "strdup.h"
#include "http2.h"
+#include "headers.h"
#include "progress.h"
-#include "warnless.h"
#include "ws.h"
/* The last 3 #include files should be in this order */
@@ -60,124 +59,426 @@
#include "memdebug.h"
-static CURLcode do_init_writer_stack(struct Curl_easy *data);
+static CURLcode do_init_stack(struct Curl_easy *data);
+
+#if defined(CURL_DO_LINEEND_CONV) && !defined(CURL_DISABLE_FTP)
+/*
+ * convert_lineends() changes CRLF (\r\n) end-of-line markers to a single LF
+ * (\n), with special processing for CRLF sequences that are split between two
+ * blocks of data. Remaining, bare CRs are changed to LFs. The possibly new
+ * size of the data is returned.
+ */
+static size_t convert_lineends(struct Curl_easy *data,
+ char *startPtr, size_t size)
+{
+ char *inPtr, *outPtr;
+
+ /* sanity check */
+ if(!startPtr || (size < 1)) {
+ return size;
+ }
+
+ if(data->state.prev_block_had_trailing_cr) {
+ /* The previous block of incoming data
+ had a trailing CR, which was turned into a LF. */
+ if(*startPtr == '\n') {
+ /* This block of incoming data starts with the
+ previous block's LF so get rid of it */
+ memmove(startPtr, startPtr + 1, size-1);
+ size--;
+ /* and it wasn't a bare CR but a CRLF conversion instead */
+ data->state.crlf_conversions++;
+ }
+ data->state.prev_block_had_trailing_cr = FALSE; /* reset the flag */
+ }
+
+ /* find 1st CR, if any */
+ inPtr = outPtr = memchr(startPtr, '\r', size);
+ if(inPtr) {
+ /* at least one CR, now look for CRLF */
+ while(inPtr < (startPtr + size-1)) {
+ /* note that it's size-1, so we'll never look past the last byte */
+ if(memcmp(inPtr, "\r\n", 2) == 0) {
+ /* CRLF found, bump past the CR and copy the NL */
+ inPtr++;
+ *outPtr = *inPtr;
+ /* keep track of how many CRLFs we converted */
+ data->state.crlf_conversions++;
+ }
+ else {
+ if(*inPtr == '\r') {
+ /* lone CR, move LF instead */
+ *outPtr = '\n';
+ }
+ else {
+ /* not a CRLF nor a CR, just copy whatever it is */
+ *outPtr = *inPtr;
+ }
+ }
+ outPtr++;
+ inPtr++;
+ } /* end of while loop */
+
+ if(inPtr < startPtr + size) {
+ /* handle last byte */
+ if(*inPtr == '\r') {
+ /* deal with a CR at the end of the buffer */
+ *outPtr = '\n'; /* copy a NL instead */
+ /* note that a CRLF might be split across two blocks */
+ data->state.prev_block_had_trailing_cr = TRUE;
+ }
+ else {
+ /* copy last byte */
+ *outPtr = *inPtr;
+ }
+ outPtr++;
+ }
+ if(outPtr < startPtr + size)
+ /* tidy up by null terminating the now shorter data */
+ *outPtr = '\0';
+
+ return (outPtr - startPtr);
+ }
+ return size;
+}
+#endif /* CURL_DO_LINEEND_CONV && !CURL_DISABLE_FTP */
+
+/*
+ * Curl_nwrite() is an internal write function that sends data to the
+ * server. Works with a socket index for the connection.
+ *
+ * If the write would block (CURLE_AGAIN), it returns CURLE_OK and
+ * (*nwritten == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_nwrite(struct Curl_easy *data,
+ int sockindex,
+ const void *buf,
+ size_t blen,
+ ssize_t *pnwritten)
+{
+ ssize_t nwritten;
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn;
+
+ DEBUGASSERT(sockindex >= 0 && sockindex < 2);
+ DEBUGASSERT(pnwritten);
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ conn = data->conn;
+#ifdef CURLDEBUG
+ {
+ /* Allow debug builds to override this logic to force short sends
+ */
+ char *p = getenv("CURL_SMALLSENDS");
+ if(p) {
+ size_t altsize = (size_t)strtoul(p, NULL, 10);
+ if(altsize)
+ blen = CURLMIN(blen, altsize);
+ }
+ }
+#endif
+ nwritten = conn->send[sockindex](data, sockindex, buf, blen, &result);
+ if(result == CURLE_AGAIN) {
+ nwritten = 0;
+ result = CURLE_OK;
+ }
+ else if(result) {
+ nwritten = -1; /* make sure */
+ }
+ else {
+ DEBUGASSERT(nwritten >= 0);
+ }
+
+ *pnwritten = nwritten;
+ return result;
+}
+
+/*
+ * Curl_write() is an internal write function that sends data to the
+ * server. Works with plain sockets, SCP, SSL or kerberos.
+ *
+ * If the write would block (CURLE_AGAIN), we return CURLE_OK and
+ * (*written == 0). Otherwise we return regular CURLcode value.
+ */
+CURLcode Curl_write(struct Curl_easy *data,
+ curl_socket_t sockfd,
+ const void *mem,
+ size_t len,
+ ssize_t *written)
+{
+ struct connectdata *conn;
+ int num;
+
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->conn);
+ conn = data->conn;
+ num = (sockfd != CURL_SOCKET_BAD && sockfd == conn->sock[SECONDARYSOCKET]);
+ return Curl_nwrite(data, num, mem, len, written);
+}
+
+static CURLcode pausewrite(struct Curl_easy *data,
+ int type, /* what type of data */
+ bool paused_body,
+ const char *ptr,
+ size_t len)
+{
+ /* signalled to pause sending on this connection, but since we have data
+ we want to send we need to dup it to save a copy for when the sending
+ is again enabled */
+ struct SingleRequest *k = &data->req;
+ struct UrlState *s = &data->state;
+ unsigned int i;
+ bool newtype = TRUE;
+
+ Curl_conn_ev_data_pause(data, TRUE);
+
+ if(s->tempcount) {
+ for(i = 0; i< s->tempcount; i++) {
+ if(s->tempwrite[i].type == type &&
+ !!s->tempwrite[i].paused_body == !!paused_body) {
+ /* data for this type exists */
+ newtype = FALSE;
+ break;
+ }
+ }
+ DEBUGASSERT(i < 3);
+ if(i >= 3)
+ /* There are more types to store than what fits: very bad */
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else
+ i = 0;
+
+ if(newtype) {
+ /* store this information in the state struct for later use */
+ Curl_dyn_init(&s->tempwrite[i].b, DYN_PAUSE_BUFFER);
+ s->tempwrite[i].type = type;
+ s->tempwrite[i].paused_body = paused_body;
+ s->tempcount++;
+ }
+
+ if(Curl_dyn_addn(&s->tempwrite[i].b, (unsigned char *)ptr, len))
+ return CURLE_OUT_OF_MEMORY;
+
+ /* mark the connection as RECV paused */
+ k->keepon |= KEEP_RECV_PAUSE;
+
+ return CURLE_OK;
+}
+
+
+/* chop_write() writes chunks of data not larger than CURL_MAX_WRITE_SIZE via
+ * client write callback(s) and takes care of pause requests from the
+ * callbacks.
+ */
+static CURLcode chop_write(struct Curl_easy *data,
+ int type,
+ bool skip_body_write,
+ char *optr,
+ size_t olen)
+{
+ struct connectdata *conn = data->conn;
+ curl_write_callback writeheader = NULL;
+ curl_write_callback writebody = NULL;
+ char *ptr = optr;
+ size_t len = olen;
+ void *writebody_ptr = data->set.out;
+
+ if(!len)
+ return CURLE_OK;
+
+ /* If reading is paused, append this data to the already held data for this
+ type. */
+ if(data->req.keepon & KEEP_RECV_PAUSE)
+ return pausewrite(data, type, !skip_body_write, ptr, len);
+
+ /* Determine the callback(s) to use. */
+ if(!skip_body_write &&
+ ((type & CLIENTWRITE_BODY) ||
+ ((type & CLIENTWRITE_HEADER) && data->set.include_header))) {
+#ifdef USE_WEBSOCKETS
+ if(conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) {
+ writebody = Curl_ws_writecb;
+ writebody_ptr = data;
+ }
+ else
+#endif
+ writebody = data->set.fwrite_func;
+ }
+ if((type & (CLIENTWRITE_HEADER|CLIENTWRITE_INFO)) &&
+ (data->set.fwrite_header || data->set.writeheader)) {
+ /*
+ * Write headers to the same callback or to the especially setup
+ * header callback function (added after version 7.7.1).
+ */
+ writeheader =
+ data->set.fwrite_header? data->set.fwrite_header: data->set.fwrite_func;
+ }
+
+ /* Chop data, write chunks. */
+ while(len) {
+ size_t chunklen = len <= CURL_MAX_WRITE_SIZE? len: CURL_MAX_WRITE_SIZE;
+
+ if(writebody) {
+ size_t wrote;
+ Curl_set_in_callback(data, true);
+ wrote = writebody(ptr, 1, chunklen, writebody_ptr);
+ Curl_set_in_callback(data, false);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote) {
+ if(conn->handler->flags & PROTOPT_NONETWORK) {
+ /* Protocols that work without network cannot be paused. This is
+ actually only FILE:// just now, and it can't pause since the
+ transfer isn't done using the "normal" procedure. */
+ failf(data, "Write callback asked for PAUSE when not supported");
+ return CURLE_WRITE_ERROR;
+ }
+ return pausewrite(data, type, TRUE, ptr, len);
+ }
+ if(wrote != chunklen) {
+ failf(data, "Failure writing output to destination");
+ return CURLE_WRITE_ERROR;
+ }
+ }
+
+ ptr += chunklen;
+ len -= chunklen;
+ }
+
+#ifndef CURL_DISABLE_HTTP
+ /* HTTP header, but not status-line */
+ if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
+ (type & CLIENTWRITE_HEADER) && !(type & CLIENTWRITE_STATUS) ) {
+ unsigned char htype = (unsigned char)
+ (type & CLIENTWRITE_CONNECT ? CURLH_CONNECT :
+ (type & CLIENTWRITE_1XX ? CURLH_1XX :
+ (type & CLIENTWRITE_TRAILER ? CURLH_TRAILER :
+ CURLH_HEADER)));
+ CURLcode result = Curl_headers_push(data, optr, htype);
+ if(result)
+ return result;
+ }
+#endif
+
+ if(writeheader) {
+ size_t wrote;
+
+ Curl_set_in_callback(data, true);
+ wrote = writeheader(optr, 1, olen, data->set.writeheader);
+ Curl_set_in_callback(data, false);
+
+ if(CURL_WRITEFUNC_PAUSE == wrote)
+ return pausewrite(data, type, FALSE, optr, olen);
+ if(wrote != olen) {
+ failf(data, "Failed writing header");
+ return CURLE_WRITE_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
/* Curl_client_write() sends data to the write callback(s)
The bit pattern defines to what "streams" to write to. Body and/or header.
The defines are in sendf.h of course.
+
+ If CURL_DO_LINEEND_CONV is enabled, data is converted IN PLACE to the
+ local character encoding. This is a problem and should be changed in
+ the future to leave the original data alone.
*/
CURLcode Curl_client_write(struct Curl_easy *data,
- int type, const char *buf, size_t blen)
+ int type, char *buf, size_t blen)
{
CURLcode result;
+#if !defined(CURL_DISABLE_FTP) && defined(CURL_DO_LINEEND_CONV)
+ /* FTP data may need conversion. */
+ if((type & CLIENTWRITE_BODY) &&
+ (data->conn->handler->protocol & PROTO_FAMILY_FTP) &&
+ data->conn->proto.ftpc.transfertype == 'A') {
+ /* convert end-of-line markers */
+ blen = convert_lineends(data, buf, blen);
+ }
+#endif
/* it is one of those, at least */
DEBUGASSERT(type & (CLIENTWRITE_BODY|CLIENTWRITE_HEADER|CLIENTWRITE_INFO));
- /* BODY is only BODY (with optional EOS) */
- DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
- ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0));
- /* INFO is only INFO (with optional EOS) */
- DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
- ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
+ /* BODY is only BODY */
+ DEBUGASSERT(!(type & CLIENTWRITE_BODY) || (type == CLIENTWRITE_BODY));
+ /* INFO is only INFO */
+ DEBUGASSERT(!(type & CLIENTWRITE_INFO) || (type == CLIENTWRITE_INFO));
if(!data->req.writer_stack) {
- result = do_init_writer_stack(data);
+ result = do_init_stack(data);
if(result)
return result;
DEBUGASSERT(data->req.writer_stack);
}
- result = Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
- CURL_TRC_WRITE(data, "client_write(type=%x, len=%zu) -> %d",
- type, blen, result);
+ return Curl_cwriter_write(data, data->req.writer_stack, type, buf, blen);
+}
+
+CURLcode Curl_client_unpause(struct Curl_easy *data)
+{
+ CURLcode result = CURLE_OK;
+
+ if(data->state.tempcount) {
+ /* there are buffers for sending that can be delivered as the receive
+ pausing is lifted! */
+ unsigned int i;
+ unsigned int count = data->state.tempcount;
+ struct tempbuf writebuf[3]; /* there can only be three */
+
+ /* copy the structs to allow for immediate re-pausing */
+ for(i = 0; i < data->state.tempcount; i++) {
+ writebuf[i] = data->state.tempwrite[i];
+ Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER);
+ }
+ data->state.tempcount = 0;
+
+ for(i = 0; i < count; i++) {
+ /* even if one function returns error, this loops through and frees
+ all buffers */
+ if(!result)
+ result = chop_write(data, writebuf[i].type,
+ !writebuf[i].paused_body,
+ Curl_dyn_ptr(&writebuf[i].b),
+ Curl_dyn_len(&writebuf[i].b));
+ Curl_dyn_free(&writebuf[i].b);
+ }
+ }
return result;
}
-static void cl_reset_writer(struct Curl_easy *data)
+void Curl_client_cleanup(struct Curl_easy *data)
{
struct Curl_cwriter *writer = data->req.writer_stack;
+ size_t i;
+
while(writer) {
data->req.writer_stack = writer->next;
writer->cwt->do_close(data, writer);
free(writer);
writer = data->req.writer_stack;
}
-}
-
-static void cl_reset_reader(struct Curl_easy *data)
-{
- struct Curl_creader *reader = data->req.reader_stack;
- while(reader) {
- data->req.reader_stack = reader->next;
- reader->crt->do_close(data, reader);
- free(reader);
- reader = data->req.reader_stack;
- }
-}
-void Curl_client_cleanup(struct Curl_easy *data)
-{
- cl_reset_reader(data);
- cl_reset_writer(data);
-
- data->req.bytecount = 0;
- data->req.headerline = 0;
-}
-
-void Curl_client_reset(struct Curl_easy *data)
-{
- if(data->req.rewind_read) {
- /* already requested */
- CURL_TRC_READ(data, "client_reset, will rewind reader");
+ for(i = 0; i < data->state.tempcount; i++) {
+ Curl_dyn_free(&data->state.tempwrite[i].b);
}
- else {
- CURL_TRC_READ(data, "client_reset, clear readers");
- cl_reset_reader(data);
- }
- cl_reset_writer(data);
-
+ data->state.tempcount = 0;
data->req.bytecount = 0;
data->req.headerline = 0;
}
-CURLcode Curl_client_start(struct Curl_easy *data)
-{
- if(data->req.rewind_read) {
- struct Curl_creader *r = data->req.reader_stack;
- CURLcode result = CURLE_OK;
-
- CURL_TRC_READ(data, "client start, rewind readers");
- while(r) {
- result = r->crt->rewind(data, r);
- if(result) {
- failf(data, "rewind of client reader '%s' failed: %d",
- r->crt->name, result);
- return result;
- }
- r = r->next;
- }
- data->req.rewind_read = FALSE;
- cl_reset_reader(data);
- }
- return CURLE_OK;
-}
-
-bool Curl_creader_will_rewind(struct Curl_easy *data)
-{
- return data->req.rewind_read;
-}
-
-void Curl_creader_set_rewind(struct Curl_easy *data, bool enable)
-{
- data->req.rewind_read = !!enable;
-}
-
-/* Write data using an unencoding writer stack. */
+/* Write data using an unencoding writer stack. "nbytes" is not
+ allowed to be 0. */
CURLcode Curl_cwriter_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
+ if(!nbytes)
+ return CURLE_OK;
if(!writer)
return CURLE_WRITE_ERROR;
return writer->cwt->do_write(data, writer, type, buf, nbytes);
@@ -205,6 +506,26 @@ void Curl_cwriter_def_close(struct Curl_easy *data,
(void) writer;
}
+/* Real client writer to installed callbacks. */
+static CURLcode cw_client_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
+{
+ (void)writer;
+ if(!nbytes)
+ return CURLE_OK;
+ return chop_write(data, type, FALSE, (char *)buf, nbytes);
+}
+
+static const struct Curl_cwtype cw_client = {
+ "client",
+ NULL,
+ Curl_cwriter_def_init,
+ cw_client_write,
+ Curl_cwriter_def_close,
+ sizeof(struct Curl_cwriter)
+};
+
static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
{
if(limit != -1) {
@@ -227,79 +548,36 @@ static size_t get_max_body_write_len(struct Curl_easy *data, curl_off_t limit)
return SIZE_T_MAX;
}
-struct cw_download_ctx {
- struct Curl_cwriter super;
- BIT(started_response);
-};
/* Download client writer in phase CURL_CW_PROTOCOL that
* sees the "real" download body data. */
static CURLcode cw_download_write(struct Curl_easy *data,
struct Curl_cwriter *writer, int type,
const char *buf, size_t nbytes)
{
- struct cw_download_ctx *ctx = writer->ctx;
CURLcode result;
size_t nwrite, excess_len = 0;
- bool is_connect = !!(type & CLIENTWRITE_CONNECT);
-
- if(!is_connect && !ctx->started_response) {
- Curl_pgrsTime(data, TIMER_STARTTRANSFER);
- ctx->started_response = TRUE;
- }
+ const char *excess_data = NULL;
if(!(type & CLIENTWRITE_BODY)) {
- if(is_connect && data->set.suppress_connect_headers)
+ if((type & CLIENTWRITE_CONNECT) && data->set.suppress_connect_headers)
return CURLE_OK;
- result = Curl_cwriter_write(data, writer->next, type, buf, nbytes);
- CURL_TRC_WRITE(data, "download_write header(type=%x, blen=%zu) -> %d",
- type, nbytes, result);
- return result;
- }
-
- /* Here, we deal with REAL BODY bytes. All filtering and transfer
- * encodings have been applied and only the true content, e.g. BODY,
- * bytes are passed here.
- * This allows us to check sizes, update stats, etc. independent
- * from the protocol in play. */
-
- if(data->req.no_body && nbytes > 0) {
- /* BODY arrives although we want none, bail out */
- streamclose(data->conn, "ignoring body");
- CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu), "
- "did not want a BODY", type, nbytes);
- data->req.download_done = TRUE;
- if(data->info.header_size)
- /* if headers have been received, this is fine */
- return CURLE_OK;
- return CURLE_WEIRD_SERVER_REPLY;
+ return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
}
- /* Determine if we see any bytes in excess to what is allowed.
- * We write the allowed bytes and handle excess further below.
- * This gives deterministic BODY writes on varying buffer receive
- * lengths. */
nwrite = nbytes;
if(-1 != data->req.maxdownload) {
size_t wmax = get_max_body_write_len(data, data->req.maxdownload);
if(nwrite > wmax) {
excess_len = nbytes - wmax;
nwrite = wmax;
+ excess_data = buf + nwrite;
}
if(nwrite == wmax) {
data->req.download_done = TRUE;
}
-
- if((type & CLIENTWRITE_EOS) && !data->req.no_body &&
- (data->req.maxdownload > data->req.bytecount)) {
- failf(data, "end of response with %" FMT_OFF_T " bytes missing",
- data->req.maxdownload - data->req.bytecount);
- return CURLE_PARTIAL_FILE;
- }
}
- /* Error on too large filesize is handled below, after writing
- * the permitted bytes */
if(data->set.max_filesize) {
size_t wmax = get_max_body_write_len(data, data->set.max_filesize);
if(nwrite > wmax) {
@@ -307,38 +585,50 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
}
- if(!data->req.ignorebody && (nwrite || (type & CLIENTWRITE_EOS))) {
+ data->req.bytecount += nwrite;
+ ++data->req.bodywrites;
+ if(!data->req.ignorebody && nwrite) {
result = Curl_cwriter_write(data, writer->next, type, buf, nwrite);
- CURL_TRC_WRITE(data, "download_write body(type=%x, blen=%zu) -> %d",
- type, nbytes, result);
if(result)
return result;
}
- /* Update stats, write and report progress */
- data->req.bytecount += nwrite;
-#ifdef USE_HYPER
- data->req.bodywritten = TRUE;
-#endif
result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
if(result)
return result;
if(excess_len) {
- if(!data->req.ignorebody) {
+ if(data->conn->handler->readwrite) {
+ /* RTSP hack moved from transfer loop to here */
+ bool readmore = FALSE; /* indicates data is incomplete, need more */
+ size_t consumed = 0;
+ result = data->conn->handler->readwrite(data, data->conn,
+ excess_data, excess_len,
+ &consumed, &readmore);
+ if(result)
+ return result;
+ DEBUGASSERT(consumed <= excess_len);
+ excess_len -= consumed;
+ if(readmore) {
+ data->req.download_done = FALSE;
+ data->req.keepon |= KEEP_RECV; /* we're not done reading */
+ }
+ }
+ if(excess_len && !data->req.ignorebody) {
infof(data,
"Excess found writing body:"
" excess = %zu"
- ", size = %" FMT_OFF_T
- ", maxdownload = %" FMT_OFF_T
- ", bytecount = %" FMT_OFF_T,
+ ", size = %" CURL_FORMAT_CURL_OFF_T
+ ", maxdownload = %" CURL_FORMAT_CURL_OFF_T
+ ", bytecount = %" CURL_FORMAT_CURL_OFF_T,
excess_len, data->req.size, data->req.maxdownload,
data->req.bytecount);
connclose(data->conn, "excess found in a read");
}
}
- else if((nwrite < nbytes) && !data->req.ignorebody) {
+ else if(nwrite < nbytes) {
failf(data, "Exceeded the maximum allowed file size "
- "(%" FMT_OFF_T ") with %" FMT_OFF_T " bytes",
+ "(%" CURL_FORMAT_CURL_OFF_T ") with %"
+ CURL_FORMAT_CURL_OFF_T " bytes",
data->set.max_filesize, data->req.bytecount);
return CURLE_FILESIZE_EXCEEDED;
}
@@ -347,12 +637,12 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
static const struct Curl_cwtype cw_download = {
- "protocol",
+ "download",
NULL,
Curl_cwriter_def_init,
cw_download_write,
Curl_cwriter_def_close,
- sizeof(struct cw_download_ctx)
+ sizeof(struct Curl_cwriter)
};
/* RAW client writer in phase CURL_CW_RAW that
@@ -382,18 +672,15 @@ CURLcode Curl_cwriter_create(struct Curl_cwriter **pwriter,
const struct Curl_cwtype *cwt,
Curl_cwriter_phase phase)
{
- struct Curl_cwriter *writer = NULL;
+ struct Curl_cwriter *writer;
CURLcode result = CURLE_OUT_OF_MEMORY;
- void *p;
DEBUGASSERT(cwt->cwriter_size >= sizeof(struct Curl_cwriter));
- p = calloc(1, cwt->cwriter_size);
- if(!p)
+ writer = (struct Curl_cwriter *) calloc(1, cwt->cwriter_size);
+ if(!writer)
goto out;
- writer = (struct Curl_cwriter *)p;
writer->cwt = cwt;
- writer->ctx = p;
writer->phase = phase;
result = cwt->do_init(data, writer);
@@ -425,14 +712,14 @@ size_t Curl_cwriter_count(struct Curl_easy *data, Curl_cwriter_phase phase)
return n;
}
-static CURLcode do_init_writer_stack(struct Curl_easy *data)
+static CURLcode do_init_stack(struct Curl_easy *data)
{
struct Curl_cwriter *writer;
CURLcode result;
DEBUGASSERT(!data->req.writer_stack);
result = Curl_cwriter_create(&data->req.writer_stack,
- data, &Curl_cwt_out, CURL_CW_CLIENT);
+ data, &cw_client, CURL_CW_CLIENT);
if(result)
return result;
@@ -461,7 +748,7 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
struct Curl_cwriter **anchor = &data->req.writer_stack;
if(!*anchor) {
- result = do_init_writer_stack(data);
+ result = do_init_stack(data);
if(result)
return result;
}
@@ -475,969 +762,41 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
return CURLE_OK;
}
-struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
- const char *name)
-{
- struct Curl_cwriter *writer;
- for(writer = data->req.writer_stack; writer; writer = writer->next) {
- if(!strcmp(name, writer->cwt->name))
- return writer;
- }
- return NULL;
-}
-
-struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
- const struct Curl_cwtype *cwt)
-{
- struct Curl_cwriter *writer;
- for(writer = data->req.writer_stack; writer; writer = writer->next) {
- if(writer->cwt == cwt)
- return writer;
- }
- return NULL;
-}
-
-void Curl_cwriter_remove_by_name(struct Curl_easy *data,
- const char *name)
-{
- struct Curl_cwriter **anchor = &data->req.writer_stack;
-
- while(*anchor) {
- if(!strcmp(name, (*anchor)->cwt->name)) {
- struct Curl_cwriter *w = (*anchor);
- *anchor = w->next;
- Curl_cwriter_free(data, w);
- continue;
- }
- anchor = &((*anchor)->next);
- }
-}
-
-bool Curl_cwriter_is_paused(struct Curl_easy *data)
-{
- return Curl_cw_out_is_paused(data);
-}
-
-CURLcode Curl_cwriter_unpause(struct Curl_easy *data)
-{
- return Curl_cw_out_unpause(data);
-}
-
-CURLcode Curl_creader_read(struct Curl_easy *data,
- struct Curl_creader *reader,
- char *buf, size_t blen, size_t *nread, bool *eos)
-{
- *nread = 0;
- *eos = FALSE;
- if(!reader)
- return CURLE_READ_ERROR;
- return reader->crt->do_read(data, reader, buf, blen, nread, eos);
-}
-
-CURLcode Curl_creader_def_init(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
- return CURLE_OK;
-}
-
-void Curl_creader_def_close(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
-}
-
-CURLcode Curl_creader_def_read(struct Curl_easy *data,
- struct Curl_creader *reader,
- char *buf, size_t blen,
- size_t *nread, bool *eos)
-{
- if(reader->next)
- return reader->next->crt->do_read(data, reader->next, buf, blen,
- nread, eos);
- else {
- *nread = 0;
- *eos = FALSE;
- return CURLE_READ_ERROR;
- }
-}
-
-bool Curl_creader_def_needs_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
- return FALSE;
-}
-
-curl_off_t Curl_creader_def_total_length(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- return reader->next?
- reader->next->crt->total_length(data, reader->next) : -1;
-}
-
-CURLcode Curl_creader_def_resume_from(struct Curl_easy *data,
- struct Curl_creader *reader,
- curl_off_t offset)
-{
- (void)data;
- (void)reader;
- (void)offset;
- return CURLE_READ_ERROR;
-}
-
-CURLcode Curl_creader_def_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
- return CURLE_OK;
-}
-
-CURLcode Curl_creader_def_unpause(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
- return CURLE_OK;
-}
-
-bool Curl_creader_def_is_paused(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)data;
- (void)reader;
- return FALSE;
-}
-
-void Curl_creader_def_done(struct Curl_easy *data,
- struct Curl_creader *reader, int premature)
-{
- (void)data;
- (void)reader;
- (void)premature;
-}
-
-struct cr_in_ctx {
- struct Curl_creader super;
- curl_read_callback read_cb;
- void *cb_user_data;
- curl_off_t total_len;
- curl_off_t read_len;
- CURLcode error_result;
- BIT(seen_eos);
- BIT(errored);
- BIT(has_used_cb);
- BIT(is_paused);
-};
-
-static CURLcode cr_in_init(struct Curl_easy *data, struct Curl_creader *reader)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- (void)data;
- ctx->read_cb = data->state.fread_func;
- ctx->cb_user_data = data->state.in;
- ctx->total_len = -1;
- ctx->read_len = 0;
- return CURLE_OK;
-}
-
-/* Real client reader to installed client callbacks. */
-static CURLcode cr_in_read(struct Curl_easy *data,
- struct Curl_creader *reader,
- char *buf, size_t blen,
- size_t *pnread, bool *peos)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- size_t nread;
-
- ctx->is_paused = FALSE;
-
- /* Once we have errored, we will return the same error forever */
- if(ctx->errored) {
- *pnread = 0;
- *peos = FALSE;
- return ctx->error_result;
- }
- if(ctx->seen_eos) {
- *pnread = 0;
- *peos = TRUE;
- return CURLE_OK;
- }
- /* respect length limitations */
- if(ctx->total_len >= 0) {
- curl_off_t remain = ctx->total_len - ctx->read_len;
- if(remain <= 0)
- blen = 0;
- else if(remain < (curl_off_t)blen)
- blen = (size_t)remain;
- }
- nread = 0;
- if(ctx->read_cb && blen) {
- Curl_set_in_callback(data, true);
- nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data);
- Curl_set_in_callback(data, false);
- ctx->has_used_cb = TRUE;
- }
-
- switch(nread) {
- case 0:
- if((ctx->total_len >= 0) && (ctx->read_len < ctx->total_len)) {
- failf(data, "client read function EOF fail, "
- "only %"FMT_OFF_T"/%"FMT_OFF_T " of needed bytes read",
- ctx->read_len, ctx->total_len);
- return CURLE_READ_ERROR;
- }
- *pnread = 0;
- *peos = TRUE;
- ctx->seen_eos = TRUE;
- break;
-
- case CURL_READFUNC_ABORT:
- failf(data, "operation aborted by callback");
- *pnread = 0;
- *peos = FALSE;
- ctx->errored = TRUE;
- ctx->error_result = CURLE_ABORTED_BY_CALLBACK;
- return CURLE_ABORTED_BY_CALLBACK;
-
- case CURL_READFUNC_PAUSE:
- if(data->conn->handler->flags & PROTOPT_NONETWORK) {
- /* protocols that work without network cannot be paused. This is
- actually only FILE:// just now, and it cannot pause since the transfer
- is not done using the "normal" procedure. */
- failf(data, "Read callback asked for PAUSE when not supported");
- return CURLE_READ_ERROR;
- }
- /* CURL_READFUNC_PAUSE pauses read callbacks that feed socket writes */
- CURL_TRC_READ(data, "cr_in_read, callback returned CURL_READFUNC_PAUSE");
- ctx->is_paused = TRUE;
- data->req.keepon |= KEEP_SEND_PAUSE; /* mark socket send as paused */
- *pnread = 0;
- *peos = FALSE;
- break; /* nothing was read */
-
- default:
- if(nread > blen) {
- /* the read function returned a too large value */
- failf(data, "read function returned funny value");
- *pnread = 0;
- *peos = FALSE;
- ctx->errored = TRUE;
- ctx->error_result = CURLE_READ_ERROR;
- return CURLE_READ_ERROR;
- }
- ctx->read_len += nread;
- if(ctx->total_len >= 0)
- ctx->seen_eos = (ctx->read_len >= ctx->total_len);
- *pnread = nread;
- *peos = ctx->seen_eos;
- break;
- }
- CURL_TRC_READ(data, "cr_in_read(len=%zu, total=%"FMT_OFF_T
- ", read=%"FMT_OFF_T") -> %d, nread=%zu, eos=%d",
- blen, ctx->total_len, ctx->read_len, CURLE_OK,
- *pnread, *peos);
- return CURLE_OK;
-}
-
-static bool cr_in_needs_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- (void)data;
- return ctx->has_used_cb;
-}
-
-static curl_off_t cr_in_total_length(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- (void)data;
- return ctx->total_len;
-}
-
-static CURLcode cr_in_resume_from(struct Curl_easy *data,
- struct Curl_creader *reader,
- curl_off_t offset)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- int seekerr = CURL_SEEKFUNC_CANTSEEK;
-
- DEBUGASSERT(data->conn);
- /* already started reading? */
- if(ctx->read_len)
- return CURLE_READ_ERROR;
-
- if(data->set.seek_func) {
- Curl_set_in_callback(data, true);
- seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET);
- Curl_set_in_callback(data, false);
- }
-
- if(seekerr != CURL_SEEKFUNC_OK) {
- curl_off_t passed = 0;
-
- if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
- failf(data, "Could not seek stream");
- return CURLE_READ_ERROR;
- }
- /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
- do {
- char scratch[4*1024];
- size_t readthisamountnow =
- (offset - passed > (curl_off_t)sizeof(scratch)) ?
- sizeof(scratch) :
- curlx_sotouz(offset - passed);
- size_t actuallyread;
-
- Curl_set_in_callback(data, true);
- actuallyread = ctx->read_cb(scratch, 1, readthisamountnow,
- ctx->cb_user_data);
- Curl_set_in_callback(data, false);
-
- passed += actuallyread;
- if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
- /* this checks for greater-than only to make sure that the
- CURL_READFUNC_ABORT return code still aborts */
- failf(data, "Could only read %" FMT_OFF_T " bytes from the input",
- passed);
- return CURLE_READ_ERROR;
- }
- } while(passed < offset);
- }
-
- /* now, decrease the size of the read */
- if(ctx->total_len > 0) {
- ctx->total_len -= offset;
-
- if(ctx->total_len <= 0) {
- failf(data, "File already completely uploaded");
- return CURLE_PARTIAL_FILE;
- }
- }
- /* we have passed, proceed as normal */
- return CURLE_OK;
-}
-
-static CURLcode cr_in_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_in_ctx *ctx = reader->ctx;
-
- /* If we never invoked the callback, there is noting to rewind */
- if(!ctx->has_used_cb)
- return CURLE_OK;
-
- if(data->set.seek_func) {
- int err;
-
- Curl_set_in_callback(data, true);
- err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
- Curl_set_in_callback(data, false);
- CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err);
- if(err) {
- failf(data, "seek callback returned error %d", (int)err);
- return CURLE_SEND_FAIL_REWIND;
- }
- }
- else if(data->set.ioctl_func) {
- curlioerr err;
-
- Curl_set_in_callback(data, true);
- err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
- data->set.ioctl_client);
- Curl_set_in_callback(data, false);
- CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err);
- if(err) {
- failf(data, "ioctl callback returned error %d", (int)err);
- return CURLE_SEND_FAIL_REWIND;
- }
- }
- else {
- /* If no CURLOPT_READFUNCTION is used, we know that we operate on a
- given FILE * stream and we can actually attempt to rewind that
- ourselves with fseek() */
- if(data->state.fread_func == (curl_read_callback)fread) {
- int err = fseek(data->state.in, 0, SEEK_SET);
- CURL_TRC_READ(data, "cr_in, rewind via fseek -> %d(%d)",
- (int)err, (int)errno);
- if(-1 != err)
- /* successful rewind */
- return CURLE_OK;
- }
-
- /* no callback set or failure above, makes us fail at once */
- failf(data, "necessary data rewind was not possible");
- return CURLE_SEND_FAIL_REWIND;
- }
- return CURLE_OK;
-}
-
-static CURLcode cr_in_unpause(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- (void)data;
- ctx->is_paused = FALSE;
- return CURLE_OK;
-}
-
-static bool cr_in_is_paused(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_in_ctx *ctx = reader->ctx;
- (void)data;
- return ctx->is_paused;
-}
-
-static const struct Curl_crtype cr_in = {
- "cr-in",
- cr_in_init,
- cr_in_read,
- Curl_creader_def_close,
- cr_in_needs_rewind,
- cr_in_total_length,
- cr_in_resume_from,
- cr_in_rewind,
- cr_in_unpause,
- cr_in_is_paused,
- Curl_creader_def_done,
- sizeof(struct cr_in_ctx)
-};
-
-CURLcode Curl_creader_create(struct Curl_creader **preader,
- struct Curl_easy *data,
- const struct Curl_crtype *crt,
- Curl_creader_phase phase)
-{
- struct Curl_creader *reader = NULL;
- CURLcode result = CURLE_OUT_OF_MEMORY;
- void *p;
-
- DEBUGASSERT(crt->creader_size >= sizeof(struct Curl_creader));
- p = calloc(1, crt->creader_size);
- if(!p)
- goto out;
-
- reader = (struct Curl_creader *)p;
- reader->crt = crt;
- reader->ctx = p;
- reader->phase = phase;
- result = crt->do_init(data, reader);
-
-out:
- *preader = result? NULL : reader;
- if(result)
- free(reader);
- return result;
-}
-
-void Curl_creader_free(struct Curl_easy *data, struct Curl_creader *reader)
-{
- if(reader) {
- reader->crt->do_close(data, reader);
- free(reader);
- }
-}
-
-struct cr_lc_ctx {
- struct Curl_creader super;
- struct bufq buf;
- BIT(read_eos); /* we read an EOS from the next reader */
- BIT(eos); /* we have returned an EOS */
- BIT(prev_cr); /* the last byte was a CR */
-};
-
-static CURLcode cr_lc_init(struct Curl_easy *data, struct Curl_creader *reader)
-{
- struct cr_lc_ctx *ctx = reader->ctx;
- (void)data;
- Curl_bufq_init2(&ctx->buf, (16 * 1024), 1, BUFQ_OPT_SOFT_LIMIT);
- return CURLE_OK;
-}
-
-static void cr_lc_close(struct Curl_easy *data, struct Curl_creader *reader)
-{
- struct cr_lc_ctx *ctx = reader->ctx;
- (void)data;
- Curl_bufq_free(&ctx->buf);
-}
-
-/* client reader doing line end conversions. */
-static CURLcode cr_lc_read(struct Curl_easy *data,
- struct Curl_creader *reader,
- char *buf, size_t blen,
- size_t *pnread, bool *peos)
-{
- struct cr_lc_ctx *ctx = reader->ctx;
- CURLcode result;
- size_t nread, i, start, n;
- bool eos;
-
- if(ctx->eos) {
- *pnread = 0;
- *peos = TRUE;
- return CURLE_OK;
- }
-
- if(Curl_bufq_is_empty(&ctx->buf)) {
- if(ctx->read_eos) {
- ctx->eos = TRUE;
- *pnread = 0;
- *peos = TRUE;
- return CURLE_OK;
- }
- /* Still getting data form the next reader, ctx->buf is empty */
- result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
- if(result)
- return result;
- ctx->read_eos = eos;
-
- if(!nread || !memchr(buf, '\n', nread)) {
- /* nothing to convert, return this right away */
- if(ctx->read_eos)
- ctx->eos = TRUE;
- *pnread = nread;
- *peos = ctx->eos;
- goto out;
- }
-
- /* at least one \n might need conversion to '\r\n', place into ctx->buf */
- for(i = start = 0; i < nread; ++i) {
- /* if this byte is not an LF character, or if the preceding character is
- a CR (meaning this already is a CRLF pair), go to next */
- if((buf[i] != '\n') || ctx->prev_cr) {
- ctx->prev_cr = (buf[i] == '\r');
- continue;
- }
- ctx->prev_cr = false;
- /* on a soft limit bufq, we do not need to check length */
- result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
- if(!result)
- result = Curl_bufq_cwrite(&ctx->buf, STRCONST("\r\n"), &n);
- if(result)
- return result;
- start = i + 1;
- if(!data->set.crlf && (data->state.infilesize != -1)) {
- /* we are here only because FTP is in ASCII mode...
- bump infilesize for the LF we just added */
- data->state.infilesize++;
- /* comment: this might work for FTP, but in HTTP we could not change
- * the content length after having started the request... */
- }
- }
-
- if(start < i) { /* leftover */
- result = Curl_bufq_cwrite(&ctx->buf, buf + start, i - start, &n);
- if(result)
- return result;
- }
- }
-
- DEBUGASSERT(!Curl_bufq_is_empty(&ctx->buf));
- *peos = FALSE;
- result = Curl_bufq_cread(&ctx->buf, buf, blen, pnread);
- if(!result && ctx->read_eos && Curl_bufq_is_empty(&ctx->buf)) {
- /* no more data, read all, done. */
- ctx->eos = TRUE;
- *peos = TRUE;
- }
-
-out:
- CURL_TRC_READ(data, "cr_lc_read(len=%zu) -> %d, nread=%zu, eos=%d",
- blen, result, *pnread, *peos);
- return result;
-}
-
-static curl_off_t cr_lc_total_length(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- /* this reader changes length depending on input */
- (void)data;
- (void)reader;
- return -1;
-}
-
-static const struct Curl_crtype cr_lc = {
- "cr-lineconv",
- cr_lc_init,
- cr_lc_read,
- cr_lc_close,
- Curl_creader_def_needs_rewind,
- cr_lc_total_length,
- Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
- Curl_creader_def_is_paused,
- Curl_creader_def_done,
- sizeof(struct cr_lc_ctx)
-};
-
-static CURLcode cr_lc_add(struct Curl_easy *data)
-{
- struct Curl_creader *reader = NULL;
- CURLcode result;
- result = Curl_creader_create(&reader, data, &cr_lc,
- CURL_CR_CONTENT_ENCODE);
- if(!result)
- result = Curl_creader_add(data, reader);
-
- if(result && reader)
- Curl_creader_free(data, reader);
- return result;
-}
-
-static CURLcode do_init_reader_stack(struct Curl_easy *data,
- struct Curl_creader *r)
-{
- CURLcode result = CURLE_OK;
- curl_off_t clen;
-
- DEBUGASSERT(r);
- DEBUGASSERT(r->crt);
- DEBUGASSERT(r->phase == CURL_CR_CLIENT);
- DEBUGASSERT(!data->req.reader_stack);
-
- data->req.reader_stack = r;
- clen = r->crt->total_length(data, r);
- /* if we do not have 0 length init, and crlf conversion is wanted,
- * add the reader for it */
- if(clen && (data->set.crlf
-#ifdef CURL_PREFER_LF_LINEENDS
- || data->state.prefer_ascii
-#endif
- )) {
- result = cr_lc_add(data);
- if(result)
- return result;
- }
-
- return result;
-}
-
-CURLcode Curl_creader_set_fread(struct Curl_easy *data, curl_off_t len)
-{
- CURLcode result;
- struct Curl_creader *r;
- struct cr_in_ctx *ctx;
-
- result = Curl_creader_create(&r, data, &cr_in, CURL_CR_CLIENT);
- if(result)
- goto out;
- ctx = r->ctx;
- ctx->total_len = len;
-
- cl_reset_reader(data);
- result = do_init_reader_stack(data, r);
-out:
- CURL_TRC_READ(data, "add fread reader, len=%"FMT_OFF_T " -> %d",
- len, result);
- return result;
-}
-
-CURLcode Curl_creader_add(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- CURLcode result;
- struct Curl_creader **anchor = &data->req.reader_stack;
-
- if(!*anchor) {
- result = Curl_creader_set_fread(data, data->state.infilesize);
- if(result)
- return result;
- }
-
- /* Insert the writer as first in its phase.
- * Skip existing readers of lower phases. */
- while(*anchor && (*anchor)->phase < reader->phase)
- anchor = &((*anchor)->next);
- reader->next = *anchor;
- *anchor = reader;
- return CURLE_OK;
-}
-
-CURLcode Curl_creader_set(struct Curl_easy *data, struct Curl_creader *r)
-{
- CURLcode result;
-
- DEBUGASSERT(r);
- DEBUGASSERT(r->crt);
- DEBUGASSERT(r->phase == CURL_CR_CLIENT);
-
- cl_reset_reader(data);
- result = do_init_reader_stack(data, r);
- if(result)
- Curl_creader_free(data, r);
- return result;
-}
-
-CURLcode Curl_client_read(struct Curl_easy *data, char *buf, size_t blen,
- size_t *nread, bool *eos)
-{
- CURLcode result;
-
- DEBUGASSERT(buf);
- DEBUGASSERT(blen);
- DEBUGASSERT(nread);
- DEBUGASSERT(eos);
-
- if(!data->req.reader_stack) {
- result = Curl_creader_set_fread(data, data->state.infilesize);
- if(result)
- return result;
- DEBUGASSERT(data->req.reader_stack);
- }
-
- result = Curl_creader_read(data, data->req.reader_stack, buf, blen,
- nread, eos);
- CURL_TRC_READ(data, "client_read(len=%zu) -> %d, nread=%zu, eos=%d",
- blen, result, *nread, *eos);
- return result;
-}
-
-bool Curl_creader_needs_rewind(struct Curl_easy *data)
-{
- struct Curl_creader *reader = data->req.reader_stack;
- while(reader) {
- if(reader->crt->needs_rewind(data, reader)) {
- CURL_TRC_READ(data, "client reader needs rewind before next request");
- return TRUE;
- }
- reader = reader->next;
- }
- return FALSE;
-}
-
-static CURLcode cr_null_read(struct Curl_easy *data,
- struct Curl_creader *reader,
- char *buf, size_t blen,
- size_t *pnread, bool *peos)
-{
- (void)data;
- (void)reader;
- (void)buf;
- (void)blen;
- *pnread = 0;
- *peos = TRUE;
- return CURLE_OK;
-}
-
-static curl_off_t cr_null_total_length(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- /* this reader changes length depending on input */
- (void)data;
- (void)reader;
- return 0;
-}
-
-static const struct Curl_crtype cr_null = {
- "cr-null",
- Curl_creader_def_init,
- cr_null_read,
- Curl_creader_def_close,
- Curl_creader_def_needs_rewind,
- cr_null_total_length,
- Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
- Curl_creader_def_is_paused,
- Curl_creader_def_done,
- sizeof(struct Curl_creader)
-};
-
-CURLcode Curl_creader_set_null(struct Curl_easy *data)
-{
- struct Curl_creader *r;
- CURLcode result;
-
- result = Curl_creader_create(&r, data, &cr_null, CURL_CR_CLIENT);
- if(result)
- return result;
-
- cl_reset_reader(data);
- return do_init_reader_stack(data, r);
-}
-
-struct cr_buf_ctx {
- struct Curl_creader super;
- const char *buf;
- size_t blen;
- size_t index;
-};
-
-static CURLcode cr_buf_read(struct Curl_easy *data,
- struct Curl_creader *reader,
- char *buf, size_t blen,
- size_t *pnread, bool *peos)
-{
- struct cr_buf_ctx *ctx = reader->ctx;
- size_t nread = ctx->blen - ctx->index;
-
- (void)data;
- if(!nread || !ctx->buf) {
- *pnread = 0;
- *peos = TRUE;
- }
- else {
- if(nread > blen)
- nread = blen;
- memcpy(buf, ctx->buf + ctx->index, nread);
- *pnread = nread;
- ctx->index += nread;
- *peos = (ctx->index == ctx->blen);
- }
- CURL_TRC_READ(data, "cr_buf_read(len=%zu) -> 0, nread=%zu, eos=%d",
- blen, *pnread, *peos);
- return CURLE_OK;
-}
-
-static bool cr_buf_needs_rewind(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_buf_ctx *ctx = reader->ctx;
- (void)data;
- return ctx->index > 0;
-}
-
-static curl_off_t cr_buf_total_length(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- struct cr_buf_ctx *ctx = reader->ctx;
- (void)data;
- return (curl_off_t)ctx->blen;
-}
-
-static CURLcode cr_buf_resume_from(struct Curl_easy *data,
- struct Curl_creader *reader,
- curl_off_t offset)
-{
- struct cr_buf_ctx *ctx = reader->ctx;
- size_t boffset;
-
- (void)data;
- DEBUGASSERT(data->conn);
- /* already started reading? */
- if(ctx->index)
- return CURLE_READ_ERROR;
- if(offset <= 0)
- return CURLE_OK;
- boffset = (size_t)offset;
- if(boffset > ctx->blen)
- return CURLE_READ_ERROR;
-
- ctx->buf += boffset;
- ctx->blen -= boffset;
- return CURLE_OK;
-}
-
-static const struct Curl_crtype cr_buf = {
- "cr-buf",
- Curl_creader_def_init,
- cr_buf_read,
- Curl_creader_def_close,
- cr_buf_needs_rewind,
- cr_buf_total_length,
- cr_buf_resume_from,
- Curl_creader_def_rewind,
- Curl_creader_def_unpause,
- Curl_creader_def_is_paused,
- Curl_creader_def_done,
- sizeof(struct cr_buf_ctx)
-};
-
-CURLcode Curl_creader_set_buf(struct Curl_easy *data,
- const char *buf, size_t blen)
-{
- CURLcode result;
- struct Curl_creader *r;
- struct cr_buf_ctx *ctx;
-
- result = Curl_creader_create(&r, data, &cr_buf, CURL_CR_CLIENT);
- if(result)
+/*
+ * Internal read-from-socket function. This is meant to deal with plain
+ * sockets, SSL sockets and kerberos sockets.
+ *
+ * Returns a regular CURLcode value.
+ */
+CURLcode Curl_read(struct Curl_easy *data, /* transfer */
+ curl_socket_t sockfd, /* read from this socket */
+ char *buf, /* store read data here */
+ size_t sizerequested, /* max amount to read */
+ ssize_t *n) /* amount bytes read */
+{
+ CURLcode result = CURLE_RECV_ERROR;
+ ssize_t nread = 0;
+ size_t bytesfromsocket = 0;
+ char *buffertofill = NULL;
+ struct connectdata *conn = data->conn;
+
+ /* Set 'num' to 0 or 1, depending on which socket that has been sent here.
+ If it is the second socket, we set num to 1. Otherwise to 0. This lets
+ us use the correct ssl handle. */
+ int num = (sockfd == conn->sock[SECONDARYSOCKET]);
+
+ *n = 0; /* reset amount to zero */
+
+ bytesfromsocket = CURLMIN(sizerequested, (size_t)data->set.buffer_size);
+ buffertofill = buf;
+
+ nread = conn->recv[num](data, num, buffertofill, bytesfromsocket, &result);
+ if(nread < 0)
goto out;
- ctx = r->ctx;
- ctx->buf = buf;
- ctx->blen = blen;
- ctx->index = 0;
- cl_reset_reader(data);
- result = do_init_reader_stack(data, r);
+ *n += nread;
+ result = CURLE_OK;
out:
- CURL_TRC_READ(data, "add buf reader, len=%zu -> %d", blen, result);
- return result;
-}
-
-curl_off_t Curl_creader_total_length(struct Curl_easy *data)
-{
- struct Curl_creader *r = data->req.reader_stack;
- return r? r->crt->total_length(data, r) : -1;
-}
-
-curl_off_t Curl_creader_client_length(struct Curl_easy *data)
-{
- struct Curl_creader *r = data->req.reader_stack;
- while(r && r->phase != CURL_CR_CLIENT)
- r = r->next;
- return r? r->crt->total_length(data, r) : -1;
-}
-
-CURLcode Curl_creader_resume_from(struct Curl_easy *data, curl_off_t offset)
-{
- struct Curl_creader *r = data->req.reader_stack;
- while(r && r->phase != CURL_CR_CLIENT)
- r = r->next;
- return r? r->crt->resume_from(data, r, offset) : CURLE_READ_ERROR;
-}
-
-CURLcode Curl_creader_unpause(struct Curl_easy *data)
-{
- struct Curl_creader *reader = data->req.reader_stack;
- CURLcode result = CURLE_OK;
-
- while(reader) {
- result = reader->crt->unpause(data, reader);
- if(result)
- break;
- reader = reader->next;
- }
return result;
}
-
-bool Curl_creader_is_paused(struct Curl_easy *data)
-{
- struct Curl_creader *reader = data->req.reader_stack;
-
- while(reader) {
- if(reader->crt->is_paused(data, reader))
- return TRUE;
- reader = reader->next;
- }
- return FALSE;
-}
-
-void Curl_creader_done(struct Curl_easy *data, int premature)
-{
- struct Curl_creader *reader = data->req.reader_stack;
- while(reader) {
- reader->crt->done(data, reader, premature);
- reader = reader->next;
- }
-}
-
-struct Curl_creader *Curl_creader_get_by_type(struct Curl_easy *data,
- const struct Curl_crtype *crt)
-{
- struct Curl_creader *r;
- for(r = data->req.reader_stack; r; r = r->next) {
- if(r->crt == crt)
- return r;
- }
- return NULL;
-
-}