From d2d804b7881f3b97bbe02f59a81a5ac936427ee8 Mon Sep 17 00:00:00 2001 From: Leynos Date: Thu, 19 Jun 2025 16:24:31 +0100 Subject: [PATCH 1/3] Add middleware request and response wrappers --- src/middleware.rs | 44 +++++++++++++++++++++++++++++++++-- tests/middleware.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 tests/middleware.rs diff --git a/src/middleware.rs b/src/middleware.rs index 927648c0..425badc1 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -7,11 +7,51 @@ use async_trait::async_trait; /// Incoming request wrapper passed through middleware. #[derive(Debug)] -pub struct ServiceRequest; +pub struct ServiceRequest { + frame: Vec, +} + +impl ServiceRequest { + /// Create a new [`ServiceRequest`] from raw frame bytes. + #[must_use] + pub fn new(frame: Vec) -> Self { Self { frame } } + + /// Borrow the underlying frame bytes. + #[must_use] + pub fn frame(&self) -> &[u8] { &self.frame } + + /// Mutable access to the inner frame bytes. + #[must_use] + pub fn frame_mut(&mut self) -> &mut Vec { &mut self.frame } + + /// Consume the request, returning the inner frame bytes. + #[must_use] + pub fn into_inner(self) -> Vec { self.frame } +} /// Response produced by a handler or middleware. #[derive(Debug, Default)] -pub struct ServiceResponse; +pub struct ServiceResponse { + frame: Vec, +} + +impl ServiceResponse { + /// Create a new [`ServiceResponse`] containing the given frame bytes. + #[must_use] + pub fn new(frame: Vec) -> Self { Self { frame } } + + /// Borrow the inner frame bytes. + #[must_use] + pub fn frame(&self) -> &[u8] { &self.frame } + + /// Mutable access to the response frame bytes. + #[must_use] + pub fn frame_mut(&mut self) -> &mut Vec { &mut self.frame } + + /// Consume the response, yielding the raw frame bytes. + #[must_use] + pub fn into_inner(self) -> Vec { self.frame } +} /// Continuation used by middleware to call the next service in the chain. pub struct Next<'a, S> diff --git a/tests/middleware.rs b/tests/middleware.rs new file mode 100644 index 00000000..ec9a3868 --- /dev/null +++ b/tests/middleware.rs @@ -0,0 +1,56 @@ +use async_trait::async_trait; +use wireframe::middleware::{Next, Service, ServiceRequest, ServiceResponse, Transform}; + +struct EchoService; + +#[async_trait] +impl Service for EchoService { + type Error = std::convert::Infallible; + + async fn call(&self, req: ServiceRequest) -> Result { + Ok(ServiceResponse::new(req.into_inner())) + } +} + +struct ModifyMiddleware; + +struct ModifyService { + inner: S, +} + +#[async_trait] +impl Transform for ModifyMiddleware +where + S: Service + Send + Sync + 'static, +{ + type Output = ModifyService; + + async fn transform(&self, service: S) -> Self::Output { ModifyService { inner: service } } +} + +#[async_trait] +impl Service for ModifyService +where + S: Service + Send + Sync + 'static, +{ + type Error = S::Error; + + async fn call(&self, mut request: ServiceRequest) -> Result { + request.frame_mut().push(b'!'); + let next = Next::new(&self.inner); + let mut response = next.call(request).await?; + response.frame_mut().push(b'?'); + Ok(response) + } +} + +#[tokio::test] +async fn middleware_modifies_request_and_response() { + let service = EchoService; + let mw = ModifyMiddleware; + let wrapped = mw.transform(service).await; + + let request = ServiceRequest::new(vec![1, 2, 3]); + let response = wrapped.call(request).await.unwrap(); + assert_eq!(response.frame(), &[1, 2, 3, b'!', b'?']); +} From babd37c5c9165ca756c12aa7dd1571b0a45d93e7 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 20 Jun 2025 01:02:15 +0100 Subject: [PATCH 2/3] Add module doc comment for middleware --- src/middleware.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/middleware.rs b/src/middleware.rs index 425badc1..9738fd69 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -1,7 +1,8 @@ -//! Traits and helpers for request middleware. +//! Middleware traits and helpers. //! -//! Middleware components implement [`Transform`] to wrap services and -//! process `ServiceRequest` instances before passing them along the chain. +//! This module defines the asynchronous [`Service`] and [`Transform`] traits, +//! along with [`ServiceRequest`] and [`ServiceResponse`] wrappers. Middleware +//! components use the [`Next`] helper to call the next service in the chain. use async_trait::async_trait; From 47815ba3007074c610905dbfbab41571edbd67b8 Mon Sep 17 00:00:00 2001 From: Leynos Date: Fri, 20 Jun 2025 03:55:59 +0100 Subject: [PATCH 3/3] Refine middleware docs and update roadmap --- docs/roadmap.md | 4 ++-- src/middleware.rs | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index f376e678..6b811280 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -87,12 +87,12 @@ after formatting. Line numbers below refer to that file. - [ ] Implement middleware using `Transform`/`Service` traits. - - [ ] Implement `ServiceRequest` and `ServiceResponse` wrappers (lines + - [x] Implement `ServiceRequest` and `ServiceResponse` wrappers (lines 866-899) and introduce a `Next` helper to build the asynchronous call chain. Trait definitions live in [`src/middleware.rs`](../src/middleware.rs#L71-L84). - [ ] Provide a `from_fn` helper for functional middleware. - - [ ] Add tests verifying middleware can modify requests and observe + - [x] Add tests verifying middleware can modify requests and observe responses. - [ ] Register middleware with `WireframeApp::wrap` and build the chain around diff --git a/src/middleware.rs b/src/middleware.rs index 9738fd69..2b119de4 100644 --- a/src/middleware.rs +++ b/src/middleware.rs @@ -66,10 +66,7 @@ impl<'a, S> Next<'a, S> where S: Service + ?Sized, { - /// Create a new [`Next`] wrapping the given service. - #[inline] - #[must_use] - /// Creates a new `Next` instance wrapping a reference to the given service. + /// Creates a new `Next` instance wrapping a reference to `service`. /// /// # Examples /// @@ -87,6 +84,8 @@ where /// let service = MyService; /// let next = Next::new(&service); /// ``` + #[inline] + #[must_use] pub fn new(service: &'a S) -> Self { Self { service } } /// Call the next service with the provided request.