diff options
author | Jay Satiro <raysatiro@yahoo.com> | 2021-04-29 17:06:49 -0400 |
---|---|---|
committer | Jay Satiro <raysatiro@yahoo.com> | 2021-08-17 03:21:29 -0400 |
commit | b654fb4cd3dda9871efb81b0942b8a80e5e81c6a (patch) | |
tree | 8f7a11540c38a79a1e8a9eca0a88a9ee53d7e663 /src | |
parent | 1828f6ae2e10204215bbb1e6cc368587cc6d8fc1 (diff) | |
download | curl-b654fb4cd3dda9871efb81b0942b8a80e5e81c6a.tar.gz |
tool_operate: Fix --fail-early with parallel transfers
- Abort via progress callback to fail early during parallel transfers.
When a critical error occurs during a transfer (eg --fail-early
constraint) then other running transfers will be aborted via progress
callback and finish with error CURLE_ABORTED_BY_CALLBACK (42). In this
case, the callback error does not become the most recent error and a
custom error message is used for those transfers:
curld --fail --fail-early --parallel
https://httpbin.org/status/404 https://httpbin.org/delay/10
curl: (22) The requested URL returned error: 404
curl: (42) Transfer aborted due to critical error in another transfer
> echo %ERRORLEVEL%
22
Fixes https://github.com/curl/curl/issues/6939
Closes https://github.com/curl/curl/pull/6984
Diffstat (limited to 'src')
-rw-r--r-- | src/tool_operate.c | 38 | ||||
-rw-r--r-- | src/tool_operate.h | 3 | ||||
-rw-r--r-- | src/tool_progress.c | 3 |
3 files changed, 43 insertions, 1 deletions
diff --git a/src/tool_operate.c b/src/tool_operate.c index e6ca575bf..74221599d 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -2123,6 +2123,7 @@ static CURLcode add_parallel_transfers(struct GlobalConfig *global, (void)curl_easy_setopt(per->curl, CURLOPT_PRIVATE, per); (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb); (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFODATA, per); + (void)curl_easy_setopt(per->curl, CURLOPT_NOPROGRESS, 0L); mcode = curl_multi_add_handle(multi, per->curl); if(mcode) @@ -2149,6 +2150,10 @@ static CURLcode parallel_transfers(struct GlobalConfig *global, struct timeval start = tvnow(); bool more_transfers; bool added_transfers; + /* wrapitup is set TRUE after a critical error occurs to end all transfers */ + bool wrapitup = FALSE; + /* wrapitup_processed is set TRUE after the per transfer abort flag is set */ + bool wrapitup_processed = FALSE; time_t tick = time(NULL); multi = curl_multi_init(); @@ -2163,6 +2168,21 @@ static CURLcode parallel_transfers(struct GlobalConfig *global, } while(!mcode && (still_running || more_transfers)) { + /* If stopping prematurely (eg due to a --fail-early condition) then signal + that any transfers in the multi should abort (via progress callback). */ + if(wrapitup) { + if(!still_running) + break; + if(!wrapitup_processed) { + struct per_transfer *per; + for(per = transfers; per; per = per->next) { + if(per->added) + per->abort = TRUE; + } + wrapitup_processed = TRUE; + } + } + mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); if(!mcode) mcode = curl_multi_perform(multi, &still_running); @@ -2184,6 +2204,10 @@ static CURLcode parallel_transfers(struct GlobalConfig *global, curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended); curl_multi_remove_handle(multi, easy); + if(ended->abort && tres == CURLE_ABORTED_BY_CALLBACK) { + msnprintf(ended->errorbuffer, sizeof(ended->errorbuffer), + "Transfer aborted due to critical error in another transfer"); + } tres = post_per_transfer(global, ended, tres, &retry, &delay); progress_finalize(ended); /* before it goes away */ all_added--; /* one fewer added */ @@ -2194,12 +2218,22 @@ static CURLcode parallel_transfers(struct GlobalConfig *global, ended->startat = delay ? time(NULL) + delay/1000 : 0; } else { - if(tres) + /* result receives this transfer's error unless the transfer was + marked for abort due to a critical error in another transfer */ + if(tres && (!ended->abort || !result)) result = tres; + if(is_fatal_error(result) || (result && global->fail_early)) + wrapitup = TRUE; (void)del_per_transfer(ended); } } } while(msg); + if(wrapitup) { + if(still_running) + continue; + else + break; + } if(!checkmore) { time_t tock = time(NULL); if(tick != tock) { @@ -2218,6 +2252,8 @@ static CURLcode parallel_transfers(struct GlobalConfig *global, /* we added new ones, make sure the loop doesn't exit yet */ still_running = 1; } + if(is_fatal_error(result) || (result && global->fail_early)) + wrapitup = TRUE; } } diff --git a/src/tool_operate.h b/src/tool_operate.h index 282b7858f..61994052f 100644 --- a/src/tool_operate.h +++ b/src/tool_operate.h @@ -56,6 +56,9 @@ struct per_transfer { time_t startat; /* when doing parallel transfers, this is a retry transfer that has been set to sleep until this time before it should get started (again) */ + bool abort; /* when doing parallel transfers and this is TRUE then a critical + error (eg --fail-early) has occurred in another transfer and + this transfer will be aborted in the progress callback */ /* for parallel progress bar */ curl_off_t dltotal; diff --git a/src/tool_progress.c b/src/tool_progress.c index da6c2bc6f..031f8b8d4 100644 --- a/src/tool_progress.c +++ b/src/tool_progress.c @@ -101,6 +101,9 @@ int xferinfo_cb(void *clientp, per->ultotal = ultotal; per->ulnow = ulnow; + if(per->abort) + return 1; + if(config->readbusy) { config->readbusy = FALSE; curl_easy_pause(per->curl, CURLPAUSE_CONT); |