use crate::network::http::client::{ check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url, request_add_authorization_header, request_add_custom_headers, request_handle_response, request_set_timeout, send_request, RequestFlags, }; use nu_engine::command_prelude::*; use super::client::HttpBody; #[derive(Clone)] pub struct SubCommand; impl Command for SubCommand { fn name(&self) -> &str { "http get" } fn signature(&self) -> Signature { Signature::build("http get") .input_output_types(vec![(Type::Nothing, Type::Any)]) .allow_variants_without_examples(true) .required( "URL", SyntaxShape::String, "The URL to fetch the contents 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( "raw", "fetch contents as text rather than a table", Some('r'), ) .switch( "insecure", "allow insecure server connections when using SSL", Some('k'), ) .switch( "full", "returns the full response instead of only the body", Some('f'), ) .switch( "allow-errors", "do not fail if the server returns an error code", Some('e'), ) .named( "redirect-mode", SyntaxShape::String, "What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').", Some('R') ) .filter() .category(Category::Network) } fn usage(&self) -> &str { "Fetch the contents from a URL." } fn extra_usage(&self) -> &str { "Performs HTTP GET operation." } fn search_terms(&self) -> Vec<&str> { vec![ "network", "fetch", "pull", "request", "download", "curl", "wget", ] } fn run( &self, engine_state: &EngineState, stack: &mut Stack, call: &Call, input: PipelineData, ) -> Result { run_get(engine_state, stack, call, input) } fn examples(&self) -> Vec { vec![ Example { description: "Get content from example.com", example: "http get https://www.example.com", result: None, }, Example { description: "Get content from example.com, with username and password", example: "http get --user myuser --password mypass https://www.example.com", result: None, }, Example { description: "Get content from example.com, with custom header", example: "http get --headers [my-header-key my-header-value] https://www.example.com", result: None, }, Example { description: "Get content from example.com, with custom headers", example: "http get --headers [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, raw: bool, insecure: bool, user: Option, password: Option, timeout: Option, full: bool, allow_errors: bool, redirect: Option>, } fn run_get( engine_state: &EngineState, stack: &mut Stack, call: &Call, _input: PipelineData, ) -> Result { let args = Arguments { url: call.req(engine_state, stack, 0)?, headers: call.get_flag(engine_state, stack, "headers")?, raw: call.has_flag(engine_state, stack, "raw")?, insecure: call.has_flag(engine_state, stack, "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")?, full: call.has_flag(engine_state, stack, "full")?, allow_errors: call.has_flag(engine_state, stack, "allow-errors")?, redirect: call.get_flag(engine_state, stack, "redirect-mode")?, }; helper(engine_state, stack, call, args) } // Helper function that actually goes to retrieve the resource from the url given // The Option 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 { let span = args.url.span(); let (requested_url, _) = http_parse_url(call, span, args.url)?; let redirect_mode = http_parse_redirect_mode(args.redirect)?; let client = http_client(args.insecure, redirect_mode, engine_state, stack)?; let mut request = client.get(&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.clone(), HttpBody::None, None, call.head, engine_state.signals(), ); let request_flags = RequestFlags { raw: args.raw, full: args.full, allow_errors: args.allow_errors, }; check_response_redirection(redirect_mode, span, &response)?; request_handle_response( engine_state, stack, span, &requested_url, request_flags, response, request, ) } #[cfg(test)] mod tests { use super::*; #[test] fn test_examples() { use crate::test_examples; test_examples(SubCommand {}) } }