-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
# Description closes: #9344 Different to other http commands, `http options` command always returns header, according to [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS), the response information is included in header. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect -A clippy::result_large_err` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass - `cargo run -- crates/nu-std/tests/run.nu` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
- Loading branch information
1 parent
bdb09a9
commit 1edd3e7
Showing
5 changed files
with
243 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
use nu_engine::CallExt; | ||
use nu_protocol::ast::Call; | ||
use nu_protocol::engine::{Command, EngineState, Stack}; | ||
use nu_protocol::{ | ||
Category, Example, PipelineData, ShellError, Signature, SyntaxShape, Type, Value, | ||
}; | ||
|
||
use crate::network::http::client::{ | ||
http_client, http_parse_url, request_add_authorization_header, request_add_custom_headers, | ||
request_handle_response, request_set_timeout, send_request, | ||
}; | ||
|
||
use super::client::RequestFlags; | ||
|
||
#[derive(Clone)] | ||
pub struct SubCommand; | ||
|
||
impl Command for SubCommand { | ||
fn name(&self) -> &str { | ||
"http options" | ||
} | ||
|
||
fn signature(&self) -> Signature { | ||
Signature::build("http options") | ||
.input_output_types(vec![(Type::Nothing, Type::Any)]) | ||
.allow_variants_without_examples(true) | ||
.required( | ||
"URL", | ||
SyntaxShape::String, | ||
"the URL to fetch the options from", | ||
) | ||
.named( | ||
"user", | ||
SyntaxShape::Any, | ||
"the username when authenticating", | ||
Some('u'), | ||
) | ||
.named( | ||
"password", | ||
SyntaxShape::Any, | ||
"the password when authenticating", | ||
Some('p'), | ||
) | ||
.named( | ||
"max-time", | ||
SyntaxShape::Int, | ||
"timeout period in seconds", | ||
Some('m'), | ||
) | ||
.named( | ||
"headers", | ||
SyntaxShape::Any, | ||
"custom headers you want to add ", | ||
Some('H'), | ||
) | ||
.switch( | ||
"insecure", | ||
"allow insecure server connections when using SSL", | ||
Some('k'), | ||
) | ||
.switch( | ||
"allow-errors", | ||
"do not fail if the server returns an error code", | ||
Some('e'), | ||
) | ||
.filter() | ||
.category(Category::Network) | ||
} | ||
|
||
fn usage(&self) -> &str { | ||
"Requests permitted communication options for a given URL." | ||
} | ||
|
||
fn extra_usage(&self) -> &str { | ||
"Performs HTTP OPTIONS operation." | ||
} | ||
|
||
fn search_terms(&self) -> Vec<&str> { | ||
vec!["network", "fetch", "pull", "request", "curl", "wget"] | ||
} | ||
|
||
fn run( | ||
&self, | ||
engine_state: &EngineState, | ||
stack: &mut Stack, | ||
call: &Call, | ||
input: PipelineData, | ||
) -> Result<PipelineData, ShellError> { | ||
run_get(engine_state, stack, call, input) | ||
} | ||
|
||
fn examples(&self) -> Vec<Example> { | ||
vec![ | ||
Example { | ||
description: "Get options from example.com", | ||
example: "http options https://www.example.com", | ||
result: None, | ||
}, | ||
Example { | ||
description: "Get options from example.com, with username and password", | ||
example: "http options -u myuser -p mypass https://www.example.com", | ||
result: None, | ||
}, | ||
Example { | ||
description: "Get options from example.com, with custom header", | ||
example: "http options -H [my-header-key my-header-value] https://www.example.com", | ||
result: None, | ||
}, | ||
Example { | ||
description: "Get options from example.com, with custom headers", | ||
example: "http options -H [my-header-key-A my-header-value-A my-header-key-B my-header-value-B] https://www.example.com", | ||
result: None, | ||
}, | ||
] | ||
} | ||
} | ||
|
||
struct Arguments { | ||
url: Value, | ||
headers: Option<Value>, | ||
insecure: bool, | ||
user: Option<String>, | ||
password: Option<String>, | ||
timeout: Option<Value>, | ||
allow_errors: bool, | ||
} | ||
|
||
fn run_get( | ||
engine_state: &EngineState, | ||
stack: &mut Stack, | ||
call: &Call, | ||
_input: PipelineData, | ||
) -> Result<PipelineData, ShellError> { | ||
let args = Arguments { | ||
url: call.req(engine_state, stack, 0)?, | ||
headers: call.get_flag(engine_state, stack, "headers")?, | ||
insecure: call.has_flag("insecure"), | ||
user: call.get_flag(engine_state, stack, "user")?, | ||
password: call.get_flag(engine_state, stack, "password")?, | ||
timeout: call.get_flag(engine_state, stack, "max-time")?, | ||
allow_errors: call.has_flag("allow-errors"), | ||
}; | ||
helper(engine_state, stack, call, args) | ||
} | ||
|
||
// Helper function that actually goes to retrieve the resource from the url given | ||
// The Option<String> return a possible file extension which can be used in AutoConvert commands | ||
fn helper( | ||
engine_state: &EngineState, | ||
stack: &mut Stack, | ||
call: &Call, | ||
args: Arguments, | ||
) -> Result<PipelineData, ShellError> { | ||
let span = args.url.span()?; | ||
let ctrl_c = engine_state.ctrlc.clone(); | ||
let (requested_url, _) = http_parse_url(call, span, args.url)?; | ||
|
||
let client = http_client(args.insecure); | ||
let mut request = client.request("OPTIONS", &requested_url); | ||
|
||
request = request_set_timeout(args.timeout, request)?; | ||
request = request_add_authorization_header(args.user, args.password, request); | ||
request = request_add_custom_headers(args.headers, request)?; | ||
|
||
let response = send_request(request, None, None, ctrl_c); | ||
|
||
// http options' response always showed in header, so we set full to true. | ||
// And `raw` is useless too because options method doesn't return body, here we set to true | ||
// too. | ||
let request_flags = RequestFlags { | ||
raw: true, | ||
full: true, | ||
allow_errors: args.allow_errors, | ||
}; | ||
|
||
request_handle_response( | ||
engine_state, | ||
stack, | ||
span, | ||
&requested_url, | ||
request_flags, | ||
response, | ||
) | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn test_examples() { | ||
use crate::test_examples; | ||
|
||
test_examples(SubCommand {}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
mod delete; | ||
mod get; | ||
mod head; | ||
mod options; | ||
mod patch; | ||
mod post; | ||
mod put; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use mockito::Server; | ||
use nu_test_support::{nu, pipeline}; | ||
|
||
#[test] | ||
fn http_options_is_success() { | ||
let mut server = Server::new(); | ||
|
||
let _mock = server | ||
.mock("OPTIONS", "/") | ||
.with_header("Allow", "OPTIONS, GET") | ||
.create(); | ||
|
||
let actual = nu!(pipeline( | ||
format!( | ||
r#" | ||
http options {url} | ||
"#, | ||
url = server.url() | ||
) | ||
.as_str() | ||
)); | ||
|
||
assert!(!actual.out.is_empty()) | ||
} | ||
|
||
#[test] | ||
fn http_options_failed_due_to_server_error() { | ||
let mut server = Server::new(); | ||
|
||
let _mock = server.mock("OPTIONS", "/").with_status(400).create(); | ||
|
||
let actual = nu!(pipeline( | ||
format!( | ||
r#" | ||
http options {url} | ||
"#, | ||
url = server.url() | ||
) | ||
.as_str() | ||
)); | ||
|
||
assert!(actual.err.contains("Bad request (400)")) | ||
} |