Skip to main content

tarpc_copy/server/request_hook/
before.rs

1// Copyright 2022 Google LLC
2//
3// Use of this source code is governed by an MIT-style
4// license that can be found in the LICENSE file or at
5// https://opensource.org/licenses/MIT.
6
7//! Provides a hook that runs before request execution.
8
9use crate::{context, server::Serve, ServerError};
10use futures::prelude::*;
11
12/// A hook that runs before request execution.
13#[allow(async_fn_in_trait)]
14pub trait BeforeRequest<Req> {
15    /// The function that is called before request execution.
16    ///
17    /// If this function returns an error, the request will not be executed and the error will be
18    /// returned instead.
19    ///
20    /// This function can also modify the request context. This could be used, for example, to
21    /// enforce a maximum deadline on all requests.
22    async fn before(&mut self, ctx: &mut context::Context, req: &Req) -> Result<(), ServerError>;
23}
24
25/// A list of hooks that run in order before request execution.
26pub trait BeforeRequestList<Req>: BeforeRequest<Req> {
27    /// The hook returned by `BeforeRequestList::then`.
28    type Then<Next>: BeforeRequest<Req>
29    where
30        Next: BeforeRequest<Req>;
31
32    /// Returns a hook that, when run, runs two hooks, first `self` and then `next`.
33    fn then<Next: BeforeRequest<Req>>(self, next: Next) -> Self::Then<Next>;
34
35    /// Same as `then`, but helps the compiler with type inference when Next is a closure.
36    fn then_fn<
37        Next: FnMut(&mut context::Context, &Req) -> Fut,
38        Fut: Future<Output = Result<(), ServerError>>,
39    >(
40        self,
41        next: Next,
42    ) -> Self::Then<Next>
43    where
44        Self: Sized,
45    {
46        self.then(next)
47    }
48
49    /// The service fn returned by `BeforeRequestList::serving`.
50    type Serve<S: Serve<Req = Req>>: Serve<Req = Req>;
51
52    /// Runs the list of request hooks before execution of the given serve fn.
53    /// This is equivalent to `serve.before(before_request_chain)` but may be syntactically nicer.
54    fn serving<S: Serve<Req = Req>>(self, serve: S) -> Self::Serve<S>;
55}
56
57impl<F, Fut, Req> BeforeRequest<Req> for F
58where
59    F: FnMut(&mut context::Context, &Req) -> Fut,
60    Fut: Future<Output = Result<(), ServerError>>,
61{
62    async fn before(&mut self, ctx: &mut context::Context, req: &Req) -> Result<(), ServerError> {
63        self(ctx, req).await
64    }
65}
66
67/// A Service function that runs a hook before request execution.
68#[derive(Clone)]
69pub struct HookThenServe<Serv, Hook> {
70    serve: Serv,
71    hook: Hook,
72}
73
74impl<Serv, Hook> HookThenServe<Serv, Hook> {
75    pub(crate) fn new(serve: Serv, hook: Hook) -> Self {
76        Self { serve, hook }
77    }
78}
79
80impl<Serv, Hook> Serve for HookThenServe<Serv, Hook>
81where
82    Serv: Serve,
83    Hook: BeforeRequest<Serv::Req>,
84{
85    type Req = Serv::Req;
86    type Resp = Serv::Resp;
87
88    async fn serve(
89        self,
90        mut ctx: context::Context,
91        req: Self::Req,
92    ) -> Result<Serv::Resp, ServerError> {
93        let HookThenServe {
94            serve, mut hook, ..
95        } = self;
96        hook.before(&mut ctx, &req).await?;
97        serve.serve(ctx, req).await
98    }
99}
100
101/// Returns a request hook builder that runs a series of hooks before request execution.
102///
103/// Example
104///
105/// ```rust
106/// use futures::{executor::block_on, future};
107/// use tarpc::{context, ServerError, server::{Serve, serve, request_hook::{self,
108///             BeforeRequest, BeforeRequestList}}};
109/// use std::{cell::Cell, io};
110///
111/// let i = Cell::new(0);
112/// let serve = request_hook::before()
113///     .then_fn(|_, _| async {
114///         assert!(i.get() == 0);
115///         i.set(1);
116///         Ok(())
117///     })
118///     .then_fn(|_, _| async {
119///         assert!(i.get() == 1);
120///         i.set(2);
121///         Ok(())
122///     })
123///     .serving(serve(|_ctx, i| async move { Ok(i + 1) }));
124/// let response = serve.clone().serve(context::current(), 1);
125/// assert!(block_on(response).is_ok());
126/// assert!(i.get() == 2);
127/// ```
128pub fn before() -> BeforeRequestNil {
129    BeforeRequestNil
130}
131
132/// A list of hooks that run in order before a request is executed.
133#[derive(Clone, Copy)]
134pub struct BeforeRequestCons<First, Rest>(First, Rest);
135
136/// A noop hook that runs before a request is executed.
137#[derive(Clone, Copy)]
138pub struct BeforeRequestNil;
139
140impl<Req, First: BeforeRequest<Req>, Rest: BeforeRequest<Req>> BeforeRequest<Req>
141    for BeforeRequestCons<First, Rest>
142{
143    async fn before(&mut self, ctx: &mut context::Context, req: &Req) -> Result<(), ServerError> {
144        let BeforeRequestCons(first, rest) = self;
145        first.before(ctx, req).await?;
146        rest.before(ctx, req).await?;
147        Ok(())
148    }
149}
150
151impl<Req> BeforeRequest<Req> for BeforeRequestNil {
152    async fn before(&mut self, _: &mut context::Context, _: &Req) -> Result<(), ServerError> {
153        Ok(())
154    }
155}
156
157impl<Req, First: BeforeRequest<Req>, Rest: BeforeRequestList<Req>> BeforeRequestList<Req>
158    for BeforeRequestCons<First, Rest>
159{
160    type Then<Next> = BeforeRequestCons<First, Rest::Then<Next>> where Next: BeforeRequest<Req>;
161
162    fn then<Next: BeforeRequest<Req>>(self, next: Next) -> Self::Then<Next> {
163        let BeforeRequestCons(first, rest) = self;
164        BeforeRequestCons(first, rest.then(next))
165    }
166
167    type Serve<S: Serve<Req = Req>> = HookThenServe<S, Self>;
168
169    fn serving<S: Serve<Req = Req>>(self, serve: S) -> Self::Serve<S> {
170        HookThenServe::new(serve, self)
171    }
172}
173
174impl<Req> BeforeRequestList<Req> for BeforeRequestNil {
175    type Then<Next> = BeforeRequestCons<Next, BeforeRequestNil> where Next: BeforeRequest<Req>;
176
177    fn then<Next: BeforeRequest<Req>>(self, next: Next) -> Self::Then<Next> {
178        BeforeRequestCons(next, BeforeRequestNil)
179    }
180
181    type Serve<S: Serve<Req = Req>> = S;
182
183    fn serving<S: Serve<Req = Req>>(self, serve: S) -> S {
184        serve
185    }
186}
187
188#[test]
189fn before_request_list() {
190    use crate::server::serve;
191    use futures::executor::block_on;
192    use std::cell::Cell;
193
194    let i = Cell::new(0);
195    let serve = before()
196        .then_fn(|_, _| async {
197            assert!(i.get() == 0);
198            i.set(1);
199            Ok(())
200        })
201        .then_fn(|_, _| async {
202            assert!(i.get() == 1);
203            i.set(2);
204            Ok(())
205        })
206        .serving(serve(|_ctx, i| async move { Ok(i + 1) }));
207    let response = serve.clone().serve(context::current(), 1);
208    assert!(block_on(response).is_ok());
209    assert!(i.get() == 2);
210}