diff --git a/Cargo.toml b/Cargo.toml index 494b189..8bdb8ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ once_cell = "1" assert-json-diff = "2.0.1" base64 = "0.21.0" url = "2.2" +serde_urlencoded = "0.7.1" [dev-dependencies] async-std = { version = "1.9.0", features = ["attributes"] } diff --git a/src/request.rs b/src/request.rs index 14d5aa7..5a93050 100644 --- a/src/request.rs +++ b/src/request.rs @@ -48,6 +48,10 @@ impl Request { serde_json::from_slice(&self.body) } + pub fn body_form(&self) -> Result { + serde_urlencoded::from_bytes(&self.body) + } + pub(crate) async fn from_hyper(request: hyper::Request) -> Request { let (parts, body) = request.into_parts(); let url = match parts.uri.authority() { diff --git a/tests/request.rs b/tests/request.rs new file mode 100644 index 0000000..639fd9d --- /dev/null +++ b/tests/request.rs @@ -0,0 +1,39 @@ +use std::{ + collections::HashMap, + sync::{Arc, RwLock}, +}; + +use surf::http::mime; +use wiremock::{matchers::any, Mock, MockServer, Request, ResponseTemplate}; + +#[async_std::test] +async fn request_form_data_body() { + // Arrange + let form_data: Arc>> = Arc::new(RwLock::new(HashMap::new())); + let mock_server = MockServer::start().await; + let form_data_clone = form_data.clone(); + Mock::given(any()) + .respond_with(move |request: &Request| { + let form_data = request.body_form::>().unwrap(); + *form_data_clone.write().unwrap() = form_data; + ResponseTemplate::new(200) + }) + .mount(&mock_server) + .await; + + // Act + let _ = surf::post(&mock_server.uri()) + .content_type(mime::FORM) + .body_string(r#"foo=bar&foo2="h%25l""#.to_string()) + .await + .unwrap() + .status(); + + // Assert + let result = form_data.read().unwrap().clone(); + let expected = HashMap::from([ + ("foo".to_string(), "bar".to_string()), + ("foo2".to_string(), "\"h%l\"".to_string()), + ]); + assert_eq!(result, expected); +}