1
2use crate::error::Error;
3use rand::{distributions::Alphanumeric, thread_rng, Rng};
4use std::collections::HashMap;
5use std::fs;
6use std::fs::File;
7use std::path::Path;
8use urlencoding::{decode, encode};
9
10#[derive(Clone, Debug)]
11pub struct HttpBody {
12 is_form_post: bool,
13 params: HashMap<String, String>,
14 raw: Vec<u8>,
15 boundary: String,
16 files: HashMap<String, String>,
17}
18
19
20impl HttpBody {
21 pub fn new(params: &HashMap<String, String>, raw: &[u8]) -> Self {
23 let boundary: String = thread_rng()
24 .sample_iter(&Alphanumeric)
25 .take(30)
26 .map(|c| c as char)
27 .collect();
28
29 Self {
30 is_form_post: params.keys().len() > 0 || raw.len() > 0,
31 params: params.clone(),
32 raw: raw.clone().to_vec(),
33 boundary,
34 files: HashMap::new(),
35 }
36 }
37
38 pub fn empty() -> Self {
40 Self::new(&HashMap::new(), &Vec::new())
41 }
42
43 pub fn from_string(data: &str) -> Self {
45 let mut params: HashMap<String, String> = HashMap::new();
47 for pair in data.split('&') {
48 if let Some(index) = pair.find('=') {
49 params.insert(
50 pair[..index].to_string(),
51 decode(pair[index + 1..].trim()).unwrap().to_string(),
52 );
53 }
54 }
55
56 Self::new(¶ms, &Vec::new())
57 }
58
59 pub fn from_map(params: &HashMap<&str, &str>) -> Self {
61 let formatted_params = params
62 .iter()
63 .map(|(key, value)| (key.to_string(), value.to_string()))
64 .collect();
65 Self::new(&formatted_params, &Vec::new())
66 }
67
68 pub fn from_raw(data: &[u8]) -> Self {
70 Self::new(&HashMap::new(), data)
71 }
72
73 pub fn from_raw_str(data: &str) -> Self {
75 Self::new(&HashMap::new(), data.as_bytes())
76 }
77
78 pub fn set_param(&mut self, key: &str, value: &str) {
80 *self
81 .params
82 .entry(key.to_string())
83 .or_insert(value.to_string()) = value.to_string();
84 self.is_form_post = true;
85 }
86
87 pub fn upload_file(&mut self, param_name: &str, file_path: &str) -> Result<(), Error> {
89 if !Path::new(&file_path).exists() {
91 return Err(Error::FileNotExists(file_path.to_string()));
92 }
93 *self
94 .files
95 .entry(param_name.to_string())
96 .or_insert(file_path.to_string()) = file_path.to_string();
97
98 Ok(())
99 }
100
101 pub fn format(&self) -> Vec<u8> {
103 if !self.files.is_empty() {
104 return self.format_multipart();
105 } else if self.raw.len() > 0 {
106 return self.raw.clone();
107 } else if !self.is_form_post {
108 return Vec::new();
109 }
110
111 let body = self
112 .params
113 .iter()
114 .map(|(key, value)| format!("{}={}", key, encode(value)))
115 .collect::<Vec<String>>()
116 .join("&");
117
118 body.as_bytes().to_vec()
119 }
120
121 fn format_multipart(&self) -> Vec<u8> {
123
124 let mut body: Vec<u8> = Vec::new();
126 for (key, value) in self.params.iter() {
127 let section = format!(
128 "--{}\r\nContent-Disposition: form-data; name=\"{}\"\r\n\r\n{}\r\n",
129 self.boundary, key, value
130 );
131 body.extend_from_slice(section.as_bytes());
132 }
133
134 for (key, filepath) in self.files.iter() {
136 let (filename, mime_type, contents) = self.get_file_info(filepath);
137 let section = format!("--{}\r\nContent-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\nContent-Type: {}\r\n\r\n", self.boundary, key, filename, mime_type);
138 body.extend_from_slice(section.as_bytes());
139 body.extend_from_slice(&contents);
140 body.extend_from_slice("\r\n".as_bytes());
141 }
142 body.extend_from_slice(format!("--{}--\r\n", self.boundary).as_bytes());
143
144 body
145 }
146
147 fn get_file_info(&self, filepath: &String) -> (String, String, Vec<u8>) {
149 let pos = filepath
151 .rfind('/')
152 .or_else(|| filepath.rfind('\\'))
153 .unwrap();
154 let filename = filepath[pos + 1..].to_string();
155
156 let mime_guess = mime_guess::from_path(filepath);
158 let mime_type = if mime_guess.count() > 0 {
159 mime_guess.first().unwrap().to_string()
160 } else {
161 "application/octet-stream".to_string()
162 };
163
164 let _file = File::open(filepath).unwrap();
165 let content =
166 fs::read(filepath).unwrap_or_else(|_| panic!("Unable to read file at, {}", filepath));
167
168 (filename, mime_type, content)
169 }
170 pub fn is_form_post(&self) -> bool {
172 self.is_form_post
173 }
174
175 pub fn params(&self) -> HashMap<String, String> {
177 self.params.clone()
178 }
179
180 pub fn add_param(&mut self, key: &str, value: &str) {
182 *self.params.entry(key.to_string()).or_default() = value.to_string();
183 self.is_form_post = true;
184 }
185
186 pub fn get_raw(&self) -> Vec<u8> {
188 self.raw.clone()
189 }
190
191 pub fn boundary(&self) -> String {
193 self.boundary.clone()
194 }
195
196 pub fn files(&self) -> HashMap<String, String> {
198 self.files.clone()
199 }
200}