How to implement a dynamic list of "middleware" functions?

Viewed 68

I'm working on some code whose structure is similar to that of "middlewares" in web frameworks, so I'll use that terminology in this minimal example. The idea of a middleware function is that it wraps the actual response handler, so it can modify the request before it goes to the handler, and modify the response after it comes back:

struct Request;
struct Response;

trait Handler {
    fn handle(&self, request: Request) -> Response;
}

trait Middleware {
    fn handle(&self, request: Request, next: Box<dyn FnOnce(Request) -> Response>) -> Response;
}

I considered having two separate functions, one to preprocess the request and one to postprocess the response, but then I wouldn't be able to store any additional state during the request without resorting to hacks.

The server contains dynamically configured handlers and middlewares, so it has to use some boxed trait objects:

struct Server {
    handler: Box<dyn Handler>,
    middlewares: Vec<Box<dyn Middleware>>,
}

Now, how to implement the response handling? Here are my two attempts:

impl Server {
    // First attempt: using Iterator::fold()
    fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
        let handler_with_middlewares = self.middlewares.iter()
            .rev()
            .fold::<Box<dyn FnOnce(Request) -> Response + 'a>, _>(
                Box::new(|request| self.handler.handle(request)),
                |next, middleware| {
                    Box::new(|request| middleware.handle(request, next))
                }
            );
        handler_with_middlewares(request)
    }
    
    // Second attempt: using recursion
    fn handle_request_recurse(&self, request: Request) -> Response {
        self.handle_request_from(request, 0)
    }
    
    fn handle_request_from<'a>(&'a self, request: Request, index: usize) -> Response {
        if index >= self.middlewares.len() {
            self.handler.handle(request)
        } else {
            let next = Box::new(
                |r: Request| self.handle_request_from(r, index + 1));
            self.middlewares[index].handle(request, next)
        }
    }
}

Both attempts give the same error, which suggests that I'm doing something fundamentally wrong here:

error[E0759]: `self` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
  --> src/lib.rs:25:67
   |
19 |     fn handle_request_fold<'a>(&'a self, request: Request) -> Response {
   |                                -------- this data with lifetime `'a`...
...
25 |                     Box::new(|request| middleware.handle(request, next))
   |                                                                   ^^^^ ...is captured and required to live as long as `'static` here

I know that trait objects have an implicit 'static lifetime, so I tried adding explicit lifetimes as you can see, but it didn't help. I don't understand why the compiler would demand a 'static lifetime anywhere here; nothing (including the closures) can escape the handle_request_* functions, right?

Playground link

1 Answers

And just after posting, I figured it out: the boxed trait object passed to the Middleware needs a non-'static lifetime as well.

trait Middleware {
    fn handle<'a>(&self, request: Request,
                  next: Box<dyn FnOnce(Request) -> Response + 'a>)
        -> Response;
}
    fn handle_request_from(&self, request: Request, index: usize) -> Response {
        if index >= self.middlewares.len() {
            self.handler.handle(request)
        } else {
            let next = Box::new(
                move |request| self.handle_request_from(request, index + 1));
            self.middlewares[index].handle(request, next)
        }
    }
Related