http_client_multipart/
lib.rs1use http_types::Request;
52
53mod encoding;
54mod multipart;
55mod part;
56mod reader_stream;
57
58pub use encoding::Encoding;
59pub use multipart::Multipart;
60
61pub type StreamChunk = std::result::Result<Vec<u8>, futures_lite::io::Error>;
62
63fn generate_boundary() -> String {
65 (0..30).map(|_| fastrand::alphanumeric()).collect()
66}
67
68pub trait RequestMultipartExt {
70 fn multipart(&mut self, multipart: Multipart);
71}
72
73impl RequestMultipartExt for Request {
74 fn multipart(&mut self, multipart: Multipart) {
75 multipart.set_request(self)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use crate::multipart::Multipart;
82
83 use super::*;
84 use http_types::{Method, Result, Url};
85
86 #[async_std::test]
87 async fn test_multipart_text() -> Result<()> {
88 let mut multipart = Multipart::new();
89 multipart.add_text("name", "John Doe");
90 multipart.add_text("age", "42");
91
92 let mut req = Request::new(Method::Post, Url::parse("http://example.com")?);
93 multipart.set_request(&mut req);
94
95 let content_type = req.header("Content-Type").unwrap().last().as_str();
96 assert!(content_type.starts_with("multipart/form-data; boundary="));
97
98 let body = req.body_string().await?;
99 assert!(body.contains("John Doe"));
100 assert!(body.contains("42"));
101 Ok(())
102 }
103
104 #[async_std::test]
105 async fn test_multipart_file() -> Result<()> {
106 let mut multipart = Multipart::new();
107 multipart.add_file("avatar", "Cargo.toml", None).await?;
108
109 let mut req = Request::new(Method::Post, Url::parse("http://example.com")?);
110 multipart.set_request(&mut req);
111
112 let content_type = req.header("Content-Type").unwrap().last().as_str();
113 assert!(content_type.starts_with("multipart/form-data; boundary="));
114
115 let body = req.body_string().await?;
116 assert!(body.contains("[package]"));
117 Ok(())
118 }
119
120 #[async_std::test]
121 async fn test_multipart_mixed() -> Result<()> {
122 let mut multipart = Multipart::new();
123 multipart.add_text("name", "John Doe");
124 multipart.add_file("avatar", "Cargo.toml", None).await?;
125
126 let mut req = Request::new(Method::Post, Url::parse("http://example.com")?);
127 multipart.set_request(&mut req);
128
129 let content_type = req.header("Content-Type").unwrap().last().as_str();
130 assert!(content_type.starts_with("multipart/form-data; boundary="));
131
132 let body = dbg!(req.body_string().await?);
133 assert!(body.contains("John Doe"));
134 assert!(body.contains("[package]"));
135 Ok(())
136 }
137
138 #[async_std::test]
139 async fn example_test() -> Result<()> {
140 use http_client::h1::H1Client as Client;
141 use http_client::HttpClient;
142
143 let mut multipart = Multipart::new();
145
146 multipart.add_text("name", "John Doe");
148
149 multipart.add_file("avatar", "Cargo.toml", None).await?;
151
152 let url = "https://httpbin.org/post".parse::<Url>()?;
154 let mut req = Request::new(Method::Post, url);
155
156 multipart.set_request(&mut req);
158
159 let client = Client::new();
161
162 let mut response = client.send(req).await?;
164
165 let body = response.body_string().await?;
167
168 println!("{}", body);
170
171 Ok(())
172 }
173}