The maximum size was 512kB until 7.88.0.
+Starting in libcurl 8.7.0, there is just a single transfer buffer allocated
+per multi handle. This buffer is used by all easy handles added to a multi
+handle no matter how many parallel transfers there are. The buffer remains
+allocated as long as there are active transfers.
+
# DEFAULT
CURL_MAX_WRITE_SIZE (16kB)
important that details from this (unrelated) disconnect does not
taint meta-data in the data handle. */
struct conncache *connc = data->state.conn_cache;
- connc->closure_handle->state.buffer = data->state.buffer;
- connc->closure_handle->set.buffer_size = data->set.buffer_size;
Curl_disconnect(connc->closure_handle, conn_candidate,
/* dead_connection */ FALSE);
}
void Curl_conncache_close_all_connections(struct conncache *connc)
{
struct connectdata *conn;
- char buffer[READBUFFER_MIN + 1];
SIGPIPE_VARIABLE(pipe_st);
if(!connc->closure_handle)
return;
- connc->closure_handle->state.buffer = buffer;
- connc->closure_handle->set.buffer_size = READBUFFER_MIN;
conn = conncache_find_first_connection(connc);
while(conn) {
conn = conncache_find_first_connection(connc);
}
- connc->closure_handle->state.buffer = NULL;
sigpipe_ignore(connc->closure_handle, &pipe_st);
Curl_hostcache_clean(connc->closure_handle,
#ifndef CURL_DISABLE_COOKIES
free(outcurl->cookies);
#endif
- free(outcurl->state.buffer);
Curl_dyn_free(&outcurl->state.headerb);
Curl_altsvc_cleanup(&outcurl->asi);
Curl_hsts_cleanup(&outcurl->hsts);
static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms);
static void process_pending_handles(struct Curl_multi *multi);
+static void multi_xfer_buf_free(struct Curl_multi *multi);
#ifdef DEBUGBUILD
static const char * const multi_statename[]={
/* changing to COMPLETED means there's one less easy handle 'alive' */
DEBUGASSERT(data->multi->num_alive > 0);
data->multi->num_alive--;
+ if(!data->multi->num_alive) {
+ /* free the transfer buffer when we have no more active transfers */
+ multi_xfer_buf_free(data->multi);
+ }
}
/* if this state has an init-function, run it */
data->state.lastconnect_id = -1;
}
- Curl_safefree(data->state.buffer);
return result;
}
*/
CURLcode Curl_preconnect(struct Curl_easy *data)
{
- if(!data->state.buffer) {
- data->state.buffer = malloc(data->set.buffer_size + 1);
- if(!data->state.buffer)
- return CURLE_OUT_OF_MEMORY;
- }
-
+ /* this used to do data->state.buffer allocation,
+ maybe remove completely now? */
+ (void)data;
return CURLE_OK;
}
{
char *newurl = NULL;
bool retry = FALSE;
- DEBUGASSERT(data->state.buffer);
/* check if over send speed */
send_timeout_ms = 0;
if(data->set.max_send_speed)
Curl_free_multi_ssl_backend_data(multi->ssl_backend_data);
#endif
+ multi_xfer_buf_free(multi);
free(multi);
return CURLM_OK;
}
return a;
}
+
+CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
+ char **pbuf, size_t *pbuflen)
+{
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->multi);
+ *pbuf = NULL;
+ *pbuflen = 0;
+ if(!data->multi) {
+ failf(data, "transfer has no multi handle");
+ return CURLE_FAILED_INIT;
+ }
+ if(!data->set.buffer_size) {
+ failf(data, "transfer buffer size is 0");
+ return CURLE_FAILED_INIT;
+ }
+ if(data->multi->xfer_buf_borrowed) {
+ failf(data, "attempt to borrow xfer_buf when already borrowed");
+ return CURLE_AGAIN;
+ }
+
+ if(data->multi->xfer_buf &&
+ data->set.buffer_size > data->multi->xfer_buf_len) {
+ /* not large enough, get a new one */
+ free(data->multi->xfer_buf);
+ data->multi->xfer_buf = NULL;
+ data->multi->xfer_buf_len = 0;
+ }
+
+ if(!data->multi->xfer_buf) {
+ data->multi->xfer_buf = malloc((size_t)data->set.buffer_size);
+ if(!data->multi->xfer_buf) {
+ failf(data, "could not allocate xfer_buf of %zu bytes",
+ (size_t)data->set.buffer_size);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ data->multi->xfer_buf_len = data->set.buffer_size;
+ }
+
+ data->multi->xfer_buf_borrowed = TRUE;
+ *pbuf = data->multi->xfer_buf;
+ *pbuflen = data->multi->xfer_buf_len;
+ return CURLE_OK;
+}
+
+void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf)
+{
+ (void)buf;
+ DEBUGASSERT(data);
+ DEBUGASSERT(data->multi);
+ DEBUGASSERT(!buf || data->multi->xfer_buf == buf);
+ data->multi->xfer_buf_borrowed = FALSE;
+}
+
+static void multi_xfer_buf_free(struct Curl_multi *multi)
+{
+ DEBUGASSERT(multi);
+ Curl_safefree(multi->xfer_buf);
+ multi->xfer_buf_len = 0;
+ multi->xfer_buf_borrowed = FALSE;
+}
times of all currently set timers */
struct Curl_tree *timetree;
+ /* buffer used for transfer data, lazy initialized */
+ char *xfer_buf; /* the actual buffer */
+ size_t xfer_buf_len; /* the allocated length */
+
#if defined(USE_SSL)
struct multi_ssl_backend_data *ssl_backend_data;
#endif
#endif
BIT(dead); /* a callback returned error, everything needs to crash and
burn */
+ BIT(xfer_buf_borrowed); /* xfer_buf is currently being borrowed */
#ifdef DEBUGBUILD
BIT(warned); /* true after user warned of DEBUGBUILD */
#endif
/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
+/**
+ * Borrow the transfer buffer from the multi, suitable
+ * for the given transfer `data`. The buffer may only be used in one
+ * multi processing of the easy handle. It MUST be returned to the
+ * multi before it can be borrowed again.
+ * Pointers into the buffer remain only valid as long as it is borrowed.
+ *
+ * @param data the easy handle
+ * @param pbuf on return, the buffer to use or NULL on error
+ * @param pbuflen on return, the size of *pbuf or 0 on error
+ * @return CURLE_OK when buffer is available and is returned.
+ * CURLE_OUT_OF_MEMORy on failure to allocate the buffer,
+ * CURLE_FAILED_INIT if the easy handle is without multi.
+ * CURLE_AGAIN if the buffer is borrowed already.
+ */
+CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data,
+ char **pbuf, size_t *pbuflen);
+/**
+ * Release the borrowed buffer. All references into the buffer become
+ * invalid after this.
+ * @param buf the buffer pointer borrowed for coding error checks.
+ */
+void Curl_multi_xfer_buf_release(struct Curl_easy *data, char *buf);
+
#endif /* HEADER_CURL_MULTIIF_H */
* The application kindly asks for a differently sized receive buffer.
* If it seems reasonable, we'll use it.
*/
- if(data->state.buffer)
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
arg = va_arg(param, long);
if(arg > READBUFFER_MAX)
{
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
- char *buf;
- size_t blen;
+ char *buf, *xfer_buf;
+ size_t blen, xfer_blen;
int maxloops = 10;
curl_off_t total_received = 0;
bool is_multiplex = FALSE;
- DEBUGASSERT(data->state.buffer);
*done = FALSE;
+ result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
+ if(result)
+ goto out;
+
/* This is where we loop until we have read everything there is to
read or we get a CURLE_AGAIN */
do {
is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
}
- buf = data->state.buffer;
- bytestoread = data->set.buffer_size;
+ buf = xfer_buf;
+ bytestoread = xfer_blen;
/* Observe any imposed speed limit */
if(bytestoread && data->set.max_recv_speed) {
}
out:
+ Curl_multi_xfer_buf_release(data, xfer_buf);
if(result)
DEBUGF(infof(data, "readwrite_data() -> %d", result));
return result;
data->state.referer = NULL;
up_free(data);
- Curl_safefree(data->state.buffer);
Curl_dyn_free(&data->state.headerb);
Curl_safefree(data->state.ulbuf);
Curl_flush_cookies(data, TRUE);
struct dynbuf headerb; /* buffer to store headers in */
struct curl_slist *hstslist; /* list of HSTS files set by
curl_easy_setopt(HSTS) calls */
- char *buffer; /* download buffer */
char *ulbuf; /* allocated upload buffer or NULL */
curl_off_t current_speed; /* the ProgressShow() function sets this,
bytes / second */