nu_command/network/http/
head.rs

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