From 9a2cbf30b81a2b57149bb20e78e2e4cb5c2ff389 Mon Sep 17 00:00:00 2001 From: John Schroeder Date: Tue, 26 Nov 2019 09:16:19 +0100 Subject: curl: fix --upload-file . hangs if delay in STDIN Attempt to unpause a busy read in the CURLOPT_XFERINFOFUNCTION. When uploading from stdin in non-blocking mode, a delay in reading the stream (EAGAIN) causes curl to pause sending data (CURL_READFUNC_PAUSE). Prior to this change, a busy read was detected and unpaused only in the CURLOPT_WRITEFUNCTION handler. This change performs the same busy read handling in a CURLOPT_XFERINFOFUNCTION handler. Fixes #2051 Closes #4599 Reported-by: bdry on github --- src/tool_cb_prg.c | 11 ++++++++++- src/tool_cb_rea.c | 26 ++++++++++++++++++++++++++ src/tool_cb_rea.h | 8 ++++++++ src/tool_operate.c | 13 ++++++++++--- src/tool_operate.h | 1 + src/tool_progress.c | 8 ++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) diff --git a/src/tool_cb_prg.c b/src/tool_cb_prg.c index a18827c8b..505ae751f 100644 --- a/src/tool_cb_prg.c +++ b/src/tool_cb_prg.c @@ -32,6 +32,7 @@ #include "tool_cfgable.h" #include "tool_cb_prg.h" #include "tool_util.h" +#include "tool_operate.h" #include "memdebug.h" /* keep this as LAST include */ @@ -121,7 +122,10 @@ int tool_progress_cb(void *clientp, and this new edition inherits some of his concepts. */ struct timeval now = tvnow(); - struct ProgressData *bar = (struct ProgressData *)clientp; + struct per_transfer *per = clientp; + struct OutStruct *outs = &per->outs; + struct OperationConfig *config = outs->config; + struct ProgressData *bar = &per->progressbar; curl_off_t total; curl_off_t point; @@ -191,6 +195,11 @@ int tool_progress_cb(void *clientp, bar->prev = point; bar->prevtime = now; + if(config->readbusy) { + config->readbusy = FALSE; + curl_easy_pause(per->curl, CURLPAUSE_CONT); + } + return 0; } diff --git a/src/tool_cb_rea.c b/src/tool_cb_rea.c index 06f60eca7..03ed4a467 100644 --- a/src/tool_cb_rea.c +++ b/src/tool_cb_rea.c @@ -27,6 +27,7 @@ #include "tool_cfgable.h" #include "tool_cb_rea.h" +#include "tool_operate.h" #include "memdebug.h" /* keep this as LAST include */ @@ -52,3 +53,28 @@ size_t tool_read_cb(void *buffer, size_t sz, size_t nmemb, void *userdata) in->config->readbusy = FALSE; return (size_t)rc; } + +/* +** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads +*/ + +int tool_readbusy_cb(void *clientp, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow) +{ + struct per_transfer *per = clientp; + struct OutStruct *outs = &per->outs; + struct OperationConfig *config = outs->config; + + (void)dltotal; /* unused */ + (void)dlnow; /* unused */ + (void)ultotal; /* unused */ + (void)ulnow; /* unused */ + + if(config->readbusy) { + config->readbusy = FALSE; + curl_easy_pause(per->curl, CURLPAUSE_CONT); + } + + return per->noprogress? 0 : CURL_PROGRESSFUNC_CONTINUE; +} diff --git a/src/tool_cb_rea.h b/src/tool_cb_rea.h index fe744e8f7..5f7e483a3 100644 --- a/src/tool_cb_rea.h +++ b/src/tool_cb_rea.h @@ -29,4 +29,12 @@ size_t tool_read_cb(void *buffer, size_t sz, size_t nmemb, void *userdata); +/* +** callback for CURLOPT_XFERINFOFUNCTION used to unpause busy reads +*/ + +int tool_readbusy_cb(void *clientp, + curl_off_t dltotal, curl_off_t dlnow, + curl_off_t ultotal, curl_off_t ulnow); + #endif /* HEADER_CURL_TOOL_CB_REA_H */ diff --git a/src/tool_operate.c b/src/tool_operate.c index 0de81cd49..4b2ffb55b 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -1080,11 +1080,11 @@ static CURLcode single_transfer(struct GlobalConfig *global, isatty(fileno(outs->stream))) /* we send the output to a tty, therefore we switch off the progress meter */ - global->noprogress = global->isatty = TRUE; + per->noprogress = global->noprogress = global->isatty = TRUE; else { /* progress meter is per download, so restore config values */ - global->noprogress = orig_noprogress; + per->noprogress = global->noprogress = orig_noprogress; global->isatty = orig_isatty; } @@ -1573,7 +1573,14 @@ static CURLcode single_transfer(struct GlobalConfig *global, /* we want the alternative style, then we have to implement it ourselves! */ my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb); - my_setopt(curl, CURLOPT_XFERINFODATA, &per->progressbar); + my_setopt(curl, CURLOPT_XFERINFODATA, per); + } + else if(per->uploadfile && !strcmp(per->uploadfile, ".")) { + /* when reading from stdin in non-blocking mode, we use the progress + function to unpause a busy read */ + my_setopt(curl, CURLOPT_NOPROGRESS, 0L); + my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_readbusy_cb); + my_setopt(curl, CURLOPT_XFERINFODATA, per); } /* new in libcurl 7.24.0: */ diff --git a/src/tool_operate.h b/src/tool_operate.h index 60257fc60..7223db767 100644 --- a/src/tool_operate.h +++ b/src/tool_operate.h @@ -44,6 +44,7 @@ struct per_transfer { char *outfile; bool infdopen; /* TRUE if infd needs closing */ int infd; + bool noprogress; struct ProgressData progressbar; struct OutStruct outs; struct OutStruct heads; diff --git a/src/tool_progress.c b/src/tool_progress.c index a2667f38e..ac4f58f41 100644 --- a/src/tool_progress.c +++ b/src/tool_progress.c @@ -95,10 +95,18 @@ int xferinfo_cb(void *clientp, curl_off_t ulnow) { struct per_transfer *per = clientp; + struct OutStruct *outs = &per->outs; + struct OperationConfig *config = outs->config; per->dltotal = dltotal; per->dlnow = dlnow; per->ultotal = ultotal; per->ulnow = ulnow; + + if(config->readbusy) { + config->readbusy = FALSE; + curl_easy_pause(per->curl, CURLPAUSE_CONT); + } + return 0; } -- cgit v1.2.3