diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15c4d06..38d4739 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,6 +23,10 @@ on: - docs/** pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-ci + cancel-in-progress: true + jobs: build: strategy: diff --git a/src/extensions.rs b/src/extensions.rs index 6bfed7d..92dfb3f 100644 --- a/src/extensions.rs +++ b/src/extensions.rs @@ -154,6 +154,63 @@ impl From<&str> for ResponseException { } } +/// Response body buffer for if you need to accumulate response +/// body chunks before you're ready to build a response object. +#[derive(Clone, Debug, Default)] +pub struct BodyBuffer { + buffer: BytesMut, +} + +impl BodyBuffer { + /// Create a new empty body + pub fn new() -> Self { + Self::default() + } + + /// Create a body with initial content + pub fn from_bytes(bytes: impl Into) -> Self { + let bytes = bytes.into(); + let mut buffer = BytesMut::with_capacity(bytes.len()); + buffer.extend_from_slice(&bytes); + Self { buffer } + } + + /// Append data to the body + pub fn append(&mut self, data: impl AsRef<[u8]>) { + self.buffer.extend_from_slice(data.as_ref()); + } + + /// Get the body content as bytes + pub fn as_bytes(&self) -> &[u8] { + &self.buffer + } + + /// Convert the body to Bytes + pub fn into_bytes(self) -> Bytes { + self.buffer.freeze() + } + + /// Convert the body to BytesMut + pub fn into_bytes_mut(self) -> BytesMut { + self.buffer + } + + /// Get the length of the body + pub fn len(&self) -> usize { + self.buffer.len() + } + + /// Check if the body is empty + pub fn is_empty(&self) -> bool { + self.buffer.is_empty() + } + + /// Clear the body + pub fn clear(&mut self) { + self.buffer.clear(); + } +} + /// Extension trait for http::Request /// /// This trait provides methods to access and modify socket information related @@ -298,6 +355,21 @@ pub trait ResponseBuilderExt { /// Set exception in response builder fn exception(self, exception: impl Into) -> http::response::Builder; + + /// Set body in response builder + fn body_buffer(self, body: BodyBuffer) -> http::response::Builder; + + /// Get mutable access to the log extension + fn log_mut(&mut self) -> &mut ResponseLog; + + /// Get mutable access to the body extension + fn body_buffer_mut(&mut self) -> &mut BodyBuffer; + + /// Append to the log extension + fn append_log(&mut self, data: impl AsRef<[u8]>) -> &mut Self; + + /// Append to the body extension + fn append_body(&mut self, data: impl AsRef<[u8]>) -> &mut Self; } impl ResponseBuilderExt for http::response::Builder { @@ -308,6 +380,36 @@ impl ResponseBuilderExt for http::response::Builder { fn exception(self, exception: impl Into) -> http::response::Builder { self.extension(ResponseException::new(exception)) } + + fn body_buffer(self, body: BodyBuffer) -> http::response::Builder { + self.extension(body) + } + + fn log_mut(&mut self) -> &mut ResponseLog { + let extensions = self.extensions_mut().unwrap(); + if extensions.get::().is_none() { + extensions.insert(ResponseLog::new()); + } + extensions.get_mut::().unwrap() + } + + fn body_buffer_mut(&mut self) -> &mut BodyBuffer { + let extensions = self.extensions_mut().unwrap(); + if extensions.get::().is_none() { + extensions.insert(BodyBuffer::new()); + } + extensions.get_mut::().unwrap() + } + + fn append_log(&mut self, data: impl AsRef<[u8]>) -> &mut Self { + self.log_mut().append(data); + self + } + + fn append_body(&mut self, data: impl AsRef<[u8]>) -> &mut Self { + self.body_buffer_mut().append(data); + self + } } #[cfg(test)] diff --git a/src/lib.rs b/src/lib.rs index 7c52fda..0f24561 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,8 +16,8 @@ pub mod types; pub mod napi; pub use extensions::{ - RequestBuilderExt, RequestExt, ResponseBuilderExt, ResponseException, ResponseExt, ResponseLog, - SocketInfo, + BodyBuffer, RequestBuilderExt, RequestExt, ResponseBuilderExt, ResponseException, ResponseExt, + ResponseLog, SocketInfo, }; pub use handler::Handler; pub use types::{Request, Response};