Skip to content

Commit

Permalink
Using explicit stdout output
Browse files Browse the repository at this point in the history
  • Loading branch information
fabricereix committed Jan 16, 2024
1 parent 0b5c164 commit 605fbc6
Show file tree
Hide file tree
Showing 19 changed files with 141 additions and 41 deletions.
30 changes: 30 additions & 0 deletions integration/hurl/tests_ok/stdout.err.pattern
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
* ------------------------------------------------------------------------------
* Executing entry 1
*
* Entry options:
* output: -
*
* Cookie store:
*
* Request:
* GET http://localhost:8000/stdout/text
*
* Request can be run with the following curl command:
* curl --output - 'http://localhost:8000/stdout/text'
*
> GET /stdout/text HTTP/1.1
> Host: localhost:8000
> Accept: */*
> User-Agent: hurl/~~~
>
* Response: (received 5 bytes in ~~~ ms)
*
< HTTP/1.1 200 OK
< Server: Werkzeug/~~~
< Date: ~~~
< Content-Type: text/html; charset=utf-8
< Content-Length: 5
< Server: Flask Server
< Connection: close
<
*
8 changes: 8 additions & 0 deletions integration/hurl/tests_ok/stdout.hurl
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
GET http://localhost:8000/stdout/text
[Options]
output: -
HTTP 200
`Hello`



1 change: 1 addition & 0 deletions integration/hurl/tests_ok/stdout.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
HelloHello
5 changes: 5 additions & 0 deletions integration/hurl/tests_ok/stdout.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Set-StrictMode -Version latest
$ErrorActionPreference = 'Stop'
hurl --verbose --output - tests_ok/stdout.hurl


7 changes: 7 additions & 0 deletions integration/hurl/tests_ok/stdout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# coding=utf-8
from app import app


@app.route("/stdout/text")
def stdout_text():
return "Hello"
5 changes: 5 additions & 0 deletions integration/hurl/tests_ok/stdout.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash
set -Eeuo pipefail
hurl --verbose tests_ok/stdout.hurl


12 changes: 9 additions & 3 deletions packages/hurl/src/cli/options/matches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use hurl_core::ast::Retry;

use super::variables::{parse as parse_variable, parse_value};
use super::OptionsError;
use crate::cli::options::{ErrorFormat, HttpVersion, IpResolve};
use crate::cli::options::{ErrorFormat, HttpVersion, IpResolve, Output};
use crate::cli::OutputType;

pub fn cacert_file(arg_matches: &ArgMatches) -> Result<Option<String>, OptionsError> {
Expand Down Expand Up @@ -254,8 +254,14 @@ pub fn no_proxy(arg_matches: &ArgMatches) -> Option<String> {
get::<String>(arg_matches, "noproxy")
}

pub fn output(arg_matches: &ArgMatches) -> Option<String> {
get::<String>(arg_matches, "output")
pub fn output(arg_matches: &ArgMatches) -> Option<Output> {
get::<String>(arg_matches, "output").map(|filename| {
if filename == "-" {
Output::StdOut
} else {
Output::File(filename)
}
})
}

pub fn output_type(arg_matches: &ArgMatches) -> OutputType {
Expand Down
3 changes: 2 additions & 1 deletion packages/hurl/src/cli/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use std::time::Duration;
use clap::ArgMatches;
use hurl::http;
use hurl::http::RequestedHttpVersion;
use hurl::runner::Output;
use hurl::util::logger::{LoggerOptions, LoggerOptionsBuilder, Verbosity};
use hurl::util::path::ContextDir;
use hurl_core::ast::{Entry, Retry};
Expand Down Expand Up @@ -63,7 +64,7 @@ pub struct Options {
pub junit_file: Option<String>,
pub max_redirect: Option<usize>,
pub no_proxy: Option<String>,
pub output: Option<String>,
pub output: Option<Output>,
pub output_type: OutputType,
pub path_as_is: bool,
pub progress_bar: bool,
Expand Down
8 changes: 5 additions & 3 deletions packages/hurl/src/http/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::http::request_spec::*;
use crate::http::response::*;
use crate::http::timings::Timings;
use crate::http::{easy_ext, Call, Header, HttpError, Verbosity};
use crate::runner::Output;
use crate::util::logger::Logger;
use crate::util::path::ContextDir;

Expand Down Expand Up @@ -737,7 +738,7 @@ impl Client {
&mut self,
request_spec: &RequestSpec,
context_dir: &ContextDir,
output: Option<&str>,
output: Option<&Output>,
options: &ClientOptions,
) -> String {
let mut arguments = vec!["curl".to_string()];
Expand Down Expand Up @@ -973,7 +974,7 @@ mod tests {
let data = b"GET /hello HTTP/1.1\r\nHost: localhost:8000\r\n\r\n";
let lines = split_lines(data);
assert_eq!(lines.len(), 3);
assert_eq!(lines.get(0).unwrap().as_str(), "GET /hello HTTP/1.1");
assert_eq!(lines.first().unwrap().as_str(), "GET /hello HTTP/1.1");
assert_eq!(lines.get(1).unwrap().as_str(), "Host: localhost:8000");
assert_eq!(lines.get(2).unwrap().as_str(), "");
}
Expand Down Expand Up @@ -1118,7 +1119,8 @@ mod tests {
..Default::default()
};
let context_dir = ContextDir::default();
let output = Some("/tmp/foo.bin");
let file = Output::File("/tmp/foo.bin".to_string());
let output = Some(&file);
let options = ClientOptions {
aws_sigv4: Some("aws:amz:sts".to_string()),
cacert_file: Some("/etc/cert.pem".to_string()),
Expand Down
1 change: 1 addition & 0 deletions packages/hurl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ fn main() {
let success = hurl_result.success;

// We can output the result, either the raw body or a structured JSON representation.

let output_body = success
&& !opts.interactive
&& matches!(opts.output_type, cli::OutputType::ResponseBody);
Expand Down
6 changes: 3 additions & 3 deletions packages/hurl/src/output/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ pub fn write_json(
hurl_result: &HurlResult,
content: &str,
filename_in: &str,
filename_out: &Option<String>,
filename_out: &Option<Output>,
) -> Result<(), Error> {
let json_result = hurl_result.to_json(content, filename_in);
let serialized = serde_json::to_string(&json_result).unwrap();
let s = format!("{serialized}\n");
let bytes = s.into_bytes();
match filename_out {
Some(file) => Output::File(file.to_string()).write(&bytes)?,
None => Output::StdOut.write(&bytes)?,
Some(Output::File(file)) => Output::File(file.to_string()).write(&bytes)?,
_ => Output::StdOut.write(&bytes)?,
}
Ok(())
}
6 changes: 3 additions & 3 deletions packages/hurl/src/output/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub fn write_body(
filename_in: &str,
include_headers: bool,
color: bool,
filename_out: &Option<String>,
filename_out: &Option<Output>,
logger: &Logger,
) -> Result<(), Error> {
// By default, we output the body response bytes of the last entry
Expand Down Expand Up @@ -65,8 +65,8 @@ pub fn write_body(
output.extend(bytes);
}
match filename_out {
Some(file) => Output::File(file.to_string()).write(&output)?,
None => Output::StdOut.write(&output)?,
Some(Output::File(file)) => Output::File(file.to_string()).write(&output)?,
_ => runner::Output::StdOut.write(&output)?,
}
} else {
logger.info("No response has been received");
Expand Down
10 changes: 3 additions & 7 deletions packages/hurl/src/runner/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,9 @@ pub fn run(
log_request_spec(&http_request, logger);

logger.debug("Request can be run with the following curl command:");
let output = &runner_options.output;
let curl_command = http_client.curl_command_line(
&http_request,
context_dir,
output.as_deref(),
&client_options,
);
let output = runner_options.output.clone();
let curl_command =
http_client.curl_command_line(&http_request, context_dir, output.as_ref(), &client_options);
logger.debug(curl_command.as_str());
logger.debug("");

Expand Down
31 changes: 21 additions & 10 deletions packages/hurl/src/runner/hurl_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use hurl_core::parser;

use crate::http::Call;
use crate::runner::runner_options::RunnerOptions;
use crate::runner::{entry, options, EntryResult, HurlResult, RunnerError, Value};
use crate::runner::{entry, options, EntryResult, HurlResult, Output, RunnerError, Value};
use crate::util::logger::{ErrorFormat, Logger, LoggerOptions, LoggerOptionsBuilder};
use crate::{http, runner};

Expand Down Expand Up @@ -220,14 +220,25 @@ pub fn run(
// an error. If we want to treat it as an error, we've to add it to the current
// `entry_result` errors, and optionally deals with retry if we can't write to the
// specified path.
if !runner_options.context_dir.is_access_allowed(&output) {
let inner = RunnerError::UnauthorizedFileAccess {
path: PathBuf::from(output.clone()),
};
let error = runner::Error::new(entry.request.source_info, inner, false);
logger.warning(&error.fixme());
} else if let Err(error) = entry_result.write_response(output) {
logger.warning(&error.fixme());

let authorized = if let Output::File(filename) = output.clone() {
if !runner_options.context_dir.is_access_allowed(&filename) {
let inner = RunnerError::UnauthorizedFileAccess {
path: PathBuf::from(filename.clone()),
};
let error = runner::Error::new(entry.request.source_info, inner, false);
logger.warning(&error.fixme());
false
} else {
true
}
} else {
true
};
if authorized {
if let Err(error) = entry_result.write_response(&output) {
logger.warning(&error.fixme());
}
}
}
}
Expand Down Expand Up @@ -508,7 +519,7 @@ mod test {
let non_default_options = get_non_default_options(&options);
assert_eq!(non_default_options.len(), 1);

let first_non_default = non_default_options.get(0).unwrap();
let first_non_default = non_default_options.first().unwrap();

assert_eq!(first_non_default.0, "delay");
assert_eq!(first_non_default.1, "500ms");
Expand Down
13 changes: 9 additions & 4 deletions packages/hurl/src/runner/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use hurl_core::ast::{

use crate::http::{IpResolve, RequestedHttpVersion};
use crate::runner::template::{eval_expression, eval_template};
use crate::runner::{Error, Number, RunnerError, RunnerOptions, Value};
use crate::runner::{Error, Number, Output, RunnerError, RunnerOptions, Value};
use crate::util::logger::{Logger, Verbosity};

/// Returns a new [`RunnerOptions`] based on the `entry` optional Options section
Expand Down Expand Up @@ -166,9 +166,14 @@ pub fn get_entry_options(
let value = eval_natural_option(value, variables)?;
runner_options.max_redirect = Some(value as usize)
}
OptionKind::Output(filename) => {
let value = eval_template(filename, variables)?;
runner_options.output = Some(value)
OptionKind::Output(output) => {
let filename = eval_template(output, variables)?;
let output = if filename == "-" {
Output::StdOut
} else {
Output::File(filename)
};
runner_options.output = Some(output)
}
OptionKind::PathAsIs(value) => {
let value = eval_boolean_option(value, variables)?;
Expand Down
13 changes: 12 additions & 1 deletion packages/hurl/src/runner/output.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,31 @@
*
*/
use std::fs::File;
use std::io;
#[cfg(target_family = "windows")]
use std::io::IsTerminal;
use std::io::Write;
use std::{fmt, io};

use crate::runner::{Error, RunnerError};
use hurl_core::ast::{Pos, SourceInfo};

/// Represents the output of write operation: can be either a file or stdout.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Output {
StdOut,
File(String),
}

impl fmt::Display for Output {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let output = match self {
Output::StdOut => "-".to_string(),
Output::File(file) => file.to_string(),
};
write!(f, "{output}")
}
}

impl Output {
/// Writes these `bytes` to the output.
pub fn write(&self, bytes: &[u8]) -> Result<(), Error> {
Expand Down
6 changes: 3 additions & 3 deletions packages/hurl/src/runner/result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ pub type PredicateResult = Result<(), Error>;
impl EntryResult {
/// Writes the last HTTP response of this entry result to the file `filename`.
/// The HTTP response can be decompressed if the entry's `compressed` option has been set.
pub fn write_response(&self, filename: String) -> Result<(), Error> {
pub fn write_response(&self, output: &Output) -> Result<(), Error> {
match self.calls.last() {
Some(call) => {
let response = &call.response;
Expand All @@ -124,9 +124,9 @@ impl EntryResult {
return Err(Error::new(source_info, e.into(), false));
}
};
Output::File(filename).write(&bytes)
output.write(&bytes)
} else {
Output::File(filename).write(&response.body)
output.write(&response.body)
}
}
None => Ok(()),
Expand Down
7 changes: 4 additions & 3 deletions packages/hurl/src/runner/runner_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use std::time::Duration;
use hurl_core::ast::{Entry, Retry};

use crate::http::{IpResolve, RequestedHttpVersion};
use crate::runner::Output;
use crate::util::path::ContextDir;

pub struct RunnerOptionsBuilder {
Expand All @@ -42,7 +43,7 @@ pub struct RunnerOptionsBuilder {
ip_resolve: IpResolve,
max_redirect: Option<usize>,
no_proxy: Option<String>,
output: Option<String>,
output: Option<Output>,
path_as_is: bool,
post_entry: Option<fn() -> bool>,
pre_entry: Option<fn(Entry) -> bool>,
Expand Down Expand Up @@ -256,7 +257,7 @@ impl RunnerOptionsBuilder {
}

/// Specifies the file to output the HTTP response instead of stdout.
pub fn output(&mut self, output: Option<String>) -> &mut Self {
pub fn output(&mut self, output: Option<Output>) -> &mut Self {
self.output = output;
self
}
Expand Down Expand Up @@ -404,7 +405,7 @@ pub struct RunnerOptions {
pub(crate) insecure: bool,
pub(crate) max_redirect: Option<usize>,
pub(crate) no_proxy: Option<String>,
pub(crate) output: Option<String>,
pub(crate) output: Option<Output>,
pub(crate) path_as_is: bool,
pub(crate) post_entry: Option<fn() -> bool>,
pub(crate) pre_entry: Option<fn(Entry) -> bool>,
Expand Down
10 changes: 10 additions & 0 deletions packages/hurlfmt/src/format/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,16 @@ impl Tokenizable for VariableValue {
}
}

//
// impl Tokenizable for Output {
// fn tokenize(&self) -> Vec<Token> {
// match self {
// Output::StdOut => vec![Token::String("-".to_string())],
// Output::File(file) => file.tokenize(),
// }
// }
// }

impl Tokenizable for Filter {
fn tokenize(&self) -> Vec<Token> {
match self.value.clone() {
Expand Down

0 comments on commit 605fbc6

Please sign in to comment.