diff --git a/examples/src/http/example-http-client.rs b/examples/src/http/example-http-client.rs index 084e2413..9e2f5438 100644 --- a/examples/src/http/example-http-client.rs +++ b/examples/src/http/example-http-client.rs @@ -55,7 +55,7 @@ async fn main() -> Result<(), BoxError> { client .request_builder() .host("httpbin.org") - .uri("/get")? + .uri("/get") .send() .await? .into_string() @@ -65,7 +65,7 @@ async fn main() -> Result<(), BoxError> { println!( "{}", client - .get("http://127.0.0.1:8080/")? + .get("http://127.0.0.1:8080/") .send() .await? .into_string() @@ -77,7 +77,7 @@ async fn main() -> Result<(), BoxError> { "{:?}", client .request_builder() - .uri("/user/json_get")? + .uri("/user/json_get") .send() .await? .into_json::() @@ -86,12 +86,12 @@ async fn main() -> Result<(), BoxError> { println!( "{:?}", client - .post("/user/json_post")? + .post("/user/json_post") .json(&Person { name: "Foo".to_string(), age: 25, phones: vec!["114514".to_string()], - })? + }) .send() .await? .into_string() @@ -103,7 +103,7 @@ async fn main() -> Result<(), BoxError> { println!( "{}", client - .get("http://127.0.0.1:8080/")? + .get("http://127.0.0.1:8080/") .send() .await? .into_string() @@ -114,7 +114,7 @@ async fn main() -> Result<(), BoxError> { println!( "{:?}", client - .get("/")? + .get("/") .send() .await .expect_err("this request should fail"), diff --git a/volo-http/src/client/mod.rs b/volo-http/src/client/mod.rs index 29bff4bd..eff1f5f0 100644 --- a/volo-http/src/client/mod.rs +++ b/volo-http/src/client/mod.rs @@ -40,7 +40,7 @@ use self::{ use crate::{ context::{client::Config, ClientContext}, error::{ - client::{builder_error, no_address, ClientError}, + client::{builder_error, no_address, ClientError, Result}, BoxError, }, request::ClientRequest, @@ -463,7 +463,7 @@ impl ClientBuilder { } /// Insert a header to the request. - pub fn header(&mut self, key: K, value: V) -> Result<&mut Self, ClientError> + pub fn header(&mut self, key: K, value: V) -> Result<&mut Self> where K: TryInto, K::Error: Error + Send + Sync + 'static, @@ -681,6 +681,7 @@ impl ClientBuilder { }; let client_inner = ClientInner { + service, caller_name, callee_name: self.callee_name, default_target: self.target, @@ -690,14 +691,14 @@ impl ClientBuilder { headers: self.headers, }; let client = Client { - service, inner: Arc::new(client_inner), }; self.mk_client.mk_client(client) } } -struct ClientInner { +struct ClientInner { + service: S, caller_name: FastStr, callee_name: FastStr, default_target: Target, @@ -718,7 +719,6 @@ struct ClientInner { /// let client = Client::builder().build(); /// let resp = client /// .get("http://httpbin.org/get") -/// .expect("invalid uri") /// .send() /// .await /// .expect("failed to send request") @@ -728,17 +728,23 @@ struct ClientInner { /// println!("{resp:?}"); /// # }) /// ``` -#[derive(Clone)] pub struct Client { - service: S, - inner: Arc, + inner: Arc>, +} + +impl Clone for Client { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } + } } macro_rules! method_requests { ($method:ident) => { paste! { #[doc = concat!("Create a request with `", stringify!([<$method:upper>]) ,"` method and the given `uri`.")] - pub fn [<$method:lower>](&self, uri: U) -> Result, ClientError> + pub fn [<$method:lower>](&self, uri: U) -> RequestBuilder where U: TryInto, U::Error: Into, @@ -759,16 +765,16 @@ impl Client<()> { impl Client { /// Create a builder for building a request. pub fn request_builder(&self) -> RequestBuilder { - RequestBuilder::new(self) + RequestBuilder::new(self.clone()) } /// Create a builder for building a request with the specified method and URI. - pub fn request(&self, method: Method, uri: U) -> Result, ClientError> + pub fn request(&self, method: Method, uri: U) -> RequestBuilder where U: TryInto, U::Error: Into, { - RequestBuilder::new(self).method(method).uri(uri) + RequestBuilder::new(self.clone()).method(method).uri(uri) } method_requests!(options); @@ -907,7 +913,7 @@ where let has_metainfo = METAINFO.try_with(|_| {}).is_ok(); - let fut = self.service.call(cx, req); + let fut = self.inner.service.call(cx, req); if has_metainfo { fut.await @@ -929,12 +935,12 @@ impl MkClient> for DefaultMkClient { } /// Create a GET request to the specified URI. -pub async fn get(uri: U) -> Result +pub async fn get(uri: U) -> Result where U: TryInto, U::Error: Into, { - ClientBuilder::new().build().get(uri)?.send().await + ClientBuilder::new().build().get(uri).send().await } // The `httpbin.org` always responses a json data. @@ -945,7 +951,10 @@ mod client_tests { use std::{collections::HashMap, future::Future}; use http::{header, StatusCode}; - use motore::{layer::Layer, service::Service}; + use motore::{ + layer::{Layer, Stack}, + service::Service, + }; use serde::Deserialize; use volo::{context::Endpoint, layer::Identity}; @@ -974,7 +983,7 @@ mod client_tests { const USER_AGENT_KEY: &str = "User-Agent"; const USER_AGENT_VAL: &str = "volo-http-unit-test"; - #[allow(unused)] + #[test] fn client_types_check() { struct TestLayer; struct TestService { @@ -1016,6 +1025,10 @@ mod client_tests { .layer_inner(TestLayer) .layer_outer(TestLayer) .build(); + let _: DefaultClient> = ClientBuilder::new() + .layer_inner(TestLayer) + .layer_inner(TestLayer) + .build(); } #[tokio::test] @@ -1038,7 +1051,6 @@ mod client_tests { let resp = client .get(HTTPBIN_GET) - .unwrap() .send() .await .unwrap() @@ -1058,7 +1070,6 @@ mod client_tests { let resp = client .get("/get") - .unwrap() .send() .await .unwrap() @@ -1081,7 +1092,6 @@ mod client_tests { let resp = client .get("/get") - .unwrap() .send() .await .unwrap() @@ -1101,7 +1111,6 @@ mod client_tests { let resp = client .get("/get") - .unwrap() .send() .await .unwrap() @@ -1128,7 +1137,6 @@ mod client_tests { let resp = client .get("/get") - .unwrap() .send() .await .unwrap() @@ -1145,7 +1153,7 @@ mod client_tests { builder.host("httpbin.org").with_port(443); let client = builder.build(); - let resp = client.get("/get").unwrap().send().await.unwrap(); + let resp = client.get("/get").send().await.unwrap(); // Send HTTP request to the HTTPS port (443), `httpbin.org` will response `400 Bad // Request`. assert_eq!(resp.status(), StatusCode::BAD_REQUEST); @@ -1161,7 +1169,6 @@ mod client_tests { "{}", client .get("/post") - .unwrap() .send() .await .expect_err("GET for httpbin.org/post should fail") @@ -1183,7 +1190,6 @@ mod client_tests { "{}", client .get("https://httpbin.org/get") - .unwrap() .send() .await .expect_err("HTTPS with disable_tls should fail") @@ -1222,7 +1228,7 @@ mod client_tests { builder.target_parser(callopt_should_not_inserted); let client = builder.build(); - let resp = client.get(HTTPBIN_GET).unwrap().send().await; + let resp = client.get(HTTPBIN_GET).send().await; assert!(resp.is_ok()); } @@ -1233,7 +1239,7 @@ mod client_tests { builder.target_parser(callopt_should_not_inserted); let client = builder.build(); - let resp = client.get(HTTPBIN_GET).unwrap().send().await; + let resp = client.get(HTTPBIN_GET).send().await; assert!(resp.is_ok()); } @@ -1245,7 +1251,6 @@ mod client_tests { let resp = client .get(HTTPBIN_GET) - .unwrap() .with_callopt(CallOpt::new().with(CallOptInserted)) .send() .await; @@ -1261,7 +1266,6 @@ mod client_tests { let resp = client .get(HTTPBIN_GET) - .unwrap() // insert an empty callopt .with_callopt(CallOpt::new()) .send() @@ -1277,7 +1281,7 @@ mod client_tests { builder.target_parser(callopt_should_not_inserted); let client = builder.build(); - let resp = client.get(HTTPBIN_GET).unwrap().send().await; + let resp = client.get(HTTPBIN_GET).send().await; assert!(resp.is_ok()); } } diff --git a/volo-http/src/client/request_builder.rs b/volo-http/src/client/request_builder.rs index 33dceaf0..41d58293 100644 --- a/volo-http/src/client/request_builder.rs +++ b/volo-http/src/client/request_builder.rs @@ -25,96 +25,127 @@ use crate::{ }; /// The builder for building a request. -pub struct RequestBuilder<'a, S, B = Body> { - client: &'a Client, +pub struct RequestBuilder { + client: Client, target: Target, call_opt: Option, - request: ClientRequest, + request: Result>, timeout: Option, } -impl<'a, S> RequestBuilder<'a, S, Body> { - pub(crate) fn new(client: &'a Client) -> Self { +impl RequestBuilder { + pub(crate) fn new(client: Client) -> Self { Self { client, target: Default::default(), call_opt: Default::default(), - request: Default::default(), + request: Ok(ClientRequest::default()), timeout: None, } } /// Set the request body. - pub fn data(mut self, data: D) -> Result + pub fn data(mut self, data: D) -> Self where D: TryInto, D::Error: Error + Send + Sync + 'static, { - let (parts, _) = self.request.into_parts(); - self.request = Request::from_parts(parts, data.try_into().map_err(builder_error)?); + if self.request.is_err() { + return self; + } + let Ok(req) = self.request else { + unreachable!(); + }; + + let body = match data.try_into() { + Ok(body) => body, + Err(err) => { + self.request = Err(builder_error(err)); + return self; + } + }; - Ok(self) + let (parts, _) = req.into_parts(); + self.request = Ok(Request::from_parts(parts, body)); + + self } /// Set the request body as json from object with [`Serialize`](serde::Serialize). #[cfg(feature = "json")] - pub fn json(mut self, json: &T) -> Result + pub fn json(mut self, json: &T) -> Self where T: serde::Serialize, { - let (mut parts, _) = self.request.into_parts(); + if self.request.is_err() { + return self; + } + let Ok(req) = self.request else { + unreachable!(); + }; + + let json = match crate::utils::json::serialize(json) { + Ok(json) => json, + Err(err) => { + self.request = Err(builder_error(err)); + return self; + } + }; + + let (mut parts, _) = req.into_parts(); parts.headers.insert( http::header::CONTENT_TYPE, - mime::APPLICATION_JSON - .essence_str() - .parse() - .expect("infallible"), - ); - self.request = Request::from_parts( - parts, - crate::utils::json::serialize(json) - .map_err(builder_error)? - .into(), + crate::utils::consts::APPLICATION_JSON, ); + self.request = Ok(Request::from_parts(parts, Body::from(json))); - Ok(self) + self } /// Set the request body as form from object with [`Serialize`](serde::Serialize). #[cfg(feature = "form")] - pub fn form(mut self, form: &T) -> Result + pub fn form(mut self, form: &T) -> Self where T: serde::Serialize, { - let (mut parts, _) = self.request.into_parts(); + if self.request.is_err() { + return self; + } + let Ok(req) = self.request else { + unreachable!(); + }; + + let form = match serde_urlencoded::to_string(form) { + Ok(form) => form, + Err(err) => { + self.request = Err(builder_error(err)); + return self; + } + }; + + let (mut parts, _) = req.into_parts(); parts.headers.insert( http::header::CONTENT_TYPE, - mime::APPLICATION_WWW_FORM_URLENCODED - .essence_str() - .parse() - .expect("infallible"), - ); - self.request = Request::from_parts( - parts, - serde_urlencoded::to_string(form) - .map_err(builder_error)? - .into(), + crate::utils::consts::APPLICATION_WWW_FORM_URLENCODED, ); + self.request = Ok(Request::from_parts(parts, Body::from(form))); - Ok(self) + self } } -impl<'a, S, B> RequestBuilder<'a, S, B> { +impl RequestBuilder { /// Set method for the request. pub fn method(mut self, method: Method) -> Self { - *self.request.method_mut() = method; + if let Ok(req) = self.request.as_mut() { + *req.method_mut() = method; + } self } /// Get a reference to method in the request. - pub fn method_ref(&self) -> &Method { - self.request.method() + pub fn method_ref(&self) -> Option<&Method> { + self.request.as_ref().ok().map(Request::method) } /// Set uri for building request. @@ -125,23 +156,41 @@ impl<'a, S, B> RequestBuilder<'a, S, B> { /// /// Note that only path and query will be set to the request uri. For setting the full uri, use /// `full_uri` instead. - pub fn uri(mut self, uri: U) -> Result + pub fn uri(mut self, uri: U) -> Self where U: TryInto, U::Error: Into, { - let uri = uri.try_into().map_err(builder_error)?; + if self.request.is_err() { + return self; + } + let uri = match uri.try_into() { + Ok(uri) => uri, + Err(err) => { + self.request = Err(builder_error(err)); + return self; + } + }; + if let Some(target) = Target::from_uri(&uri) { + match target { + Ok(target) => self.target = target, + Err(err) => { + self.request = Err(err); + return self; + } + } + } let rela_uri = uri .path_and_query() .map(PathAndQuery::to_owned) .unwrap_or_else(|| PathAndQuery::from_static("/")) .into(); - if let Some(target) = Target::from_uri(&uri) { - let target = target?; - self.target = target; - } - *self.request.uri_mut() = rela_uri; - Ok(self) + let Ok(req) = self.request.as_mut() else { + unreachable!(); + }; + *req.uri_mut() = rela_uri; + + self } /// Set full uri for building request. @@ -150,18 +199,36 @@ impl<'a, S, B> RequestBuilder<'a, S, B> { /// will be set as the request uri. /// /// This function is only used for using http(s) proxy. - pub fn full_uri(mut self, uri: U) -> Result + pub fn full_uri(mut self, uri: U) -> Self where U: TryInto, U::Error: Into, { - let uri = uri.try_into().map_err(builder_error)?; + if self.request.is_err() { + return self; + } + let uri = match uri.try_into() { + Ok(uri) => uri, + Err(err) => { + self.request = Err(builder_error(err)); + return self; + } + }; if let Some(target) = Target::from_uri(&uri) { - let target = target?; - self.target = target; + match target { + Ok(target) => self.target = target, + Err(err) => { + self.request = Err(err); + return self; + } + } } - *self.request.uri_mut() = uri; - Ok(self) + let Ok(req) = self.request.as_mut() else { + unreachable!(); + }; + *req.uri_mut() = uri; + + self } /// Set a [`CallOpt`] to the request. @@ -176,59 +243,102 @@ impl<'a, S, B> RequestBuilder<'a, S, B> { /// Set query for the uri in request from object with [`Serialize`](serde::Serialize). #[cfg(feature = "query")] - pub fn set_query(mut self, query: &T) -> Result + pub fn set_query(mut self, query: &T) -> Self where T: serde::Serialize, { - let mut path = self.request.uri().path().to_owned(); + if self.request.is_err() { + return self; + } + let query_str = match serde_urlencoded::to_string(query) { + Ok(query) => query, + Err(err) => { + self.request = Err(builder_error(err)); + return self; + } + }; + let Ok(req) = self.request.as_mut() else { + unreachable!(); + }; + + // We should keep path only without query + let path_str = req.uri().path(); + let mut path = String::with_capacity(path_str.len() + 1 + query_str.len()); + path.push_str(path_str); path.push('?'); - let query_str = serde_urlencoded::to_string(query).map_err(builder_error)?; path.push_str(&query_str); + let Ok(uri) = Uri::from_maybe_shared(path) else { + // path part is from a valid uri, and the result of urlencoded must be valid. + unreachable!(); + }; - *self.request.uri_mut() = Uri::from_maybe_shared(path).map_err(builder_error)?; + *req.uri_mut() = uri; - Ok(self) + self } /// Get a reference to uri in the request. - pub fn uri_ref(&self) -> &Uri { - self.request.uri() + pub fn uri_ref(&self) -> Option<&Uri> { + self.request.as_ref().ok().map(Request::uri) } /// Set version of the HTTP request. pub fn version(mut self, version: Version) -> Self { - *self.request.version_mut() = version; + if let Ok(req) = self.request.as_mut() { + *req.version_mut() = version; + } self } /// Get a reference to version in the request. - pub fn version_ref(&self) -> Version { - self.request.version() + pub fn version_ref(&self) -> Option { + self.request.as_ref().ok().map(Request::version) } /// Insert a header into the request header map. - pub fn header(mut self, key: K, value: V) -> Result + pub fn header(mut self, key: K, value: V) -> Self where K: TryInto, K::Error: Into, V: TryInto, V::Error: Into, { - self.request.headers_mut().insert( - key.try_into().map_err(|e| builder_error(e.into()))?, - value.try_into().map_err(|e| builder_error(e.into()))?, - ); - Ok(self) + if self.request.is_err() { + return self; + } + + let key = match key.try_into() { + Ok(key) => key, + Err(err) => { + self.request = Err(builder_error(err.into())); + return self; + } + }; + let value = match value.try_into() { + Ok(value) => value, + Err(err) => { + self.request = Err(builder_error(err.into())); + return self; + } + }; + + let Ok(req) = self.request.as_mut() else { + unreachable!(); + }; + + req.headers_mut().insert(key, value); + + self } /// Get a reference to headers in the request. - pub fn headers(&self) -> &HeaderMap { - self.request.headers() + pub fn headers(&self) -> Option<&HeaderMap> { + self.request.as_ref().ok().map(Request::headers) } /// Get a mutable reference to headers in the request. - pub fn headers_mut(&mut self) -> &mut HeaderMap { - self.request.headers_mut() + pub fn headers_mut(&mut self) -> Option<&mut HeaderMap> { + self.request.as_mut().ok().map(Request::headers_mut) } /// Set target address for the request. @@ -288,9 +398,14 @@ impl<'a, S, B> RequestBuilder<'a, S, B> { } /// Set a request body. - pub fn body(self, body: B2) -> RequestBuilder<'a, S, B2> { - let (parts, _) = self.request.into_parts(); - let request = Request::from_parts(parts, body); + pub fn body(self, body: B2) -> RequestBuilder { + let request = match self.request { + Ok(req) => { + let (parts, _) = req.into_parts(); + Ok(Request::from_parts(parts, body)) + } + Err(err) => Err(err), + }; RequestBuilder { client: self.client, @@ -302,8 +417,8 @@ impl<'a, S, B> RequestBuilder<'a, S, B> { } /// Get a reference to body in the request. - pub fn body_ref(&self) -> &B { - self.request.body() + pub fn body_ref(&self) -> Option<&B> { + self.request.as_ref().ok().map(Request::body) } /// Set maximin idle time for the request. @@ -316,7 +431,7 @@ impl<'a, S, B> RequestBuilder<'a, S, B> { } } -impl<'a, S, B> RequestBuilder<'a, S, B> +impl RequestBuilder where S: Service, Response = ClientResponse, Error = ClientError> + Send @@ -327,7 +442,7 @@ where /// Send the request and get the response. pub async fn send(self) -> Result { self.client - .send_request(self.target, self.call_opt, self.request, self.timeout) + .send_request(self.target, self.call_opt, self.request?, self.timeout) .await } } @@ -336,8 +451,6 @@ where #[cfg(feature = "json")] #[cfg(test)] mod request_tests { - #![allow(unused)] - use std::collections::HashMap; use serde::Deserialize; @@ -352,29 +465,69 @@ mod request_tests { headers: HashMap, origin: String, url: String, + #[serde(default)] + form: HashMap, + #[serde(default)] + json: Option>, + } + + fn test_data() -> HashMap { + HashMap::from([ + ("key1".to_string(), "val1".to_string()), + ("key2".to_string(), "val2".to_string()), + ]) } #[cfg(feature = "query")] #[tokio::test] async fn set_query() { - let mut builder = Client::builder(); - builder.host("httpbin.org"); - let client = builder.build(); - let query = HashMap::from([ - ("key".to_string(), "val".to_string()), - ("key2".to_string(), "val2".to_string()), - ]); + let data = test_data(); + + let client = Client::builder().build(); let resp = client - .get("/get") + .get("http://httpbin.org/get") + .set_query(&data) + .send() + .await .unwrap() - .set_query(&query) + .into_json::() + .await + .unwrap(); + assert_eq!(resp.args, data); + } + + #[cfg(feature = "form")] + #[tokio::test] + async fn set_form() { + let data = test_data(); + + let client = Client::builder().build(); + let resp = client + .post("http://httpbin.org/post") + .form(&data) + .send() + .await .unwrap() + .into_json::() + .await + .unwrap(); + assert_eq!(resp.form, data); + } + + #[tokio::test] + async fn set_json() { + let data = test_data(); + + let client = Client::builder().build(); + let resp = client + .post("http://httpbin.org/post") + .json(&data) .send() .await .unwrap() .into_json::() .await .unwrap(); - assert_eq!(resp.args, query); + assert_eq!(resp.json, Some(data)); } } diff --git a/volo-http/src/client/test_helpers.rs b/volo-http/src/client/test_helpers.rs index 23f44dfc..f53992d2 100644 --- a/volo-http/src/client/test_helpers.rs +++ b/volo-http/src/client/test_helpers.rs @@ -1,3 +1,5 @@ +//! Test utilities for client of Volo-HTTP. + use std::sync::Arc; use faststr::FastStr; @@ -143,6 +145,7 @@ impl ClientBuilder { }; let client_inner = ClientInner { + service, caller_name, callee_name: self.callee_name, // set a default target so that we can create a request without authority @@ -154,7 +157,6 @@ impl ClientBuilder { headers: self.headers, }; let client = Client { - service, inner: Arc::new(client_inner), }; self.mk_client.mk_client(client) @@ -223,7 +225,7 @@ mod mock_transport_tests { #[tokio::test] async fn empty_response_test() { let client = ClientBuilder::new().mock(MockTransport::default()); - let resp = client.get("/").unwrap().send().await.unwrap(); + let resp = client.get("/").send().await.unwrap(); assert_eq!(resp.status(), StatusCode::OK); assert!(resp.headers().is_empty()); assert!(resp.into_body().into_vec().await.unwrap().is_empty()); @@ -234,7 +236,7 @@ mod mock_transport_tests { { let client = ClientBuilder::new().mock(MockTransport::status_code(StatusCode::IM_A_TEAPOT)); - let resp = client.get("/").unwrap().send().await.unwrap(); + let resp = client.get("/").send().await.unwrap(); assert_eq!(resp.status(), StatusCode::IM_A_TEAPOT); assert!(resp.headers().is_empty()); assert!(resp.into_body().into_vec().await.unwrap().is_empty()); @@ -245,7 +247,7 @@ mod mock_transport_tests { builder.fail_on_error_status(true); builder.mock(MockTransport::status_code(StatusCode::IM_A_TEAPOT)) }; - assert!(client.get("/").unwrap().send().await.is_err()); + assert!(client.get("/").send().await.is_err()); } } } diff --git a/volo-http/src/error/client.rs b/volo-http/src/error/client.rs index e3e4a864..b24ff507 100644 --- a/volo-http/src/error/client.rs +++ b/volo-http/src/error/client.rs @@ -8,10 +8,8 @@ use paste::paste; use super::BoxError; use crate::body::BodyConvertError; -/// [`Result`][Result] with [`ClientError`] as its error type -/// -/// [Result]: std::result::Result -pub type Result = std::result::Result; +/// [`Result`](std::result::Result) with [`ClientError`] as its error by default. +pub type Result = std::result::Result; /// Generic client error #[derive(Debug)] diff --git a/volo-http/src/utils/consts.rs b/volo-http/src/utils/consts.rs index 754aced1..26fd347e 100644 --- a/volo-http/src/utils/consts.rs +++ b/volo-http/src/utils/consts.rs @@ -1,6 +1,14 @@ //! Constants of HTTP(S) protocol. +use http::header::HeaderValue; + /// Default port of HTTP server. pub const HTTP_DEFAULT_PORT: u16 = 80; /// Default port of HTTPS server. pub const HTTPS_DEFAULT_PORT: u16 = 443; + +/// `application/json` +pub const APPLICATION_JSON: HeaderValue = HeaderValue::from_static("application/json"); +/// `application/x-www-form-urlencoded` +pub const APPLICATION_WWW_FORM_URLENCODED: HeaderValue = + HeaderValue::from_static("application/x-www-form-urlencoded"); diff --git a/volo-http/src/utils/test_helpers.rs b/volo-http/src/utils/test_helpers.rs index fb04fa4e..81ce6d64 100644 --- a/volo-http/src/utils/test_helpers.rs +++ b/volo-http/src/utils/test_helpers.rs @@ -142,7 +142,6 @@ mod helper_tests { { let ret = client .get("/get") - .unwrap() .send() .await .unwrap() @@ -154,7 +153,6 @@ mod helper_tests { { let ret = client .get("http://127.0.0.1/get") - .unwrap() .send() .await .unwrap() @@ -164,11 +162,11 @@ mod helper_tests { assert_eq!(ret, HELLO_WORLD); } { - let resp = client.get("/").unwrap().send().await.unwrap(); + let resp = client.get("/").send().await.unwrap(); assert_eq!(resp.status(), StatusCode::NOT_FOUND); } { - let resp = client.post("/get").unwrap().send().await.unwrap(); + let resp = client.post("/get").send().await.unwrap(); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); } }