nu_command/network/http/
head.rs

1use super::client::HttpBody;
2use crate::network::http::client::{
3    check_response_redirection, http_client, http_parse_redirect_mode, http_parse_url,
4    request_add_authorization_header, request_add_custom_headers, request_handle_response_headers,
5    request_set_timeout, send_request,
6};
7use nu_engine::command_prelude::*;
8use nu_protocol::Signals;
9
10#[derive(Clone)]
11pub struct HttpHead;
12
13impl Command for HttpHead {
14    fn name(&self) -> &str {
15        "http head"
16    }
17
18    fn signature(&self) -> Signature {
19        Signature::build("http head")
20            .input_output_types(vec![(Type::Nothing, Type::Any)])
21            .allow_variants_without_examples(true)
22            .required(
23                "URL",
24                SyntaxShape::String,
25                "The URL to fetch the contents from.",
26            )
27            .named(
28                "user",
29                SyntaxShape::Any,
30                "the username when authenticating",
31                Some('u'),
32            )
33            .named(
34                "password",
35                SyntaxShape::Any,
36                "the password when authenticating",
37                Some('p'),
38            )
39            .named(
40                "max-time",
41                SyntaxShape::Duration,
42                "max duration before timeout occurs",
43                Some('m'),
44            )
45            .named(
46                "headers",
47                SyntaxShape::Any,
48                "custom headers you want to add ",
49                Some('H'),
50            )
51            .switch(
52                "insecure",
53                "allow insecure server connections when using SSL",
54                Some('k'),
55            ).named(
56                "redirect-mode",
57                SyntaxShape::String,
58                "What to do when encountering redirects. Default: 'follow'. Valid options: 'follow' ('f'), 'manual' ('m'), 'error' ('e').",
59                Some('R')
60            )
61            .filter()
62            .category(Category::Network)
63    }
64
65    fn description(&self) -> &str {
66        "Get the headers from a URL."
67    }
68
69    fn extra_description(&self) -> &str {
70        "Performs HTTP HEAD operation."
71    }
72
73    fn search_terms(&self) -> Vec<&str> {
74        vec!["network", "request", "curl", "wget", "headers", "header"]
75    }
76
77    fn run(
78        &self,
79        engine_state: &EngineState,
80        stack: &mut Stack,
81        call: &Call,
82        input: PipelineData,
83    ) -> Result<PipelineData, ShellError> {
84        run_head(engine_state, stack, call, input)
85    }
86
87    fn examples(&self) -> Vec<Example> {
88        vec![
89            Example {
90                description: "Get headers from example.com",
91                example: "http head https://www.example.com",
92                result: None,
93            },
94            Example {
95                description: "Get headers from example.com, with username and password",
96                example: "http head --user myuser --password mypass https://www.example.com",
97                result: None,
98            },
99            Example {
100                description: "Get headers from example.com, with custom header using a record",
101                example: "http head --headers {my-header-key: my-header-value} https://www.example.com",
102                result: None,
103            },
104            Example {
105                description: "Get headers from example.com, with custom header using a list",
106                example: "http head --headers [my-header-key-A my-header-value-A my-header-key-B my-header-value-B] https://www.example.com",
107                result: None,
108            },
109        ]
110    }
111}
112
113struct Arguments {
114    url: Value,
115    headers: Option<Value>,
116    insecure: bool,
117    user: Option<String>,
118    password: Option<String>,
119    timeout: Option<Value>,
120    redirect: Option<Spanned<String>>,
121}
122
123fn run_head(
124    engine_state: &EngineState,
125    stack: &mut Stack,
126    call: &Call,
127    _input: PipelineData,
128) -> Result<PipelineData, ShellError> {
129    let args = Arguments {
130        url: call.req(engine_state, stack, 0)?,
131        headers: call.get_flag(engine_state, stack, "headers")?,
132        insecure: call.has_flag(engine_state, stack, "insecure")?,
133        user: call.get_flag(engine_state, stack, "user")?,
134        password: call.get_flag(engine_state, stack, "password")?,
135        timeout: call.get_flag(engine_state, stack, "max-time")?,
136        redirect: call.get_flag(engine_state, stack, "redirect-mode")?,
137    };
138
139    helper(engine_state, stack, call, args, engine_state.signals())
140}
141
142// Helper function that actually goes to retrieve the resource from the url given
143// The Option<String> return a possible file extension which can be used in AutoConvert commands
144fn helper(
145    engine_state: &EngineState,
146    stack: &mut Stack,
147    call: &Call,
148    args: Arguments,
149    signals: &Signals,
150) -> Result<PipelineData, ShellError> {
151    let span = args.url.span();
152    let (requested_url, _) = http_parse_url(call, span, args.url)?;
153    let redirect_mode = http_parse_redirect_mode(args.redirect)?;
154
155    let client = http_client(args.insecure, redirect_mode, engine_state, stack)?;
156    let mut request = client.head(&requested_url);
157
158    request = request_set_timeout(args.timeout, request)?;
159    request = request_add_authorization_header(args.user, args.password, request);
160    request = request_add_custom_headers(args.headers, request)?;
161
162    let response = send_request(
163        engine_state,
164        request,
165        HttpBody::None,
166        None,
167        call.head,
168        signals,
169    );
170    check_response_redirection(redirect_mode, span, &response)?;
171    request_handle_response_headers(span, response)
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177
178    #[test]
179    fn test_examples() {
180        use crate::test_examples;
181
182        test_examples(HttpHead {})
183    }
184}