1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 08:08:50 -05:00

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.
This commit is contained in:
Daniel Stenberg 2008-01-08 14:52:05 +00:00
parent 5e1c9e90d9
commit de23b98522
14 changed files with 358 additions and 51 deletions

View File

@ -6,6 +6,11 @@
Changelog 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) Daniel S (6 Jan 2008)
- Jeff Johnson filed bug report #1863171 - Jeff Johnson filed bug report #1863171
(http://curl.haxx.se/bug/view.cgi?id=1863171) where he pointed out that (http://curl.haxx.se/bug/view.cgi?id=1863171) where he pointed out that

View File

@ -1,9 +1,9 @@
Curl and libcurl 7.17.2 Curl and libcurl 7.18.0
Public curl releases: 103 Public curl releases: 103
Command line options: 125 Command line options: 125
curl_easy_setopt() options: 148 curl_easy_setopt() options: 148
Public functions in libcurl: 55 Public functions in libcurl: 56
Public web site mirrors: 42 Public web site mirrors: 42
Known libcurl bindings: 36 Known libcurl bindings: 36
Contributors: 597 Contributors: 597
@ -16,6 +16,7 @@ This release includes the following changes:
keep-alive enabled by default keep-alive enabled by default
o --socks4a added (proxy type CURLPROXY_SOCKS4A for libcurl) o --socks4a added (proxy type CURLPROXY_SOCKS4A for libcurl)
o --socks5-hostname added (CURLPROXY_SOCKS5_HOSTNAME for libcurl) o --socks5-hostname added (CURLPROXY_SOCKS5_HOSTNAME for libcurl)
o curl_easy_pause() added
This release includes the following bugfixes: This release includes the following bugfixes:

View File

@ -21,7 +21,7 @@
.\" * $Id$ .\" * $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 .SH NAME
curl \- transfer a URL curl \- transfer a URL
.SH SYNOPSIS .SH SYNOPSIS
@ -256,7 +256,7 @@ If this option is used several times, the ones following the first will append
data. As described in \fI-d/--data\fP. data. As described in \fI-d/--data\fP.
.IP "--data-urlencode <data>" .IP "--data-urlencode <data>"
(HTTP) This posts data, similar to the other --data options with the exception (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 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 by a separator and a content specification. The <data> part can be passed to
@ -1077,7 +1077,7 @@ mutually exclusive.
If this option is used several times, the last one will be used. If this option is used several times, the last one will be used.
.IP "--socks4a <host[:port]>" .IP "--socks4a <host[:port]>"
Use the specified SOCKS4a proxy. If the port number is not specified, it is 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 This option overrides any previous use of \fI-x/--proxy\fP, as they are
mutually exclusive. mutually exclusive.
@ -1086,7 +1086,7 @@ If this option is used several times, the last one will be used.
.IP "--socks5-hostname <host[:port]>" .IP "--socks5-hostname <host[:port]>"
Use the specified SOCKS5 proxy (and let the proxy resolve the host name). If 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 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 This option overrides any previous use of \fI-x/--proxy\fP, as they are
mutually exclusive. mutually exclusive.

View File

@ -18,7 +18,8 @@ man_MANS = curl_easy_cleanup.3 curl_easy_getinfo.3 curl_easy_init.3 \
curl_multi_strerror.3 curl_share_strerror.3 curl_global_init_mem.3 \ curl_multi_strerror.3 curl_share_strerror.3 curl_global_init_mem.3 \
libcurl-tutorial.3 curl_easy_reset.3 curl_easy_escape.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_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 \ HTMLPAGES = curl_easy_cleanup.html curl_easy_getinfo.html \
curl_easy_init.html curl_easy_perform.html curl_easy_setopt.html \ curl_easy_init.html curl_easy_perform.html curl_easy_setopt.html \
@ -36,7 +37,7 @@ HTMLPAGES = curl_easy_cleanup.html curl_easy_getinfo.html \
curl_share_strerror.html curl_global_init_mem.html libcurl-tutorial.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_easy_reset.html curl_easy_escape.html curl_easy_unescape.html \
curl_multi_setopt.html curl_multi_socket.html curl_multi_timeout.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 \ 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_easy_perform.pdf curl_easy_setopt.pdf curl_easy_duphandle.pdf \
@ -53,7 +54,7 @@ PDFPAGES = curl_easy_cleanup.pdf curl_easy_getinfo.pdf curl_easy_init.pdf \
curl_share_strerror.pdf curl_global_init_mem.pdf libcurl-tutorial.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_easy_reset.pdf curl_easy_escape.pdf curl_easy_unescape.pdf \
curl_multi_setopt.pdf curl_multi_socket.pdf curl_multi_timeout.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) CLEANFILES = $(HTMLPAGES) $(PDFPAGES)

View File

@ -0,0 +1,63 @@
.\" $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)"

View File

@ -21,7 +21,7 @@
.\" * $Id$ .\" * $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 .SH NAME
curl_easy_setopt \- set options for a curl easy handle curl_easy_setopt \- set options for a curl easy handle
.SH SYNOPSIS .SH SYNOPSIS
@ -95,6 +95,10 @@ of bytes actually taken care of. If that amount differs from the amount passed
to your function, it'll signal an error to the library and it will abort the to your function, it'll signal an error to the library and it will abort the
transfer and return \fICURLE_WRITE_ERROR\fP. 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 This function may be called with zero bytes data if the transfered file is
empty. empty.
@ -142,6 +146,10 @@ The read callback may return \fICURL_READFUNC_ABORT\fP to stop the current
operation immediately, resulting in a \fICURLE_ABORTED_BY_CALLBACK\fP error operation immediately, resulting in a \fICURLE_ABORTED_BY_CALLBACK\fP error
code from the transfer (Added in 7.12.1) 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 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 internal read function will be used. It is simply doing an fread() on the FILE
* stream set with \fICURLOPT_READDATA\fP. * stream set with \fICURLOPT_READDATA\fP.
@ -431,8 +439,8 @@ specified in the proxy string \fICURLOPT_PROXY\fP.
.IP CURLOPT_PROXYTYPE .IP CURLOPT_PROXYTYPE
Pass a long with this option to set type of the proxy. Available options for 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), 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\fP, \fICURLPROXY_SOCKS4A\fP (added in 7.18.0) and
\fICURLPROXY_SOCKS5_HOSTNAME\fP (added in 7.17.2). The HTTP type is \fICURLPROXY_SOCKS5_HOSTNAME\fP (added in 7.18.0). The HTTP type is
default. (Added in 7.10) default. (Added in 7.10)
.IP CURLOPT_HTTPPROXYTUNNEL .IP CURLOPT_HTTPPROXYTUNNEL
Set the parameter to non-zero to get the library to tunnel all operations Set the parameter to non-zero to get the library to tunnel all operations
@ -443,8 +451,8 @@ don't want this tunneling option.
Set the parameter to 1 to get the library to resolve the host name locally 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. 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 Note that libcurl before 7.18.0 always resolved the host name locally even
when SOCKS5 was used. (Added in 7.17.2) when SOCKS5 was used. (Added in 7.18.0)
.IP CURLOPT_INTERFACE .IP CURLOPT_INTERFACE
Pass a char * as parameter. This set the interface name to use as outgoing 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 network interface. The name can be an interface name, an IP address or a host
@ -1080,7 +1088,7 @@ transfer mode (binary or ASCII) for FTP transfers done via an HTTP proxy, by
appending ;type=a or ;type=i to the URL. Without this setting, or it being 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 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. 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 .IP CURLOPT_CRLF
Convert Unix newlines to CRLF newlines on transfers. Convert Unix newlines to CRLF newlines on transfers.
.IP CURLOPT_RANGE .IP CURLOPT_RANGE

View File

@ -230,7 +230,9 @@ typedef int (*curl_progress_callback)(void *clientp,
time for those who feel adventurous. */ time for those who feel adventurous. */
#define CURL_MAX_WRITE_SIZE 16384 #define CURL_MAX_WRITE_SIZE 16384
#endif #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, typedef size_t (*curl_write_callback)(char *buffer,
size_t size, size_t size,
size_t nitems, size_t nitems,
@ -239,6 +241,9 @@ typedef size_t (*curl_write_callback)(char *buffer,
/* This is a return code for the read callback that, when returned, will /* This is a return code for the read callback that, when returned, will
signal libcurl to immediately abort the current transfer. */ signal libcurl to immediately abort the current transfer. */
#define CURL_READFUNC_ABORT 0x10000000 #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, typedef size_t (*curl_read_callback)(char *buffer,
size_t size, size_t size,
size_t nitems, size_t nitems,
@ -257,7 +262,7 @@ struct curl_sockaddr {
int family; int family;
int socktype; int socktype;
int protocol; 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 turned really ugly and painful on the systems that
lack this type */ lack this type */
struct sockaddr addr; struct sockaddr addr;
@ -499,10 +504,10 @@ typedef enum {
CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already
in 7.10 */ in 7.10 */
CURLPROXY_SOCKS5 = 5, /* added 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 CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the
host name rather than the IP address. added 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 */ } curl_proxytype; /* this enum was added in 7.10 */
#define CURLAUTH_NONE 0 /* nothing */ #define CURLAUTH_NONE 0 /* nothing */
@ -1749,6 +1754,26 @@ CURL_EXTERN const char *curl_easy_strerror(CURLcode);
*/ */
CURL_EXTERN const char *curl_share_strerror(CURLSHcode); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___ * | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____| * \___|\___/|_| \_\_____|
* *
* 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 * This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms * you should have received as part of this distribution. The terms
@ -28,13 +28,13 @@
/* This is the version number of the libcurl package from which this header /* This is the version number of the libcurl package from which this header
file origins: */ 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 /* The numeric version number is also available "in parts" by using these
defines: */ defines: */
#define LIBCURL_VERSION_MAJOR 7 #define LIBCURL_VERSION_MAJOR 7
#define LIBCURL_VERSION_MINOR 17 #define LIBCURL_VERSION_MINOR 18
#define LIBCURL_VERSION_PATCH 2 #define LIBCURL_VERSION_PATCH 0
/* This is the numeric version of the libcurl version number, meant for easier /* This is the numeric version of the libcurl version number, meant for easier
parsing and comparions by programs. The LIBCURL_VERSION_NUM define will parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
@ -51,7 +51,7 @@
and it is always a greater number in a more recent release. It makes and it is always a greater number in a more recent release. It makes
comparisons with greater than and less than work. 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 * This is the date and time when the full source package was created. The

View File

@ -744,6 +744,107 @@ void curl_easy_reset(CURL *curl)
data->set.new_directory_perms = 0755; /* Default permissions */ 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 #ifdef CURL_DOES_CONVERSIONS
/* /*
* Curl_convert_to_network() is an internal function * Curl_convert_to_network() is an internal function

View File

@ -1255,13 +1255,13 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
k = &easy->easy_handle->req; k = &easy->easy_handle->req;
if(!(k->keepon & KEEP_READ)) { if(!(k->keepon & KEEP_READ)) {
/* We're done reading */ /* We're done reading */
easy->easy_conn->readchannel_inuse = FALSE; easy->easy_conn->readchannel_inuse = FALSE;
} }
if(!(k->keepon & KEEP_WRITE)) { if(!(k->keepon & KEEP_WRITE)) {
/* We're done writing */ /* We're done writing */
easy->easy_conn->writechannel_inuse = FALSE; easy->easy_conn->writechannel_inuse = FALSE;
} }
if(easy->result) { if(easy->result) {

View File

@ -376,6 +376,36 @@ CURLcode Curl_write(struct connectdata *conn,
return retcode; 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) /* client_write() sends data to the write callback(s)
The bit pattern defines to what "streams" to write to. Body and/or header. The bit pattern defines to what "streams" to write to. Body and/or header.
@ -390,8 +420,37 @@ CURLcode Curl_client_write(struct connectdata *conn,
size_t wrote; size_t wrote;
if(data->state.cancelled) { if(data->state.cancelled) {
/* We just suck everything into a black hole */ /* We just suck everything into a black hole */
return CURLE_OK; 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) if(0 == len)
@ -422,8 +481,11 @@ CURLcode Curl_client_write(struct connectdata *conn,
wrote = len; wrote = len;
} }
if(CURL_WRITEFUNC_PAUSE == wrote)
return pausewrite(data, type, ptr, len);
if(wrote != len) { if(wrote != len) {
failf (data, "Failed writing body"); failf(data, "Failed writing body (%d != %d)", (int)wrote, (int)len);
return CURLE_WRITE_ERROR; return CURLE_WRITE_ERROR;
} }
} }
@ -441,6 +503,12 @@ CURLcode Curl_client_write(struct connectdata *conn,
regardless of the ftp transfer mode (ASCII/Image) */ regardless of the ftp transfer mode (ASCII/Image) */
wrote = writeit(ptr, 1, len, data->set.writeheader); 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) { if(wrote != len) {
failf (data, "Failed writing header"); failf (data, "Failed writing header");
return CURLE_WRITE_ERROR; return CURLE_WRITE_ERROR;

View File

@ -134,6 +134,14 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
failf(data, "operation aborted by callback\n"); failf(data, "operation aborted by callback\n");
return CURLE_ABORTED_BY_CALLBACK; 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(!conn->bits.forbidchunk && conn->bits.upload_chunky) {
/* if chunked Transfer-Encoding */ /* if chunked Transfer-Encoding */
@ -330,7 +338,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
/* only use the proper socket if the *_HOLD bit is not set simultaneously as /* 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 */ 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; fd_read = conn->sockfd;
#if defined(USE_LIBSSH2) #if defined(USE_LIBSSH2)
if(conn->protocol & (PROT_SCP|PROT_SFTP)) if(conn->protocol & (PROT_SCP|PROT_SFTP))
@ -339,7 +347,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
} else } else
fd_read = CURL_SOCKET_BAD; 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; fd_write = conn->writesockfd;
else else
fd_write = CURL_SOCKET_BAD; fd_write = CURL_SOCKET_BAD;
@ -1425,9 +1433,11 @@ CURLcode Curl_readwrite(struct connectdata *conn,
else else
nread = 0; /* we're done uploading/reading */ nread = 0; /* we're done uploading/reading */
/* the signed int typecase of nread of for systems that has if(!nread && (k->keepon & KEEP_READ_PAUSE)) {
unsigned size_t */ /* this is a paused transfer */
if(nread<=0) { break;
}
else if(nread<=0) {
/* done */ /* done */
k->keepon &= ~KEEP_WRITE; /* we're done writing */ k->keepon &= ~KEEP_WRITE; /* we're done writing */
writedone = TRUE; writedone = TRUE;
@ -1635,7 +1645,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
} }
/* Now update the "done" boolean we return */ /* 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; return CURLE_OK;
} }
@ -1660,7 +1670,8 @@ int Curl_single_getsock(const struct connectdata *conn,
/* simple check but we might need two slots */ /* simple check but we might need two slots */
return GETSOCK_BLANK; 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); DEBUGASSERT(conn->sockfd != CURL_SOCKET_BAD);
@ -1668,7 +1679,8 @@ int Curl_single_getsock(const struct connectdata *conn,
sock[sockindex] = conn->sockfd; 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) || if((conn->sockfd != conn->writesockfd) ||
!(data->req.keepon & KEEP_READ)) { !(data->req.keepon & KEEP_READ)) {
@ -1751,10 +1763,17 @@ Transfer(struct connectdata *conn)
k->keepon |= KEEP_READ_HOLD; /* hold it */ k->keepon |= KEEP_READ_HOLD; /* hold it */
} }
/* The *_HOLD logic is necessary since even though there might be no /* pause logic. Don't check descriptors for paused connections */
traffic during the select interval, we still call Curl_readwrite() for if(k->keepon & KEEP_READ_PAUSE)
the timeout case and if we limit transfer speed we must make sure that fd_read = CURL_SOCKET_BAD;
this function doesn't transfer anything while in HOLD status. */ 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)) { switch (Curl_socket_ready(fd_read, fd_write, 1000)) {
case -1: /* select() error, stop reading */ case -1: /* select() error, stop reading */

View File

@ -4433,6 +4433,13 @@ CURLcode Curl_done(struct connectdata **connp,
Curl_pgrsDone(conn); /* done with the operation */ 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 /* for ares-using, make sure all possible outstanding requests are properly
cancelled before we proceed */ cancelled before we proceed */
ares_cancel(data->state.areschannel); ares_cancel(data->state.areschannel);

View File

@ -629,12 +629,18 @@ struct hostname {
*/ */
#define KEEP_NONE 0 #define KEEP_NONE 0
#define KEEP_READ 1 /* there is or may be data to read */ #define KEEP_READ (1<<0) /* there is or may be data to read */
#define KEEP_WRITE 2 /* there is or may be data to write */ #define KEEP_WRITE (1<<1) /* there is or may be data to write */
#define KEEP_READ_HOLD 4 /* when set, no reading should be done but there #define KEEP_READ_HOLD (1<<2) /* when set, no reading should be done but there
might still be data to read */ might still be data to read */
#define KEEP_WRITE_HOLD 8 /* when set, no writing should be done but there #define KEEP_WRITE_HOLD (1<<3) /* when set, no writing should be done but there
might still be data to write */ 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 #ifdef HAVE_LIBZ
typedef enum { typedef enum {
@ -1126,10 +1132,13 @@ struct UrlState {
following not keep sending user+password... This is following not keep sending user+password... This is
strdup() data. strdup() data.
*/ */
struct curl_ssl_session *session; /* array of 'numsessions' size */ struct curl_ssl_session *session; /* array of 'numsessions' size */
long sessionage; /* number of the most recent session */ 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 */ char *scratch; /* huge buffer[BUFSIZE*2] when doing upload CRLF replacing */
bool errorbuf; /* Set to TRUE if the error buffer is already filled in. bool errorbuf; /* Set to TRUE if the error buffer is already filled in.
This must be set to FALSE every time _easy_perform() is This must be set to FALSE every time _easy_perform() is