From 0e4687dbe311830690267248422fba4407477b47 Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Thu, 10 Oct 2024 11:08:54 +0800 Subject: [PATCH 1/5] edge-http: make fields in {Req,Resp}Headers non-optional Ensures that the {Req,Resp}Headers is always complete and adheres to the HTTP spec. Saves a lot code from dealing with `Option`s. Also, `send_status_line` is removed, and inlined into `send_request` and `send_status`. --- edge-http/src/io.rs | 202 +++++++++++++++---------------------- edge-http/src/io/client.rs | 14 +-- edge-http/src/io/server.rs | 21 ++-- edge-http/src/lib.rs | 76 +++++--------- examples/http_server.rs | 4 +- examples/ws_server.rs | 4 +- 6 files changed, 118 insertions(+), 203 deletions(-) diff --git a/edge-http/src/io.rs b/edge-http/src/io.rs index 390cd2c..bca21f8 100644 --- a/edge-http/src/io.rs +++ b/edge-http/src/io.rs @@ -100,11 +100,10 @@ impl std::error::Error for Error where E: std::error::Error {} impl<'b, const N: usize> RequestHeaders<'b, N> { /// Parse the headers from the input stream pub async fn receive( - &mut self, buf: &'b mut [u8], mut input: R, exact: bool, - ) -> Result<(&'b mut [u8], usize), Error> + ) -> Result<(Self, &'b mut [u8], usize), Error> where R: Read, { @@ -114,7 +113,8 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { Err(e) => return Err(e), }; - let mut parser = httparse::Request::new(&mut self.headers.0); + let mut headers = Headers::<'b, N>::new(); + let mut parser = httparse::Request::new(&mut headers.0); let (headers_buf, body_buf) = buf.split_at_mut(headers_len); @@ -128,22 +128,26 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { unreachable!("Should not happen. HTTP header parsing is indeterminate.") } - self.http11 = if let Some(version) = parser.version { - if version > 1 { - Err(Error::InvalidHeaders)?; - } - - Some(version == 1) - } else { - None + let http11 = match parser.version { + Some(0) => false, + Some(1) => true, + _ => Err(Error::InvalidHeaders)?, }; - self.method = parser.method.and_then(Method::new); - self.path = parser.path; + let method_str = parser.method.ok_or(Error::InvalidHeaders)?; + let method = Method::new(method_str).ok_or(Error::InvalidHeaders)?; + let path = parser.path.ok_or(Error::InvalidHeaders)?; - trace!("Received:\n{}", self); + let result = Self { + http11, + method, + path, + headers, + }; - Ok((body_buf, read_len - headers_len)) + trace!("Received:\n{}", result); + + Ok((result, body_buf, read_len - headers_len)) } else { unreachable!("Secondary parse of already loaded buffer failed.") } @@ -151,8 +155,7 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { /// Resolve the connection type and body type from the headers pub fn resolve(&self) -> Result<(ConnectionType, BodyType), Error> { - self.headers - .resolve::(None, true, self.http11.unwrap_or(false)) + self.headers.resolve::(None, true, self.http11) } /// Send the headers to the output stream, returning the connection type and body type @@ -164,12 +167,10 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { where W: Write, { - let http11 = self.http11.unwrap_or(false); - - send_request(http11, self.method, self.path, &mut output).await?; + send_request(self.http11, self.method, self.path, &mut output).await?; self.headers - .send(None, true, http11, chunked_if_unspecified, output) + .send(None, true, self.http11, chunked_if_unspecified, output) .await } } @@ -177,18 +178,18 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { impl<'b, const N: usize> ResponseHeaders<'b, N> { /// Parse the headers from the input stream pub async fn receive( - &mut self, buf: &'b mut [u8], mut input: R, exact: bool, - ) -> Result<(&'b mut [u8], usize), Error> + ) -> Result<(Self, &'b mut [u8], usize), Error> where R: Read, { let (read_len, headers_len) = raw::read_reply_buf::(&mut input, buf, false, exact).await?; - let mut parser = httparse::Response::new(&mut self.headers.0); + let mut headers = Headers::<'b, N>::new(); + let mut parser = httparse::Response::new(&mut headers.0); let (headers_buf, body_buf) = buf.split_at_mut(headers_len); @@ -199,22 +200,25 @@ impl<'b, const N: usize> ResponseHeaders<'b, N> { unreachable!("Should not happen. HTTP header parsing is indeterminate.") } - self.http11 = if let Some(version) = parser.version { - if version > 1 { - Err(Error::InvalidHeaders)?; - } - - Some(version == 1) - } else { - None + let http11 = match parser.version { + Some(0) => false, + Some(1) => true, + _ => Err(Error::InvalidHeaders)?, }; - self.code = parser.code; - self.reason = parser.reason; + let code = parser.code.ok_or(Error::InvalidHeaders)?; + let reason = parser.reason; + + let result = Self { + http11, + code, + reason, + headers, + }; - trace!("Received:\n{}", self); + trace!("Received:\n{}", result); - Ok((body_buf, read_len - headers_len)) + Ok((result, body_buf, read_len - headers_len)) } else { unreachable!("Secondary parse of already loaded buffer failed.") } @@ -225,11 +229,8 @@ impl<'b, const N: usize> ResponseHeaders<'b, N> { &self, request_connection_type: ConnectionType, ) -> Result<(ConnectionType, BodyType), Error> { - self.headers.resolve::( - Some(request_connection_type), - false, - self.http11.unwrap_or(false), - ) + self.headers + .resolve::(Some(request_connection_type), false, self.http11) } /// Send the headers to the output stream, returning the connection type and body type @@ -242,15 +243,13 @@ impl<'b, const N: usize> ResponseHeaders<'b, N> { where W: Write, { - let http11 = self.http11.unwrap_or(false); - - send_status(http11, self.code, self.reason, &mut output).await?; + send_status(self.http11, self.code, self.reason, &mut output).await?; self.headers .send( Some(request_connection_type), false, - http11, + self.http11, chunked_if_unspecified, output, ) @@ -260,42 +259,56 @@ impl<'b, const N: usize> ResponseHeaders<'b, N> { pub(crate) async fn send_request( http11: bool, - method: Option, - path: Option<&str>, - output: W, + method: Method, + path: &str, + mut output: W, ) -> Result<(), Error> where W: Write, { - raw::send_status_line( - true, - http11, - method.map(|method| method.as_str()), - path, - output, - ) - .await + // RFC 9112: request-line = method SP request-target SP HTTP-version + + output + .write_all(method.as_str().as_bytes()) + .await + .map_err(Error::Io)?; + output.write_all(b" ").await.map_err(Error::Io)?; + output.write_all(path.as_bytes()).await.map_err(Error::Io)?; + output.write_all(b" ").await.map_err(Error::Io)?; + raw::send_version(&mut output, http11).await?; + output.write_all(b"\r\n").await.map_err(Error::Io)?; + + Ok(()) } pub(crate) async fn send_status( http11: bool, - status: Option, + status: u16, reason: Option<&str>, - output: W, + mut output: W, ) -> Result<(), Error> where W: Write, { - let status_str: Option> = status.map(|status| status.try_into().unwrap()); + // RFC 9112: status-line = HTTP-version SP status-code SP [ reason-phrase ] - raw::send_status_line( - false, - http11, - status_str.as_ref().map(|status| status.as_str()), - reason, - output, - ) - .await + raw::send_version(&mut output, http11).await?; + output.write_all(b" ").await.map_err(Error::Io)?; + let status_str: heapless::String<5> = status.try_into().unwrap(); + output + .write_all(status_str.as_bytes()) + .await + .map_err(Error::Io)?; + output.write_all(b" ").await.map_err(Error::Io)?; + if let Some(reason) = reason { + output + .write_all(reason.as_bytes()) + .await + .map_err(Error::Io)?; + } + output.write_all(b"\r\n").await.map_err(Error::Io)?; + + Ok(()) } pub(crate) async fn send_headers<'a, H, W>( @@ -1181,61 +1194,6 @@ mod raw { } } - pub(crate) async fn send_status_line( - request: bool, - http11: bool, - token: Option<&str>, - extra: Option<&str>, - mut output: W, - ) -> Result<(), Error> - where - W: Write, - { - let mut written = false; - - if !request { - send_version(&mut output, http11).await?; - written = true; - } - - if let Some(token) = token { - if written { - output.write_all(b" ").await.map_err(Error::Io)?; - } - - output - .write_all(token.as_bytes()) - .await - .map_err(Error::Io)?; - - written = true; - } - - if written { - output.write_all(b" ").await.map_err(Error::Io)?; - } - if let Some(extra) = extra { - output - .write_all(extra.as_bytes()) - .await - .map_err(Error::Io)?; - - written = true; - } - - if request { - if written { - output.write_all(b" ").await.map_err(Error::Io)?; - } - - send_version(&mut output, http11).await?; - } - - output.write_all(b"\r\n").await.map_err(Error::Io)?; - - Ok(()) - } - pub(crate) async fn send_version(mut output: W, http11: bool) -> Result<(), Error> where W: Write, diff --git a/edge-http/src/io/client.rs b/edge-http/src/io/client.rs index 3775e1d..fe92f17 100644 --- a/edge-http/src/io/client.rs +++ b/edge-http/src/io/client.rs @@ -174,7 +174,7 @@ where let mut state = self.unbind(); let result = async { - match send_request(http11, Some(method), Some(uri), state.io.as_mut().unwrap()).await { + match send_request(http11, method, uri, state.io.as_mut().unwrap()).await { Ok(_) => (), Err(Error::Io(_)) => { if !fresh_connection { @@ -182,8 +182,7 @@ where state.io = None; state.io = Some(state.socket.connect(state.addr).await.map_err(Error::Io)?); - send_request(http11, Some(method), Some(uri), state.io.as_mut().unwrap()) - .await?; + send_request(http11, method, uri, state.io.as_mut().unwrap()).await?; } } Err(other) => Err(other)?, @@ -264,13 +263,8 @@ where let mut state = self.unbind(); let buf_ptr: *mut [u8] = state.buf; - let mut response = ResponseHeaders::new(); - - match response - .receive(state.buf, &mut state.io.as_mut().unwrap(), true) - .await - { - Ok((buf, read_len)) => { + match ResponseHeaders::receive(state.buf, &mut state.io.as_mut().unwrap(), true).await { + Ok((response, buf, read_len)) => { let (connection_type, body_type) = response.resolve::(request_connection_type)?; diff --git a/edge-http/src/io/server.rs b/edge-http/src/io/server.rs index 1b92a7b..6327c05 100644 --- a/edge-http/src/io/server.rs +++ b/edge-http/src/io/server.rs @@ -52,9 +52,7 @@ where buf: &'b mut [u8], mut io: T, ) -> Result, Error> { - let mut request = RequestHeaders::new(); - - let (buf, read_len) = request.receive(buf, &mut io, true).await?; + let (request, buf, read_len) = RequestHeaders::receive(buf, &mut io, true).await?; let (connection_type, body_type) = request.resolve::()?; @@ -103,7 +101,7 @@ where message: Option<&str>, headers: &[(&str, &str)], ) -> Result<(), Error> { - self.complete_request(Some(status), message, headers).await + self.complete_request(status, message, headers).await } /// A convenience method to initiate a WebSocket upgrade response @@ -125,7 +123,7 @@ where /// If the connection is still in a request state, and empty 200 OK response is sent pub async fn complete(&mut self) -> Result<(), Error> { if self.is_request_initiated() { - self.complete_request(Some(200), Some("OK"), &[]).await?; + self.complete_request(200, Some("OK"), &[]).await?; } if self.is_response_initiated() { @@ -145,7 +143,7 @@ where Ok(_) => { let headers = [("Connection", "Close"), ("Content-Type", "text/plain")]; - self.complete_request(Some(500), Some("Internal Error"), &headers) + self.complete_request(500, Some("Internal Error"), &headers) .await?; let response = self.response_mut()?; @@ -181,7 +179,7 @@ where async fn complete_request( &mut self, - status: Option, + status: u16, reason: Option<&str>, headers: &[(&str, &str)], ) -> Result<(), Error> { @@ -190,7 +188,7 @@ where let mut buf = [0; COMPLETION_BUF_SIZE]; while request.io.read(&mut buf).await? > 0 {} - let http11 = request.request.http11.unwrap_or(false); + let http11 = request.request.http11; let request_connection_type = request.connection_type; let mut io = self.unbind_mut(); @@ -918,12 +916,7 @@ mod embedded_svc_compat { let headers = connection.headers().ok(); if let Some(headers) = headers { - if headers.path.map(|path| self.path == path).unwrap_or(false) - && headers - .method - .map(|method| self.method == method.into()) - .unwrap_or(false) - { + if headers.path == self.path && headers.method == self.method.into() { return self.handler.handle(connection).await; } } diff --git a/edge-http/src/lib.rs b/edge-http/src/lib.rs index bdb77c6..eac027f 100644 --- a/edge-http/src/lib.rs +++ b/edge-http/src/lib.rs @@ -704,30 +704,19 @@ impl Display for BodyType { } /// Request headers including the request line (method, path) -#[derive(Default, Debug)] +#[derive(Debug)] pub struct RequestHeaders<'b, const N: usize> { - /// Whether the request is HTTP/1.1, if present. If not present, HTTP/1.0 should be assumed - pub http11: Option, - /// The HTTP method, if present - pub method: Option, - /// The request path, if present - pub path: Option<&'b str>, + /// Whether the request is HTTP/1.1 + pub http11: bool, + /// The HTTP method + pub method: Method, + /// The request path + pub path: &'b str, /// The headers pub headers: Headers<'b, N>, } impl RequestHeaders<'_, N> { - /// Create a new RequestHeaders instance for HTTP/1.1 - #[inline(always)] - pub const fn new() -> Self { - Self { - http11: Some(true), - method: None, - path: None, - headers: Headers::::new(), - } - } - /// A utility method to check if the request is a Websocket upgrade request pub fn is_ws_upgrade_request(&self) -> bool { is_upgrade_request(self.method, self.headers.iter()) @@ -736,13 +725,9 @@ impl RequestHeaders<'_, N> { impl Display for RequestHeaders<'_, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(http11) = self.http11 { - write!(f, "{} ", if http11 { "HTTP/1.1" } else { "HTTP/1.0" })?; - } + write!(f, "{} ", if self.http11 { "HTTP/1.1" } else { "HTTP/1.0" })?; - if let Some(method) = self.method { - writeln!(f, "{method} {}", self.path.unwrap_or(""))?; - } + writeln!(f, "{} {}", self.method, self.path)?; for (name, value) in self.headers.iter() { if name.is_empty() { @@ -757,12 +742,12 @@ impl Display for RequestHeaders<'_, N> { } /// Response headers including the response line (HTTP version, status code, reason phrase) -#[derive(Default, Debug)] +#[derive(Debug)] pub struct ResponseHeaders<'b, const N: usize> { - /// Whether the response is HTTP/1.1, if present. If not present, HTTP/1.0 should be assumed - pub http11: Option, - /// The status code, if present - pub code: Option, + /// Whether the response is HTTP/1.1 + pub http11: bool, + /// The status code + pub code: u16, /// The reason phrase, if present pub reason: Option<&'b str>, /// The headers @@ -770,17 +755,6 @@ pub struct ResponseHeaders<'b, const N: usize> { } impl ResponseHeaders<'_, N> { - /// Create a new ResponseHeaders instance for HTTP/1.1 - #[inline(always)] - pub const fn new() -> Self { - Self { - http11: Some(true), - code: None, - reason: None, - headers: Headers::::new(), - } - } - /// A utility method to check if the response is a Websocket upgrade response /// and if the upgrade was accepted pub fn is_ws_upgrade_accepted( @@ -794,13 +768,9 @@ impl ResponseHeaders<'_, N> { impl Display for ResponseHeaders<'_, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(http11) = self.http11 { - writeln!(f, "{} ", if http11 { "HTTP/1.1 " } else { "HTTP/1.0" })?; - } + write!(f, "{} ", if self.http11 { "HTTP/1.1 " } else { "HTTP/1.0" })?; - if let Some(code) = self.code { - writeln!(f, "{code} {}", self.reason.unwrap_or(""))?; - } + writeln!(f, "{} {}", self.code, self.reason.unwrap_or(""))?; for (name, value) in self.headers.iter() { if name.is_empty() { @@ -859,11 +829,11 @@ pub mod ws { } /// Check if the request is a Websocket upgrade request - pub fn is_upgrade_request<'a, H>(method: Option, request_headers: H) -> bool + pub fn is_upgrade_request<'a, H>(method: Method, request_headers: H) -> bool where H: IntoIterator, { - if !matches!(method, Some(Method::Get)) { + if method == Method::Get { return false; } @@ -960,7 +930,7 @@ pub mod ws { /// - `nonce`: The nonce used for the `Sec-WebSocket-Key` header in the WS upgrade request /// - `buf`: A buffer to use when performing the check pub fn is_upgrade_accepted<'a, H>( - code: Option, + code: u16, response_headers: H, nonce: &[u8; NONCE_LEN], buf: &'a mut [u8; MAX_BASE64_KEY_RESPONSE_LEN], @@ -968,7 +938,7 @@ pub mod ws { where H: IntoIterator, { - if !matches!(code, Some(101)) { + if code != 101 { return false; } @@ -1407,11 +1377,11 @@ mod embedded_svc_compat { impl<'b, const N: usize> embedded_svc::http::Query for super::RequestHeaders<'b, N> { fn uri(&self) -> &'_ str { - self.path.unwrap_or("") + self.path } fn method(&self) -> Method { - self.method.unwrap_or(super::Method::Get).into() + self.method.into() } } @@ -1423,7 +1393,7 @@ mod embedded_svc_compat { impl<'b, const N: usize> embedded_svc::http::Status for super::ResponseHeaders<'b, N> { fn status(&self) -> u16 { - self.code.unwrap_or(200) + self.code } fn status_message(&self) -> Option<&'_ str> { diff --git a/examples/http_server.rs b/examples/http_server.rs index 5b01887..bbdda29 100644 --- a/examples/http_server.rs +++ b/examples/http_server.rs @@ -42,10 +42,10 @@ where async fn handle(&self, conn: &mut Connection<'b, T, N>) -> Result<(), Self::Error> { let headers = conn.headers()?; - if !matches!(headers.method, Some(Method::Get)) { + if headers.method != Method::Get { conn.initiate_response(405, Some("Method Not Allowed"), &[]) .await?; - } else if !matches!(headers.path, Some("/")) { + } else if headers.path != "/" { conn.initiate_response(404, Some("Not Found"), &[]).await?; } else { conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/plain")]) diff --git a/examples/ws_server.rs b/examples/ws_server.rs index cdbd48a..a047a1a 100644 --- a/examples/ws_server.rs +++ b/examples/ws_server.rs @@ -58,10 +58,10 @@ where async fn handle(&self, conn: &mut Connection<'b, T, N>) -> Result<(), Self::Error> { let headers = conn.headers()?; - if !matches!(headers.method, Some(Method::Get)) { + if headers.method != Method::Get { conn.initiate_response(405, Some("Method Not Allowed"), &[]) .await?; - } else if !matches!(headers.path, Some("/")) { + } else if headers.path != "/" { conn.initiate_response(404, Some("Not Found"), &[]).await?; } else if !conn.is_ws_upgrade_request()? { conn.initiate_response(200, Some("OK"), &[("Content-Type", "text/plain")]) From d42c63fbdfd61785e833f25e1822f46d3f66a4ce Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Thu, 10 Oct 2024 22:59:59 +0800 Subject: [PATCH 2/5] Fix is_upgrade_request --- edge-http/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/edge-http/src/lib.rs b/edge-http/src/lib.rs index eac027f..6021813 100644 --- a/edge-http/src/lib.rs +++ b/edge-http/src/lib.rs @@ -833,7 +833,7 @@ pub mod ws { where H: IntoIterator, { - if method == Method::Get { + if method != Method::Get { return false; } From 8079ce40f8d32d302cff0ebf9fa1d498508bc5f5 Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:01:20 +0800 Subject: [PATCH 3/5] Let receive be a method, rather than a constructor function --- edge-http/src/io.rs | 46 +++++++++++++------------------------- edge-http/src/io/client.rs | 8 +++++-- edge-http/src/io/server.rs | 4 +++- edge-http/src/lib.rs | 22 ++++++++++++++++++ 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/edge-http/src/io.rs b/edge-http/src/io.rs index bca21f8..3d58acd 100644 --- a/edge-http/src/io.rs +++ b/edge-http/src/io.rs @@ -100,10 +100,11 @@ impl std::error::Error for Error where E: std::error::Error {} impl<'b, const N: usize> RequestHeaders<'b, N> { /// Parse the headers from the input stream pub async fn receive( + &mut self, buf: &'b mut [u8], mut input: R, exact: bool, - ) -> Result<(Self, &'b mut [u8], usize), Error> + ) -> Result<(&'b mut [u8], usize), Error> where R: Read, { @@ -113,8 +114,7 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { Err(e) => return Err(e), }; - let mut headers = Headers::<'b, N>::new(); - let mut parser = httparse::Request::new(&mut headers.0); + let mut parser = httparse::Request::new(&mut self.headers.0); let (headers_buf, body_buf) = buf.split_at_mut(headers_len); @@ -128,26 +128,19 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { unreachable!("Should not happen. HTTP header parsing is indeterminate.") } - let http11 = match parser.version { + self.http11 = match parser.version { Some(0) => false, Some(1) => true, _ => Err(Error::InvalidHeaders)?, }; let method_str = parser.method.ok_or(Error::InvalidHeaders)?; - let method = Method::new(method_str).ok_or(Error::InvalidHeaders)?; - let path = parser.path.ok_or(Error::InvalidHeaders)?; - - let result = Self { - http11, - method, - path, - headers, - }; + self.method = Method::new(method_str).ok_or(Error::InvalidHeaders)?; + self.path = parser.path.ok_or(Error::InvalidHeaders)?; - trace!("Received:\n{}", result); + trace!("Received:\n{}", self); - Ok((result, body_buf, read_len - headers_len)) + Ok((body_buf, read_len - headers_len)) } else { unreachable!("Secondary parse of already loaded buffer failed.") } @@ -178,18 +171,18 @@ impl<'b, const N: usize> RequestHeaders<'b, N> { impl<'b, const N: usize> ResponseHeaders<'b, N> { /// Parse the headers from the input stream pub async fn receive( + &mut self, buf: &'b mut [u8], mut input: R, exact: bool, - ) -> Result<(Self, &'b mut [u8], usize), Error> + ) -> Result<(&'b mut [u8], usize), Error> where R: Read, { let (read_len, headers_len) = raw::read_reply_buf::(&mut input, buf, false, exact).await?; - let mut headers = Headers::<'b, N>::new(); - let mut parser = httparse::Response::new(&mut headers.0); + let mut parser = httparse::Response::new(&mut self.headers.0); let (headers_buf, body_buf) = buf.split_at_mut(headers_len); @@ -200,25 +193,18 @@ impl<'b, const N: usize> ResponseHeaders<'b, N> { unreachable!("Should not happen. HTTP header parsing is indeterminate.") } - let http11 = match parser.version { + self.http11 = match parser.version { Some(0) => false, Some(1) => true, _ => Err(Error::InvalidHeaders)?, }; - let code = parser.code.ok_or(Error::InvalidHeaders)?; - let reason = parser.reason; - - let result = Self { - http11, - code, - reason, - headers, - }; + self.code = parser.code.ok_or(Error::InvalidHeaders)?; + self.reason = parser.reason; - trace!("Received:\n{}", result); + trace!("Received:\n{}", self); - Ok((result, body_buf, read_len - headers_len)) + Ok((body_buf, read_len - headers_len)) } else { unreachable!("Secondary parse of already loaded buffer failed.") } diff --git a/edge-http/src/io/client.rs b/edge-http/src/io/client.rs index fe92f17..2d447f0 100644 --- a/edge-http/src/io/client.rs +++ b/edge-http/src/io/client.rs @@ -262,9 +262,13 @@ where let mut state = self.unbind(); let buf_ptr: *mut [u8] = state.buf; + let mut response = ResponseHeaders::default(); - match ResponseHeaders::receive(state.buf, &mut state.io.as_mut().unwrap(), true).await { - Ok((response, buf, read_len)) => { + match response + .receive(state.buf, &mut state.io.as_mut().unwrap(), true) + .await + { + Ok((buf, read_len)) => { let (connection_type, body_type) = response.resolve::(request_connection_type)?; diff --git a/edge-http/src/io/server.rs b/edge-http/src/io/server.rs index 6327c05..2ba646b 100644 --- a/edge-http/src/io/server.rs +++ b/edge-http/src/io/server.rs @@ -52,7 +52,9 @@ where buf: &'b mut [u8], mut io: T, ) -> Result, Error> { - let (request, buf, read_len) = RequestHeaders::receive(buf, &mut io, true).await?; + let mut request = RequestHeaders::default(); + + let (buf, read_len) = request.receive(buf, &mut io, true).await?; let (connection_type, body_type) = request.resolve::()?; diff --git a/edge-http/src/lib.rs b/edge-http/src/lib.rs index 6021813..f377963 100644 --- a/edge-http/src/lib.rs +++ b/edge-http/src/lib.rs @@ -723,6 +723,17 @@ impl RequestHeaders<'_, N> { } } +impl<'b, const N: usize> Default for RequestHeaders<'b, N> { + fn default() -> Self { + Self { + http11: true, + method: Method::Get, + path: "/", + headers: Headers::new(), + } + } +} + impl Display for RequestHeaders<'_, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} ", if self.http11 { "HTTP/1.1" } else { "HTTP/1.0" })?; @@ -766,6 +777,17 @@ impl ResponseHeaders<'_, N> { } } +impl<'b, const N: usize> Default for ResponseHeaders<'b, N> { + fn default() -> Self { + Self { + http11: true, + code: 200, + reason: None, + headers: Headers::new(), + } + } +} + impl Display for ResponseHeaders<'_, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{} ", if self.http11 { "HTTP/1.1 " } else { "HTTP/1.0" })?; From 89d692b3829975e5f01c855abba2c467bb591732 Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Thu, 10 Oct 2024 23:05:25 +0800 Subject: [PATCH 4/5] Add inline attribute to default --- edge-http/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/edge-http/src/lib.rs b/edge-http/src/lib.rs index f377963..98cffac 100644 --- a/edge-http/src/lib.rs +++ b/edge-http/src/lib.rs @@ -724,6 +724,7 @@ impl RequestHeaders<'_, N> { } impl<'b, const N: usize> Default for RequestHeaders<'b, N> { + #[inline(always)] fn default() -> Self { Self { http11: true, @@ -778,6 +779,7 @@ impl ResponseHeaders<'_, N> { } impl<'b, const N: usize> Default for ResponseHeaders<'b, N> { + #[inline(always)] fn default() -> Self { Self { http11: true, From 5ed9f6741739e948cb01c5e14f3e56e39c4c2fdf Mon Sep 17 00:00:00 2001 From: Shaw Drastin <168159404+showier-drastic@users.noreply.github.com> Date: Fri, 11 Oct 2024 09:27:09 +0800 Subject: [PATCH 5/5] Restore the const constructor `new()` and call it in the library --- edge-http/src/io/client.rs | 2 +- edge-http/src/io/server.rs | 2 +- edge-http/src/lib.rs | 36 ++++++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 14 deletions(-) diff --git a/edge-http/src/io/client.rs b/edge-http/src/io/client.rs index 2d447f0..6f2fb10 100644 --- a/edge-http/src/io/client.rs +++ b/edge-http/src/io/client.rs @@ -262,7 +262,7 @@ where let mut state = self.unbind(); let buf_ptr: *mut [u8] = state.buf; - let mut response = ResponseHeaders::default(); + let mut response = ResponseHeaders::new(); match response .receive(state.buf, &mut state.io.as_mut().unwrap(), true) diff --git a/edge-http/src/io/server.rs b/edge-http/src/io/server.rs index 2ba646b..1d7a756 100644 --- a/edge-http/src/io/server.rs +++ b/edge-http/src/io/server.rs @@ -52,7 +52,7 @@ where buf: &'b mut [u8], mut io: T, ) -> Result, Error> { - let mut request = RequestHeaders::default(); + let mut request = RequestHeaders::new(); let (buf, read_len) = request.receive(buf, &mut io, true).await?; diff --git a/edge-http/src/lib.rs b/edge-http/src/lib.rs index 98cffac..6fe9546 100644 --- a/edge-http/src/lib.rs +++ b/edge-http/src/lib.rs @@ -717,6 +717,17 @@ pub struct RequestHeaders<'b, const N: usize> { } impl RequestHeaders<'_, N> { + // Create a new RequestHeaders instance, defaults to GET / HTTP/1.1 + #[inline(always)] + pub const fn new() -> Self { + Self { + http11: true, + method: Method::Get, + path: "/", + headers: Headers::new(), + } + } + /// A utility method to check if the request is a Websocket upgrade request pub fn is_ws_upgrade_request(&self) -> bool { is_upgrade_request(self.method, self.headers.iter()) @@ -726,12 +737,7 @@ impl RequestHeaders<'_, N> { impl<'b, const N: usize> Default for RequestHeaders<'b, N> { #[inline(always)] fn default() -> Self { - Self { - http11: true, - method: Method::Get, - path: "/", - headers: Headers::new(), - } + Self::new() } } @@ -767,6 +773,17 @@ pub struct ResponseHeaders<'b, const N: usize> { } impl ResponseHeaders<'_, N> { + /// Create a new ResponseHeaders instance, defaults to HTTP/1.1 200 OK + #[inline(always)] + pub const fn new() -> Self { + Self { + http11: true, + code: 200, + reason: None, + headers: Headers::new(), + } + } + /// A utility method to check if the response is a Websocket upgrade response /// and if the upgrade was accepted pub fn is_ws_upgrade_accepted( @@ -781,12 +798,7 @@ impl ResponseHeaders<'_, N> { impl<'b, const N: usize> Default for ResponseHeaders<'b, N> { #[inline(always)] fn default() -> Self { - Self { - http11: true, - code: 200, - reason: None, - headers: Headers::new(), - } + Self::new() } }