curl: add --fail-with-body

Prevent both --fail and --fail-with-body on the same command line.

Verify with test 349, 360 and 361.

Closes #6449
This commit is contained in:
Daniel Stenberg 2021-02-11 08:30:39 +01:00
parent c386a0df44
commit 8a964cb217
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
13 changed files with 179 additions and 14 deletions

View File

@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@ -61,10 +61,11 @@ DPAGES = \
dump-header.d \
egd-file.d \
engine.d \
etag-compare.d \
etag-save.d \
etag-compare.d \
etag-save.d \
expect100-timeout.d \
fail-early.d \
fail-with-body.d \
fail.d \
false-start.d \
form-string.d \

View File

@ -0,0 +1,16 @@
Long: fail-with-body
Protocols: HTTP
Help: Fail on HTTP errors but save the body
Category: http output
Added: 7.76.0
See-also: fail
---
Return an error on server errors where the HTTP response code is 400 or
greater). In normal cases when an HTTP server fails to deliver a document, it
returns an HTML document stating so (which often also describes why and
more). This flag will still allow curl to outputting and save that content but
also to return error 22.
This is an alternative option to --fail which makes curl fail for the same
circumstances but without saving the content.

View File

@ -2,6 +2,7 @@ Long: fail
Short: f
Protocols: HTTP
Help: Fail silently (no output at all) on HTTP errors
See-also: fail-with-body
Category: important http
---
Fail silently (no output at all) on server errors. This is mostly done to

View File

@ -59,6 +59,7 @@
--expect100-timeout 7.47.0
--fail (-f) 4.0
--fail-early 7.52.0
--fail-with-body 7.76.0
--false-start 7.42.0
--form (-F) 5.0
--form-string 7.13.2

View File

@ -118,6 +118,7 @@ struct OperationConfig {
bool use_ascii; /* select ascii or text transfer */
bool autoreferer; /* automatically set referer */
bool failonerror; /* fail on (HTTP) errors */
bool failwithbody; /* fail on (HTTP) errors but still store body */
bool show_headers; /* show headers to data output */
bool no_body; /* don't get the body */
bool dirlistonly; /* only get the FTP dir list */

View File

@ -280,6 +280,7 @@ static const struct LongShort aliases[]= {
{"fa", "fail-early", ARG_BOOL},
{"fb", "styled-output", ARG_BOOL},
{"fc", "mail-rcpt-allowfails", ARG_BOOL},
{"fd", "fail-with-body", ARG_BOOL},
{"F", "form", ARG_STRING},
{"Fs", "form-string", ARG_STRING},
{"g", "globoff", ARG_BOOL},
@ -1766,8 +1767,17 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
case 'c': /* --mail-rcpt-allowfails */
config->mail_rcpt_allowfails = toggle;
break;
case 'd': /* --fail-with-body */
config->failwithbody = toggle;
break;
default: /* --fail (hard on errors) */
config->failonerror = toggle;
break;
}
if(config->failonerror && config->failwithbody) {
errorf(config->global, "You must select either --fail or "
"--fail-with-body, not both.\n");
return PARAM_BAD_USE;
}
break;
case 'F':

View File

@ -268,6 +268,9 @@ static const struct helptxt helptext[] = {
{" --fail-early",
"Fail on first transfer error, do not continue",
CURLHELP_CURL},
{" --fail-with-body",
"Fail on HTTP errors but save the body",
CURLHELP_HTTP | CURLHELP_OUTPUT},
{" --false-start",
"Enable TLS False Start",
CURLHELP_TLS},

View File

@ -369,7 +369,18 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
if(result == CURLE_PEER_FAILED_VERIFICATION)
fputs(CURL_CA_CERT_ERRORMSG, global->errors);
}
else if(config->failwithbody) {
/* if HTTP response >= 400, return error */
long code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
if(code >= 400) {
if(global->showerror)
fprintf(global->errors,
"curl: (%d) The requested URL returned error: %ld\n",
CURLE_HTTP_RETURNED_ERROR, code);
result = CURLE_HTTP_RETURNED_ERROR;
}
}
/* Set file extended attributes */
if(!result && config->xattr && outs->fopened && outs->stream) {
int rc = fwrite_xattr(curl, fileno(outs->stream));
@ -670,7 +681,7 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
free(per->outfile);
free(per->uploadfile);
return CURLE_OK;
return result;
}
static void single_transfer_cleanup(struct OperationConfig *config)
@ -2326,18 +2337,14 @@ static CURLcode serial_transfers(struct GlobalConfig *global,
#endif
result = curl_easy_perform(per->curl);
/* store the result of the actual transfer */
returncode = result;
result = post_per_transfer(global, per, result, &retry, &delay);
returncode = post_per_transfer(global, per, result, &retry, &delay);
if(retry) {
tool_go_sleep(delay);
continue;
}
/* Bail out upon critical errors or --fail-early */
if(result || is_fatal_error(returncode) ||
(returncode && global->fail_early))
if(is_fatal_error(returncode) || (returncode && global->fail_early))
bailout = TRUE;
else {
/* setup the next one just before we delete this */

View File

@ -58,9 +58,10 @@ test307 test308 test309 test310 test311 test312 test313 test314 test315 \
test316 test317 test318 test319 test320 test321 test322 test323 test324 \
test325 test326 test327 test328 test329 test330 test331 test332 test333 \
test334 test335 test336 test337 test338 test339 test340 test341 test342 \
test343 test344 test345 test346 test347 test348 \
test350 test351 test352 test353 test354 test355 test356 test357 test358 \
test359 \
test343 test344 test345 test346 test347 test348 test349 test350 test351 \
test352 test353 test354 test355 test356 test357 test358 test359 test360 \
test361 \
\
test393 test394 test395 test396 test397 \
\
test400 test401 test402 test403 test404 test405 test406 test407 test408 \

View File

@ -3,6 +3,7 @@
<keywords>
HTTP
HTTP GET
--fail
</keywords>
</info>
# Server-side

45
tests/data/test349 Normal file
View File

@ -0,0 +1,45 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
--fail-with-body
</keywords>
</info>
# Server-side
<reply>
<data>
HTTP/1.0 404 BAD BOY swsclose
Content-Type: text/html
This silly page doesn't reaaaaaly exist so you should not get it.
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP GET --fail-with-body on HTTP error return
</name>
<command>
http://%HOSTIP:%HTTPPORT/349 --fail-with-body
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol>
GET /349 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
</protocol>
<errorcode>
22
</errorcode>
</verify>
</testcase>

28
tests/data/test360 Normal file
View File

@ -0,0 +1,28 @@
<testcase>
<info>
<keywords>
--fail
--fail-with-body
</keywords>
</info>
# Client-side
<client>
<server>
http
</server>
<name>
Error on both --fail-with-body and --fail
</name>
<command>
http://%HOSTIP:%HTTPPORT/360 --fail-with-body --fail
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<errorcode>
2
</errorcode>
</verify>
</testcase>

50
tests/data/test361 Normal file
View File

@ -0,0 +1,50 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
--fail-with-body
</keywords>
</info>
# Server-side
<reply>
<data>
HTTP/1.0 404 BAD BOY swsclose
Content-Type: text/html
This silly page doesn't reaaaaaly exist so you should not get it.
</data>
</reply>
# Client-side
<client>
<server>
http
</server>
<name>
HTTP GET --fail-with-body on HTTP error return - twice
</name>
<command>
http://%HOSTIP:%HTTPPORT/361 http://%HOSTIP:%HTTPPORT/361 --fail-with-body
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol>
GET /361 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
GET /361 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
User-Agent: curl/%VERSION
Accept: */*
</protocol>
<errorcode>
22
</errorcode>
</verify>
</testcase>