From 4a7beaa897f4468cabf4ba8e6f1ed4ebaf4bc6bb Mon Sep 17 00:00:00 2001 From: Josiah Bull Date: Sat, 27 Aug 2022 18:50:48 +1200 Subject: [PATCH 1/3] feat: error responses return body upon non 101 status code modified `client::Response` type to contain `String` instead of `()` to achieve this. --- src/handshake/client.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/handshake/client.rs b/src/handshake/client.rs index 0700fa2..32a3c40 100644 --- a/src/handshake/client.rs +++ b/src/handshake/client.rs @@ -26,7 +26,7 @@ use crate::{ pub type Request = HttpRequest<()>; /// Client response type. -pub type Response = HttpResponse<()>; +pub type Response = HttpResponse>; /// Client handshake role. #[derive(Debug)] @@ -83,7 +83,7 @@ impl HandshakeRole for ClientHandshake { ProcessingResult::Continue(HandshakeMachine::start_read(stream)) } StageResult::DoneReading { stream, result, tail } => { - let result = self.verify_data.verify_response(result)?; + let result = self.verify_data.verify_response(result, &tail)?; debug!("Client handshake done."); let websocket = WebSocket::from_partially_read(stream, tail, Role::Client, self.config); @@ -178,11 +178,12 @@ struct VerifyData { } impl VerifyData { - pub fn verify_response(&self, response: Response) -> Result { + pub fn verify_response(&self, mut response: Response, tail: &[u8]) -> Result { // 1. If the status code received from the server is not 101, the // client handles the response per HTTP [RFC2616] procedures. (RFC 6455) if response.status() != StatusCode::SWITCHING_PROTOCOLS { - return Err(Error::Http(response.map(|_| None))); + *response.body_mut() = Some(String::from_utf8_lossy(tail).to_string()); + return Err(Error::Http(response)); } let headers = response.headers(); @@ -255,7 +256,7 @@ impl<'h, 'b: 'h> FromHttparse> for Response { let headers = HeaderMap::from_httparse(raw.headers)?; - let mut response = Response::new(()); + let mut response = Response::new(None); *response.status_mut() = StatusCode::from_u16(raw.code.expect("Bug: no HTTP status code"))?; *response.headers_mut() = headers; // TODO: httparse only supports HTTP 0.9/1.0/1.1 but not HTTP 2.0 From 36ab77005952a1f6aecc0b1f41c7e0cbf0282172 Mon Sep 17 00:00:00 2001 From: Josiah Bull Date: Tue, 30 Aug 2022 09:48:36 +1200 Subject: [PATCH 2/3] feat: error type returns `Vec` instead of `Option` --- examples/callback-error.rs | 2 +- src/error.rs | 2 +- src/handshake/client.rs | 17 ++++++++++++----- src/handshake/server.rs | 6 ++---- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/examples/callback-error.rs b/examples/callback-error.rs index cf78a2e..ee51136 100644 --- a/examples/callback-error.rs +++ b/examples/callback-error.rs @@ -13,7 +13,7 @@ fn main() { let callback = |_req: &Request, _resp| { let resp = Response::builder() .status(StatusCode::FORBIDDEN) - .body(Some("Access denied".into())) + .body("Access denied".into()) .unwrap(); Err(resp) }; diff --git a/src/error.rs b/src/error.rs index 3e15c7c..9d6576e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -65,7 +65,7 @@ pub enum Error { /// HTTP error. #[error("HTTP error: {}", .0.status())] #[cfg(feature = "handshake")] - Http(Response>), + Http(Response>), /// HTTP format error. #[error("HTTP format error: {0}")] #[cfg(feature = "handshake")] diff --git a/src/handshake/client.rs b/src/handshake/client.rs index 32a3c40..87305f4 100644 --- a/src/handshake/client.rs +++ b/src/handshake/client.rs @@ -26,7 +26,7 @@ use crate::{ pub type Request = HttpRequest<()>; /// Client response type. -pub type Response = HttpResponse>; +pub type Response = HttpResponse>; /// Client handshake role. #[derive(Debug)] @@ -83,7 +83,15 @@ impl HandshakeRole for ClientHandshake { ProcessingResult::Continue(HandshakeMachine::start_read(stream)) } StageResult::DoneReading { stream, result, tail } => { - let result = self.verify_data.verify_response(result, &tail)?; + let result = match self.verify_data.verify_response(result) { + Ok(r) => r, + Err(Error::Http(mut e)) => { + *e.body_mut() = tail; + return Err(Error::Http(e)) + }, + Err(e) => return Err(e), + }; + debug!("Client handshake done."); let websocket = WebSocket::from_partially_read(stream, tail, Role::Client, self.config); @@ -178,11 +186,10 @@ struct VerifyData { } impl VerifyData { - pub fn verify_response(&self, mut response: Response, tail: &[u8]) -> Result { + pub fn verify_response(&self, response: Response) -> Result { // 1. If the status code received from the server is not 101, the // client handles the response per HTTP [RFC2616] procedures. (RFC 6455) if response.status() != StatusCode::SWITCHING_PROTOCOLS { - *response.body_mut() = Some(String::from_utf8_lossy(tail).to_string()); return Err(Error::Http(response)); } @@ -256,7 +263,7 @@ impl<'h, 'b: 'h> FromHttparse> for Response { let headers = HeaderMap::from_httparse(raw.headers)?; - let mut response = Response::new(None); + let mut response = Response::new(vec![]); *response.status_mut() = StatusCode::from_u16(raw.code.expect("Bug: no HTTP status code"))?; *response.headers_mut() = headers; // TODO: httparse only supports HTTP 0.9/1.0/1.1 but not HTTP 2.0 diff --git a/src/handshake/server.rs b/src/handshake/server.rs index 2f095ce..7e3a57f 100644 --- a/src/handshake/server.rs +++ b/src/handshake/server.rs @@ -30,7 +30,7 @@ pub type Request = HttpRequest<()>; pub type Response = HttpResponse<()>; /// Server error response type. -pub type ErrorResponse = HttpResponse>; +pub type ErrorResponse = HttpResponse>; fn create_parts(request: &HttpRequest) -> Result { if request.method() != http::Method::GET { @@ -265,9 +265,7 @@ impl HandshakeRole for ServerHandshake { let mut output = vec![]; write_response(&mut output, resp)?; - if let Some(body) = resp.body() { - output.extend_from_slice(body.as_bytes()); - } + output.extend_from_slice(resp.body()); ProcessingResult::Continue(HandshakeMachine::start_write(stream, output)) } From 1c657d4c6ac55fe6f8640557a9894afb57a1cd75 Mon Sep 17 00:00:00 2001 From: Josiah Bull Date: Wed, 19 Oct 2022 12:28:44 +1300 Subject: [PATCH 3/3] feat: switched response type from Vec to Option> to allow returning of differentiation of no body from empty body. --- examples/callback-error.rs | 2 +- src/error.rs | 2 +- src/handshake/client.rs | 6 +++--- src/handshake/server.rs | 11 ++++++++--- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/examples/callback-error.rs b/examples/callback-error.rs index ee51136..cf78a2e 100644 --- a/examples/callback-error.rs +++ b/examples/callback-error.rs @@ -13,7 +13,7 @@ fn main() { let callback = |_req: &Request, _resp| { let resp = Response::builder() .status(StatusCode::FORBIDDEN) - .body("Access denied".into()) + .body(Some("Access denied".into())) .unwrap(); Err(resp) }; diff --git a/src/error.rs b/src/error.rs index 9d6576e..c830024 100644 --- a/src/error.rs +++ b/src/error.rs @@ -65,7 +65,7 @@ pub enum Error { /// HTTP error. #[error("HTTP error: {}", .0.status())] #[cfg(feature = "handshake")] - Http(Response>), + Http(Response>>), /// HTTP format error. #[error("HTTP format error: {0}")] #[cfg(feature = "handshake")] diff --git a/src/handshake/client.rs b/src/handshake/client.rs index 87305f4..aaeabb7 100644 --- a/src/handshake/client.rs +++ b/src/handshake/client.rs @@ -26,7 +26,7 @@ use crate::{ pub type Request = HttpRequest<()>; /// Client response type. -pub type Response = HttpResponse>; +pub type Response = HttpResponse>>; /// Client handshake role. #[derive(Debug)] @@ -86,7 +86,7 @@ impl HandshakeRole for ClientHandshake { let result = match self.verify_data.verify_response(result) { Ok(r) => r, Err(Error::Http(mut e)) => { - *e.body_mut() = tail; + *e.body_mut() = Some(tail); return Err(Error::Http(e)) }, Err(e) => return Err(e), @@ -263,7 +263,7 @@ impl<'h, 'b: 'h> FromHttparse> for Response { let headers = HeaderMap::from_httparse(raw.headers)?; - let mut response = Response::new(vec![]); + let mut response = Response::new(None); *response.status_mut() = StatusCode::from_u16(raw.code.expect("Bug: no HTTP status code"))?; *response.headers_mut() = headers; // TODO: httparse only supports HTTP 0.9/1.0/1.1 but not HTTP 2.0 diff --git a/src/handshake/server.rs b/src/handshake/server.rs index 7e3a57f..4cf1c5c 100644 --- a/src/handshake/server.rs +++ b/src/handshake/server.rs @@ -30,7 +30,7 @@ pub type Request = HttpRequest<()>; pub type Response = HttpResponse<()>; /// Server error response type. -pub type ErrorResponse = HttpResponse>; +pub type ErrorResponse = HttpResponse>; fn create_parts(request: &HttpRequest) -> Result { if request.method() != http::Method::GET { @@ -265,7 +265,9 @@ impl HandshakeRole for ServerHandshake { let mut output = vec![]; write_response(&mut output, resp)?; - output.extend_from_slice(resp.body()); + if let Some(body) = resp.body() { + output.extend_from_slice(body.as_bytes()); + } ProcessingResult::Continue(HandshakeMachine::start_write(stream, output)) } @@ -275,7 +277,10 @@ impl HandshakeRole for ServerHandshake { StageResult::DoneWriting(stream) => { if let Some(err) = self.error_response.take() { debug!("Server handshake failed."); - return Err(Error::Http(err)); + + let (parts, body) = err.into_parts(); + let body = body.map(|b| b.as_bytes().to_vec()); + return Err(Error::Http(http::Response::from_parts(parts, body))); } else { debug!("Server handshake done."); let websocket = WebSocket::from_raw_socket(stream, Role::Server, self.config);