Skip to content

Implement middleware handler chain#88

Merged
leynos merged 4 commits intomainfrom
codex/implement-middleware-chain-using-handlerservice
Jun 21, 2025
Merged

Implement middleware handler chain#88
leynos merged 4 commits intomainfrom
codex/implement-middleware-chain-using-handlerservice

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jun 20, 2025

Summary

  • implement HandlerService to invoke route handlers
  • bind middleware to Transform<HandlerService> and update WireframeApp
  • build middleware chains in connections and dispatch via services
  • add test verifying middleware order
  • update roadmap documentation

Testing

  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_68556ded20148322a71269bd06e29654

Summary by Sourcery

Implement a middleware handler chain by introducing HandlerService and Transform-based middleware in WireframeApp, refactor handler types and dispatch flow to use ServiceRequest/ServiceResponse, enhance Envelope API, and add tests verifying middleware order.

New Features:

  • Introduce HandlerService to wrap route handlers and middleware chains
  • Add public Envelope::new and Envelope::into_parts methods

Enhancements:

  • Refactor WireframeApp to use a Handler alias and Register middleware as Transform
  • Implement build_chains method to assemble middleware stacks before processing connections
  • Update frame dispatch logic to use ServiceRequest/ServiceResponse with middleware-enhanced services

Documentation:

  • Update roadmap documentation to mark middleware and service wrapper tasks as completed

Tests:

  • Add middleware_order.rs test to verify reverse-order application of middleware

Summary by CodeRabbit

  • New Features
    • Introduced a middleware transformation pipeline, enabling middleware to wrap route handlers and process requests and responses in sequence.
    • Added a new service abstraction for route handling and middleware chaining.
  • Bug Fixes
    • Improved request and response handling to ensure middleware is correctly applied in both directions.
  • Tests
    • Added tests to verify correct middleware application order.
  • Documentation
    • Updated the roadmap to reflect completed middleware implementation tasks.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jun 20, 2025

Reviewer's Guide

This PR implements a Transform/Service-based middleware chaining mechanism around route handlers by introducing a HandlerService abstraction, refactors WireframeApp to register and dispatch Handlers via built middleware chains using ServiceRequest/ServiceResponse, extends the Envelope API for ergonomic construction and deconstruction, and adds corresponding tests and documentation updates.

Sequence diagram for middleware chain invocation on request dispatch

sequenceDiagram
    participant Client
    participant WireframeApp
    participant MiddlewareChain as "Middleware Chain"
    participant HandlerService
    participant RouteHandler as "Route Handler"
    Client->>WireframeApp: Send request (Envelope)
    WireframeApp->>MiddlewareChain: Dispatch request (ServiceRequest)
    MiddlewareChain->>HandlerService: call(ServiceRequest)
    HandlerService->>RouteHandler: call(&Envelope)
    RouteHandler-->>HandlerService: (processes request)
    HandlerService-->>MiddlewareChain: ServiceResponse
    MiddlewareChain-->>WireframeApp: ServiceResponse
    WireframeApp-->>Client: Send response (Envelope)
Loading

Class diagram for updated WireframeApp structure

classDiagram
    class WireframeApp {
        -routes: HashMap<u32, Handler>
        -services: Vec<Handler>
        -middleware: Vec<Box<dyn Transform<HandlerService, Output = HandlerService>>>
        -frame_processor: BoxedFrameProcessor
        -serializer: S
        -app_data: HashMap<TypeId, Arc<dyn Any + Send + Sync>>
        -on_disconnect: Option<Arc<ConnectionTeardown<C>>>
        +route(id: u32, handler: Handler) -> Result<Self>
        +service(handler: Handler) -> Result<Self>
        +wrap(mw: Transform<HandlerService, Output = HandlerService>) -> Result<Self>
        +build_chains() -> HashMap<u32, HandlerService>
        +process_stream(stream, routes)
        +handle_frame(stream, frame, deser_failures, routes)
    }
    WireframeApp --> Handler : uses
    WireframeApp --> HandlerService : builds chains of
    WireframeApp --> Transform : uses for middleware
    WireframeApp --> Envelope : uses
Loading

File-Level Changes

Change Details Files
Introduce HandlerService abstraction and simplify middleware trait
  • Add HandlerService and RouteService structs with async Service implementations
  • Implement HandlerService::new, from_service and id methods
  • Simplify Middleware trait to be Transform<HandlerService, Output=HandlerService>
src/middleware.rs
src/app.rs
Refactor WireframeApp to use Handler and build middleware chains
  • Replace raw Service alias with Handler in routes, services, and wrap signature
  • Add build_chains method to compose middleware in reverse order per route
  • Update process_stream and handle_frame to dispatch via HandlerService and ServiceRequest/ServiceResponse
src/app.rs
Extend Envelope API
  • Make Envelope fields pub(crate) for reuse
  • Add Envelope::new constructor and into_parts extractor
src/app.rs
Add middleware order tests and update documentation
  • Add integration test verifying reverse application of middleware tags
  • Mark middleware implementation steps as complete in roadmap.md
tests/middleware_order.rs
docs/roadmap.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 20, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The changes introduce a middleware transformation pipeline in the application framework. Route handlers are now wrapped as HandlerService instances, and middleware implementing the Transform trait is applied in reverse order to build a chain. The invocation model is refactored to use asynchronous service calls with encapsulated request and response types. Tests confirm the correct middleware order.

Changes

File(s) Change Summary
docs/roadmap.md Updated middleware implementation checklist items to completed and adjusted formatting for wrapper descriptions.
src/app.rs Refactored routing and handler invocation: route handlers now use Handler type, middleware chains are built using Transform, and HandlerService wraps handlers. Changed method signatures and added new methods for chain construction and envelope access. Refactored connection and frame handling to use middleware-aware service calls.
src/middleware.rs Added HandlerService struct for wrapping handlers and middleware chains, with constructors and Service trait implementation. Added private RouteService for direct handler invocation. Extended service abstractions for middleware support.
tests/middleware_order.rs Added test module to verify middleware application order using custom TagMiddleware and TagService. Test asserts correct tag order in request and response payloads after middleware chain execution.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant WireframeApp
    participant MiddlewareChain
    participant HandlerService
    participant Handler

    Client->>WireframeApp: Sends Envelope (frame)
    WireframeApp->>MiddlewareChain: Passes ServiceRequest
    MiddlewareChain->>HandlerService: Calls service
    HandlerService->>Handler: Invokes handler with Envelope
    Handler-->>HandlerService: Returns response
    HandlerService-->>MiddlewareChain: Returns ServiceResponse
    MiddlewareChain-->>WireframeApp: Returns ServiceResponse
    WireframeApp->>Client: Sends Envelope (response)
Loading

Possibly related PRs

  • Implement trait foundations #8: Introduced foundational middleware abstractions (Service, Transform), which are extended here for middleware chaining and handler wrapping.
  • Implement connection handling #76: Refactored connection handling, upon which this PR builds by integrating middleware transformation and asynchronous service invocation.
  • Implement basic middleware wrappers #80: Added ServiceRequest and ServiceResponse wrappers, which this PR utilises and extends for encapsulated request/response handling in middleware chains.

Poem

Oh, what a hop through the middleware chain,
Each handler wrapped, then wrapped again!
With tags and bytes, the rabbits play,
Ensuring order, come what may.
Now every frame that hops inside,
Emerges changed, with tags applied!
🐇✨

✨ Finishing Touches
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in Comment
  • Commit Unit Tests in branch codex/implement-middleware-chain-using-handlerservice

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes - here's some feedback:

  • Currently build_chains is invoked inside each new connection—consider building and storing the middleware chains once during app setup to avoid reconstructing the same HandlerService map on every connection.
  • The process_stream method now requires passing in the routes map; you could encapsulate the routes and stream handling into a dedicated struct or context to simplify the method signature and reduce parameter passing.
  • HandlerService uses boxed Service trait objects for every route, incurring dynamic dispatch and heap allocations—if this is a hotspot, consider leveraging generics or type erasure at compile time to reduce overhead.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Currently `build_chains` is invoked inside each new connection—consider building and storing the middleware chains once during app setup to avoid reconstructing the same `HandlerService` map on every connection.
- The `process_stream` method now requires passing in the routes map; you could encapsulate the routes and stream handling into a dedicated struct or context to simplify the method signature and reduce parameter passing.
- `HandlerService` uses boxed `Service` trait objects for every route, incurring dynamic dispatch and heap allocations—if this is a hotspot, consider leveraging generics or type erasure at compile time to reduce overhead.

## Individual Comments

### Comment 1
<location> `src/middleware.rs:263` </location>
<code_context>
+            id: self.id,
+            msg: req.into_inner(),
+        };
+        (self.handler.as_ref())(&env).await;
+        let (_, bytes) = env.into_parts();
+        Ok(ServiceResponse::new(bytes))
+    }
</code_context>

<issue_to_address>
Handler is called with a reference to Envelope, but Envelope is then consumed.

Since Envelope is consumed after being passed by reference, verify that handlers do not mutate or require ownership of Envelope. Confirm all handlers are compatible with this pattern.
</issue_to_address>

### Comment 2
<location> `docs/roadmap.md:91` </location>
<code_context>
-      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).
+    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).
</code_context>

<issue_to_address>
This bullet point exceeds the 80 column wrapping limit for bullets/paragraphs.

Please wrap this line so that no line in the bullet point exceeds 80 columns, as per the style guide.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/middleware.rs
Comment thread docs/roadmap.md Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a81e2d8 and a4f4dc7.

📒 Files selected for processing (4)
  • docs/roadmap.md (1 hunks)
  • src/app.rs (10 hunks)
  • src/middleware.rs (2 hunks)
  • tests/middleware_order.rs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-test
🔇 Additional comments (6)
docs/roadmap.md (1)

88-102: Documentation updates look good!

The roadmap correctly reflects the completion of middleware implementation tasks, including the Transform/Service traits and the WireframeApp::wrap registration method.

src/middleware.rs (1)

219-277: Well-designed service abstraction for route handlers!

The HandlerService implementation provides a clean abstraction for wrapping route handlers and applying middleware chains. The use of Infallible as the error type is appropriate since handlers don't return errors in the current design. The separation between HandlerService (public API) and RouteService (implementation detail) follows good encapsulation practices.

tests/middleware_order.rs (1)

1-83: Excellent test coverage for middleware ordering!

The test comprehensively verifies that middleware is applied in reverse order during request processing and forward order during response processing. The TagMiddleware implementation is a clever way to track the execution order by appending tags at each stage.

src/app.rs (3)

71-90: Clean type abstractions for the middleware system!

The refactoring from boxed Service functions to Arc<Handler> provides better ergonomics, and the Middleware trait with its blanket implementation elegantly abstracts the Transform<HandlerService> constraint.


136-148: Good encapsulation of Envelope fields!

Making the fields pub(crate) with public constructor and accessor methods provides the right balance between internal access and API encapsulation.


385-395: Correct middleware chain construction!

The build_chains method properly applies middleware in reverse order, ensuring that the last registered middleware runs first on requests and last on responses. The async transformation allows middleware to perform any necessary async initialisation.

Comment thread src/app.rs
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jun 20, 2025

@sourcery-ai review

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jun 20, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 20, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes and they look great!


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@leynos leynos force-pushed the codex/implement-middleware-chain-using-handlerservice branch from f49a1bf to 0925f63 Compare June 20, 2025 22:51
@leynos leynos force-pushed the codex/implement-middleware-chain-using-handlerservice branch from 0925f63 to 044b431 Compare June 20, 2025 23:08
@leynos leynos merged commit 6b6b132 into main Jun 21, 2025
5 checks passed
@leynos leynos deleted the codex/implement-middleware-chain-using-handlerservice branch June 21, 2025 00:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant