From fe6a2620dcb88c388f64c8fe8d9024795e91b657 Mon Sep 17 00:00:00 2001 From: Lorenzo Leonardo Date: Sun, 17 Mar 2024 19:37:30 +0800 Subject: [PATCH 1/3] feat: request body can accept Option> or Vec --- src/http_client.rs | 26 ++++++++++- src/test/post.rs | 113 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 2 deletions(-) diff --git a/src/http_client.rs b/src/http_client.rs index 0890c50..b16bc6a 100644 --- a/src/http_client.rs +++ b/src/http_client.rs @@ -58,7 +58,7 @@ where /// /// The HttpRequest can be customized by the caller by setting the Url, Method Type, /// Headers and the Body. - pub fn request(mut self, request: Request>>) -> Result> { + pub fn request(mut self, request: Request) -> Result> { self.easy .url(request.uri().to_string().as_str()) .map_err(|e| { @@ -94,7 +94,7 @@ where Method::POST => { self.easy.post(true).map_err(Error::Curl)?; - if let Some(body) = request.body() { + if let Some(body) = request.body().get_bytes() { self.easy.post_field_size(body.len() as u64).map_err(|e| { trace!("{:?}", e); Error::Curl(e) @@ -1013,3 +1013,25 @@ impl From for FileSize { Self(value) } } + +/// The purpose of this trait is to be able to accept +/// request body with Option> or Vec +pub trait CurlBodyRequest { + fn get_bytes(&self) -> Option<&Vec>; +} + +impl CurlBodyRequest for Vec { + fn get_bytes(&self) -> Option<&Vec> { + if self.is_empty() { + None + } else { + Some(self) + } + } +} + +impl CurlBodyRequest for Option> { + fn get_bytes(&self) -> Option<&Vec> { + self.as_ref() + } +} diff --git a/src/test/post.rs b/src/test/post.rs index 2728427..d7bf748 100644 --- a/src/test/post.rs +++ b/src/test/post.rs @@ -35,6 +35,64 @@ async fn test_post() { assert!(!response.headers().is_empty()); } +#[tokio::test] +async fn test_post_none() { + let responder = MockResponder::new(ResponderType::Body(Vec::new())); + let (server, _tempdir) = setup_test_environment(responder).await; + let target_url = Url::parse(format!("{}/test", server.uri()).as_str()).unwrap(); + + let actor = CurlActor::new(); + let collector = Collector::Ram(Vec::new()); + let request = Request::builder() + .uri(target_url.as_str()) + .method(Method::POST) + .body(None) + .unwrap(); + + let response = HttpClient::new(collector) + .request(request) + .unwrap() + .nonblocking(actor) + .perform() + .await + .unwrap(); + + println!("Response: {:?}", response); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(*response.body(), Some(Vec::new())); + assert!(!response.headers().is_empty()); +} + +#[tokio::test] +async fn test_post_none_no_option() { + let responder = MockResponder::new(ResponderType::Body(Vec::new())); + let (server, _tempdir) = setup_test_environment(responder).await; + let target_url = Url::parse(format!("{}/test", server.uri()).as_str()).unwrap(); + + let actor = CurlActor::new(); + let collector = Collector::Ram(Vec::new()); + let request = Request::builder() + .uri(target_url.as_str()) + .method(Method::POST) + .body(Vec::new()) + .unwrap(); + + let response = HttpClient::new(collector) + .request(request) + .unwrap() + .nonblocking(actor) + .perform() + .await + .unwrap(); + + println!("Response: {:?}", response); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(*response.body(), Some(Vec::new())); + assert!(!response.headers().is_empty()); +} + #[tokio::test] async fn test_post_with_headers() { let responder = MockResponder::new(ResponderType::Body("test body".as_bytes().to_vec())); @@ -88,3 +146,58 @@ async fn test_post_sync() { assert_eq!(*response.body(), Some(Vec::new())); assert!(!response.headers().is_empty()); } + +#[tokio::test] +async fn test_post_sync_not_option() { + let responder = MockResponder::new(ResponderType::Body("test body".as_bytes().to_vec())); + let (server, _tempdir) = setup_test_environment(responder).await; + let target_url = Url::parse(format!("{}/test", server.uri()).as_str()).unwrap(); + + let collector = Collector::Ram(Vec::new()); + let request = Request::builder() + .uri(target_url.as_str()) + .method(Method::POST) + .body("test body".as_bytes().to_vec()) + .unwrap(); + + let response = HttpClient::new(collector) + .request(request) + .unwrap() + .blocking() + .perform() + .unwrap(); + + println!("Response: {:?}", response); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(*response.body(), Some(Vec::new())); + assert!(!response.headers().is_empty()); +} + +#[tokio::test] +async fn test_post_async_not_option() { + let responder = MockResponder::new(ResponderType::Body("test body".as_bytes().to_vec())); + let (server, _tempdir) = setup_test_environment(responder).await; + let target_url = Url::parse(format!("{}/test", server.uri()).as_str()).unwrap(); + + let actor = CurlActor::new(); + let collector = Collector::Ram(Vec::new()); + let request = Request::builder() + .uri(target_url.as_str()) + .method(Method::POST) + .body("test body".as_bytes().to_vec()) + .unwrap(); + + let response = HttpClient::new(collector) + .request(request) + .unwrap() + .nonblocking(actor) + .perform() + .await + .unwrap(); + + println!("Response: {:?}", response); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(*response.body(), Some(Vec::new())); + assert!(!response.headers().is_empty()); +} From 504c34c40f923706e0a107512313674bf306a80c Mon Sep 17 00:00:00 2001 From: Lorenzo Leonardo Date: Sun, 17 Mar 2024 21:28:15 +0800 Subject: [PATCH 2/3] fix: if response body empty, Response must be None --- src/collector.rs | 30 ++++++++++++++++++--- src/test/post.rs | 69 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 11 deletions(-) diff --git a/src/collector.rs b/src/collector.rs index 18f3a8c..df429a8 100644 --- a/src/collector.rs +++ b/src/collector.rs @@ -325,8 +325,20 @@ impl ExtendedHandler for Collector { fn get_response_body(&self) -> Option> { match self { Collector::File(_) => None, - Collector::Ram(container) => Some(container.clone()), - Collector::RamAndHeaders(container, _) => Some(container.clone()), + Collector::Ram(container) => { + if container.is_empty() { + None + } else { + Some(container.clone()) + } + } + Collector::RamAndHeaders(container, _) => { + if container.is_empty() { + None + } else { + Some(container.clone()) + } + } Collector::FileAndHeaders(_, _) => None, } } @@ -338,7 +350,13 @@ impl ExtendedHandler for Collector { fn get_response_body_and_headers(&self) -> (Option>, Option) { match self { Collector::File(_) => (None, None), - Collector::Ram(container) => (Some(container.clone()), None), + Collector::Ram(container) => { + if container.is_empty() { + (None, None) + } else { + (Some(container.clone()), None) + } + } Collector::RamAndHeaders(container, headers) => { let header_str = std::str::from_utf8(headers).unwrap(); let mut header_map = HeaderMap::new(); @@ -354,7 +372,11 @@ impl ExtendedHandler for Collector { } } } - (Some(container.clone()), Some(header_map)) + if container.is_empty() { + (None, Some(header_map)) + } else { + (Some(container.clone()), Some(header_map)) + } } Collector::FileAndHeaders(_, headers) => { let header_str = std::str::from_utf8(headers).unwrap(); diff --git a/src/test/post.rs b/src/test/post.rs index d7bf748..559c16b 100644 --- a/src/test/post.rs +++ b/src/test/post.rs @@ -31,7 +31,7 @@ async fn test_post() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } @@ -60,7 +60,7 @@ async fn test_post_none() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } @@ -89,7 +89,7 @@ async fn test_post_none_no_option() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } @@ -117,7 +117,7 @@ async fn test_post_with_headers() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } @@ -143,7 +143,7 @@ async fn test_post_sync() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } @@ -169,7 +169,7 @@ async fn test_post_sync_not_option() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } @@ -198,6 +198,61 @@ async fn test_post_async_not_option() { println!("Response: {:?}", response); assert_eq!(response.status(), StatusCode::OK); - assert_eq!(*response.body(), Some(Vec::new())); + assert_eq!(*response.body(), None); + assert!(!response.headers().is_empty()); +} + +#[tokio::test] +async fn test_post_sync_not_option_empty_string() { + let responder = MockResponder::new(ResponderType::Body(Vec::new())); + let (server, _tempdir) = setup_test_environment(responder).await; + let target_url = Url::parse(format!("{}/test", server.uri()).as_str()).unwrap(); + + let collector = Collector::Ram(Vec::new()); + let request = Request::builder() + .uri(target_url.as_str()) + .method(Method::POST) + .body(Vec::new()) + .unwrap(); + + let response = HttpClient::new(collector) + .request(request) + .unwrap() + .blocking() + .perform() + .unwrap(); + + println!("Response: {:?}", response); + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(*response.body(), None); + assert!(!response.headers().is_empty()); +} + +#[tokio::test] +async fn test_post_async_not_option_empty_string() { + let responder = MockResponder::new(ResponderType::Body(Vec::new())); + let (server, _tempdir) = setup_test_environment(responder).await; + let target_url = Url::parse(format!("{}/test", server.uri()).as_str()).unwrap(); + + let actor = CurlActor::new(); + let collector = Collector::Ram(Vec::new()); + let request = Request::builder() + .uri(target_url.as_str()) + .method(Method::POST) + .body(Vec::new()) + .unwrap(); + + let response = HttpClient::new(collector) + .request(request) + .unwrap() + .nonblocking(actor) + .perform() + .await + .unwrap(); + + println!("Response: {:?}", response); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(*response.body(), None); assert!(!response.headers().is_empty()); } From cc4ff453924cbce79eab3b4fb13f35cdbd128f63 Mon Sep 17 00:00:00 2001 From: Lorenzo Leonardo Date: Sun, 17 Mar 2024 21:31:37 +0800 Subject: [PATCH 3/3] Version up to v2.3.0 changes: ======== Create dependabot.yml feat: request body can accept Option> or Vec fix: if response body empty, Response must be None Version up to v2.3.0 --- Cargo.lock | 66 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20113be..f3768d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,9 +82,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" +checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" [[package]] name = "assert-json-diff" @@ -154,7 +154,7 @@ checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ "async-channel 2.2.0", "async-executor", - "async-io 2.3.1", + "async-io 2.3.2", "async-lock 3.3.0", "blocking", "futures-lite 2.2.0", @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ "async-lock 3.3.0", "cfg-if", @@ -254,13 +254,13 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -341,9 +341,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.3" +version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" [[package]] name = "bytes" @@ -353,9 +353,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.89" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -461,7 +461,7 @@ dependencies = [ [[package]] name = "curl-http-client" -version = "2.2.0" +version = "2.3.0" dependencies = [ "async-curl", "curl", @@ -716,7 +716,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -817,9 +817,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "4fbd2820c5e49886948654ab546d0688ff24530286bdcf8fca3cefb16d4618eb" dependencies = [ "bytes", "fnv", @@ -1238,9 +1238,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -1449,7 +1449,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1620,9 +1620,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "7383cd0e49fff4b6b90ca5670bfd3e9d6a733b3f90c686605aa7eec8c4996032" dependencies = [ "proc-macro2", "quote", @@ -1659,7 +1659,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1670,28 +1670,28 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" +checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.57" +version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" +checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1771,7 +1771,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", ] [[package]] @@ -1870,9 +1870,9 @@ dependencies = [ [[package]] name = "value-bag" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" +checksum = "8fec26a25bd6fca441cdd0f769fd7f891bae119f996de31f86a5eddccef54c1d" [[package]] name = "vcpkg" @@ -1934,7 +1934,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-shared", ] @@ -1968,7 +1968,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.53", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/Cargo.toml b/Cargo.toml index 6147804..95d47ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "curl-http-client" -version = "2.2.0" +version = "2.3.0" edition = "2021" authors = ["Lorenzo Leonardo "] license = "MIT"