Skip to content

Commit d6306fc

Browse files
committed
Make content-type optional, guessed using file ext
Signed-off-by: Didier Wenzek <[email protected]>
1 parent db15573 commit d6306fc

File tree

2 files changed

+115
-26
lines changed

2 files changed

+115
-26
lines changed

crates/core/tedge/src/cli/http/cli.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ pub enum TEdgeHttpCli {
2727
content: Content,
2828

2929
/// MIME type of the content
30-
#[clap(long, default_value = "application/json")]
30+
#[clap(long)]
3131
#[arg(value_parser = parse_mime_type)]
32-
content_type: String,
32+
content_type: Option<String>,
3333

3434
/// MIME type of the expected content
35-
#[clap(long, default_value = "application/json")]
35+
#[clap(long)]
3636
#[arg(value_parser = parse_mime_type)]
37-
accept_type: String,
37+
accept_type: Option<String>,
3838

3939
/// Optional c8y cloud profile
4040
#[clap(long)]
@@ -51,9 +51,14 @@ pub enum TEdgeHttpCli {
5151
content: Content,
5252

5353
/// MIME type of the content
54-
#[clap(long, default_value = "application/json")]
54+
#[clap(long)]
5555
#[arg(value_parser = parse_mime_type)]
56-
content_type: String,
56+
content_type: Option<String>,
57+
58+
/// MIME type of the expected content
59+
#[clap(long)]
60+
#[arg(value_parser = parse_mime_type)]
61+
accept_type: Option<String>,
5762

5863
/// Optional c8y cloud profile
5964
#[clap(long)]
@@ -66,9 +71,9 @@ pub enum TEdgeHttpCli {
6671
uri: String,
6772

6873
/// MIME type of the expected content
69-
#[clap(long, default_value = "application/json")]
74+
#[clap(long)]
7075
#[arg(value_parser = parse_mime_type)]
71-
accept_type: String,
76+
accept_type: Option<String>,
7277

7378
/// Optional c8y cloud profile
7479
#[clap(long)]
@@ -172,10 +177,12 @@ impl From<TEdgeHttpCli> for HttpAction {
172177
TEdgeHttpCli::Put {
173178
content,
174179
content_type,
180+
accept_type,
175181
..
176182
} => HttpAction::Put {
177183
content,
178184
content_type,
185+
accept_type,
179186
},
180187
TEdgeHttpCli::Get { accept_type, .. } => HttpAction::Get { accept_type },
181188
TEdgeHttpCli::Delete { .. } => HttpAction::Delete,
@@ -219,3 +226,26 @@ fn http_client(
219226
};
220227
Ok(builder.build()?)
221228
}
229+
230+
impl Content {
231+
pub fn length(&self) -> Option<usize> {
232+
if let Some(content) = &self.arg2 {
233+
Some(content.len())
234+
} else if let Some(data) = &self.data {
235+
Some(data.len())
236+
} else if let Some(file) = &self.file {
237+
Some(std::fs::metadata(file).ok()?.len().try_into().ok()?)
238+
} else {
239+
None
240+
}
241+
}
242+
243+
pub fn mime_type(&self) -> Option<String> {
244+
let file = self.file.as_ref()?;
245+
Some(
246+
mime_guess::from_path(file)
247+
.first_or_octet_stream()
248+
.to_string(),
249+
)
250+
}
251+
}

crates/core/tedge/src/cli/http/command.rs

Lines changed: 77 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ use crate::cli::http::cli::Content;
22
use crate::command::Command;
33
use crate::log::MaybeFancy;
44
use anyhow::Error;
5+
use hyper::http::HeaderValue;
56
use reqwest::blocking;
7+
use reqwest::header::HeaderMap;
68

79
pub struct HttpCommand {
810
/// HTTP client
@@ -18,15 +20,16 @@ pub struct HttpCommand {
1820
pub enum HttpAction {
1921
Post {
2022
content: Content,
21-
content_type: String,
22-
accept_type: String,
23+
content_type: Option<String>,
24+
accept_type: Option<String>,
2325
},
2426
Put {
2527
content: Content,
26-
content_type: String,
28+
content_type: Option<String>,
29+
accept_type: Option<String>,
2730
},
2831
Get {
29-
accept_type: String,
32+
accept_type: Option<String>,
3033
},
3134
Delete,
3235
}
@@ -53,25 +56,18 @@ impl HttpCommand {
5356
fn request(&self) -> Result<blocking::RequestBuilder, Error> {
5457
let client = &self.client;
5558
let url = &self.url;
59+
let headers = self.action.headers();
5660
let request = match &self.action {
57-
HttpAction::Post {
58-
content,
59-
content_type,
60-
accept_type,
61-
} => client
61+
HttpAction::Post { content, .. } => client
6262
.post(url)
63-
.header("Accept", accept_type)
64-
.header("Content-Type", content_type)
63+
.headers(headers)
6564
.body(blocking::Body::try_from(content.clone())?),
66-
HttpAction::Put {
67-
content,
68-
content_type,
69-
} => client
65+
HttpAction::Put { content, .. } => client
7066
.put(url)
71-
.header("Content-Type", content_type)
67+
.headers(headers)
7268
.body(blocking::Body::try_from(content.clone())?),
73-
HttpAction::Get { accept_type } => client.get(url).header("Accept", accept_type),
74-
HttpAction::Delete => client.delete(url),
69+
HttpAction::Get { .. } => client.get(url).headers(headers),
70+
HttpAction::Delete => client.delete(url).headers(headers),
7571
};
7672

7773
Ok(request)
@@ -84,3 +80,66 @@ impl HttpCommand {
8480
Ok(())
8581
}
8682
}
83+
84+
impl HttpAction {
85+
pub fn headers(&self) -> HeaderMap {
86+
let mut headers = HeaderMap::new();
87+
88+
if let Some(content_length) = self.content_length() {
89+
headers.insert("Content-Length", content_length);
90+
}
91+
if let Some(content_type) = self.content_type() {
92+
headers.insert("Content-Type", content_type);
93+
}
94+
if let Some(accept_type) = self.accept_type() {
95+
headers.insert("Accept", accept_type);
96+
}
97+
98+
headers
99+
}
100+
101+
pub fn content_type(&self) -> Option<HeaderValue> {
102+
match self {
103+
HttpAction::Post {
104+
content,
105+
content_type,
106+
..
107+
}
108+
| HttpAction::Put {
109+
content,
110+
content_type,
111+
..
112+
} => content_type
113+
.as_ref()
114+
.cloned()
115+
.or(content.mime_type())
116+
.or(Some("application/json".to_string()))
117+
.and_then(|s| HeaderValue::from_str(&s).ok()),
118+
119+
_ => None,
120+
}
121+
}
122+
123+
pub fn accept_type(&self) -> Option<HeaderValue> {
124+
match self {
125+
HttpAction::Post { accept_type, .. }
126+
| HttpAction::Put { accept_type, .. }
127+
| HttpAction::Get { accept_type } => accept_type
128+
.as_ref()
129+
.and_then(|s| HeaderValue::from_str(s).ok()),
130+
131+
_ => None,
132+
}
133+
}
134+
135+
pub fn content_length(&self) -> Option<HeaderValue> {
136+
match self {
137+
HttpAction::Post { content, .. } | HttpAction::Put { content, .. } => content
138+
.length()
139+
.map(|length| length.to_string())
140+
.and_then(|s| HeaderValue::from_str(&s).ok()),
141+
142+
_ => None,
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)