curl: return error if etag options are used with multiple URLs

And document it.

Add tests 484 and 485

Fixes #15729
Reported-by: Tamir Duberstein
Closes #15731
This commit is contained in:
Daniel Stenberg 2024-12-12 17:03:59 +01:00
parent 0439499170
commit a300879b63
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
7 changed files with 113 additions and 6 deletions

View File

@ -25,3 +25,5 @@ line with the desired ETag. An empty file is parsed as an empty ETag.
Use the option --etag-save to first save the ETag from a response, and then Use the option --etag-save to first save the ETag from a response, and then
use this option to compare against the saved ETag in a subsequent request. use this option to compare against the saved ETag in a subsequent request.
Use this option with a single URL only.

View File

@ -17,6 +17,6 @@ Example:
# `--etag-save` # `--etag-save`
Save an HTTP ETag to the specified file. An ETag is a caching related header, Save an HTTP ETag to the specified file. An ETag is a caching related header,
usually returned in a response. usually returned in a response. Use this option with a single URL only.
If no ETag is sent by the server, an empty file is created. If no ETag is sent by the server, an empty file is created.

View File

@ -130,6 +130,7 @@ struct OperationConfig {
struct getout *url_get; /* point to the node to fill in URL */ struct getout *url_get; /* point to the node to fill in URL */
struct getout *url_out; /* point to the node to fill in outfile */ struct getout *url_out; /* point to the node to fill in outfile */
struct getout *url_ul; /* point to the node to fill in upload */ struct getout *url_ul; /* point to the node to fill in upload */
size_t num_urls; /* number of URLs added to the list */
#ifndef CURL_DISABLE_IPFS #ifndef CURL_DISABLE_IPFS
char *ipfs_gateway; char *ipfs_gateway;
#endif /* !CURL_DISABLE_IPFS */ #endif /* !CURL_DISABLE_IPFS */

View File

@ -1019,7 +1019,8 @@ const struct LongShort *findlongopt(const char *opt)
sizeof(aliases[0]), findarg); sizeof(aliases[0]), findarg);
} }
static ParameterError parse_url(struct OperationConfig *config, static ParameterError parse_url(struct GlobalConfig *global,
struct OperationConfig *config,
const char *nextarg) const char *nextarg)
{ {
ParameterError err = PARAM_OK; ParameterError err = PARAM_OK;
@ -1050,6 +1051,11 @@ static ParameterError parse_url(struct OperationConfig *config,
/* fill in the URL */ /* fill in the URL */
err = getstr(&url->url, nextarg, DENY_BLANK); err = getstr(&url->url, nextarg, DENY_BLANK);
url->flags |= GETOUT_URL; url->flags |= GETOUT_URL;
if((++config->num_urls > 1) && (config->etag_save_file ||
config->etag_compare_file)) {
errorf(global, "The etag options only work on a single URL");
return PARAM_BAD_USE;
}
} }
return err; return err;
} }
@ -1911,7 +1917,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->xattr = toggle; config->xattr = toggle;
break; break;
case C_URL: /* --url */ case C_URL: /* --url */
err = parse_url(config, nextarg); err = parse_url(global, config, nextarg);
break; break;
case C_FTP_SSL: /* --ftp-ssl */ case C_FTP_SSL: /* --ftp-ssl */
case C_SSL: /* --ssl */ case C_SSL: /* --ssl */
@ -2549,10 +2555,20 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->socks5_auth &= ~CURLAUTH_GSSAPI; config->socks5_auth &= ~CURLAUTH_GSSAPI;
break; break;
case C_ETAG_SAVE: /* --etag-save */ case C_ETAG_SAVE: /* --etag-save */
err = getstr(&config->etag_save_file, nextarg, DENY_BLANK); if(config->num_urls > 1) {
errorf(global, "The etag options only work on a single URL");
err = PARAM_BAD_USE;
}
else
err = getstr(&config->etag_save_file, nextarg, DENY_BLANK);
break; break;
case C_ETAG_COMPARE: /* --etag-compare */ case C_ETAG_COMPARE: /* --etag-compare */
err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK); if(config->num_urls > 1) {
errorf(global, "The etag options only work on a single URL");
err = PARAM_BAD_USE;
}
else
err = getstr(&config->etag_compare_file, nextarg, DENY_BLANK);
break; break;
case C_CURVES: /* --curves */ case C_CURVES: /* --curves */
err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK); err = getstr(&config->ssl_ec_curves, nextarg, DENY_BLANK);

View File

@ -78,7 +78,7 @@ test444 test445 test446 test447 test448 test449 test450 test451 test452 \
test453 test454 test455 test456 test457 test458 test459 test460 test461 \ test453 test454 test455 test456 test457 test458 test459 test460 test461 \
test462 test463 test467 test468 test469 test470 test471 test472 test473 \ test462 test463 test467 test468 test469 test470 test471 test472 test473 \
test474 test475 test476 test477 test478 test479 test480 test481 test482 \ test474 test475 test476 test477 test478 test479 test480 test481 test482 \
test483 \ test483 test484 test485 \
test490 test491 test492 test493 test494 test495 test496 test497 test498 \ test490 test491 test492 test493 test494 test495 test496 test497 test498 \
test499 test500 test501 test502 test503 test504 test505 test506 test507 \ test499 test500 test501 test502 test503 test504 test505 test506 test507 \
test508 test509 test510 test511 test512 test513 test514 test515 test516 \ test508 test509 test510 test511 test512 test513 test514 test515 test516 \

44
tests/data/test484 Normal file
View File

@ -0,0 +1,44 @@
<testcase>
<info>
<keywords>
HTTP
etags
</keywords>
</info>
#
# Server-side
<reply>
</reply>
#
# Client-side
<client>
<server>
none
</server>
<name>
Use --etag-compare and -save with more than one URL
</name>
<command>
http://example.com/%TESTNUMBER --etag-compare %LOGDIR/etag%TESTNUMBER --etag-save %LOGDIR/etag%TESTNUMBER --url http://example.net/fooo
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<errorcode>
2
</errorcode>
<stderr mode="text">
curl: The etag options only work on a single URL
curl: option --url: is badly used here
%if manual
curl: try 'curl --help' or 'curl --manual' for more information
%else
curl: try 'curl --help' for more information
%endif
</stderr>
</verify>
</testcase>

44
tests/data/test485 Normal file
View File

@ -0,0 +1,44 @@
<testcase>
<info>
<keywords>
HTTP
etags
</keywords>
</info>
#
# Server-side
<reply>
</reply>
#
# Client-side
<client>
<server>
none
</server>
<name>
Use --etag-compare and -save with more than one URL, URLs specified first
</name>
<command>
http://example.com/%TESTNUMBER http://example.net/fooo --etag-save %LOGDIR/etag%TESTNUMBER
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<errorcode>
2
</errorcode>
<stderr mode="text">
curl: The etag options only work on a single URL
curl: option --etag-save: is badly used here
%if manual
curl: try 'curl --help' or 'curl --manual' for more information
%else
curl: try 'curl --help' for more information
%endif
</stderr>
</verify>
</testcase>