Changelog
+Daniel S (8 Jan 2008)
+- Introducing curl_easy_pause() and new magic return codes for both the read
+ and the write callbacks that now can make a connection's reading and/or
+ writing get paused.
+
Daniel S (6 Jan 2008)
- Jeff Johnson filed bug report #1863171
(http://curl.haxx.se/bug/view.cgi?id=1863171) where he pointed out that
-Curl and libcurl 7.17.2
+Curl and libcurl 7.18.0
Public curl releases: 103
Command line options: 125
curl_easy_setopt() options: 148
- Public functions in libcurl: 55
+ Public functions in libcurl: 56
Public web site mirrors: 42
Known libcurl bindings: 36
Contributors: 597
keep-alive enabled by default
o --socks4a added (proxy type CURLPROXY_SOCKS4A for libcurl)
o --socks5-hostname added (CURLPROXY_SOCKS5_HOSTNAME for libcurl)
+ o curl_easy_pause() added
This release includes the following bugfixes:
.\" * $Id$
.\" **************************************************************************
.\"
-.TH curl 1 "5 Jan 2008" "Curl 7.17.2" "Curl Manual"
+.TH curl 1 "5 Jan 2008" "Curl 7.18.0" "Curl Manual"
.SH NAME
curl \- transfer a URL
.SH SYNOPSIS
data. As described in \fI-d/--data\fP.
.IP "--data-urlencode <data>"
(HTTP) This posts data, similar to the other --data options with the exception
-that this performs URL encoding. (Added in 7.17.2)
+that this performs URL encoding. (Added in 7.18.0)
To be CGI compliant, the <data> part should begin with a \fIname\fP followed
by a separator and a content specification. The <data> part can be passed to
If this option is used several times, the last one will be used.
.IP "--socks4a <host[:port]>"
Use the specified SOCKS4a proxy. If the port number is not specified, it is
-assumed at port 1080. (Added in 7.17.2)
+assumed at port 1080. (Added in 7.18.0)
This option overrides any previous use of \fI-x/--proxy\fP, as they are
mutually exclusive.
.IP "--socks5-hostname <host[:port]>"
Use the specified SOCKS5 proxy (and let the proxy resolve the host name). If
the port number is not specified, it is assumed at port 1080. (Added in
-7.17.2)
+7.18.0)
This option overrides any previous use of \fI-x/--proxy\fP, as they are
mutually exclusive.
curl_multi_strerror.3 curl_share_strerror.3 curl_global_init_mem.3 \
libcurl-tutorial.3 curl_easy_reset.3 curl_easy_escape.3 \
curl_easy_unescape.3 curl_multi_setopt.3 curl_multi_socket.3 \
- curl_multi_timeout.3 curl_formget.3 curl_multi_assign.3
+ curl_multi_timeout.3 curl_formget.3 curl_multi_assign.3 \
+ curl_easy_pause.3
HTMLPAGES = curl_easy_cleanup.html curl_easy_getinfo.html \
curl_easy_init.html curl_easy_perform.html curl_easy_setopt.html \
curl_share_strerror.html curl_global_init_mem.html libcurl-tutorial.html \
curl_easy_reset.html curl_easy_escape.html curl_easy_unescape.html \
curl_multi_setopt.html curl_multi_socket.html curl_multi_timeout.html \
- curl_formget.html curl_multi_assign.html
+ curl_formget.html curl_multi_assign.html curl_easy_pause.html
PDFPAGES = curl_easy_cleanup.pdf curl_easy_getinfo.pdf curl_easy_init.pdf \
curl_easy_perform.pdf curl_easy_setopt.pdf curl_easy_duphandle.pdf \
curl_share_strerror.pdf curl_global_init_mem.pdf libcurl-tutorial.pdf \
curl_easy_reset.pdf curl_easy_escape.pdf curl_easy_unescape.pdf \
curl_multi_setopt.pdf curl_multi_socket.pdf curl_multi_timeout.pdf \
- curl_formget.pdf curl_multi_assign.pdf
+ curl_formget.pdf curl_multi_assign.pdf curl_easy_pause.pdf
CLEANFILES = $(HTMLPAGES) $(PDFPAGES)
--- /dev/null
+.\" $Id$
+.\"
+.TH curl_easy_pause 3 "17 Dec 2007" "libcurl 7.18.0" "libcurl Manual"
+.SH NAME
+curl_easy_pause - pause and unpause a connection
+.SH SYNOPSIS
+.B #include <curl/curl.h>
+
+.BI "CURLcode curl_easy_pause(CURL *"handle ", int "bitmask " );"
+
+.SH DESCRIPTION
+Using this function, you can explicitly mark a running connection to get
+paused, and you can unpause a connection that was previously paused.
+
+A connection can made to pause by using this function or by letting the read
+or the write callbacks return the proper magic return code
+(\fICURL_READFUNC_PAUSE\fP and \fICURL_WRITEFUNC_PAUSE\fP).
+
+NOTE: while it may feel tempting, take care and notice that you cannot call
+this function from another thread.
+
+When this function is called to unpause reading, the chance is high that you
+will get your write callback called before this function returns.
+
+The \fBhandle\fP argument is of course identifying the handle that operates on
+the connection you want to pause or unpause.
+
+The \fBbitmask\fP argument is a set of bits that sets the new state of the
+connection. The following bits can be used:
+.IP CURLPAUSE_RECV
+Pause receiving data. There will be no data received on this conneciton until
+this function is called again without this bit set. Thus, the write callback
+(\fICURLOPT_WRITEFUNCTION\fP) won't be called.
+.IP CURLPAUSE_SEND
+Pause sending data. There will be no data sent on this connection until this
+function is called again without this bit set. Thus, the read callback
+(\fICURLOPT_READFUNCTION\fP) won't be called.
+.IP CURLPAUSE_ALL
+Convenience define that pauses both directions.
+.IP CURLPAUSE_CONT
+Convenience define that unpauses both directions
+.SH RETURN VALUE
+CURLE_OK (zero) means that the option was set properly, and a non-zero return
+code means something wrong occurred after the new state was set. See the
+\fIlibcurl-errors(3)\fP man page for the full list with descriptions.
+.SH AVAILABILITY
+This function was added in libcurl 7.18.0. Before this version, there was no
+explicit support for pausing transfers.
+.SH "MEMORY USE"
+When pausing a read by returning the magic return code from a write callback,
+the read data is already in libcurl's internal buffers so it'll have to keep
+it in an allocated buffer until the reading is again unpaused using this
+function.
+
+If the downloaded data is compressed and is asked to get uncompressed
+automatially on download, libcurl will continue to uncompress the entire
+downloaded chunk and it will cache the data uncompressed. This has the side-
+effect that if you download something that is compressed a lot, it can result
+in a very large data amount needing to be allocated to save the data during
+the pause. This said, you should probably consider not using paused reading if
+you allow libcurl to uncompress data automatically.
+.SH "SEE ALSO"
+.BR curl_easy_cleanup "(3), " curl_easy_reset "(3)"
.\" * $Id$
.\" **************************************************************************
.\"
-.TH curl_easy_setopt 3 "5 Jan 2008" "libcurl 7.17.2" "libcurl Manual"
+.TH curl_easy_setopt 3 "5 Jan 2008" "libcurl 7.18.0" "libcurl Manual"
.SH NAME
curl_easy_setopt \- set options for a curl easy handle
.SH SYNOPSIS
to your function, it'll signal an error to the library and it will abort the
transfer and return \fICURLE_WRITE_ERROR\fP.
+From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will
+cause writing to this connection to become paused. See
+\fIcurl_easy_pause(3)\fP for further details.
+
This function may be called with zero bytes data if the transfered file is
empty.
operation immediately, resulting in a \fICURLE_ABORTED_BY_CALLBACK\fP error
code from the transfer (Added in 7.12.1)
+From 7.18.0, the function can return CURL_READFUNC_PAUSE which then will cause
+reading from this connection to become paused. See \fIcurl_easy_pause(3)\fP
+for further details.
+
If you set the callback pointer to NULL, or doesn't set it at all, the default
internal read function will be used. It is simply doing an fread() on the FILE
* stream set with \fICURLOPT_READDATA\fP.
.IP CURLOPT_PROXYTYPE
Pass a long with this option to set type of the proxy. Available options for
this are \fICURLPROXY_HTTP\fP, \fICURLPROXY_SOCKS4\fP (added in 7.15.2),
-\fICURLPROXY_SOCKS5\fP, \fICURLPROXY_SOCKS4A\fP (added in 7.17.2) and
-\fICURLPROXY_SOCKS5_HOSTNAME\fP (added in 7.17.2). The HTTP type is
+\fICURLPROXY_SOCKS5\fP, \fICURLPROXY_SOCKS4A\fP (added in 7.18.0) and
+\fICURLPROXY_SOCKS5_HOSTNAME\fP (added in 7.18.0). The HTTP type is
default. (Added in 7.10)
.IP CURLOPT_HTTPPROXYTUNNEL
Set the parameter to non-zero to get the library to tunnel all operations
Set the parameter to 1 to get the library to resolve the host name locally
instead of passing it to the proxy to resolve, when using a SOCKS5 proxy.
-Note that libcurl before 7.17.2 always resolved the host name locally even
-when SOCKS5 was used. (Added in 7.17.2)
+Note that libcurl before 7.18.0 always resolved the host name locally even
+when SOCKS5 was used. (Added in 7.18.0)
.IP CURLOPT_INTERFACE
Pass a char * as parameter. This set the interface name to use as outgoing
network interface. The name can be an interface name, an IP address or a host
appending ;type=a or ;type=i to the URL. Without this setting, or it being
set to 0 (zero, the default), \fICURLOPT_TRANSFERTEXT\fP has no effect when
doing FTP via a proxy. Beware that not all proxies support this feature.
-(Added in 7.17.2)
+(Added in 7.18.0)
.IP CURLOPT_CRLF
Convert Unix newlines to CRLF newlines on transfers.
.IP CURLOPT_RANGE
time for those who feel adventurous. */
#define CURL_MAX_WRITE_SIZE 16384
#endif
-
+/* This is a magic return code for the write callback that, when returned,
+ will signal libcurl to pause receving on the current transfer. */
+#define CURL_WRITEFUNC_PAUSE 0x10000001
typedef size_t (*curl_write_callback)(char *buffer,
size_t size,
size_t nitems,
/* This is a return code for the read callback that, when returned, will
signal libcurl to immediately abort the current transfer. */
#define CURL_READFUNC_ABORT 0x10000000
+/* This is a return code for the read callback that, when returned, will
+ signal libcurl to pause sending data on the current transfer. */
+#define CURL_READFUNC_PAUSE 0x10000001
typedef size_t (*curl_read_callback)(char *buffer,
size_t size,
size_t nitems,
int family;
int socktype;
int protocol;
- unsigned int addrlen; /* addrlen was a socklen_t type before 7.17.2 but it
+ unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it
turned really ugly and painful on the systems that
lack this type */
struct sockaddr addr;
CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
in 7.10 */
CURLPROXY_SOCKS5 = 5, /* added in 7.10 */
- CURLPROXY_SOCKS4A = 6, /* added in 7.17.2 */
+ CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */
CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the
host name rather than the IP address. added
- in 7.17.2 */
+ in 7.18.0 */
} curl_proxytype; /* this enum was added in 7.10 */
#define CURLAUTH_NONE 0 /* nothing */
*/
CURL_EXTERN const char *curl_share_strerror(CURLSHcode);
+/*
+ * NAME curl_easy_pause()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_pause function pauses or unpauses transfers. Select the new
+ * state by setting the bitmask, use the convenience defines below.
+ *
+ */
+CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
+
+#define CURLPAUSE_RECV (1<<0)
+#define CURLPAUSE_RECV_CONT (0)
+
+#define CURLPAUSE_SEND (1<<2)
+#define CURLPAUSE_SEND_CONT (0)
+
+#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND)
+#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT)
+
#ifdef __cplusplus
}
#endif
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "7.17.2-CVS"
+#define LIBCURL_VERSION "7.18.0-CVS"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 17
-#define LIBCURL_VERSION_PATCH 2
+#define LIBCURL_VERSION_MINOR 18
+#define LIBCURL_VERSION_PATCH 0
/* This is the numeric version of the libcurl version number, meant for easier
parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
and it is always a greater number in a more recent release. It makes
comparisons with greater than and less than work.
*/
-#define LIBCURL_VERSION_NUM 0x071102
+#define LIBCURL_VERSION_NUM 0x071200
/*
* This is the date and time when the full source package was created. The
data->set.new_directory_perms = 0755; /* Default permissions */
}
+/*
+ * curl_easy_pause() allows an application to pause or unpause a specific
+ * transfer and direction. This function sets the full new state for the
+ * current connection this easy handle operates on.
+ *
+ * NOTE: if you have the receiving paused and you call this function to remove
+ * the pausing, you may get your write callback called at this point.
+ *
+ * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
+ */
+CURLcode curl_easy_pause(CURL *curl, int action)
+{
+ struct SessionHandle *data = (struct SessionHandle *)curl;
+ struct SingleRequest *k = &data->req;
+ CURLcode result = CURLE_OK;
+
+ /* first switch off both pause bits */
+ int newstate = k->keepon &~ (KEEP_READ_PAUSE| KEEP_WRITE_PAUSE);
+
+ /* set the new desired pause bits */
+ newstate |= ((action & CURLPAUSE_RECV)?KEEP_READ_PAUSE:0) |
+ ((action & CURLPAUSE_SEND)?KEEP_WRITE_PAUSE:0);
+
+ /* put it back in the keepon */
+ k->keepon = newstate;
+
+ if(!(newstate & KEEP_READ_PAUSE) && data->state.tempwrite) {
+ /* we have a buffer for writing that we now seem to be able to deliver since
+ the receive pausing is lifted! */
+
+ /* get the pointer, type and length in local copies since the function may
+ return PAUSE again and then we'll get a new copy allocted and stored in
+ the tempwrite variables */
+ char *tempwrite = data->state.tempwrite;
+ size_t tempsize = data->state.tempwritesize;
+ int temptype = data->state.tempwritetype;
+ size_t chunklen;
+
+ /* clear tempwrite here just to make sure it gets cleared if there's no
+ further use of it, and make sure we don't clear it after the function
+ invoke as it may have been set to a new value by then */
+ data->state.tempwrite = NULL;
+
+ /* since the write callback API is define to never exceed
+ CURL_MAX_WRITE_SIZE bytes in a single call, and since we may in fact
+ have more data than that in our buffer here, we must loop sending the
+ data in multiple calls until there's no data left or we get another
+ pause returned.
+
+ A tricky part is that the function we call will "buffer" the data
+ itself when it pauses on a particular buffer, so we may need to do some
+ extra trickery if we get a pause return here.
+ */
+ do {
+ chunklen = (tempsize > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:tempsize;
+
+ result = Curl_client_write(data->state.current_conn,
+ temptype, tempwrite, chunklen);
+ if(!result)
+ /* failures abort the loop at once */
+ break;
+
+ if(data->state.tempwrite && (tempsize - chunklen)) {
+ /* Ouch, the reading is again paused and the block we send is now
+ "cached". If this is the final chunk we can leave it like this, but
+ if we have more chunks that is cached after this, we need to free
+ the newly cached one and put back a version that is truly the entire
+ contents that is saved for later
+ */
+ char *newptr;
+
+ free(data->state.tempwrite); /* free the one just cached as it isn't
+ enough */
+
+ /* note that tempsize is still the size as before the callback was
+ used, and thus the whole piece of data to keep */
+ newptr = malloc(tempsize);
+ if(!newptr) {
+ result = CURLE_OUT_OF_MEMORY;
+ /* tempwrite will be freed further down */
+ break;
+ }
+ data->state.tempwrite = newptr; /* store new pointer */
+ memcpy(newptr, tempwrite, tempsize);
+ data->state.tempwritesize = tempsize; /* store new size */
+ /* tempwrite will be freed further down */
+ break; /* go back to pausing until further notice */
+ }
+ else {
+ tempsize -= chunklen; /* left after the call above */
+ tempwrite += chunklen; /* advance the pointer */
+ }
+
+ } while((result == CURLE_OK) && tempsize);
+
+ free(tempwrite); /* this is unconditionally no longer used */
+ }
+
+ return result;
+}
+
#ifdef CURL_DOES_CONVERSIONS
/*
* Curl_convert_to_network() is an internal function
k = &easy->easy_handle->req;
if(!(k->keepon & KEEP_READ)) {
- /* We're done reading */
- easy->easy_conn->readchannel_inuse = FALSE;
+ /* We're done reading */
+ easy->easy_conn->readchannel_inuse = FALSE;
}
if(!(k->keepon & KEEP_WRITE)) {
- /* We're done writing */
- easy->easy_conn->writechannel_inuse = FALSE;
+ /* We're done writing */
+ easy->easy_conn->writechannel_inuse = FALSE;
}
if(easy->result) {
return retcode;
}
+static CURLcode pausewrite(struct SessionHandle *data,
+ int type, /* what type of data */
+ 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;
+ char *dupl = malloc(len);
+ if(!dupl)
+ return CURLE_OUT_OF_MEMORY;
+
+ memcpy(dupl, ptr, len);
+
+ /* store this information in the state struct for later use */
+ data->state.tempwrite = dupl;
+ data->state.tempwritesize = len;
+ data->state.tempwritetype = type;
+
+ /* mark the connection as RECV paused */
+ k->keepon |= KEEP_READ_PAUSE;
+
+ DEBUGF(infof(data, "Pausing with %d bytes in buffer for type %02x\n",
+ (int)len, type));
+
+ return CURLE_OK;
+}
+
+
/* client_write() sends data to the write callback(s)
The bit pattern defines to what "streams" to write to. Body and/or header.
size_t wrote;
if(data->state.cancelled) {
- /* We just suck everything into a black hole */
- return CURLE_OK;
+ /* We just suck everything into a black hole */
+ return CURLE_OK;
+ }
+
+ /* If reading is actually paused, we're forced to append this chunk of data
+ to the already held data, but only if it is the same type as otherwise it
+ can't work and it'll return error instead. */
+ if(data->req.keepon & KEEP_READ_PAUSE) {
+ size_t newlen;
+ char *newptr;
+ if(type != data->state.tempwritetype)
+ /* major internal confusion */
+ return CURLE_RECV_ERROR;
+
+ /* figure out the new size of the data to save */
+ newlen = len + data->state.tempwritesize;
+ /* allocate the new memory area */
+ newptr = malloc(newlen);
+ if(!newptr)
+ return CURLE_OUT_OF_MEMORY;
+ /* copy the previously held data to the new area */
+ memcpy(newptr, data->state.tempwrite, data->state.tempwritesize);
+ /* copy the new data to the end of the new area */
+ memcpy(newptr + data->state.tempwritesize, ptr, len);
+ /* free the old data */
+ free(data->state.tempwrite);
+ /* update the pointer and the size */
+ data->state.tempwrite = newptr;
+ data->state.tempwritesize = newlen;
+
+ return CURLE_OK;
}
if(0 == len)
wrote = len;
}
+ if(CURL_WRITEFUNC_PAUSE == wrote)
+ return pausewrite(data, type, ptr, len);
+
if(wrote != len) {
- failf (data, "Failed writing body");
+ failf(data, "Failed writing body (%d != %d)", (int)wrote, (int)len);
return CURLE_WRITE_ERROR;
}
}
regardless of the ftp transfer mode (ASCII/Image) */
wrote = writeit(ptr, 1, len, data->set.writeheader);
+ if(CURL_WRITEFUNC_PAUSE == wrote)
+ /* here we pass in the HEADER bit only since if this was body as well
+ then it was passed already and clearly that didn't trigger the pause,
+ so this is saved for later with the HEADER bit only */
+ return pausewrite(data, CLIENTWRITE_HEADER, ptr, len);
+
if(wrote != len) {
failf (data, "Failed writing header");
return CURLE_WRITE_ERROR;
failf(data, "operation aborted by callback\n");
return CURLE_ABORTED_BY_CALLBACK;
}
+ else if(nread == CURL_READFUNC_PAUSE) {
+ struct SingleRequest *k = &data->req;
+ k->keepon |= KEEP_READ_PAUSE; /* mark reading as paused */
+ return 0; /* nothing was read */
+ }
+ else if((size_t)nread > buffersize)
+ /* the read function returned a too large value */
+ return CURLE_READ_ERROR;
if(!conn->bits.forbidchunk && conn->bits.upload_chunky) {
/* if chunked Transfer-Encoding */
/* only use the proper socket if the *_HOLD bit is not set simultaneously as
then we are in rate limiting state in that transfer direction */
- if((k->keepon & (KEEP_READ|KEEP_READ_HOLD)) == KEEP_READ) {
+ if((k->keepon & KEEP_READBITS) == KEEP_READ) {
fd_read = conn->sockfd;
#if defined(USE_LIBSSH2)
if(conn->protocol & (PROT_SCP|PROT_SFTP))
} else
fd_read = CURL_SOCKET_BAD;
- if((k->keepon & (KEEP_WRITE|KEEP_WRITE_HOLD)) == KEEP_WRITE)
+ if((k->keepon & KEEP_WRITEBITS) == KEEP_WRITE)
fd_write = conn->writesockfd;
else
fd_write = CURL_SOCKET_BAD;
else
nread = 0; /* we're done uploading/reading */
- /* the signed int typecase of nread of for systems that has
- unsigned size_t */
- if(nread<=0) {
+ if(!nread && (k->keepon & KEEP_READ_PAUSE)) {
+ /* this is a paused transfer */
+ break;
+ }
+ else if(nread<=0) {
/* done */
k->keepon &= ~KEEP_WRITE; /* we're done writing */
writedone = TRUE;
}
/* Now update the "done" boolean we return */
- *done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE)));
+ *done = (bool)(0 == (k->keepon&(KEEP_READ|KEEP_WRITE|KEEP_READ_PAUSE|KEEP_WRITE_PAUSE)));
return CURLE_OK;
}
/* simple check but we might need two slots */
return GETSOCK_BLANK;
- if(data->req.keepon & KEEP_READ) {
+ /* don't include HOLD and PAUSE connections */
+ if((data->req.keepon & KEEP_READBITS) == KEEP_READ) {
DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
sock[sockindex] = conn->sockfd;
}
- if(data->req.keepon & KEEP_WRITE) {
+ /* don't include HOLD and PAUSE connections */
+ if((data->req.keepon & KEEP_WRITEBITS) == KEEP_WRITE) {
if((conn->sockfd != conn->writesockfd) ||
!(data->req.keepon & KEEP_READ)) {
k->keepon |= KEEP_READ_HOLD; /* hold it */
}
- /* The *_HOLD logic is necessary since even though there might be no
- traffic during the select interval, we still call Curl_readwrite() for
- the timeout case and if we limit transfer speed we must make sure that
- this function doesn't transfer anything while in HOLD status. */
+ /* pause logic. Don't check descriptors for paused connections */
+ if(k->keepon & KEEP_READ_PAUSE)
+ fd_read = CURL_SOCKET_BAD;
+ if(k->keepon & KEEP_WRITE_PAUSE)
+ fd_write = CURL_SOCKET_BAD;
+
+ /* The *_HOLD and *_PAUSE logic is necessary since even though there might
+ be no traffic during the select interval, we still call
+ Curl_readwrite() for the timeout case and if we limit transfer speed we
+ must make sure that this function doesn't transfer anything while in
+ HOLD status. */
switch (Curl_socket_ready(fd_read, fd_write, 1000)) {
case -1: /* select() error, stop reading */
Curl_pgrsDone(conn); /* done with the operation */
+ /* if the transfer was completed in a paused state there can be buffered
+ data left to write and then kill */
+ if(data->state.tempwrite) {
+ free(data->state.tempwrite);
+ data->state.tempwrite = NULL;
+ }
+
/* for ares-using, make sure all possible outstanding requests are properly
cancelled before we proceed */
ares_cancel(data->state.areschannel);
*/
#define KEEP_NONE 0
-#define KEEP_READ 1 /* there is or may be data to read */
-#define KEEP_WRITE 2 /* there is or may be data to write */
-#define KEEP_READ_HOLD 4 /* when set, no reading should be done but there
- might still be data to read */
-#define KEEP_WRITE_HOLD 8 /* when set, no writing should be done but there
- might still be data to write */
+#define KEEP_READ (1<<0) /* there is or may be data to read */
+#define KEEP_WRITE (1<<1) /* there is or may be data to write */
+#define KEEP_READ_HOLD (1<<2) /* when set, no reading should be done but there
+ might still be data to read */
+#define KEEP_WRITE_HOLD (1<<3) /* when set, no writing should be done but there
+ might still be data to write */
+#define KEEP_READ_PAUSE (1<<4) /* reading is paused */
+#define KEEP_WRITE_PAUSE (1<<5) /* writing is paused */
+
+#define KEEP_READBITS (KEEP_READ | KEEP_READ_HOLD | KEEP_READ_PAUSE)
+#define KEEP_WRITEBITS (KEEP_WRITE | KEEP_WRITE_HOLD | KEEP_WRITE_PAUSE)
+
#ifdef HAVE_LIBZ
typedef enum {
following not keep sending user+password... This is
strdup() data.
*/
-
struct curl_ssl_session *session; /* array of 'numsessions' size */
long sessionage; /* number of the most recent session */
-
+ char *tempwrite; /* allocated buffer to keep data in when a write
+ callback returns to make the connection paused */
+ size_t tempwritesize; /* size of the 'tempwrite' allocated buffer */
+ int tempwritetype; /* type of the 'tempwrite' buffer as a bitmask that is
+ used with Curl_client_write() */
char *scratch; /* huge buffer[BUFSIZE*2] when doing upload CRLF replacing */
bool errorbuf; /* Set to TRUE if the error buffer is already filled in.
This must be set to FALSE every time _easy_perform() is