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