1
0
mirror of https://github.com/moparisthebest/curl synced 2024-11-11 20:15:03 -05:00

curl: create easy handles on-demand and not ahead of time

This should again enable crazy-large download ranges of the style
[1-10000000] that otherwise easily ran out of memory starting in 7.66.0
when this new handle allocating scheme was introduced.

Reported-by: Peter Sumatra
Fixes #4393
Closes #4438
This commit is contained in:
Daniel Stenberg 2019-09-23 17:11:22 +02:00
parent c124e6b3c0
commit e59371a493
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
7 changed files with 307 additions and 218 deletions

View File

@ -22,11 +22,9 @@
* *
***************************************************************************/ ***************************************************************************/
#include "tool_setup.h" #include "tool_setup.h"
#include "tool_sdecls.h" #include "tool_sdecls.h"
#include "tool_metalink.h" #include "tool_metalink.h"
#include "tool_urlglob.h"
#include "tool_formparse.h" #include "tool_formparse.h"
typedef enum { typedef enum {
@ -37,6 +35,20 @@ typedef enum {
struct GlobalConfig; struct GlobalConfig;
struct State {
struct getout *urlnode;
URLGlob *inglob;
URLGlob *urls;
char *outfiles;
char *httpgetfields;
char *uploadfile;
unsigned long infilenum; /* number of files to upload */
unsigned long up; /* upload file counter within a single upload glob */
unsigned long urlnum; /* how many iterations this single URL has with ranges
etc */
unsigned long li;
};
struct OperationConfig { struct OperationConfig {
bool remote_time; bool remote_time;
char *random_file; char *random_file;
@ -262,6 +274,7 @@ struct OperationConfig {
struct GlobalConfig *global; struct GlobalConfig *global;
struct OperationConfig *prev; struct OperationConfig *prev;
struct OperationConfig *next; /* Always last in the struct */ struct OperationConfig *next; /* Always last in the struct */
struct State state; /* for create_transfer() */
}; };
struct GlobalConfig { struct GlobalConfig {

View File

@ -984,6 +984,7 @@ void delete_metalinkfile(metalinkfile *mlfile)
void clean_metalink(struct OperationConfig *config) void clean_metalink(struct OperationConfig *config)
{ {
if(config) {
while(config->metalinkfile_list) { while(config->metalinkfile_list) {
metalinkfile *mlfile = config->metalinkfile_list; metalinkfile *mlfile = config->metalinkfile_list;
config->metalinkfile_list = config->metalinkfile_list->next; config->metalinkfile_list = config->metalinkfile_list->next;
@ -991,6 +992,7 @@ void clean_metalink(struct OperationConfig *config)
} }
config->metalinkfile_last = 0; config->metalinkfile_last = 0;
} }
}
void metalink_cleanup(void) void metalink_cleanup(void)
{ {

View File

@ -103,10 +103,14 @@ CURLcode curl_easy_perform_ev(CURL *easy);
"this situation and\nhow to fix it, please visit the web page mentioned " \ "this situation and\nhow to fix it, please visit the web page mentioned " \
"above.\n" "above.\n"
static CURLcode create_transfers(struct GlobalConfig *global, static CURLcode single_transfer(struct GlobalConfig *global,
struct OperationConfig *config, struct OperationConfig *config,
CURLSH *share, CURLSH *share,
bool capath_from_env); bool capath_from_env,
bool *added);
static CURLcode get_transfer(struct GlobalConfig *global,
CURLSH *share,
bool *added);
static bool is_fatal_error(CURLcode code) static bool is_fatal_error(CURLcode code)
{ {
@ -316,7 +320,6 @@ static CURLcode pre_transfer(struct GlobalConfig *global,
my_setopt(per->curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize); my_setopt(per->curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
per->input.fd = per->infd; per->input.fd = per->infd;
} }
show_error:
return result; return result;
} }
@ -324,7 +327,6 @@ static CURLcode pre_transfer(struct GlobalConfig *global,
* Call this after a transfer has completed. * Call this after a transfer has completed.
*/ */
static CURLcode post_transfer(struct GlobalConfig *global, static CURLcode post_transfer(struct GlobalConfig *global,
CURLSH *share,
struct per_transfer *per, struct per_transfer *per,
CURLcode result, CURLcode result,
bool *retryp) bool *retryp)
@ -333,6 +335,9 @@ static CURLcode post_transfer(struct GlobalConfig *global,
CURL *curl = per->curl; CURL *curl = per->curl;
struct OperationConfig *config = per->config; struct OperationConfig *config = per->config;
if(!curl || !config)
return result;
*retryp = FALSE; *retryp = FALSE;
if(per->infdopen) if(per->infdopen)
@ -401,7 +406,6 @@ static CURLcode post_transfer(struct GlobalConfig *global,
else if(rv == -1) else if(rv == -1)
fprintf(config->global->errors, "Metalink: parsing (%s) FAILED\n", fprintf(config->global->errors, "Metalink: parsing (%s) FAILED\n",
per->this_url); per->this_url);
result = create_transfers(global, config, share, FALSE);
} }
else if(per->metalink && result == CURLE_OK && !per->metalink_next_res) { else if(per->metalink && result == CURLE_OK && !per->metalink_next_res) {
int rv; int rv;
@ -410,8 +414,6 @@ static CURLcode post_transfer(struct GlobalConfig *global,
if(!rv) if(!rv)
per->metalink_next_res = 1; per->metalink_next_res = 1;
} }
#else
(void)share;
#endif /* USE_METALINK */ #endif /* USE_METALINK */
#ifdef USE_METALINK #ifdef USE_METALINK
@ -651,60 +653,81 @@ static CURLcode post_transfer(struct GlobalConfig *global,
return CURLE_OK; return CURLE_OK;
} }
/* go through the list of URLs and configs and add transfers */ static void single_transfer_cleanup(struct OperationConfig *config)
{
if(config) {
struct State *state = &config->state;
if(state->urls) {
/* Free list of remaining URLs */
glob_cleanup(state->urls);
state->urls = NULL;
}
Curl_safefree(state->outfiles);
Curl_safefree(state->httpgetfields);
Curl_safefree(state->uploadfile);
if(state->inglob) {
/* Free list of globbed upload files */
glob_cleanup(state->inglob);
state->inglob = NULL;
}
}
}
static CURLcode create_transfers(struct GlobalConfig *global, /* create the next (singular) transfer */
static CURLcode single_transfer(struct GlobalConfig *global,
struct OperationConfig *config, struct OperationConfig *config,
CURLSH *share, CURLSH *share,
bool capath_from_env) bool capath_from_env,
bool *added)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
struct getout *urlnode; struct getout *urlnode;
metalinkfile *mlfile_last = NULL; metalinkfile *mlfile_last = NULL;
bool orig_noprogress = global->noprogress; bool orig_noprogress = global->noprogress;
bool orig_isatty = global->isatty; bool orig_isatty = global->isatty;
char *httpgetfields = NULL; struct State *state = &config->state;
char *httpgetfields = state->httpgetfields;
*added = FALSE; /* not yet */
if(config->postfields) { if(config->postfields) {
if(config->use_httpget) { if(config->use_httpget) {
if(!httpgetfields) {
/* Use the postfields data for a http get */ /* Use the postfields data for a http get */
httpgetfields = strdup(config->postfields); httpgetfields = state->httpgetfields = strdup(config->postfields);
Curl_safefree(config->postfields); Curl_safefree(config->postfields);
if(!httpgetfields) { if(!httpgetfields) {
helpf(global->errors, "out of memory\n"); helpf(global->errors, "out of memory\n");
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto quit_curl;
} }
if(SetHTTPrequest(config, else if(SetHTTPrequest(config,
(config->no_body?HTTPREQ_HEAD:HTTPREQ_GET), (config->no_body?HTTPREQ_HEAD:HTTPREQ_GET),
&config->httpreq)) { &config->httpreq)) {
result = CURLE_FAILED_INIT; result = CURLE_FAILED_INIT;
goto quit_curl; }
} }
} }
else { else {
if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq)) { if(SetHTTPrequest(config, HTTPREQ_SIMPLEPOST, &config->httpreq))
result = CURLE_FAILED_INIT; result = CURLE_FAILED_INIT;
goto quit_curl;
} }
if(result)
return result;
} }
if(!state->urlnode) {
/* first time caller, setup things */
state->urlnode = config->url_list;
state->infilenum = 1;
} }
for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) { while(config->state.urlnode) {
unsigned long li;
unsigned long up; /* upload file counter within a single upload glob */
char *infiles; /* might be a glob pattern */ char *infiles; /* might be a glob pattern */
char *outfiles; URLGlob *inglob = state->inglob;
unsigned long infilenum;
URLGlob *inglob;
bool metalink = FALSE; /* metalink download? */ bool metalink = FALSE; /* metalink download? */
metalinkfile *mlfile; metalinkfile *mlfile;
metalink_resource *mlres; metalink_resource *mlres;
outfiles = NULL; urlnode = config->state.urlnode;
infilenum = 1;
inglob = NULL;
if(urlnode->flags & GETOUT_METALINK) { if(urlnode->flags & GETOUT_METALINK) {
metalink = 1; metalink = 1;
if(mlfile_last == NULL) { if(mlfile_last == NULL) {
@ -727,13 +750,15 @@ static CURLcode create_transfers(struct GlobalConfig *global,
Curl_safefree(urlnode->outfile); Curl_safefree(urlnode->outfile);
Curl_safefree(urlnode->infile); Curl_safefree(urlnode->infile);
urlnode->flags = 0; urlnode->flags = 0;
config->state.urlnode = urlnode->next;
state->up = 0;
continue; /* next URL please */ continue; /* next URL please */
} }
/* save outfile pattern before expansion */ /* save outfile pattern before expansion */
if(urlnode->outfile) { if(urlnode->outfile && !state->outfiles) {
outfiles = strdup(urlnode->outfile); state->outfiles = strdup(urlnode->outfile);
if(!outfiles) { if(!state->outfiles) {
helpf(global->errors, "out of memory\n"); helpf(global->errors, "out of memory\n");
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
break; break;
@ -742,48 +767,41 @@ static CURLcode create_transfers(struct GlobalConfig *global,
infiles = urlnode->infile; infiles = urlnode->infile;
if(!config->globoff && infiles) { if(!config->globoff && infiles && !inglob) {
/* Unless explicitly shut off */ /* Unless explicitly shut off */
result = glob_url(&inglob, infiles, &infilenum, result = glob_url(&inglob, infiles, &state->infilenum,
global->showerror?global->errors:NULL); global->showerror?global->errors:NULL);
if(result) { if(result)
Curl_safefree(outfiles);
break; break;
} config->state.inglob = inglob;
} }
/* Here's the loop for uploading multiple files within the same {
single globbed string. If no upload, we enter the loop once anyway. */
for(up = 0 ; up < infilenum; up++) {
char *uploadfile; /* a single file, never a glob */
int separator; int separator;
URLGlob *urls;
unsigned long urlnum; unsigned long urlnum;
uploadfile = NULL; if(!state->up && !infiles)
urls = NULL;
urlnum = 0;
if(!up && !infiles)
Curl_nop_stmt; Curl_nop_stmt;
else { else {
if(!state->uploadfile) {
if(inglob) { if(inglob) {
result = glob_next_url(&uploadfile, inglob); result = glob_next_url(&state->uploadfile, inglob);
if(result == CURLE_OUT_OF_MEMORY) if(result == CURLE_OUT_OF_MEMORY)
helpf(global->errors, "out of memory\n"); helpf(global->errors, "out of memory\n");
} }
else if(!up) { else if(!state->up) {
uploadfile = strdup(infiles); state->uploadfile = strdup(infiles);
if(!uploadfile) { if(!state->uploadfile) {
helpf(global->errors, "out of memory\n"); helpf(global->errors, "out of memory\n");
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
} }
} }
if(!uploadfile) }
if(result)
break; break;
} }
if(!state->urlnum) {
if(metalink) { if(metalink) {
/* For Metalink download, we don't use glob. Instead we use /* For Metalink download, we don't use glob. Instead we use
the number of resources as urlnum. */ the number of resources as urlnum. */
@ -792,38 +810,52 @@ static CURLcode create_transfers(struct GlobalConfig *global,
else if(!config->globoff) { else if(!config->globoff) {
/* Unless explicitly shut off, we expand '{...}' and '[...]' /* Unless explicitly shut off, we expand '{...}' and '[...]'
expressions and return total number of URLs in pattern set */ expressions and return total number of URLs in pattern set */
result = glob_url(&urls, urlnode->url, &urlnum, result = glob_url(&state->urls, urlnode->url, &state->urlnum,
global->showerror?global->errors:NULL); global->showerror?global->errors:NULL);
if(result) { if(result)
Curl_safefree(uploadfile);
break; break;
} urlnum = state->urlnum;
} }
else else
urlnum = 1; /* without globbing, this is a single URL */ urlnum = 1; /* without globbing, this is a single URL */
}
else
urlnum = state->urlnum;
/* if multiple files extracted to stdout, insert separators! */ /* if multiple files extracted to stdout, insert separators! */
separator = ((!outfiles || !strcmp(outfiles, "-")) && urlnum > 1); separator = ((!state->outfiles ||
!strcmp(state->outfiles, "-")) && urlnum > 1);
/* Here's looping around each globbed URL */ /* Here's looping around each globbed URL */
for(li = 0 ; li < urlnum; li++) {
if(state->li >= urlnum) {
state->li = 0;
state->up++;
}
if(state->up < state->infilenum) {
struct per_transfer *per; struct per_transfer *per;
struct OutStruct *outs; struct OutStruct *outs;
struct InStruct *input; struct InStruct *input;
struct OutStruct *heads; struct OutStruct *heads;
struct HdrCbData *hdrcbdata = NULL; struct HdrCbData *hdrcbdata = NULL;
CURL *curl = curl_easy_init(); CURL *curl = curl_easy_init();
result = add_transfer(&per); result = add_transfer(&per);
if(result || !curl) { if(result || !curl) {
free(uploadfile);
curl_easy_cleanup(curl); curl_easy_cleanup(curl);
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
if(state->uploadfile) {
per->uploadfile = strdup(state->uploadfile);
if(!per->uploadfile) {
curl_easy_cleanup(curl);
result = CURLE_OUT_OF_MEMORY;
break;
}
}
*added = TRUE;
per->config = config; per->config = config;
per->curl = curl; per->curl = curl;
per->uploadfile = uploadfile;
/* default headers output stream is stdout */ /* default headers output stream is stdout */
heads = &per->heads; heads = &per->heads;
@ -838,7 +870,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(!newfile) { if(!newfile) {
warnf(config->global, "Failed to open %s\n", config->headerfile); warnf(config->global, "Failed to open %s\n", config->headerfile);
result = CURLE_WRITE_ERROR; result = CURLE_WRITE_ERROR;
goto quit_curl; break;
} }
else { else {
heads->filename = config->headerfile; heads->filename = config->headerfile;
@ -873,26 +905,26 @@ static CURLcode create_transfers(struct GlobalConfig *global,
per->outfile = strdup(mlfile->filename); per->outfile = strdup(mlfile->filename);
if(!per->outfile) { if(!per->outfile) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
per->this_url = strdup(mlres->url); per->this_url = strdup(mlres->url);
if(!per->this_url) { if(!per->this_url) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
per->mlfile = mlfile; per->mlfile = mlfile;
} }
else { else {
if(urls) { if(state->urls) {
result = glob_next_url(&per->this_url, urls); result = glob_next_url(&per->this_url, state->urls);
if(result) if(result)
goto show_error; break;
} }
else if(!li) { else if(!state->li) {
per->this_url = strdup(urlnode->url); per->this_url = strdup(urlnode->url);
if(!per->this_url) { if(!per->this_url) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
} }
else else
@ -900,11 +932,11 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(!per->this_url) if(!per->this_url)
break; break;
if(outfiles) { if(state->outfiles) {
per->outfile = strdup(outfiles); per->outfile = strdup(state->outfiles);
if(!per->outfile) { if(!per->outfile) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
} }
} }
@ -922,22 +954,22 @@ static CURLcode create_transfers(struct GlobalConfig *global,
/* extract the file name from the URL */ /* extract the file name from the URL */
result = get_url_file_name(&per->outfile, per->this_url); result = get_url_file_name(&per->outfile, per->this_url);
if(result) if(result)
goto show_error; break;
if(!*per->outfile && !config->content_disposition) { if(!*per->outfile && !config->content_disposition) {
helpf(global->errors, "Remote file name has no length!\n"); helpf(global->errors, "Remote file name has no length!\n");
result = CURLE_WRITE_ERROR; result = CURLE_WRITE_ERROR;
goto quit_urls; break;
} }
} }
else if(urls) { else if(state->urls) {
/* fill '#1' ... '#9' terms from URL pattern */ /* fill '#1' ... '#9' terms from URL pattern */
char *storefile = per->outfile; char *storefile = per->outfile;
result = glob_match_url(&per->outfile, storefile, urls); result = glob_match_url(&per->outfile, storefile, state->urls);
Curl_safefree(storefile); Curl_safefree(storefile);
if(result) { if(result) {
/* bad globbing */ /* bad globbing */
warnf(config->global, "bad output glob!\n"); warnf(config->global, "bad output glob!\n");
goto quit_urls; break;
} }
} }
@ -947,11 +979,8 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(config->create_dirs || metalink) { if(config->create_dirs || metalink) {
result = create_dir_hierarchy(per->outfile, global->errors); result = create_dir_hierarchy(per->outfile, global->errors);
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */ /* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
if(result == CURLE_WRITE_ERROR) if(result)
goto quit_urls; break;
if(result) {
goto show_error;
}
} }
if((urlnode->flags & GETOUT_USEREMOTE) if((urlnode->flags & GETOUT_USEREMOTE)
@ -986,7 +1015,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(!file) { if(!file) {
helpf(global->errors, "Can't open '%s'!\n", per->outfile); helpf(global->errors, "Can't open '%s'!\n", per->outfile);
result = CURLE_WRITE_ERROR; result = CURLE_WRITE_ERROR;
goto quit_urls; break;
} }
outs->fopened = TRUE; outs->fopened = TRUE;
outs->stream = file; outs->stream = file;
@ -1006,7 +1035,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
char *nurl = add_file_name_to_url(per->this_url, per->uploadfile); char *nurl = add_file_name_to_url(per->this_url, per->uploadfile);
if(!nurl) { if(!nurl) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
per->this_url = nurl; per->this_url = nurl;
} }
@ -1065,7 +1094,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(urlnum > 1 && !global->mute) { if(urlnum > 1 && !global->mute) {
per->separator_err = per->separator_err =
aprintf("\n[%lu/%lu]: %s --> %s", aprintf("\n[%lu/%lu]: %s --> %s",
li + 1, urlnum, per->this_url, state->li + 1, urlnum, per->this_url,
per->outfile ? per->outfile : "<stdout>"); per->outfile ? per->outfile : "<stdout>");
if(separator) if(separator)
per->separator = aprintf("%s%s", CURLseparator, per->this_url); per->separator = aprintf("%s%s", CURLseparator, per->this_url);
@ -1103,7 +1132,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(!urlbuffer) { if(!urlbuffer) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
Curl_safefree(per->this_url); /* free previous URL */ Curl_safefree(per->this_url); /* free previous URL */
@ -1124,11 +1153,10 @@ static CURLcode create_transfers(struct GlobalConfig *global,
config->terminal_binary_ok = config->terminal_binary_ok =
(per->outfile && !strcmp(per->outfile, "-")); (per->outfile && !strcmp(per->outfile, "-"));
/* avoid having this setopt added to the --libcurl source /* Avoid having this setopt added to the --libcurl source output. */
output */
result = curl_easy_setopt(curl, CURLOPT_SHARE, share); result = curl_easy_setopt(curl, CURLOPT_SHARE, share);
if(result) if(result)
goto show_error; break;
if(!config->tcp_nodelay) if(!config->tcp_nodelay)
my_setopt(curl, CURLOPT_TCP_NODELAY, 0L); my_setopt(curl, CURLOPT_TCP_NODELAY, 0L);
@ -1256,12 +1284,14 @@ static CURLcode create_transfers(struct GlobalConfig *global,
case HTTPREQ_MIMEPOST: case HTTPREQ_MIMEPOST:
result = tool2curlmime(curl, config->mimeroot, &config->mimepost); result = tool2curlmime(curl, config->mimeroot, &config->mimepost);
if(result) if(result)
goto show_error; break;
my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost); my_setopt_mimepost(curl, CURLOPT_MIMEPOST, config->mimepost);
break; break;
default: default:
break; break;
} }
if(result)
break;
/* new in libcurl 7.10.6 (default is Basic) */ /* new in libcurl 7.10.6 (default is Basic) */
if(config->authtype) if(config->authtype)
@ -1371,7 +1401,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
"SSL_CERT_DIR environment variable":"--capath"); "SSL_CERT_DIR environment variable":"--capath");
} }
else if(result) else if(result)
goto show_error; break;
} }
/* For the time being if --proxy-capath is not set then we use the /* For the time being if --proxy-capath is not set then we use the
--capath value for it, if any. See #1257 */ --capath value for it, if any. See #1257 */
@ -1388,7 +1418,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
} }
} }
else if(result) else if(result)
goto show_error; break;
} }
if(config->crlfile) if(config->crlfile)
@ -1503,7 +1533,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
Curl_safefree(home); Curl_safefree(home);
} }
if(result) if(result)
goto show_error; break;
} }
} }
@ -1607,7 +1637,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
if(config->engine) { if(config->engine) {
result = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine); result = res_setopt_str(curl, CURLOPT_SSLENGINE, config->engine);
if(result) if(result)
goto show_error; break;
} }
/* new in curl 7.10.7, extended in 7.19.4. Modified to use /* new in curl 7.10.7, extended in 7.19.4. Modified to use
@ -1855,7 +1885,7 @@ static CURLcode create_transfers(struct GlobalConfig *global,
outs->metalink_parser = metalink_parser_context_new(); outs->metalink_parser = metalink_parser_context_new();
if(outs->metalink_parser == NULL) { if(outs->metalink_parser == NULL) {
result = CURLE_OUT_OF_MEMORY; result = CURLE_OUT_OF_MEMORY;
goto show_error; break;
} }
fprintf(config->global->errors, fprintf(config->global->errors,
"Metalink: parsing (%s) metalink/XML...\n", per->this_url); "Metalink: parsing (%s) metalink/XML...\n", per->this_url);
@ -1874,48 +1904,37 @@ static CURLcode create_transfers(struct GlobalConfig *global,
per->retry_sleep = per->retry_sleep_default; /* ms */ per->retry_sleep = per->retry_sleep_default; /* ms */
per->retrystart = tvnow(); per->retrystart = tvnow();
} /* loop to the next URL */ state->li++;
show_error:
quit_urls:
if(urls) {
/* Free list of remaining URLs */
glob_cleanup(urls);
urls = NULL;
} }
else {
if(result)
/* exit loop upon error */
break;
} /* loop to the next globbed upload file */
/* Free loop-local allocated memory */
Curl_safefree(outfiles);
if(inglob) {
/* Free list of globbed upload files */
glob_cleanup(inglob);
inglob = NULL;
}
/* Free this URL node data without destroying the /* Free this URL node data without destroying the
the node itself nor modifying next pointer. */ the node itself nor modifying next pointer. */
Curl_safefree(urlnode->url);
Curl_safefree(urlnode->outfile); Curl_safefree(urlnode->outfile);
Curl_safefree(urlnode->infile); Curl_safefree(urlnode->infile);
urlnode->flags = 0; urlnode->flags = 0;
glob_cleanup(state->urls);
state->urls = NULL;
state->urlnum = 0;
if(result) Curl_safefree(state->outfiles);
/* exit loop upon error */ Curl_safefree(state->uploadfile);
if(state->inglob) {
/* Free list of globbed upload files */
glob_cleanup(state->inglob);
state->inglob = NULL;
}
config->state.urlnode = urlnode->next;
state->up = 0;
continue;
}
}
break; break;
} /* for-loop through all URLs */ }
quit_curl:
/* Free function-local referenced allocated memory */
Curl_safefree(httpgetfields);
if(!*added || result) {
*added = FALSE;
single_transfer_cleanup(config);
}
return result; return result;
} }
@ -1926,18 +1945,23 @@ static long all_added; /* number of easy handles currently added */
* to add even after this call returns. sets 'addedp' to TRUE if one or more * to add even after this call returns. sets 'addedp' to TRUE if one or more
* transfers were added. * transfers were added.
*/ */
static int add_parallel_transfers(struct GlobalConfig *global, static CURLcode add_parallel_transfers(struct GlobalConfig *global,
CURLM *multi, CURLM *multi,
CURLSH *share,
bool *morep, bool *morep,
bool *addedp) bool *addedp)
{ {
struct per_transfer *per; struct per_transfer *per;
CURLcode result; CURLcode result = CURLE_OK;
CURLMcode mcode; CURLMcode mcode;
*addedp = FALSE; *addedp = FALSE;
*morep = FALSE; *morep = FALSE;
result = get_transfer(global, share, addedp);
if(result || !*addedp)
return result;
for(per = transfers; per && (all_added < global->parallel_max); for(per = transfers; per && (all_added < global->parallel_max);
per = per->next) { per = per->next) {
bool getadded = FALSE;
if(per->added) if(per->added)
/* already added */ /* already added */
continue; continue;
@ -1953,6 +1977,10 @@ static int add_parallel_transfers(struct GlobalConfig *global,
mcode = curl_multi_add_handle(multi, per->curl); mcode = curl_multi_add_handle(multi, per->curl);
if(mcode) if(mcode)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
/* get the next transfer created */
result = get_transfer(global, share, &getadded);
if(result)
return result;
per->added = TRUE; per->added = TRUE;
all_added++; all_added++;
*addedp = TRUE; *addedp = TRUE;
@ -1976,7 +2004,7 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
if(!multi) if(!multi)
return CURLE_OUT_OF_MEMORY; return CURLE_OUT_OF_MEMORY;
result = add_parallel_transfers(global, multi, result = add_parallel_transfers(global, multi, share,
&more_transfers, &added_transfers); &more_transfers, &added_transfers);
if(result) if(result)
return result; return result;
@ -2002,7 +2030,7 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended); curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended);
curl_multi_remove_handle(multi, easy); curl_multi_remove_handle(multi, easy);
result = post_transfer(global, share, ended, result, &retry); result = post_transfer(global, ended, result, &retry);
if(retry) if(retry)
continue; continue;
progress_finalize(ended); /* before it goes away */ progress_finalize(ended); /* before it goes away */
@ -2013,7 +2041,8 @@ static CURLcode parallel_transfers(struct GlobalConfig *global,
} while(msg); } while(msg);
if(removed) { if(removed) {
/* one or more transfers completed, add more! */ /* one or more transfers completed, add more! */
(void)add_parallel_transfers(global, multi, &more_transfers, (void)add_parallel_transfers(global, multi, share,
&more_transfers,
&added_transfers); &added_transfers);
if(added_transfers) if(added_transfers)
/* we added new ones, make sure the loop doesn't exit yet */ /* we added new ones, make sure the loop doesn't exit yet */
@ -2043,8 +2072,14 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
CURLcode returncode = CURLE_OK; CURLcode returncode = CURLE_OK;
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
struct per_transfer *per; struct per_transfer *per;
bool added = FALSE;
result = get_transfer(global, share, &added);
if(result || !added)
return result;
for(per = transfers; per;) { for(per = transfers; per;) {
bool retry; bool retry;
bool bailout = FALSE;
result = pre_transfer(global, per); result = pre_transfer(global, per);
if(result) if(result)
break; break;
@ -2066,32 +2101,47 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
/* store the result of the actual transfer */ /* store the result of the actual transfer */
returncode = result; returncode = result;
result = post_transfer(global, share, per, result, &retry); result = post_transfer(global, per, result, &retry);
if(retry) if(retry)
continue; continue;
/* Bail out upon critical errors or --fail-early */
if(result || is_fatal_error(returncode) ||
(returncode && global->fail_early))
bailout = TRUE;
else {
/* setup the next one just before we delete this */
result = get_transfer(global, share, &added);
if(result)
bailout = TRUE;
}
/* Release metalink related resources here */ /* Release metalink related resources here */
delete_metalinkfile(per->mlfile); delete_metalinkfile(per->mlfile);
per = del_transfer(per); per = del_transfer(per);
/* Bail out upon critical errors or --fail-early */ if(bailout)
if(result || is_fatal_error(returncode) ||
(returncode && global->fail_early))
break; break;
} }
if(returncode) if(returncode)
/* returncode errors have priority */ /* returncode errors have priority */
result = returncode; result = returncode;
if(result)
single_transfer_cleanup(global->current);
return result; return result;
} }
static CURLcode operate_do(struct GlobalConfig *global, static CURLcode operate_do(struct GlobalConfig *global,
struct OperationConfig *config, struct OperationConfig *config,
CURLSH *share) CURLSH *share,
bool *added)
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
bool capath_from_env; bool capath_from_env;
*added = FALSE;
/* Check we have a url */ /* Check we have a url */
if(!config->url_list || !config->url_list->url) { if(!config->url_list || !config->url_list->url) {
@ -2180,12 +2230,33 @@ static CURLcode operate_do(struct GlobalConfig *global,
} }
if(!result) if(!result)
/* loop through the list of given URLs */ result = single_transfer(global, config, share, capath_from_env, added);
result = create_transfers(global, config, share, capath_from_env);
return result; return result;
} }
/*
* 'get_transfer' gets the details and sets up *one* new transfer if 'added'
* returns TRUE.
*/
static CURLcode get_transfer(struct GlobalConfig *global,
CURLSH *share,
bool *added)
{
CURLcode result = CURLE_OK;
*added = FALSE;
while(global->current) {
result = operate_do(global, global->current, share, added);
if(!result && !*added) {
/* when one set is drained, continue to next */
global->current = global->current->next;
continue;
}
break;
}
return result;
}
static CURLcode operate_transfers(struct GlobalConfig *global, static CURLcode operate_transfers(struct GlobalConfig *global,
CURLSH *share, CURLSH *share,
CURLcode result) CURLcode result)
@ -2206,7 +2277,11 @@ static CURLcode operate_transfers(struct GlobalConfig *global,
/* cleanup if there are any left */ /* cleanup if there are any left */
for(per = transfers; per;) { for(per = transfers; per;) {
bool retry; bool retry;
(void)post_transfer(global, share, per, result, &retry); CURLcode result2 = post_transfer(global, per, result, &retry);
if(!result)
/* don't overwrite the original error */
result = result2;
/* Free list of given URLs */ /* Free list of given URLs */
clean_getout(per->config); clean_getout(per->config);
@ -2223,7 +2298,7 @@ static CURLcode operate_transfers(struct GlobalConfig *global,
return result; return result;
} }
CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[]) CURLcode operate(struct GlobalConfig *global, int argc, argv_item_t argv[])
{ {
CURLcode result = CURLE_OK; CURLcode result = CURLE_OK;
@ -2236,18 +2311,18 @@ CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[])
if((argc == 1) || if((argc == 1) ||
(!curl_strequal(argv[1], "-q") && (!curl_strequal(argv[1], "-q") &&
!curl_strequal(argv[1], "--disable"))) { !curl_strequal(argv[1], "--disable"))) {
parseconfig(NULL, config); /* ignore possible failure */ parseconfig(NULL, global); /* ignore possible failure */
/* If we had no arguments then make sure a url was specified in .curlrc */ /* If we had no arguments then make sure a url was specified in .curlrc */
if((argc < 2) && (!config->first->url_list)) { if((argc < 2) && (!global->first->url_list)) {
helpf(config->errors, NULL); helpf(global->errors, NULL);
result = CURLE_FAILED_INIT; result = CURLE_FAILED_INIT;
} }
} }
if(!result) { if(!result) {
/* Parse the command line arguments */ /* Parse the command line arguments */
ParameterError res = parse_args(config, argc, argv); ParameterError res = parse_args(global, argc, argv);
if(res) { if(res) {
result = CURLE_OK; result = CURLE_OK;
@ -2270,7 +2345,7 @@ CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[])
} }
else { else {
#ifndef CURL_DISABLE_LIBCURL_OPTION #ifndef CURL_DISABLE_LIBCURL_OPTION
if(config->libcurl) { if(global->libcurl) {
/* Initialise the libcurl source output */ /* Initialise the libcurl source output */
result = easysrc_init(); result = easysrc_init();
} }
@ -2279,11 +2354,11 @@ CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[])
/* Perform the main operations */ /* Perform the main operations */
if(!result) { if(!result) {
size_t count = 0; size_t count = 0;
struct OperationConfig *operation = config->first; struct OperationConfig *operation = global->first;
CURLSH *share = curl_share_init(); CURLSH *share = curl_share_init();
if(!share) { if(!share) {
#ifndef CURL_DISABLE_LIBCURL_OPTION #ifndef CURL_DISABLE_LIBCURL_OPTION
if(config->libcurl) { if(global->libcurl) {
/* Cleanup the libcurl source output */ /* Cleanup the libcurl source output */
easysrc_cleanup(); easysrc_cleanup();
} }
@ -2305,30 +2380,24 @@ CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[])
} while(!result && operation); } while(!result && operation);
/* Set the current operation pointer */ /* Set the current operation pointer */
config->current = config->first; global->current = global->first;
/* Setup all transfers */
while(!result && config->current) {
result = operate_do(config, config->current, share);
config->current = config->current->next;
}
/* now run! */ /* now run! */
result = operate_transfers(config, share, result); result = operate_transfers(global, share, result);
curl_share_cleanup(share); curl_share_cleanup(share);
#ifndef CURL_DISABLE_LIBCURL_OPTION #ifndef CURL_DISABLE_LIBCURL_OPTION
if(config->libcurl) { if(global->libcurl) {
/* Cleanup the libcurl source output */ /* Cleanup the libcurl source output */
easysrc_cleanup(); easysrc_cleanup();
/* Dump the libcurl code if previously enabled */ /* Dump the libcurl code if previously enabled */
dumpeasysrc(config); dumpeasysrc(global);
} }
#endif #endif
} }
else else
helpf(config->errors, "out of memory\n"); helpf(global->errors, "out of memory\n");
} }
} }

View File

@ -37,6 +37,7 @@
void clean_getout(struct OperationConfig *config) void clean_getout(struct OperationConfig *config)
{ {
if(config) {
struct getout *next; struct getout *next;
struct getout *node = config->url_list; struct getout *node = config->url_list;
@ -50,6 +51,7 @@ void clean_getout(struct OperationConfig *config)
} }
config->url_list = NULL; config->url_list = NULL;
} }
}
bool output_expected(const char *url, const char *uploadfile) bool output_expected(const char *url, const char *uploadfile)
{ {

View File

@ -33,7 +33,7 @@
if(!tool_setopt_skip(opt)) { \ if(!tool_setopt_skip(opt)) { \
result = (v); \ result = (v); \
if(result) \ if(result) \
goto show_error; \ break; \
} \ } \
} WHILE_FALSE } WHILE_FALSE

View File

@ -488,6 +488,9 @@ void glob_cleanup(URLGlob* glob)
size_t i; size_t i;
int elem; int elem;
if(!glob)
return;
for(i = 0; i < glob->size; i++) { for(i = 0; i < glob->size; i++) {
if((glob->pattern[i].type == UPTSet) && if((glob->pattern[i].type == UPTSet) &&
(glob->pattern[i].content.Set.elements)) { (glob->pattern[i].content.Set.elements)) {

View File

@ -29,7 +29,7 @@ http
HTTP, send cookies when using custom Host: HTTP, send cookies when using custom Host:
</name> </name>
<command> <command>
http://%HOSTIP:%HTTPPORT/we/want/62 http://%HOSTIP:%HTTPPORT/we/want?hoge=fuga -b log/jar62.txt -H "Host: www.host.foo.com" http://%HOSTIP:%HTTPPORT/we/want/62 http://%HOSTIP:%HTTPPORT/we/want/62?hoge=fuga -b log/jar62.txt -H "Host: www.host.foo.com"
</command> </command>
<file name="log/jar62.txt"> <file name="log/jar62.txt">
# Netscape HTTP Cookie File # Netscape HTTP Cookie File
@ -55,7 +55,7 @@ Host: www.host.foo.com
Accept: */* Accept: */*
Cookie: test2=yes; test=yes Cookie: test2=yes; test=yes
GET /we/want?hoge=fuga HTTP/1.1 GET /we/want/62?hoge=fuga HTTP/1.1
Host: www.host.foo.com Host: www.host.foo.com
Accept: */* Accept: */*
Cookie: test2=yes; test=yes Cookie: test2=yes; test=yes