#![cfg(not(target_arch = "wasm32"))] mod support; use futures_util::stream::StreamExt; use support::server; #[tokio::test] async fn text_part() { let _ = env_logger::try_init(); let form = reqwest::multipart::Form::new().text("foo", "bar"); let expected_body = format!( "\ --{0}\r\n\ Content-Disposition: form-data; name=\"foo\"\r\n\r\n\ bar\r\n\ --{0}--\r\n\ ", form.boundary() ); let ct = format!("multipart/form-data; boundary={}", form.boundary()); let server = server::http(move |mut req| { let ct = ct.clone(); let expected_body = expected_body.clone(); async move { assert_eq!(req.method(), "POST"); assert_eq!(req.headers()["content-type"], ct); assert_eq!( req.headers()["content-length"], expected_body.len().to_string() ); let mut full: Vec = Vec::new(); while let Some(item) = req.body_mut().next().await { full.extend(&*item.unwrap()); } assert_eq!(full, expected_body.as_bytes()); http::Response::default() } }); let url = format!("http://{}/multipart/1", server.addr()); let res = reqwest::Client::new() .post(&url) .multipart(form) .send() .await .unwrap(); assert_eq!(res.url().as_str(), &url); assert_eq!(res.status(), reqwest::StatusCode::OK); } #[cfg(feature = "stream")] #[tokio::test] async fn stream_part() { use futures_util::{future, stream}; let _ = env_logger::try_init(); let stream = reqwest::Body::wrap_stream(stream::once(future::ready(Ok::<_, reqwest::Error>( "part1 part2".to_owned(), )))); let part = reqwest::multipart::Part::stream(stream); let form = reqwest::multipart::Form::new() .text("foo", "bar") .part("part_stream", part); let expected_body = format!( "\ --{0}\r\n\ Content-Disposition: form-data; name=\"foo\"\r\n\ \r\n\ bar\r\n\ --{0}\r\n\ Content-Disposition: form-data; name=\"part_stream\"\r\n\ \r\n\ part1 part2\r\n\ --{0}--\r\n\ ", form.boundary() ); let ct = format!("multipart/form-data; boundary={}", form.boundary()); let server = server::http(move |mut req| { let ct = ct.clone(); let expected_body = expected_body.clone(); async move { assert_eq!(req.method(), "POST"); assert_eq!(req.headers()["content-type"], ct); assert_eq!(req.headers()["transfer-encoding"], "chunked"); let mut full: Vec = Vec::new(); while let Some(item) = req.body_mut().next().await { full.extend(&*item.unwrap()); } assert_eq!(full, expected_body.as_bytes()); http::Response::default() } }); let url = format!("http://{}/multipart/1", server.addr()); let client = reqwest::Client::new(); let res = client .post(&url) .multipart(form) .send() .await .expect("Failed to post multipart"); assert_eq!(res.url().as_str(), &url); assert_eq!(res.status(), reqwest::StatusCode::OK); } #[cfg(feature = "blocking")] #[test] fn blocking_file_part() { let _ = env_logger::try_init(); let form = reqwest::blocking::multipart::Form::new() .file("foo", "Cargo.lock") .unwrap(); let fcontents = std::fs::read_to_string("Cargo.lock").unwrap(); let expected_body = format!( "\ --{0}\r\n\ Content-Disposition: form-data; name=\"foo\"; filename=\"Cargo.lock\"\r\n\ Content-Type: application/octet-stream\r\n\r\n\ {1}\r\n\ --{0}--\r\n\ ", form.boundary(), fcontents ); let ct = format!("multipart/form-data; boundary={}", form.boundary()); let server = server::http(move |mut req| { let ct = ct.clone(); let expected_body = expected_body.clone(); async move { assert_eq!(req.method(), "POST"); assert_eq!(req.headers()["content-type"], ct); // files know their exact size assert_eq!( req.headers()["content-length"], expected_body.len().to_string() ); let mut full: Vec = Vec::new(); while let Some(item) = req.body_mut().next().await { full.extend(&*item.unwrap()); } assert_eq!(full, expected_body.as_bytes()); http::Response::default() } }); let url = format!("http://{}/multipart/2", server.addr()); let res = reqwest::blocking::Client::new() .post(&url) .multipart(form) .send() .unwrap(); assert_eq!(res.url().as_str(), &url); assert_eq!(res.status(), reqwest::StatusCode::OK); }