Apply delay only once per entry, no matter how many retry.

This commit is contained in:
Jean-Christophe Amiel 2024-06-18 16:50:58 +02:00
parent 438fbf721a
commit 90bb395bae
No known key found for this signature in database
GPG Key ID: 07FF11CFD55356CC
12 changed files with 196 additions and 175 deletions

View File

@ -1,17 +1,17 @@
<mxfile host="app.diagrams.net" modified="2024-06-18T11:11:48.851Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15" etag="JIFONLN-45vT0ClTlasG" version="24.5.5" type="device">
<mxfile host="app.diagrams.net" modified="2024-06-18T14:42:17.996Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.3.1 Safari/605.1.15" etag="q-OuEenRq_H6SQyWZ8w5" version="24.5.5" type="device">
<diagram name="Page-1" id="Xiz49D0FumDPF5wHgXgK">
<mxGraphModel dx="2020" dy="1139" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2000" pageHeight="2000" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="rCkY846AgjEdWc3Gds_q-5" value="&lt;b&gt;START&lt;/b&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;fillColor=none;strokeColor=#FF0288;fillStyle=auto;fontColor=#FF0288;" vertex="1" parent="1">
<mxGeometry x="360" y="90" width="80" height="40" as="geometry" />
<mxGeometry x="360" y="100" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-7" value="Entry&amp;nbsp;&lt;div&gt;to run?&lt;/div&gt;" style="strokeWidth=0;html=1;shape=mxgraph.flowchart.decision;whiteSpace=wrap;spacingTop=-3;fillColor=#CCFF66;strokeColor=none;fontStyle=0" vertex="1" parent="1">
<mxGeometry x="350" y="170" width="100" height="80" as="geometry" />
<mxGeometry x="350" y="180" width="100" height="80" as="geometry" />
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-11" value="&lt;font color=&quot;#6c8ebf&quot;&gt;Eval&lt;/font&gt;&lt;div&gt;&lt;font color=&quot;#6c8ebf&quot;&gt;entry options&lt;/font&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=0;arcSize=18;labelBorderColor=none;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="350" y="290" width="100" height="50" as="geometry" />
<mxGeometry x="350" y="300" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-12" value="" style="endArrow=block;html=1;rounded=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;endFill=1;" edge="1" parent="1" source="rCkY846AgjEdWc3Gds_q-5" target="rCkY846AgjEdWc3Gds_q-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
@ -31,7 +31,7 @@
</mxGeometry>
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-15" value="&lt;b&gt;SUCCESS&lt;/b&gt;" style="strokeWidth=2;html=1;shape=mxgraph.flowchart.start_1;whiteSpace=wrap;fillColor=none;strokeColor=#FF0288;fillStyle=auto;fontColor=#FF0288;" vertex="1" parent="1">
<mxGeometry x="220" y="190" width="80" height="40" as="geometry" />
<mxGeometry x="220" y="200" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-16" value="" style="endArrow=block;html=1;rounded=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;endFill=1;dashed=1;" edge="1" parent="1" source="rCkY846AgjEdWc3Gds_q-7" target="rCkY846AgjEdWc3Gds_q-15">
<mxGeometry width="50" height="50" relative="1" as="geometry">
@ -68,7 +68,7 @@
</mxGeometry>
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-23" value="&lt;font color=&quot;#6c8ebf&quot;&gt;&lt;span style=&quot;caret-color: rgb(108, 142, 191);&quot;&gt;Sleep delay&lt;/span&gt;&lt;/font&gt;" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;fontStyle=0;arcSize=18;labelBorderColor=none;strokeWidth=2;" vertex="1" parent="1">
<mxGeometry x="200" y="600" width="100" height="40" as="geometry" />
<mxGeometry x="200" y="600" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="rCkY846AgjEdWc3Gds_q-25" value="" style="endArrow=block;html=1;rounded=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;endFill=1;exitX=0.2;exitY=0.75;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="rCkY846AgjEdWc3Gds_q-20" target="rCkY846AgjEdWc3Gds_q-23">
<mxGeometry width="50" height="50" relative="1" as="geometry">

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -26,7 +26,7 @@ def main():
+ get_files("tests_failed/*." + extension)
+ get_files("tests_failed_not_linted/*." + extension)
+ get_files("tests_error_parser/*." + extension)
+ get_files("ssl/*." + extension)
# + get_files("ssl/*." + extension)
)
for f in sorted(script_files):
test_script.test(f)

View File

@ -42,10 +42,6 @@
* ------------------------------------------------------------------------------
* Executing entry 1
*
* Entry options:
* retry: 2
* retry-interval: 0
*
* Cookie store:
*
* Request:
@ -83,10 +79,6 @@
* ------------------------------------------------------------------------------
* Executing entry 1
*
* Entry options:
* retry: 2
* retry-interval: 0
*
* Cookie store:
*
* Request:

View File

@ -31,7 +31,7 @@
* Entry options:
* delay: 1000
*
* Delay entry 2 (x1 by 1000 ms)
* Delay entry 2 (pause 1000 ms)
*
* Cookie store:
*

View File

@ -7,7 +7,7 @@
* Entry options:
* delay: 5000
*
* Delay entry 1 (x1 by 5000 ms)
* Delay entry 1 (pause 5000 ms)
*
* Cookie store:
*
@ -41,7 +41,7 @@
* Entry options:
* delay: 5000
*
* Delay entry 1 (x1 by 5000 ms)
* Delay entry 1 (pause 5000 ms)
*
* Cookie store:
*
@ -75,7 +75,7 @@
* Entry options:
* delay: 5000
*
* Delay entry 1 (x1 by 5000 ms)
* Delay entry 1 (pause 5000 ms)
*
* Cookie store:
*
@ -109,7 +109,7 @@
* Entry options:
* delay: 5000
*
* Delay entry 1 (x1 by 5000 ms)
* Delay entry 1 (pause 5000 ms)
*
* Cookie store:
*

View File

@ -72,10 +72,6 @@
* ------------------------------------------------------------------------------
* Executing entry 2
*
* Entry options:
* retry: 10
* retry-interval: 100
*
* Cookie store:
*
* Request:
@ -114,10 +110,6 @@
* ------------------------------------------------------------------------------
* Executing entry 2
*
* Entry options:
* retry: 10
* retry-interval: 100
*
* Cookie store:
*
* Request:
@ -156,10 +148,6 @@
* ------------------------------------------------------------------------------
* Executing entry 2
*
* Entry options:
* retry: 10
* retry-interval: 100
*
* Cookie store:
*
* Request:
@ -198,10 +186,6 @@
* ------------------------------------------------------------------------------
* Executing entry 2
*
* Entry options:
* retry: 10
* retry-interval: 100
*
* Cookie store:
*
* Request:

View File

@ -142,7 +142,10 @@ def test_stderr(f, result):
actual = ignore_lines(decode_string(result.stderr))
if actual != expected:
print(">>> error in stderr")
print(f"actual: <{actual}>\nexpected: <{expected}>")
print("actual:")
print(actual)
print("expected:")
print(expected)
sys.exit(1)

View File

@ -140,7 +140,6 @@ pub fn run_entries(
let mut variables = variables.clone();
let mut entry_index = runner_options.from_entry.unwrap_or(1);
let n = runner_options.to_entry.unwrap_or(entries.len());
let mut retry_count = 1;
let default_verbosity = logger.verbosity;
let start = Instant::now();
let timestamp = Utc::now().timestamp();
@ -151,6 +150,7 @@ pub fn run_entries(
// The `entry_index` is not always incremented of each loop tick: an entry can be retried upon
// errors for instance. Each entry is executed with options that are computed from the global
// runner options and the "overridden" request options.
// See <docs/spec/runner/run_cycle.md>
loop {
if entry_index > n {
break;
@ -173,120 +173,74 @@ pub fn run_entries(
logger.verbosity = entry_verbosity;
}
logger.debug_important(
"------------------------------------------------------------------------------",
);
logger.debug_important(&format!("Executing entry {entry_index}"));
log_run_entry(entry_index, logger);
warn_deprecated(entry, logger);
// We can report the progression of the run for --test mode.
if let Some(listener) = listener {
listener.on_running(entry_index - 1, n);
}
// The real execution of the entry happens here, with the overridden entry options.
// The real execution of the entry happens here, first: we compute the overridden request
// options.
let options = options::get_entry_options(entry, runner_options, &mut variables, logger);
let mut entry_result = match &options {
Err(error) => EntryResult {
if let Err(error) = &options {
// If we have error evaluating request options, we consider it as a non retryable error
// and either break the runner or go to the next entries.
let entry_result = EntryResult {
entry_index,
source_info: entry.source_info(),
errors: vec![error.clone()],
..Default::default()
},
Ok(options) => {
if options.skip {
logger.debug("");
logger.debug_important(&format!("Entry {entry_index} has been skipped"));
entry_index += 1;
continue;
}
let delay = options.delay;
let delay_ms = delay.as_millis();
if delay_ms > 0 {
logger.debug("");
logger.debug_important(&format!(
"Delay entry {entry_index} (x{retry_count} by {delay_ms} ms)"
));
thread::sleep(delay);
};
entry::run(
entry,
entry_index,
&mut http_client,
&mut variables,
options,
logger,
)
}
};
// Check if we need to retry.
let mut has_error = !entry_result.errors.is_empty();
let (retry_opts, retry_interval) = match &options {
Ok(options) => (options.retry, options.retry_interval),
Err(_) => (runner_options.retry, runner_options.retry_interval),
};
// The retry threshold can only reached with a finite positive number of retries
let retry_max_reached = if let Retry::Finite(r) = retry_opts {
retry_count > r
} else {
false
};
// If `retry_max_reached` is true, we print now a warning, before displaying any assert
// error so any potential error is the last thing displayed to the user.
// If `retry_max_reached` is not true (for instance `retry`is true, or there is no error
// we first log the error and a potential warning about retrying.
if retry_max_reached {
logger.debug_important("Retry max count reached, no more retry");
logger.debug("");
}
// We logs eventual errors, only if we're not retrying the current entry...
// The retry does not take into account a possible output Error
let retry = !matches!(retry_opts, Retry::None) && !retry_max_reached && has_error;
// When --output is overridden on a request level, we output the HTTP response only if the
// call has succeeded.
if let Ok(RunnerOptions {
output: Some(output),
..
}) = options
{
if !has_error {
let source_info = get_output_source_info(entry);
if let Err(error) = entry_result.write_response(
&output,
&runner_options.context_dir,
stdout,
source_info,
) {
entry_result.errors.push(error);
has_error = true;
}
};
log_errors(&entry_result, content, false, logger);
entries_result.push(entry_result);
if runner_options.continue_on_error {
entry_index += 1;
continue;
} else {
break;
}
}
if has_error {
log_errors(&entry_result, content, retry, logger);
}
entries_result.push(entry_result);
let options = options.unwrap();
if retry {
let delay = retry_interval.as_millis();
// Should we skip?
if options.skip {
logger.debug("");
logger.debug_important(&format!(
"Retry entry {entry_index} (x{retry_count} pause {delay} ms)"
));
retry_count += 1;
// If we retry the entry, we do not want to display a 'blank' progress bar during the
// sleep delay. During the pause, we artificially show the previously erased progress
// line.
thread::sleep(retry_interval);
logger.debug_important(&format!("Entry {entry_index} has been skipped"));
entry_index += 1;
continue;
}
// Should we delay?
let delay = options.delay;
let delay_ms = delay.as_millis();
if delay_ms > 0 {
logger.debug("");
logger.debug_important(&format!("Delay entry {entry_index} (pause {delay_ms} ms)"));
thread::sleep(delay);
};
// Loop for executing HTTP run requests, with optional retry. Only "HTTP" errors in options
// are taken into account for retry (errors while computing entry options and output error
// are not retried).
let results = run_request(
entry,
entry_index,
content,
&mut http_client,
&options,
&mut variables,
stdout,
logger,
);
let has_error = results.last().map_or(false, |r| !r.errors.is_empty());
entries_result.extend(results);
if let Some(post_entry) = runner_options.post_entry {
let exit = post_entry();
if exit {
@ -299,7 +253,6 @@ pub fn run_entries(
// We pass to the next entry
entry_index += 1;
retry_count = 1;
}
let time_in_ms = start.elapsed().as_millis();
@ -314,6 +267,90 @@ pub fn run_entries(
}
}
/// Runs an HTTP request and optional retry it until there are no HTTP errors. Returns a list of
/// [`EntryResult`].
#[allow(clippy::too_many_arguments)]
fn run_request(
entry: &Entry,
entry_index: usize,
content: &str,
http_client: &mut Client,
options: &RunnerOptions,
variables: &mut HashMap<String, Value>,
stdout: &mut Stdout,
logger: &mut Logger,
) -> Vec<EntryResult> {
let mut results = vec![];
let mut retry_count = 1;
loop {
let mut result = entry::run(entry, entry_index, http_client, variables, options, logger);
// Check if we need to retry.
let mut has_error = !result.errors.is_empty();
// The retry threshold can only be reached with a finite positive number of retries
let retry_max_reached = if let Retry::Finite(r) = options.retry {
retry_count > r
} else {
false
};
// If `retry_max_reached` is true, we print now a warning, before displaying any assert
// error so any potential error is the last thing displayed to the user.
// If `retry_max_reached` is not true (for instance `retry`is true, or there is no error
// we first log the error and a potential warning about retrying.
if retry_max_reached {
logger.debug_important("Retry max count reached, no more retry");
logger.debug("");
}
// We log eventual errors, only if we're not retrying the current entry...
// The retry does not take into account a possible output Error
let retry = !matches!(options.retry, Retry::None) && !retry_max_reached && has_error;
// When --output is overridden on a request level, we output the HTTP response only if the
// call has succeeded. Output errors are not taken into account for retrying requests.
if let Some(output) = &options.output {
if !has_error {
let source_info = get_output_source_info(entry);
if let Err(error) =
result.write_response(output, &options.context_dir, stdout, source_info)
{
result.errors.push(error);
has_error = true;
}
}
}
if has_error {
log_errors(&result, content, retry, logger);
}
results.push(result);
// No retry, we leave the HTTP run requests loop.
if !retry {
break;
}
let delay = options.retry_interval.as_millis();
logger.debug("");
logger.debug_important(&format!(
"Retry entry {entry_index} (x{retry_count} pause {delay} ms)"
));
retry_count += 1;
// If we retry the entry, we do not want to display a 'blank' progress bar during the
// sleep delay. During the pause, we artificially show the previously erased progress
// line.
thread::sleep(options.retry_interval);
// TODO: We keep this log because we don't want to change stderr with the changes
// introduced by <https://github.com/Orange-OpenSource/hurl/issues/1973>
log_run_entry(entry_index, logger);
}
results
}
/// Use source_info from output option if this option has been defined
fn get_output_source_info(entry: &Entry) -> SourceInfo {
let mut source_info = entry.source_info();
@ -509,6 +546,14 @@ fn log_errors(entry_result: &EntryResult, content: &str, retry: bool, logger: &m
.for_each(|error| logger.error_runtime_rich(content, error, entry_result.source_info));
}
/// Logs the header indicating the begin of the entry run.
fn log_run_entry(entry_index: usize, logger: &mut Logger) {
logger.debug_important(
"------------------------------------------------------------------------------",
);
logger.debug_important(&format!("Executing entry {entry_index}"));
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -41,12 +41,12 @@ pub fn get_entry_options(
// When used globally (on the command line), `--output` writes the last successful request
// to `output` file. We don't want to output every entry's response, so we initialise
// output to `None`.
let mut runner_options = RunnerOptions {
let mut entry_options = RunnerOptions {
output: None,
..runner_options
};
if !has_options(entry) {
return Ok(runner_options);
return Ok(entry_options);
}
logger.debug("");
@ -58,31 +58,31 @@ pub fn get_entry_options(
match &option.kind {
OptionKind::AwsSigV4(value) => {
let value = eval_template(value, variables)?;
runner_options.aws_sigv4 = Some(value);
entry_options.aws_sigv4 = Some(value);
}
OptionKind::CaCertificate(filename) => {
let value = eval_template(filename, variables)?;
runner_options.cacert_file = Some(value);
entry_options.cacert_file = Some(value);
}
OptionKind::ClientCert(filename) => {
let value = eval_template(filename, variables)?;
runner_options.client_cert_file = Some(value);
entry_options.client_cert_file = Some(value);
}
OptionKind::ClientKey(filename) => {
let value = eval_template(filename, variables)?;
runner_options.client_key_file = Some(value);
entry_options.client_key_file = Some(value);
}
OptionKind::Compressed(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.compressed = value;
entry_options.compressed = value;
}
OptionKind::ConnectTo(value) => {
let value = eval_template(value, variables)?;
runner_options.connects_to.push(value);
entry_options.connects_to.push(value);
}
OptionKind::Delay(value) => {
let value = eval_natural_option(value, variables)?;
runner_options.delay = Duration::from_millis(value);
entry_options.delay = Duration::from_millis(value);
}
// HTTP version options (such as http1.0, http1.1, http2 etc...) are activated
// through a flag. In an `[Options]` section, the signification of such a flag is:
@ -111,51 +111,51 @@ pub fn get_entry_options(
OptionKind::Http10(value) => {
let value = eval_boolean_option(value, variables)?;
if value {
runner_options.http_version = RequestedHttpVersion::Http10;
entry_options.http_version = RequestedHttpVersion::Http10;
}
}
OptionKind::Http11(value) => {
let value = eval_boolean_option(value, variables)?;
if value {
runner_options.http_version = RequestedHttpVersion::Http11;
entry_options.http_version = RequestedHttpVersion::Http11;
} else {
runner_options.http_version = RequestedHttpVersion::Http10;
entry_options.http_version = RequestedHttpVersion::Http10;
}
}
OptionKind::Http2(value) => {
let value = eval_boolean_option(value, variables)?;
if value {
runner_options.http_version = RequestedHttpVersion::Http2;
entry_options.http_version = RequestedHttpVersion::Http2;
} else {
runner_options.http_version = RequestedHttpVersion::Http11;
entry_options.http_version = RequestedHttpVersion::Http11;
}
}
OptionKind::Http3(value) => {
let value = eval_boolean_option(value, variables)?;
if value {
runner_options.http_version = RequestedHttpVersion::Http3;
entry_options.http_version = RequestedHttpVersion::Http3;
} else {
runner_options.http_version = RequestedHttpVersion::Http2;
entry_options.http_version = RequestedHttpVersion::Http2;
}
}
OptionKind::FollowLocation(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.follow_location = value;
entry_options.follow_location = value;
}
OptionKind::FollowLocationTrusted(value) => {
let value = eval_boolean_option(value, variables)?;
if value {
runner_options.follow_location = true;
entry_options.follow_location = true;
}
runner_options.follow_location_trusted = value;
entry_options.follow_location_trusted = value;
}
OptionKind::Insecure(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.insecure = value;
entry_options.insecure = value;
}
OptionKind::IpV4(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.ip_resolve = if value {
entry_options.ip_resolve = if value {
IpResolve::IpV4
} else {
IpResolve::IpV6
@ -163,7 +163,7 @@ pub fn get_entry_options(
}
OptionKind::IpV6(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.ip_resolve = if value {
entry_options.ip_resolve = if value {
IpResolve::IpV6
} else {
IpResolve::IpV4
@ -171,56 +171,56 @@ pub fn get_entry_options(
}
OptionKind::MaxRedirect(value) => {
let value = eval_natural_option(value, variables)?;
runner_options.max_redirect = Some(value as usize);
entry_options.max_redirect = Some(value as usize);
}
OptionKind::NetRc(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.netrc = value;
entry_options.netrc = value;
}
OptionKind::NetRcFile(value) => {
let filename = eval_template(value, variables)?;
runner_options.netrc_file = Some(filename);
entry_options.netrc_file = Some(filename);
}
OptionKind::NetRcOptional(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.netrc_optional = value;
entry_options.netrc_optional = value;
}
OptionKind::Output(output) => {
let filename = eval_template(output, variables)?;
let output = Output::new(&filename);
runner_options.output = Some(output);
entry_options.output = Some(output);
}
OptionKind::PathAsIs(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.path_as_is = value;
entry_options.path_as_is = value;
}
OptionKind::Proxy(value) => {
let value = eval_template(value, variables)?;
runner_options.proxy = Some(value);
entry_options.proxy = Some(value);
}
OptionKind::Resolve(value) => {
let value = eval_template(value, variables)?;
runner_options.resolves.push(value);
entry_options.resolves.push(value);
}
OptionKind::Retry(value) => {
let value = eval_retry_option(value, variables)?;
runner_options.retry = value;
entry_options.retry = value;
}
OptionKind::RetryInterval(value) => {
let value = eval_natural_option(value, variables)?;
runner_options.retry_interval = Duration::from_millis(value);
entry_options.retry_interval = Duration::from_millis(value);
}
OptionKind::Skip(value) => {
let value = eval_boolean_option(value, variables)?;
runner_options.skip = value;
entry_options.skip = value;
}
OptionKind::UnixSocket(value) => {
let value = eval_template(value, variables)?;
runner_options.unix_socket = Some(value);
entry_options.unix_socket = Some(value);
}
OptionKind::User(value) => {
let value = eval_template(value, variables)?;
runner_options.user = Some(value);
entry_options.user = Some(value);
}
OptionKind::Variable(VariableDefinition { name, value, .. }) => {
let value = eval_variable_value(value, variables)?;
@ -240,7 +240,7 @@ pub fn get_entry_options(
}
}
}
Ok(runner_options)
Ok(entry_options)
}
/// Logs an entry option.

View File

@ -73,7 +73,7 @@ pub fn eval_html(html: &str, expr: &str) -> Result<Value, XpathError> {
/// - <https://github.com/Orange-OpenSource/hurl/issues/1535>
/// These two functions should be removed when the issue is fixed in libxml crate.
fn try_usize_to_i32(value: usize) -> Result<i32, XmlParseError> {
if cfg!(target_pointer_width = "16") || (value < i32::max_value() as usize) {
if cfg!(target_pointer_width = "16") || (value < i32::MAX as usize) {
// Cannot safely use our value comparison, but the conversion if always safe.
// Or, if the value can be safely represented as a 32-bit signed integer.
Ok(value as i32)

View File

@ -59,10 +59,7 @@ pub fn url(arg_matches: &ArgMatches) -> String {
}
pub fn headers(arg_matches: &ArgMatches) -> Vec<String> {
let mut headers = match get_strings(arg_matches, "headers") {
None => vec![],
Some(v) => v,
};
let mut headers = get_strings(arg_matches, "headers").unwrap_or_default();
if !has_content_type(&headers) {
if let Some(data) = get_string(arg_matches, "data") {
if !data.starts_with('@') {