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
142fn 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}