transfer: Curl_sendrecv() and event related improvements
- Renames Curl_readwrite() to Curl_sendrecv() to reflect that it is mainly about talking to the server, not reads or writes to the client. Add a `nowp` parameter since the single caller already has this. - Curl_sendrecv() now runs all possible operations whenever it is called and either it had been polling sockets or the 'select_bits' are set. POLL_IN/POLL_OUT are not always directly related to send/recv operations. Filters like HTTP/2, QUIC or TLS may monitor reverse directions. If a transfer does not want to send (KEEP_SEND), it will not do so, as before. Same for receives. - Curl_update_timer() now checks the absolute timestamp of an expiry and the last/new timeout to determine if the application needs to stop/start/restart its timer. This fixes edge cases where updates did not happen as they should have. - improved --test-event curl_easy_perform() simulation to handle situations where no sockets are registered but a timeout is in place. - fixed bug in events_socket() that complained about removing a socket that was unknown, when indeed it had removed the socket just before, only it was the last in the list - fixed conncache's internal handle to carry the multi instance (where the cache has one) so that operations on the closure handle trigger event callbacks correctly. - fixed conncache to not POLL_REMOVE a socket twice when a conneciton was closed. Closes #14561
This commit is contained in:
parent
432f2fd9ac
commit
a58b50fca6
@ -404,6 +404,9 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
|
|||||||
static int socket_close(struct Curl_easy *data, struct connectdata *conn,
|
static int socket_close(struct Curl_easy *data, struct connectdata *conn,
|
||||||
int use_callback, curl_socket_t sock)
|
int use_callback, curl_socket_t sock)
|
||||||
{
|
{
|
||||||
|
if(CURL_SOCKET_BAD == sock)
|
||||||
|
return 0;
|
||||||
|
|
||||||
if(use_callback && conn && conn->fclosesocket) {
|
if(use_callback && conn && conn->fclosesocket) {
|
||||||
int rc;
|
int rc;
|
||||||
Curl_multi_closed(data, sock);
|
Curl_multi_closed(data, sock);
|
||||||
|
|||||||
@ -131,6 +131,12 @@ int Curl_conncache_init(struct conncache *connc,
|
|||||||
if(!connc->closure_handle)
|
if(!connc->closure_handle)
|
||||||
return 1; /* bad */
|
return 1; /* bad */
|
||||||
connc->closure_handle->state.internal = true;
|
connc->closure_handle->state.internal = true;
|
||||||
|
/* TODO: this is quirky. We need an internal handle for certain
|
||||||
|
* operations, but we do not add it to the multi (if there is one).
|
||||||
|
* But we give it the multi so that socket event operations can work.
|
||||||
|
* Probably better to have an internal handle owned by the multi that
|
||||||
|
* can be used for conncache operations. */
|
||||||
|
connc->closure_handle->multi = multi;
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
if(getenv("CURL_DEBUG"))
|
if(getenv("CURL_DEBUG"))
|
||||||
connc->closure_handle->set.verbose = true;
|
connc->closure_handle->set.verbose = true;
|
||||||
@ -146,6 +152,10 @@ int Curl_conncache_init(struct conncache *connc,
|
|||||||
void Curl_conncache_destroy(struct conncache *connc)
|
void Curl_conncache_destroy(struct conncache *connc)
|
||||||
{
|
{
|
||||||
if(connc) {
|
if(connc) {
|
||||||
|
if(connc->closure_handle) {
|
||||||
|
connc->closure_handle->multi = NULL;
|
||||||
|
Curl_close(&connc->closure_handle);
|
||||||
|
}
|
||||||
Curl_hash_destroy(&connc->hash);
|
Curl_hash_destroy(&connc->hash);
|
||||||
connc->multi = NULL;
|
connc->multi = NULL;
|
||||||
}
|
}
|
||||||
@ -611,7 +621,8 @@ static void connc_close_all(struct conncache *connc)
|
|||||||
|
|
||||||
sigpipe_apply(data, &pipe_st);
|
sigpipe_apply(data, &pipe_st);
|
||||||
Curl_hostcache_clean(data, data->dns.hostcache);
|
Curl_hostcache_clean(data, data->dns.hostcache);
|
||||||
Curl_close(&data);
|
connc->closure_handle->multi = NULL;
|
||||||
|
Curl_close(&connc->closure_handle);
|
||||||
sigpipe_restore(&pipe_st);
|
sigpipe_restore(&pipe_st);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -980,14 +991,6 @@ static void connc_disconnect(struct Curl_easy *data,
|
|||||||
|
|
||||||
Curl_attach_connection(data, conn);
|
Curl_attach_connection(data, conn);
|
||||||
|
|
||||||
if(connc && connc->multi && connc->multi->socket_cb) {
|
|
||||||
struct easy_pollset ps;
|
|
||||||
/* With an empty pollset, all previously polled sockets will be removed
|
|
||||||
* via the multi_socket API callback. */
|
|
||||||
memset(&ps, 0, sizeof(ps));
|
|
||||||
(void)Curl_multi_pollset_ev(connc->multi, data, &ps, &conn->shutdown_poll);
|
|
||||||
}
|
|
||||||
|
|
||||||
connc_run_conn_shutdown_handler(data, conn);
|
connc_run_conn_shutdown_handler(data, conn);
|
||||||
if(do_shutdown) {
|
if(do_shutdown) {
|
||||||
/* Make a last attempt to shutdown handlers and filters, if
|
/* Make a last attempt to shutdown handlers and filters, if
|
||||||
|
|||||||
73
lib/easy.c
73
lib/easy.c
@ -391,25 +391,22 @@ struct events {
|
|||||||
int running_handles; /* store the returned number */
|
int running_handles; /* store the returned number */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define DEBUG_EV_POLL 0
|
||||||
|
|
||||||
/* events_timer
|
/* events_timer
|
||||||
*
|
*
|
||||||
* Callback that gets called with a new value when the timeout should be
|
* Callback that gets called with a new value when the timeout should be
|
||||||
* updated.
|
* updated.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int events_timer(struct Curl_multi *multi, /* multi handle */
|
static int events_timer(struct Curl_multi *multi, /* multi handle */
|
||||||
long timeout_ms, /* see above */
|
long timeout_ms, /* see above */
|
||||||
void *userp) /* private callback pointer */
|
void *userp) /* private callback pointer */
|
||||||
{
|
{
|
||||||
struct events *ev = userp;
|
struct events *ev = userp;
|
||||||
(void)multi;
|
(void)multi;
|
||||||
if(timeout_ms == -1)
|
#if DEBUG_EV_POLL
|
||||||
/* timeout removed */
|
fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms);
|
||||||
timeout_ms = 0;
|
#endif
|
||||||
else if(timeout_ms == 0)
|
|
||||||
/* timeout is already reached! */
|
|
||||||
timeout_ms = 1; /* trigger asap */
|
|
||||||
|
|
||||||
ev->ms = timeout_ms;
|
ev->ms = timeout_ms;
|
||||||
ev->msbump = TRUE;
|
ev->msbump = TRUE;
|
||||||
return 0;
|
return 0;
|
||||||
@ -463,6 +460,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
|
|||||||
struct events *ev = userp;
|
struct events *ev = userp;
|
||||||
struct socketmonitor *m;
|
struct socketmonitor *m;
|
||||||
struct socketmonitor *prev = NULL;
|
struct socketmonitor *prev = NULL;
|
||||||
|
bool found = FALSE;
|
||||||
|
|
||||||
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
|
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
|
||||||
(void) easy;
|
(void) easy;
|
||||||
@ -472,7 +470,7 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
|
|||||||
m = ev->list;
|
m = ev->list;
|
||||||
while(m) {
|
while(m) {
|
||||||
if(m->socket.fd == s) {
|
if(m->socket.fd == s) {
|
||||||
|
found = TRUE;
|
||||||
if(what == CURL_POLL_REMOVE) {
|
if(what == CURL_POLL_REMOVE) {
|
||||||
struct socketmonitor *nxt = m->next;
|
struct socketmonitor *nxt = m->next;
|
||||||
/* remove this node from the list of monitored sockets */
|
/* remove this node from the list of monitored sockets */
|
||||||
@ -481,7 +479,6 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
|
|||||||
else
|
else
|
||||||
ev->list = nxt;
|
ev->list = nxt;
|
||||||
free(m);
|
free(m);
|
||||||
m = nxt;
|
|
||||||
infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
|
infof(easy, "socket cb: socket %" CURL_FORMAT_SOCKET_T
|
||||||
" REMOVED", s);
|
" REMOVED", s);
|
||||||
}
|
}
|
||||||
@ -499,12 +496,13 @@ static int events_socket(struct Curl_easy *easy, /* easy handle */
|
|||||||
prev = m;
|
prev = m;
|
||||||
m = m->next; /* move to next node */
|
m = m->next; /* move to next node */
|
||||||
}
|
}
|
||||||
if(!m) {
|
|
||||||
|
if(!found) {
|
||||||
if(what == CURL_POLL_REMOVE) {
|
if(what == CURL_POLL_REMOVE) {
|
||||||
/* this happens a bit too often, libcurl fix perhaps? */
|
/* should not happen if our logic is correct, but is no drama. */
|
||||||
/* fprintf(stderr,
|
DEBUGF(infof(easy, "socket cb: asked to REMOVE socket %"
|
||||||
"%s: socket %d asked to be REMOVED but not present!\n",
|
CURL_FORMAT_SOCKET_T "but not present!", s));
|
||||||
__func__, s); */
|
DEBUGASSERT(0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
m = malloc(sizeof(struct socketmonitor));
|
m = malloc(sizeof(struct socketmonitor));
|
||||||
@ -565,14 +563,15 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
|
|||||||
int pollrc;
|
int pollrc;
|
||||||
int i;
|
int i;
|
||||||
struct curltime before;
|
struct curltime before;
|
||||||
struct curltime after;
|
|
||||||
|
|
||||||
/* populate the fds[] array */
|
/* populate the fds[] array */
|
||||||
for(m = ev->list, f = &fds[0]; m; m = m->next) {
|
for(m = ev->list, f = &fds[0]; m; m = m->next) {
|
||||||
f->fd = m->socket.fd;
|
f->fd = m->socket.fd;
|
||||||
f->events = m->socket.events;
|
f->events = m->socket.events;
|
||||||
f->revents = 0;
|
f->revents = 0;
|
||||||
/* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */
|
#if DEBUG_EV_POLL
|
||||||
|
fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
|
||||||
|
#endif
|
||||||
f++;
|
f++;
|
||||||
numfds++;
|
numfds++;
|
||||||
}
|
}
|
||||||
@ -580,12 +579,27 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
|
|||||||
/* get the time stamp to use to figure out how long poll takes */
|
/* get the time stamp to use to figure out how long poll takes */
|
||||||
before = Curl_now();
|
before = Curl_now();
|
||||||
|
|
||||||
/* wait for activity or timeout */
|
if(numfds) {
|
||||||
pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms);
|
/* wait for activity or timeout */
|
||||||
if(pollrc < 0)
|
#if DEBUG_EV_POLL
|
||||||
return CURLE_UNRECOVERABLE_POLL;
|
fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms);
|
||||||
|
#endif
|
||||||
after = Curl_now();
|
pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms);
|
||||||
|
#if DEBUG_EV_POLL
|
||||||
|
fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n",
|
||||||
|
numfds, ev->ms, pollrc);
|
||||||
|
#endif
|
||||||
|
if(pollrc < 0)
|
||||||
|
return CURLE_UNRECOVERABLE_POLL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#if DEBUG_EV_POLL
|
||||||
|
fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms);
|
||||||
|
#endif
|
||||||
|
pollrc = 0;
|
||||||
|
if(ev->ms > 0)
|
||||||
|
Curl_wait_ms(ev->ms);
|
||||||
|
}
|
||||||
|
|
||||||
ev->msbump = FALSE; /* reset here */
|
ev->msbump = FALSE; /* reset here */
|
||||||
|
|
||||||
@ -618,12 +632,17 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!ev->msbump) {
|
|
||||||
|
if(!ev->msbump && ev->ms >= 0) {
|
||||||
/* If nothing updated the timeout, we decrease it by the spent time.
|
/* If nothing updated the timeout, we decrease it by the spent time.
|
||||||
* If it was updated, it has the new timeout time stored already.
|
* If it was updated, it has the new timeout time stored already.
|
||||||
*/
|
*/
|
||||||
timediff_t timediff = Curl_timediff(after, before);
|
timediff_t timediff = Curl_timediff(Curl_now(), before);
|
||||||
if(timediff > 0) {
|
if(timediff > 0) {
|
||||||
|
#if DEBUG_EV_POLL
|
||||||
|
fprintf(stderr, "poll timeout %ldms not updated, decrease by "
|
||||||
|
"time spent %ldms\n", ev->ms, (long)timediff);
|
||||||
|
#endif
|
||||||
if(timediff > ev->ms)
|
if(timediff > ev->ms)
|
||||||
ev->ms = 0;
|
ev->ms = 0;
|
||||||
else
|
else
|
||||||
@ -656,7 +675,7 @@ static CURLcode easy_events(struct Curl_multi *multi)
|
|||||||
{
|
{
|
||||||
/* this struct is made static to allow it to be used after this function
|
/* this struct is made static to allow it to be used after this function
|
||||||
returns and curl_multi_remove_handle() is called */
|
returns and curl_multi_remove_handle() is called */
|
||||||
static struct events evs = {2, FALSE, 0, NULL, 0};
|
static struct events evs = {-1, FALSE, 0, NULL, 0};
|
||||||
|
|
||||||
/* if running event-based, do some further multi inits */
|
/* if running event-based, do some further multi inits */
|
||||||
events_setup(multi, &evs);
|
events_setup(multi, &evs);
|
||||||
@ -1121,7 +1140,7 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
|
|||||||
(data->mstate == MSTATE_PERFORMING ||
|
(data->mstate == MSTATE_PERFORMING ||
|
||||||
data->mstate == MSTATE_RATELIMITING));
|
data->mstate == MSTATE_RATELIMITING));
|
||||||
/* Unpausing writes is detected on the next run in
|
/* Unpausing writes is detected on the next run in
|
||||||
* transfer.c:Curl_readwrite(). This is because this may result
|
* transfer.c:Curl_sendrecv(). This is because this may result
|
||||||
* in a transfer error if the application's callbacks fail */
|
* in a transfer error if the application's callbacks fail */
|
||||||
|
|
||||||
/* Set the new keepon state, so it takes effect no matter what error
|
/* Set the new keepon state, so it takes effect no matter what error
|
||||||
|
|||||||
@ -4441,7 +4441,8 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
|
|||||||
}
|
}
|
||||||
/* We are now waiting for a reply from the server or
|
/* We are now waiting for a reply from the server or
|
||||||
* a timeout on our side IFF the request has been fully sent. */
|
* a timeout on our side IFF the request has been fully sent. */
|
||||||
DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE"));
|
DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
|
||||||
|
"timeout %ldms", data->set.expect_100_timeout));
|
||||||
ctx->state = EXP100_AWAITING_CONTINUE;
|
ctx->state = EXP100_AWAITING_CONTINUE;
|
||||||
ctx->start = Curl_now();
|
ctx->start = Curl_now();
|
||||||
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
|
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
|
||||||
|
|||||||
380
lib/multi.c
380
lib/multi.c
@ -94,9 +94,12 @@ static CURLMcode add_next_timeout(struct curltime now,
|
|||||||
struct Curl_multi *multi,
|
struct Curl_multi *multi,
|
||||||
struct Curl_easy *d);
|
struct Curl_easy *d);
|
||||||
static CURLMcode multi_timeout(struct Curl_multi *multi,
|
static CURLMcode multi_timeout(struct Curl_multi *multi,
|
||||||
|
struct curltime *expire_time,
|
||||||
long *timeout_ms);
|
long *timeout_ms);
|
||||||
static void process_pending_handles(struct Curl_multi *multi);
|
static void process_pending_handles(struct Curl_multi *multi);
|
||||||
static void multi_xfer_bufs_free(struct Curl_multi *multi);
|
static void multi_xfer_bufs_free(struct Curl_multi *multi);
|
||||||
|
static void Curl_expire_ex(struct Curl_easy *data, const struct curltime *nowp,
|
||||||
|
timediff_t milli, expire_id id);
|
||||||
|
|
||||||
#ifdef DEBUGBUILD
|
#ifdef DEBUGBUILD
|
||||||
static const char * const multi_statename[]={
|
static const char * const multi_statename[]={
|
||||||
@ -418,6 +421,7 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
|
|||||||
|
|
||||||
multi->multiplexing = TRUE;
|
multi->multiplexing = TRUE;
|
||||||
multi->max_concurrent_streams = 100;
|
multi->max_concurrent_streams = 100;
|
||||||
|
multi->last_timeout_ms = -1;
|
||||||
|
|
||||||
#ifdef USE_WINSOCK
|
#ifdef USE_WINSOCK
|
||||||
multi->wsa_event = WSACreateEvent();
|
multi->wsa_event = WSACreateEvent();
|
||||||
@ -527,18 +531,6 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
|
|||||||
happen. */
|
happen. */
|
||||||
Curl_expire(data, 0, EXPIRE_RUN_NOW);
|
Curl_expire(data, 0, EXPIRE_RUN_NOW);
|
||||||
|
|
||||||
/* A somewhat crude work-around for a little glitch in Curl_update_timer()
|
|
||||||
that happens if the lastcall time is set to the same time when the handle
|
|
||||||
is removed as when the next handle is added, as then the check in
|
|
||||||
Curl_update_timer() that prevents calling the application multiple times
|
|
||||||
with the same timer info will not trigger and then the new handle's
|
|
||||||
timeout will not be notified to the app.
|
|
||||||
|
|
||||||
The work-around is thus simply to clear the 'lastcall' variable to force
|
|
||||||
Curl_update_timer() to always trigger a callback to the app when a new
|
|
||||||
easy handle is added */
|
|
||||||
memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
|
|
||||||
|
|
||||||
rc = Curl_update_timer(multi);
|
rc = Curl_update_timer(multi);
|
||||||
if(rc) {
|
if(rc) {
|
||||||
data->multi = NULL; /* not anymore */
|
data->multi = NULL; /* not anymore */
|
||||||
@ -799,6 +791,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
|
|||||||
bool premature;
|
bool premature;
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
CURLMcode rc;
|
CURLMcode rc;
|
||||||
|
bool removed_timer = FALSE;
|
||||||
|
|
||||||
/* First, make some basic checks that the CURLM handle is a good handle */
|
/* First, make some basic checks that the CURLM handle is a good handle */
|
||||||
if(!GOOD_MULTI_HANDLE(multi))
|
if(!GOOD_MULTI_HANDLE(multi))
|
||||||
@ -849,7 +842,7 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
|
|||||||
/* The timer must be shut down before data->multi is set to NULL, else the
|
/* The timer must be shut down before data->multi is set to NULL, else the
|
||||||
timenode will remain in the splay tree after curl_easy_cleanup is
|
timenode will remain in the splay tree after curl_easy_cleanup is
|
||||||
called. Do it after multi_done() in case that sets another time! */
|
called. Do it after multi_done() in case that sets another time! */
|
||||||
Curl_expire_clear(data);
|
removed_timer = Curl_expire_clear(data);
|
||||||
|
|
||||||
/* the handle is in a list, remove it from whichever it is */
|
/* the handle is in a list, remove it from whichever it is */
|
||||||
Curl_node_remove(&data->multi_queue);
|
Curl_node_remove(&data->multi_queue);
|
||||||
@ -930,9 +923,11 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
|
|||||||
|
|
||||||
process_pending_handles(multi);
|
process_pending_handles(multi);
|
||||||
|
|
||||||
rc = Curl_update_timer(multi);
|
if(removed_timer) {
|
||||||
if(rc)
|
rc = Curl_update_timer(multi);
|
||||||
return rc;
|
if(rc)
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1167,7 +1162,6 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
|
|||||||
Some easy handles may not have connected to the remote host yet,
|
Some easy handles may not have connected to the remote host yet,
|
||||||
and then we must make sure that is done. */
|
and then we must make sure that is done. */
|
||||||
int this_max_fd = -1;
|
int this_max_fd = -1;
|
||||||
struct easy_pollset ps;
|
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
(void)exc_fd_set; /* not used */
|
(void)exc_fd_set; /* not used */
|
||||||
|
|
||||||
@ -1177,23 +1171,22 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
|
|||||||
if(multi->in_callback)
|
if(multi->in_callback)
|
||||||
return CURLM_RECURSIVE_API_CALL;
|
return CURLM_RECURSIVE_API_CALL;
|
||||||
|
|
||||||
memset(&ps, 0, sizeof(ps));
|
|
||||||
for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
|
for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
|
||||||
struct Curl_easy *data = Curl_node_elem(e);
|
struct Curl_easy *data = Curl_node_elem(e);
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
multi_getsock(data, &ps);
|
multi_getsock(data, &data->last_poll);
|
||||||
|
|
||||||
for(i = 0; i < ps.num; i++) {
|
for(i = 0; i < data->last_poll.num; i++) {
|
||||||
if(!FDSET_SOCK(ps.sockets[i]))
|
if(!FDSET_SOCK(data->last_poll.sockets[i]))
|
||||||
/* pretend it does not exist */
|
/* pretend it does not exist */
|
||||||
continue;
|
continue;
|
||||||
if(ps.actions[i] & CURL_POLL_IN)
|
if(data->last_poll.actions[i] & CURL_POLL_IN)
|
||||||
FD_SET(ps.sockets[i], read_fd_set);
|
FD_SET(data->last_poll.sockets[i], read_fd_set);
|
||||||
if(ps.actions[i] & CURL_POLL_OUT)
|
if(data->last_poll.actions[i] & CURL_POLL_OUT)
|
||||||
FD_SET(ps.sockets[i], write_fd_set);
|
FD_SET(data->last_poll.sockets[i], write_fd_set);
|
||||||
if((int)ps.sockets[i] > this_max_fd)
|
if((int)data->last_poll.sockets[i] > this_max_fd)
|
||||||
this_max_fd = (int)ps.sockets[i];
|
this_max_fd = (int)data->last_poll.sockets[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1208,7 +1201,6 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
|
|||||||
unsigned int *fd_count)
|
unsigned int *fd_count)
|
||||||
{
|
{
|
||||||
struct curl_waitfds cwfds;
|
struct curl_waitfds cwfds;
|
||||||
struct easy_pollset ps;
|
|
||||||
CURLMcode result = CURLM_OK;
|
CURLMcode result = CURLM_OK;
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
|
|
||||||
@ -1222,11 +1214,10 @@ CURLMcode curl_multi_waitfds(struct Curl_multi *multi,
|
|||||||
return CURLM_RECURSIVE_API_CALL;
|
return CURLM_RECURSIVE_API_CALL;
|
||||||
|
|
||||||
Curl_waitfds_init(&cwfds, ufds, size);
|
Curl_waitfds_init(&cwfds, ufds, size);
|
||||||
memset(&ps, 0, sizeof(ps));
|
|
||||||
for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
|
for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
|
||||||
struct Curl_easy *data = Curl_node_elem(e);
|
struct Curl_easy *data = Curl_node_elem(e);
|
||||||
multi_getsock(data, &ps);
|
multi_getsock(data, &data->last_poll);
|
||||||
if(Curl_waitfds_add_ps(&cwfds, &ps)) {
|
if(Curl_waitfds_add_ps(&cwfds, &data->last_poll)) {
|
||||||
result = CURLM_OUT_OF_MEMORY;
|
result = CURLM_OUT_OF_MEMORY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1269,8 +1260,8 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
|||||||
bool extrawait, /* when no socket, wait */
|
bool extrawait, /* when no socket, wait */
|
||||||
bool use_wakeup)
|
bool use_wakeup)
|
||||||
{
|
{
|
||||||
struct easy_pollset ps;
|
|
||||||
size_t i;
|
size_t i;
|
||||||
|
struct curltime expire_time;
|
||||||
long timeout_internal;
|
long timeout_internal;
|
||||||
int retcode = 0;
|
int retcode = 0;
|
||||||
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
|
||||||
@ -1297,14 +1288,13 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
|||||||
return CURLM_BAD_FUNCTION_ARGUMENT;
|
return CURLM_BAD_FUNCTION_ARGUMENT;
|
||||||
|
|
||||||
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
|
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
|
||||||
memset(&ps, 0, sizeof(ps));
|
|
||||||
|
|
||||||
/* Add the curl handles to our pollfds first */
|
/* Add the curl handles to our pollfds first */
|
||||||
for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
|
for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
|
||||||
struct Curl_easy *data = Curl_node_elem(e);
|
struct Curl_easy *data = Curl_node_elem(e);
|
||||||
|
|
||||||
multi_getsock(data, &ps);
|
multi_getsock(data, &data->last_poll);
|
||||||
if(Curl_pollfds_add_ps(&cpfds, &ps)) {
|
if(Curl_pollfds_add_ps(&cpfds, &data->last_poll)) {
|
||||||
result = CURLM_OUT_OF_MEMORY;
|
result = CURLM_OUT_OF_MEMORY;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -1367,7 +1357,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
|||||||
* poll. Collecting the sockets may install new timers by protocols
|
* poll. Collecting the sockets may install new timers by protocols
|
||||||
* and connection filters.
|
* and connection filters.
|
||||||
* Use the shorter one of the internal and the caller requested timeout. */
|
* Use the shorter one of the internal and the caller requested timeout. */
|
||||||
(void)multi_timeout(multi, &timeout_internal);
|
(void)multi_timeout(multi, &expire_time, &timeout_internal);
|
||||||
if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
|
if((timeout_internal >= 0) && (timeout_internal < (long)timeout_ms))
|
||||||
timeout_ms = (int)timeout_internal;
|
timeout_ms = (int)timeout_internal;
|
||||||
|
|
||||||
@ -1443,16 +1433,15 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
|
|||||||
for(e = Curl_llist_head(&multi->process); e && !result;
|
for(e = Curl_llist_head(&multi->process); e && !result;
|
||||||
e = Curl_node_next(e)) {
|
e = Curl_node_next(e)) {
|
||||||
struct Curl_easy *data = Curl_node_elem(e);
|
struct Curl_easy *data = Curl_node_elem(e);
|
||||||
multi_getsock(data, &ps);
|
|
||||||
|
|
||||||
for(i = 0; i < ps.num; i++) {
|
for(i = 0; i < data->last_poll.num; i++) {
|
||||||
wsa_events.lNetworkEvents = 0;
|
wsa_events.lNetworkEvents = 0;
|
||||||
if(WSAEnumNetworkEvents(ps.sockets[i], NULL,
|
if(WSAEnumNetworkEvents(data->last_poll.sockets[i], NULL,
|
||||||
&wsa_events) == 0) {
|
&wsa_events) == 0) {
|
||||||
if(ret && !pollrc && wsa_events.lNetworkEvents)
|
if(ret && !pollrc && wsa_events.lNetworkEvents)
|
||||||
retcode++;
|
retcode++;
|
||||||
}
|
}
|
||||||
WSAEventSelect(ps.sockets[i], multi->wsa_event, 0);
|
WSAEventSelect(data->last_poll.sockets[i], multi->wsa_event, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2397,7 +2386,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* read/write data if it is ready to do so */
|
/* read/write data if it is ready to do so */
|
||||||
result = Curl_readwrite(data);
|
result = Curl_sendrecv(data, nowp);
|
||||||
|
|
||||||
if(data->req.done || (result == CURLE_RECV_ERROR)) {
|
if(data->req.done || (result == CURLE_RECV_ERROR)) {
|
||||||
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
|
/* If CURLE_RECV_ERROR happens early enough, we assume it was a race
|
||||||
@ -3064,11 +3053,15 @@ void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
|
|||||||
if(data) {
|
if(data) {
|
||||||
/* if there is still an easy handle associated with this connection */
|
/* if there is still an easy handle associated with this connection */
|
||||||
struct Curl_multi *multi = data->multi;
|
struct Curl_multi *multi = data->multi;
|
||||||
|
DEBUGF(infof(data, "Curl_multi_closed, fd=%" CURL_FORMAT_SOCKET_T
|
||||||
|
" multi is %p", s, (void *)multi));
|
||||||
if(multi) {
|
if(multi) {
|
||||||
/* this is set if this connection is part of a handle that is added to
|
/* this is set if this connection is part of a handle that is added to
|
||||||
a multi handle, and only then this is necessary */
|
a multi handle, and only then this is necessary */
|
||||||
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
|
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
|
||||||
|
|
||||||
|
DEBUGF(infof(data, "Curl_multi_closed, fd=%" CURL_FORMAT_SOCKET_T
|
||||||
|
" entry is %p", s, (void *)entry));
|
||||||
if(entry) {
|
if(entry) {
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
if(multi->socket_cb) {
|
if(multi->socket_cb) {
|
||||||
@ -3145,6 +3138,59 @@ static CURLMcode add_next_timeout(struct curltime now,
|
|||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct multi_run_ctx {
|
||||||
|
struct Curl_multi *multi;
|
||||||
|
struct curltime now;
|
||||||
|
size_t run_xfers;
|
||||||
|
SIGPIPE_MEMBER(pipe_st);
|
||||||
|
bool run_conn_cache;
|
||||||
|
};
|
||||||
|
|
||||||
|
static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
|
||||||
|
{
|
||||||
|
struct Curl_multi *multi = mrc->multi;
|
||||||
|
struct Curl_easy *data = NULL;
|
||||||
|
struct Curl_tree *t = NULL;
|
||||||
|
CURLMcode result = CURLM_OK;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The loop following here will go on as long as there are expire-times left
|
||||||
|
* to process (compared to mrc->now) in the splay and 'data' will be
|
||||||
|
* re-assigned for every expired handle we deal with.
|
||||||
|
*/
|
||||||
|
while(1) {
|
||||||
|
/* Check if there is one (more) expired timer to deal with! This function
|
||||||
|
extracts a matching node if there is one */
|
||||||
|
multi->timetree = Curl_splaygetbest(mrc->now, multi->timetree, &t);
|
||||||
|
if(!t)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
data = Curl_splayget(t); /* assign this for next loop */
|
||||||
|
if(!data)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
(void)add_next_timeout(mrc->now, multi, data);
|
||||||
|
if(data == multi->conn_cache.closure_handle) {
|
||||||
|
mrc->run_conn_cache = TRUE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mrc->run_xfers++;
|
||||||
|
sigpipe_apply(data, &mrc->pipe_st);
|
||||||
|
result = multi_runsingle(multi, &mrc->now, data);
|
||||||
|
|
||||||
|
if(CURLM_OK >= result) {
|
||||||
|
/* get the socket(s) and check if the state has been changed since
|
||||||
|
last */
|
||||||
|
result = singlesocket(multi, data);
|
||||||
|
if(result)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
return result;
|
||||||
|
}
|
||||||
static CURLMcode multi_socket(struct Curl_multi *multi,
|
static CURLMcode multi_socket(struct Curl_multi *multi,
|
||||||
bool checkall,
|
bool checkall,
|
||||||
curl_socket_t s,
|
curl_socket_t s,
|
||||||
@ -3153,10 +3199,13 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
{
|
{
|
||||||
CURLMcode result = CURLM_OK;
|
CURLMcode result = CURLM_OK;
|
||||||
struct Curl_easy *data = NULL;
|
struct Curl_easy *data = NULL;
|
||||||
struct Curl_tree *t = NULL;
|
struct multi_run_ctx mrc;
|
||||||
struct curltime now = Curl_now();
|
|
||||||
bool run_conn_cache = FALSE;
|
(void)ev_bitmask;
|
||||||
SIGPIPE_VARIABLE(pipe_st);
|
memset(&mrc, 0, sizeof(mrc));
|
||||||
|
mrc.multi = multi;
|
||||||
|
mrc.now = Curl_now();
|
||||||
|
sigpipe_init(&mrc.pipe_st);
|
||||||
|
|
||||||
if(checkall) {
|
if(checkall) {
|
||||||
struct Curl_llist_node *e;
|
struct Curl_llist_node *e;
|
||||||
@ -3171,10 +3220,10 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
result = singlesocket(multi, Curl_node_elem(e));
|
result = singlesocket(multi, Curl_node_elem(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mrc.run_conn_cache = TRUE;
|
||||||
/* or should we fall-through and do the timer-based stuff? */
|
goto out;
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(s != CURL_SOCKET_TIMEOUT) {
|
if(s != CURL_SOCKET_TIMEOUT) {
|
||||||
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
|
struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
|
||||||
|
|
||||||
@ -3201,76 +3250,42 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
|
|||||||
DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
|
DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
|
||||||
|
|
||||||
if(data == multi->conn_cache.closure_handle)
|
if(data == multi->conn_cache.closure_handle)
|
||||||
run_conn_cache = TRUE;
|
mrc.run_conn_cache = TRUE;
|
||||||
else {
|
else {
|
||||||
if(data->conn && !(data->conn->handler->flags & PROTOPT_DIRLOCK))
|
/* Expire with out current now, so we will get it below when
|
||||||
/* set socket event bitmask if they are not locked */
|
* asking the splaytree for expired transfers. */
|
||||||
data->state.select_bits |= (unsigned char)ev_bitmask;
|
Curl_expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW);
|
||||||
|
|
||||||
Curl_expire(data, 0, EXPIRE_RUN_NOW);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now we fall-through and do the timer-based stuff, since we do not want
|
|
||||||
to force the user to have to deal with timeouts as long as at least
|
|
||||||
one connection in fact has traffic. */
|
|
||||||
|
|
||||||
now = Curl_now(); /* get a newer time since the multi_runsingle() loop
|
|
||||||
may have taken some time */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
/* Asked to run due to time-out. Clear the 'lastcall' variable to force
|
result = multi_run_expired(&mrc);
|
||||||
Curl_update_timer() to trigger a callback to the app again even if the
|
if(result)
|
||||||
same timeout is still the one to run after this call. That handles the
|
goto out;
|
||||||
case when the application asks libcurl to run the timeout
|
|
||||||
prematurely. */
|
if(mrc.run_xfers) {
|
||||||
memset(&multi->timer_lastcall, 0, sizeof(multi->timer_lastcall));
|
/* Running transfers takes time. With a new timestamp, we might catch
|
||||||
|
* other expires which are due now. Instead of telling the application
|
||||||
|
* to set a 0 timeout and call us again, we run them here.
|
||||||
|
* Do that only once or it might be unfair to transfers on other
|
||||||
|
* sockets. */
|
||||||
|
mrc.now = Curl_now();
|
||||||
|
result = multi_run_expired(&mrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
out:
|
||||||
* The loop following here will go on as long as there are expire-times left
|
if(mrc.run_conn_cache) {
|
||||||
* to process in the splay and 'data' will be re-assigned for every expired
|
sigpipe_apply(multi->conn_cache.closure_handle, &mrc.pipe_st);
|
||||||
* handle we deal with.
|
|
||||||
*/
|
|
||||||
sigpipe_init(&pipe_st);
|
|
||||||
do {
|
|
||||||
if(data == multi->conn_cache.closure_handle)
|
|
||||||
run_conn_cache = TRUE;
|
|
||||||
/* the first loop lap 'data' can be NULL */
|
|
||||||
else if(data) {
|
|
||||||
sigpipe_apply(data, &pipe_st);
|
|
||||||
result = multi_runsingle(multi, &now, data);
|
|
||||||
|
|
||||||
if(CURLM_OK >= result) {
|
|
||||||
/* get the socket(s) and check if the state has been changed since
|
|
||||||
last */
|
|
||||||
result = singlesocket(multi, data);
|
|
||||||
if(result)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if there is one (more) expired timer to deal with! This function
|
|
||||||
extracts a matching node if there is one */
|
|
||||||
|
|
||||||
multi->timetree = Curl_splaygetbest(now, multi->timetree, &t);
|
|
||||||
if(t) {
|
|
||||||
data = Curl_splayget(t); /* assign this for next loop */
|
|
||||||
(void)add_next_timeout(now, multi, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
} while(t);
|
|
||||||
|
|
||||||
if(run_conn_cache) {
|
|
||||||
sigpipe_apply(multi->conn_cache.closure_handle, &pipe_st);
|
|
||||||
Curl_conncache_multi_perform(multi);
|
Curl_conncache_multi_perform(multi);
|
||||||
}
|
}
|
||||||
|
sigpipe_restore(&mrc.pipe_st);
|
||||||
sigpipe_restore(&pipe_st);
|
|
||||||
|
|
||||||
if(running_handles)
|
if(running_handles)
|
||||||
*running_handles = (int)multi->num_alive;
|
*running_handles = (int)multi->num_alive;
|
||||||
|
|
||||||
|
if(CURLM_OK >= result)
|
||||||
|
result = Curl_update_timer(multi);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3359,39 +3374,28 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
|
|||||||
CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
|
CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
|
||||||
int *running_handles)
|
int *running_handles)
|
||||||
{
|
{
|
||||||
CURLMcode result;
|
|
||||||
if(multi->in_callback)
|
if(multi->in_callback)
|
||||||
return CURLM_RECURSIVE_API_CALL;
|
return CURLM_RECURSIVE_API_CALL;
|
||||||
result = multi_socket(multi, FALSE, s, 0, running_handles);
|
return multi_socket(multi, FALSE, s, 0, running_handles);
|
||||||
if(CURLM_OK >= result)
|
|
||||||
result = Curl_update_timer(multi);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
|
CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
|
||||||
int ev_bitmask, int *running_handles)
|
int ev_bitmask, int *running_handles)
|
||||||
{
|
{
|
||||||
CURLMcode result;
|
|
||||||
if(multi->in_callback)
|
if(multi->in_callback)
|
||||||
return CURLM_RECURSIVE_API_CALL;
|
return CURLM_RECURSIVE_API_CALL;
|
||||||
result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
|
return multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
|
||||||
if(CURLM_OK >= result)
|
|
||||||
result = Curl_update_timer(multi);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles)
|
CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles)
|
||||||
{
|
{
|
||||||
CURLMcode result;
|
|
||||||
if(multi->in_callback)
|
if(multi->in_callback)
|
||||||
return CURLM_RECURSIVE_API_CALL;
|
return CURLM_RECURSIVE_API_CALL;
|
||||||
result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
|
return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
|
||||||
if(CURLM_OK >= result)
|
|
||||||
result = Curl_update_timer(multi);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static CURLMcode multi_timeout(struct Curl_multi *multi,
|
static CURLMcode multi_timeout(struct Curl_multi *multi,
|
||||||
|
struct curltime *expire_time,
|
||||||
long *timeout_ms)
|
long *timeout_ms)
|
||||||
{
|
{
|
||||||
static const struct curltime tv_zero = {0, 0};
|
static const struct curltime tv_zero = {0, 0};
|
||||||
@ -3407,6 +3411,9 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
|
|||||||
|
|
||||||
/* splay the lowest to the bottom */
|
/* splay the lowest to the bottom */
|
||||||
multi->timetree = Curl_splay(tv_zero, multi->timetree);
|
multi->timetree = Curl_splay(tv_zero, multi->timetree);
|
||||||
|
/* this will not return NULL from a non-emtpy tree, but some compilers
|
||||||
|
* are not convinced of that. Analyzers are hard. */
|
||||||
|
*expire_time = multi->timetree? multi->timetree->key : tv_zero;
|
||||||
|
|
||||||
/* 'multi->timetree' will be non-NULL here but the compilers sometimes
|
/* 'multi->timetree' will be non-NULL here but the compilers sometimes
|
||||||
yell at us if we assume so */
|
yell at us if we assume so */
|
||||||
@ -3418,12 +3425,15 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
|
|||||||
overly long timeouts */
|
overly long timeouts */
|
||||||
*timeout_ms = (long)diff;
|
*timeout_ms = (long)diff;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
/* 0 means immediately */
|
/* 0 means immediately */
|
||||||
*timeout_ms = 0;
|
*timeout_ms = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
|
*expire_time = tv_zero;
|
||||||
*timeout_ms = -1;
|
*timeout_ms = -1;
|
||||||
|
}
|
||||||
|
|
||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
@ -3431,6 +3441,8 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
|
|||||||
CURLMcode curl_multi_timeout(struct Curl_multi *multi,
|
CURLMcode curl_multi_timeout(struct Curl_multi *multi,
|
||||||
long *timeout_ms)
|
long *timeout_ms)
|
||||||
{
|
{
|
||||||
|
struct curltime expire_time;
|
||||||
|
|
||||||
/* First, make some basic checks that the CURLM handle is a good handle */
|
/* First, make some basic checks that the CURLM handle is a good handle */
|
||||||
if(!GOOD_MULTI_HANDLE(multi))
|
if(!GOOD_MULTI_HANDLE(multi))
|
||||||
return CURLM_BAD_HANDLE;
|
return CURLM_BAD_HANDLE;
|
||||||
@ -3438,56 +3450,79 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi,
|
|||||||
if(multi->in_callback)
|
if(multi->in_callback)
|
||||||
return CURLM_RECURSIVE_API_CALL;
|
return CURLM_RECURSIVE_API_CALL;
|
||||||
|
|
||||||
return multi_timeout(multi, timeout_ms);
|
return multi_timeout(multi, &expire_time, timeout_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define DEBUG_UPDATE_TIMER 0
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tell the application it should update its timers, if it subscribes to the
|
* Tell the application it should update its timers, if it subscribes to the
|
||||||
* update timer callback.
|
* update timer callback.
|
||||||
*/
|
*/
|
||||||
CURLMcode Curl_update_timer(struct Curl_multi *multi)
|
CURLMcode Curl_update_timer(struct Curl_multi *multi)
|
||||||
{
|
{
|
||||||
|
struct curltime expire_ts;
|
||||||
long timeout_ms;
|
long timeout_ms;
|
||||||
int rc;
|
int rc;
|
||||||
|
bool set_value = FALSE;
|
||||||
|
|
||||||
if(!multi->timer_cb || multi->dead)
|
if(!multi->timer_cb || multi->dead)
|
||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
if(multi_timeout(multi, &timeout_ms)) {
|
if(multi_timeout(multi, &expire_ts, &timeout_ms)) {
|
||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
if(timeout_ms < 0) {
|
|
||||||
static const struct curltime none = {0, 0};
|
if(timeout_ms < 0 && multi->last_timeout_ms < 0) {
|
||||||
if(Curl_timediff_us(none, multi->timer_lastcall)) {
|
#if DEBUG_UPDATE_TIMER
|
||||||
multi->timer_lastcall = none;
|
fprintf(stderr, "Curl_update_timer(), still no timeout, no change\n");
|
||||||
/* there is no timeout now but there was one previously, tell the app to
|
#endif
|
||||||
disable it */
|
}
|
||||||
set_in_callback(multi, TRUE);
|
else if(timeout_ms < 0) {
|
||||||
rc = multi->timer_cb(multi, -1, multi->timer_userp);
|
/* there is no timeout now but there was one previously */
|
||||||
set_in_callback(multi, FALSE);
|
#if DEBUG_UPDATE_TIMER
|
||||||
if(rc == -1) {
|
fprintf(stderr, "Curl_update_timer(), remove timeout, "
|
||||||
multi->dead = TRUE;
|
" last_timeout=%ldms\n", multi->last_timeout_ms);
|
||||||
return CURLM_ABORTED_BY_CALLBACK;
|
#endif
|
||||||
}
|
timeout_ms = -1; /* normalize */
|
||||||
return CURLM_OK;
|
set_value = TRUE;
|
||||||
|
}
|
||||||
|
else if(multi->last_timeout_ms < 0) {
|
||||||
|
#if DEBUG_UPDATE_TIMER
|
||||||
|
fprintf(stderr, "Curl_update_timer(), had no timeout, set now\n");
|
||||||
|
#endif
|
||||||
|
set_value = TRUE;
|
||||||
|
}
|
||||||
|
else if(Curl_timediff_us(multi->last_expire_ts, expire_ts)) {
|
||||||
|
/* We had a timeout before and have one now, the absolute timestamp
|
||||||
|
* differs. The relative timeout_ms may be the same, but the starting
|
||||||
|
* point differs. Let the application restart its timer. */
|
||||||
|
#if DEBUG_UPDATE_TIMER
|
||||||
|
fprintf(stderr, "Curl_update_timer(), expire timestamp changed\n");
|
||||||
|
#endif
|
||||||
|
set_value = TRUE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* We have same expire time as previously. Our relative 'timeout_ms'
|
||||||
|
* may be different now, but the application has the timer running
|
||||||
|
* and we do not to tell it to start this again. */
|
||||||
|
#if DEBUG_UPDATE_TIMER
|
||||||
|
fprintf(stderr, "Curl_update_timer(), same expire timestamp, no change\n");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if(set_value) {
|
||||||
|
#if DEBUG_UPDATE_TIMER
|
||||||
|
fprintf(stderr, "Curl_update_timer(), set timeout %ldms\n", timeout_ms);
|
||||||
|
#endif
|
||||||
|
multi->last_expire_ts = expire_ts;
|
||||||
|
multi->last_timeout_ms = timeout_ms;
|
||||||
|
set_in_callback(multi, TRUE);
|
||||||
|
rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp);
|
||||||
|
set_in_callback(multi, FALSE);
|
||||||
|
if(rc == -1) {
|
||||||
|
multi->dead = TRUE;
|
||||||
|
return CURLM_ABORTED_BY_CALLBACK;
|
||||||
}
|
}
|
||||||
return CURLM_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* When multi_timeout() is done, multi->timetree points to the node with the
|
|
||||||
* timeout we got the (relative) time-out time for. We can thus easily check
|
|
||||||
* if this is the same (fixed) time as we got in a previous call and then
|
|
||||||
* avoid calling the callback again. */
|
|
||||||
if(Curl_timediff_us(multi->timetree->key, multi->timer_lastcall) == 0)
|
|
||||||
return CURLM_OK;
|
|
||||||
|
|
||||||
multi->timer_lastcall = multi->timetree->key;
|
|
||||||
|
|
||||||
set_in_callback(multi, TRUE);
|
|
||||||
rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp);
|
|
||||||
set_in_callback(multi, FALSE);
|
|
||||||
if(rc == -1) {
|
|
||||||
multi->dead = TRUE;
|
|
||||||
return CURLM_ABORTED_BY_CALLBACK;
|
|
||||||
}
|
}
|
||||||
return CURLM_OK;
|
return CURLM_OK;
|
||||||
}
|
}
|
||||||
@ -3566,10 +3601,12 @@ multi_addtimeout(struct Curl_easy *data,
|
|||||||
*
|
*
|
||||||
* Expire replaces a former timeout using the same id if already set.
|
* Expire replaces a former timeout using the same id if already set.
|
||||||
*/
|
*/
|
||||||
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
|
static void Curl_expire_ex(struct Curl_easy *data,
|
||||||
|
const struct curltime *nowp,
|
||||||
|
timediff_t milli, expire_id id)
|
||||||
{
|
{
|
||||||
struct Curl_multi *multi = data->multi;
|
struct Curl_multi *multi = data->multi;
|
||||||
struct curltime *nowp = &data->state.expiretime;
|
struct curltime *curr_expire = &data->state.expiretime;
|
||||||
struct curltime set;
|
struct curltime set;
|
||||||
|
|
||||||
/* this is only interesting while there is still an associated multi struct
|
/* this is only interesting while there is still an associated multi struct
|
||||||
@ -3579,7 +3616,7 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
|
|||||||
|
|
||||||
DEBUGASSERT(id < EXPIRE_LAST);
|
DEBUGASSERT(id < EXPIRE_LAST);
|
||||||
|
|
||||||
set = Curl_now();
|
set = *nowp;
|
||||||
set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */
|
set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bits conversion */
|
||||||
set.tv_usec += (int)(milli%1000)*1000;
|
set.tv_usec += (int)(milli%1000)*1000;
|
||||||
|
|
||||||
@ -3595,11 +3632,11 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
|
|||||||
in case we need to recompute the minimum timer later. */
|
in case we need to recompute the minimum timer later. */
|
||||||
multi_addtimeout(data, &set, id);
|
multi_addtimeout(data, &set, id);
|
||||||
|
|
||||||
if(nowp->tv_sec || nowp->tv_usec) {
|
if(curr_expire->tv_sec || curr_expire->tv_usec) {
|
||||||
/* This means that the struct is added as a node in the splay tree.
|
/* This means that the struct is added as a node in the splay tree.
|
||||||
Compare if the new time is earlier, and only remove-old/add-new if it
|
Compare if the new time is earlier, and only remove-old/add-new if it
|
||||||
is. */
|
is. */
|
||||||
timediff_t diff = Curl_timediff(set, *nowp);
|
timediff_t diff = Curl_timediff(set, *curr_expire);
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if(diff > 0) {
|
if(diff > 0) {
|
||||||
@ -3618,12 +3655,18 @@ void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
|
|||||||
|
|
||||||
/* Indicate that we are in the splay tree and insert the new timer expiry
|
/* Indicate that we are in the splay tree and insert the new timer expiry
|
||||||
value since it is our local minimum. */
|
value since it is our local minimum. */
|
||||||
*nowp = set;
|
*curr_expire = set;
|
||||||
Curl_splayset(&data->state.timenode, data);
|
Curl_splayset(&data->state.timenode, data);
|
||||||
multi->timetree = Curl_splayinsert(*nowp, multi->timetree,
|
multi->timetree = Curl_splayinsert(*curr_expire, multi->timetree,
|
||||||
&data->state.timenode);
|
&data->state.timenode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
|
||||||
|
{
|
||||||
|
struct curltime now = Curl_now();
|
||||||
|
Curl_expire_ex(data, &now, milli, id);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_expire_done()
|
* Curl_expire_done()
|
||||||
*
|
*
|
||||||
@ -3641,7 +3684,7 @@ void Curl_expire_done(struct Curl_easy *data, expire_id id)
|
|||||||
*
|
*
|
||||||
* Clear ALL timeout values for this handle.
|
* Clear ALL timeout values for this handle.
|
||||||
*/
|
*/
|
||||||
void Curl_expire_clear(struct Curl_easy *data)
|
bool Curl_expire_clear(struct Curl_easy *data)
|
||||||
{
|
{
|
||||||
struct Curl_multi *multi = data->multi;
|
struct Curl_multi *multi = data->multi;
|
||||||
struct curltime *nowp = &data->state.expiretime;
|
struct curltime *nowp = &data->state.expiretime;
|
||||||
@ -3649,7 +3692,7 @@ void Curl_expire_clear(struct Curl_easy *data)
|
|||||||
/* this is only interesting while there is still an associated multi struct
|
/* this is only interesting while there is still an associated multi struct
|
||||||
remaining! */
|
remaining! */
|
||||||
if(!multi)
|
if(!multi)
|
||||||
return;
|
return FALSE;
|
||||||
|
|
||||||
if(nowp->tv_sec || nowp->tv_usec) {
|
if(nowp->tv_sec || nowp->tv_usec) {
|
||||||
/* Since this is an cleared time, we must remove the previous entry from
|
/* Since this is an cleared time, we must remove the previous entry from
|
||||||
@ -3670,12 +3713,11 @@ void Curl_expire_clear(struct Curl_easy *data)
|
|||||||
#endif
|
#endif
|
||||||
nowp->tv_sec = 0;
|
nowp->tv_sec = 0;
|
||||||
nowp->tv_usec = 0;
|
nowp->tv_usec = 0;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
|
CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
|
||||||
void *hashp)
|
void *hashp)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -151,8 +151,9 @@ struct Curl_multi {
|
|||||||
/* timer callback and user data pointer for the *socket() API */
|
/* timer callback and user data pointer for the *socket() API */
|
||||||
curl_multi_timer_callback timer_cb;
|
curl_multi_timer_callback timer_cb;
|
||||||
void *timer_userp;
|
void *timer_userp;
|
||||||
struct curltime timer_lastcall; /* the fixed time for the timeout for the
|
long last_timeout_ms; /* the last timeout value set via timer_cb */
|
||||||
previous callback */
|
struct curltime last_expire_ts; /* timestamp of last expiry */
|
||||||
|
|
||||||
#ifdef USE_WINSOCK
|
#ifdef USE_WINSOCK
|
||||||
WSAEVENT wsa_event; /* Winsock event used for waits */
|
WSAEVENT wsa_event; /* Winsock event used for waits */
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
CURLcode Curl_updatesocket(struct Curl_easy *data);
|
CURLcode Curl_updatesocket(struct Curl_easy *data);
|
||||||
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
|
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
|
||||||
void Curl_expire_clear(struct Curl_easy *data);
|
bool Curl_expire_clear(struct Curl_easy *data);
|
||||||
void Curl_expire_done(struct Curl_easy *data, expire_id id);
|
void Curl_expire_done(struct Curl_easy *data, expire_id id);
|
||||||
CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
|
CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
|
||||||
void Curl_attach_connection(struct Curl_easy *data,
|
void Curl_attach_connection(struct Curl_easy *data,
|
||||||
|
|||||||
@ -35,6 +35,7 @@ struct sigpipe_ignore {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
|
#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
|
||||||
|
#define SIGPIPE_MEMBER(x) struct sigpipe_ignore x
|
||||||
|
|
||||||
static void sigpipe_init(struct sigpipe_ignore *ig)
|
static void sigpipe_init(struct sigpipe_ignore *ig)
|
||||||
{
|
{
|
||||||
@ -92,6 +93,7 @@ static void sigpipe_apply(struct Curl_easy *data,
|
|||||||
#define sigpipe_init(x) Curl_nop_stmt
|
#define sigpipe_init(x) Curl_nop_stmt
|
||||||
#define sigpipe_restore(x) Curl_nop_stmt
|
#define sigpipe_restore(x) Curl_nop_stmt
|
||||||
#define SIGPIPE_VARIABLE(x)
|
#define SIGPIPE_VARIABLE(x)
|
||||||
|
#define SIGPIPE_MEMBER(x) bool x
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* HEADER_CURL_SIGPIPE_H */
|
#endif /* HEADER_CURL_SIGPIPE_H */
|
||||||
|
|||||||
106
lib/transfer.c
106
lib/transfer.c
@ -196,18 +196,6 @@ CURLcode Curl_xfer_send_shutdown(struct Curl_easy *data, bool *done)
|
|||||||
return Curl_conn_shutdown(data, sockindex, done);
|
return Curl_conn_shutdown(data, sockindex, done);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool xfer_send_shutdown_started(struct Curl_easy *data)
|
|
||||||
{
|
|
||||||
int sockindex;
|
|
||||||
|
|
||||||
if(!data || !data->conn)
|
|
||||||
return CURLE_FAILED_INIT;
|
|
||||||
if(data->conn->writesockfd == CURL_SOCKET_BAD)
|
|
||||||
return CURLE_FAILED_INIT;
|
|
||||||
sockindex = (data->conn->writesockfd == data->conn->sock[SECONDARYSOCKET]);
|
|
||||||
return Curl_shutdown_started(data, sockindex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive raw response data for the transfer.
|
* Receive raw response data for the transfer.
|
||||||
* @param data the transfer
|
* @param data the transfer
|
||||||
@ -261,7 +249,7 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DEBUGF(infof(data, "readwrite_data: we are done"));
|
DEBUGF(infof(data, "sendrecv_dl: we are done"));
|
||||||
}
|
}
|
||||||
DEBUGASSERT(nread >= 0);
|
DEBUGASSERT(nread >= 0);
|
||||||
return nread;
|
return nread;
|
||||||
@ -272,9 +260,9 @@ static ssize_t Curl_xfer_recv_resp(struct Curl_easy *data,
|
|||||||
* the stream was rewound (in which case we have data in a
|
* the stream was rewound (in which case we have data in a
|
||||||
* buffer)
|
* buffer)
|
||||||
*/
|
*/
|
||||||
static CURLcode readwrite_data(struct Curl_easy *data,
|
static CURLcode sendrecv_dl(struct Curl_easy *data,
|
||||||
struct SingleRequest *k,
|
struct SingleRequest *k,
|
||||||
int *didwhat)
|
int *didwhat)
|
||||||
{
|
{
|
||||||
struct connectdata *conn = data->conn;
|
struct connectdata *conn = data->conn;
|
||||||
CURLcode result = CURLE_OK;
|
CURLcode result = CURLE_OK;
|
||||||
@ -381,14 +369,14 @@ static CURLcode readwrite_data(struct Curl_easy *data,
|
|||||||
out:
|
out:
|
||||||
Curl_multi_xfer_buf_release(data, xfer_buf);
|
Curl_multi_xfer_buf_release(data, xfer_buf);
|
||||||
if(result)
|
if(result)
|
||||||
DEBUGF(infof(data, "readwrite_data() -> %d", result));
|
DEBUGF(infof(data, "sendrecv_dl() -> %d", result));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send data to upload to the server, when the socket is writable.
|
* Send data to upload to the server, when the socket is writable.
|
||||||
*/
|
*/
|
||||||
static CURLcode readwrite_upload(struct Curl_easy *data, int *didwhat)
|
static CURLcode sendrecv_ul(struct Curl_easy *data, int *didwhat)
|
||||||
{
|
{
|
||||||
/* We should not get here when the sending is already done. It
|
/* We should not get here when the sending is already done. It
|
||||||
* probably means that someone set `data-req.keepon |= KEEP_SEND`
|
* probably means that someone set `data-req.keepon |= KEEP_SEND`
|
||||||
@ -421,66 +409,44 @@ static int select_bits_paused(struct Curl_easy *data, int select_bits)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Curl_readwrite() is the low-level function to be called when data is to
|
* Curl_sendrecv() is the low-level function to be called when data is to
|
||||||
* be read and written to/from the connection.
|
* be read and written to/from the connection.
|
||||||
*/
|
*/
|
||||||
CURLcode Curl_readwrite(struct Curl_easy *data)
|
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
|
||||||
{
|
{
|
||||||
struct connectdata *conn = data->conn;
|
|
||||||
struct SingleRequest *k = &data->req;
|
struct SingleRequest *k = &data->req;
|
||||||
CURLcode result;
|
CURLcode result = CURLE_OK;
|
||||||
struct curltime now;
|
|
||||||
int didwhat = 0;
|
int didwhat = 0;
|
||||||
int select_bits;
|
int select_bits = 0;
|
||||||
|
|
||||||
|
DEBUGASSERT(nowp);
|
||||||
if(data->state.select_bits) {
|
if(data->state.select_bits) {
|
||||||
if(select_bits_paused(data, data->state.select_bits)) {
|
if(select_bits_paused(data, data->state.select_bits)) {
|
||||||
/* leave the bits unchanged, so they'll tell us what to do when
|
/* leave the bits unchanged, so they'll tell us what to do when
|
||||||
* this transfer gets unpaused. */
|
* this transfer gets unpaused. */
|
||||||
DEBUGF(infof(data, "readwrite, select_bits, early return on PAUSED"));
|
/* DEBUGF(infof(data, "sendrecv, select_bits, early return on PAUSED"));
|
||||||
|
*/
|
||||||
result = CURLE_OK;
|
result = CURLE_OK;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
select_bits = data->state.select_bits;
|
|
||||||
data->state.select_bits = 0;
|
data->state.select_bits = 0;
|
||||||
|
/* DEBUGF(infof(data, "sendrecv, select_bits %x, RUN", select_bits)); */
|
||||||
|
select_bits = (CURL_CSELECT_OUT|CURL_CSELECT_IN);
|
||||||
}
|
}
|
||||||
else if(((k->keepon & KEEP_RECVBITS) == KEEP_RECV) &&
|
else if(data->last_poll.num) {
|
||||||
xfer_recv_shutdown_started(data)) {
|
/* The transfer wanted something polled. Let's run all available
|
||||||
DEBUGF(infof(data, "readwrite, recv for finishing shutdown"));
|
* send/receives. Worst case we EAGAIN on some. */
|
||||||
select_bits = CURL_CSELECT_IN;
|
/* DEBUGF(infof(data, "sendrecv, had poll sockets, RUN")); */
|
||||||
|
select_bits = (CURL_CSELECT_OUT|CURL_CSELECT_IN);
|
||||||
}
|
}
|
||||||
else if(((k->keepon & KEEP_SENDBITS) == KEEP_SEND) &&
|
else if(data->req.keepon & KEEP_SEND_TIMED) {
|
||||||
xfer_send_shutdown_started(data)) {
|
/* DEBUGF(infof(data, "sendrecv, KEEP_SEND_TIMED, RUN ul")); */
|
||||||
DEBUGF(infof(data, "readwrite, send for finishing shutdown"));
|
|
||||||
select_bits = CURL_CSELECT_OUT;
|
select_bits = CURL_CSELECT_OUT;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
curl_socket_t fd_read;
|
|
||||||
curl_socket_t fd_write;
|
|
||||||
/* 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_RECVBITS) == KEEP_RECV)
|
|
||||||
fd_read = conn->sockfd;
|
|
||||||
else
|
|
||||||
fd_read = CURL_SOCKET_BAD;
|
|
||||||
|
|
||||||
if(Curl_req_want_send(data))
|
|
||||||
fd_write = conn->writesockfd;
|
|
||||||
else
|
|
||||||
fd_write = CURL_SOCKET_BAD;
|
|
||||||
|
|
||||||
select_bits = Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(select_bits == CURL_CSELECT_ERR) {
|
|
||||||
failf(data, "select/poll returned error");
|
|
||||||
result = CURLE_SEND_ERROR;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_HYPER
|
#ifdef USE_HYPER
|
||||||
if(conn->datastream) {
|
if(data->conn->datastream) {
|
||||||
result = conn->datastream(data, conn, &didwhat, select_bits);
|
result = data->conn->datastream(data, data->conn, &didwhat, select_bits);
|
||||||
if(result || data->req.done)
|
if(result || data->req.done)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -490,17 +456,15 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
|
|||||||
the stream was rewound (in which case we have data in a
|
the stream was rewound (in which case we have data in a
|
||||||
buffer) */
|
buffer) */
|
||||||
if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
|
if((k->keepon & KEEP_RECV) && (select_bits & CURL_CSELECT_IN)) {
|
||||||
result = readwrite_data(data, k, &didwhat);
|
result = sendrecv_dl(data, k, &didwhat);
|
||||||
if(result || data->req.done)
|
if(result || data->req.done)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we still have writing to do, we check if we have a writable socket. */
|
/* If we still have writing to do, we check if we have a writable socket. */
|
||||||
if((Curl_req_want_send(data) && (select_bits & CURL_CSELECT_OUT)) ||
|
if((Curl_req_want_send(data) || (data->req.keepon & KEEP_SEND_TIMED)) &&
|
||||||
(k->keepon & KEEP_SEND_TIMED)) {
|
(select_bits & CURL_CSELECT_OUT)) {
|
||||||
/* write */
|
result = sendrecv_ul(data, &didwhat);
|
||||||
|
|
||||||
result = readwrite_upload(data, &didwhat);
|
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
@ -508,8 +472,8 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
now = Curl_now();
|
if(select_bits && !didwhat) {
|
||||||
if(!didwhat) {
|
/* Transfer wanted to send/recv, but nothing was possible. */
|
||||||
result = Curl_conn_ev_data_idle(data);
|
result = Curl_conn_ev_data_idle(data);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
@ -518,23 +482,23 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
|
|||||||
if(Curl_pgrsUpdate(data))
|
if(Curl_pgrsUpdate(data))
|
||||||
result = CURLE_ABORTED_BY_CALLBACK;
|
result = CURLE_ABORTED_BY_CALLBACK;
|
||||||
else
|
else
|
||||||
result = Curl_speedcheck(data, now);
|
result = Curl_speedcheck(data, *nowp);
|
||||||
if(result)
|
if(result)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if(k->keepon) {
|
if(k->keepon) {
|
||||||
if(0 > Curl_timeleft(data, &now, FALSE)) {
|
if(0 > Curl_timeleft(data, nowp, FALSE)) {
|
||||||
if(k->size != -1) {
|
if(k->size != -1) {
|
||||||
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
|
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
|
||||||
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
|
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " out of %"
|
||||||
CURL_FORMAT_CURL_OFF_T " bytes received",
|
CURL_FORMAT_CURL_OFF_T " bytes received",
|
||||||
Curl_timediff(now, data->progress.t_startsingle),
|
Curl_timediff(*nowp, data->progress.t_startsingle),
|
||||||
k->bytecount, k->size);
|
k->bytecount, k->size);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
|
failf(data, "Operation timed out after %" CURL_FORMAT_TIMEDIFF_T
|
||||||
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
|
" milliseconds with %" CURL_FORMAT_CURL_OFF_T " bytes received",
|
||||||
Curl_timediff(now, data->progress.t_startsingle),
|
Curl_timediff(*nowp, data->progress.t_startsingle),
|
||||||
k->bytecount);
|
k->bytecount);
|
||||||
}
|
}
|
||||||
result = CURLE_OPERATION_TIMEDOUT;
|
result = CURLE_OPERATION_TIMEDOUT;
|
||||||
@ -573,7 +537,7 @@ CURLcode Curl_readwrite(struct Curl_easy *data)
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
if(result)
|
if(result)
|
||||||
DEBUGF(infof(data, "Curl_readwrite() -> %d", result));
|
DEBUGF(infof(data, "Curl_sendrecv() -> %d", result));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ typedef enum {
|
|||||||
|
|
||||||
CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
|
CURLcode Curl_follow(struct Curl_easy *data, char *newurl,
|
||||||
followtype type);
|
followtype type);
|
||||||
CURLcode Curl_readwrite(struct Curl_easy *data);
|
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
|
||||||
int Curl_single_getsock(struct Curl_easy *data,
|
int Curl_single_getsock(struct Curl_easy *data,
|
||||||
struct connectdata *conn, curl_socket_t *socks);
|
struct connectdata *conn, curl_socket_t *socks);
|
||||||
CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
|
CURLcode Curl_retry_request(struct Curl_easy *data, char **url);
|
||||||
|
|||||||
@ -234,8 +234,6 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||||||
data = *datap;
|
data = *datap;
|
||||||
*datap = NULL;
|
*datap = NULL;
|
||||||
|
|
||||||
Curl_expire_clear(data); /* shut off timers */
|
|
||||||
|
|
||||||
/* Detach connection if any is left. This should not be normal, but can be
|
/* Detach connection if any is left. This should not be normal, but can be
|
||||||
the case for example with CONNECT_ONLY + recv/send (test 556) */
|
the case for example with CONNECT_ONLY + recv/send (test 556) */
|
||||||
Curl_detach_connection(data);
|
Curl_detach_connection(data);
|
||||||
@ -253,6 +251,8 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Curl_expire_clear(data); /* shut off any timers left */
|
||||||
|
|
||||||
data->magic = 0; /* force a clear AFTER the possibly enforced removal from
|
data->magic = 0; /* force a clear AFTER the possibly enforced removal from
|
||||||
the multi handle, since that function uses the magic
|
the multi handle, since that function uses the magic
|
||||||
field! */
|
field! */
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
fun:zstd_unencode_write
|
fun:zstd_unencode_write
|
||||||
fun:Curl_unencode_write
|
fun:Curl_unencode_write
|
||||||
fun:readwrite_data
|
fun:readwrite_data
|
||||||
fun:Curl_readwrite
|
fun:Curl_sendrecv
|
||||||
fun:multi_runsingle
|
fun:multi_runsingle
|
||||||
fun:curl_multi_perform
|
fun:curl_multi_perform
|
||||||
fun:easy_transfer
|
fun:easy_transfer
|
||||||
@ -31,7 +31,7 @@
|
|||||||
Memcheck:Cond
|
Memcheck:Cond
|
||||||
fun:ZSTD_decompressStream
|
fun:ZSTD_decompressStream
|
||||||
fun:zstd_unencode_write
|
fun:zstd_unencode_write
|
||||||
fun:Curl_readwrite
|
fun:Curl_sendrecv
|
||||||
fun:multi_runsingle
|
fun:multi_runsingle
|
||||||
fun:curl_multi_perform
|
fun:curl_multi_perform
|
||||||
fun:curl_easy_perform
|
fun:curl_easy_perform
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user