tarpc/server/request_hook.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
// Copyright 2022 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
//! Hooks for horizontal functionality that can run either before or after a request is executed.
use crate::server::Serve;
/// A request hook that runs before a request is executed.
mod before;
/// A request hook that runs after a request is completed.
mod after;
/// A request hook that runs both before a request is executed and after it is completed.
mod before_and_after;
pub use {
after::{AfterRequest, ServeThenHook},
before::{
before, BeforeRequest, BeforeRequestCons, BeforeRequestList, BeforeRequestNil,
HookThenServe,
},
before_and_after::HookThenServeThenHook,
};
/// Hooks that run before and/or after serving a request.
pub trait RequestHook: Serve {
/// Runs a hook before execution of the request.
///
/// If the hook returns an error, the request will not be executed and the error will be
/// returned instead.
///
/// The hook can also modify the request context. This could be used, for example, to enforce a
/// maximum deadline on all requests.
///
/// Any type that implements [`BeforeRequest`] can be used as the hook. Types that implement
/// `FnMut(&mut Context, &RequestType) -> impl Future<Output = Result<(), ServerError>>` can
/// also be used.
///
/// # Example
///
/// ```rust
/// use futures::{executor::block_on, future};
/// use tarpc::{context, ServerError, server::{Serve, request_hook::RequestHook, serve}};
/// use std::io;
///
/// let serve = serve(|_ctx, i| async move { Ok(i + 1) })
/// .before(|_ctx: &mut context::Context, req: &i32| {
/// future::ready(
/// if *req == 1 {
/// Err(ServerError::new(
/// io::ErrorKind::Other,
/// format!("I don't like {req}")))
/// } else {
/// Ok(())
/// })
/// });
/// let response = serve.serve(context::current(), 1);
/// assert!(block_on(response).is_err());
/// ```
fn before<Hook>(self, hook: Hook) -> HookThenServe<Self, Hook>
where
Hook: BeforeRequest<Self::Req>,
Self: Sized,
{
HookThenServe::new(self, hook)
}
/// Runs a hook after completion of a request.
///
/// The hook can modify the request context and the response.
///
/// Any type that implements [`AfterRequest`] can be used as the hook. Types that implement
/// `FnMut(&mut Context, &mut Result<ResponseType, ServerError>) -> impl Future<Output = ()>`
/// can also be used.
///
/// # Example
///
/// ```rust
/// use futures::{executor::block_on, future};
/// use tarpc::{context, ServerError, server::{Serve, request_hook::RequestHook, serve}};
/// use std::io;
///
/// let serve = serve(
/// |_ctx, i| async move {
/// if i == 1 {
/// Err(ServerError::new(
/// io::ErrorKind::Other,
/// format!("{i} is the loneliest number")))
/// } else {
/// Ok(i + 1)
/// }
/// })
/// .after(|_ctx: &mut context::Context, resp: &mut Result<i32, ServerError>| {
/// if let Err(e) = resp {
/// eprintln!("server error: {e:?}");
/// }
/// future::ready(())
/// });
///
/// let response = serve.serve(context::current(), 1);
/// assert!(block_on(response).is_err());
/// ```
fn after<Hook>(self, hook: Hook) -> ServeThenHook<Self, Hook>
where
Hook: AfterRequest<Self::Resp>,
Self: Sized,
{
ServeThenHook::new(self, hook)
}
/// Runs a hook before and after execution of the request.
///
/// If the hook returns an error, the request will not be executed and the error will be
/// returned instead.
///
/// The hook can also modify the request context and the response. This could be used, for
/// example, to enforce a maximum deadline on all requests.
///
/// # Example
///
/// ```rust
/// use futures::{executor::block_on, future};
/// use tarpc::{
/// context, ServerError,
/// server::{Serve, serve, request_hook::{BeforeRequest, AfterRequest, RequestHook}}
/// };
/// use std::{io, time::Instant};
///
/// struct PrintLatency(Instant);
///
/// impl<Req> BeforeRequest<Req> for PrintLatency {
/// async fn before(&mut self, _: &mut context::Context, _: &Req) -> Result<(), ServerError> {
/// self.0 = Instant::now();
/// Ok(())
/// }
/// }
///
/// impl<Resp> AfterRequest<Resp> for PrintLatency {
/// async fn after(
/// &mut self,
/// _: &mut context::Context,
/// _: &mut Result<Resp, ServerError>,
/// ) {
/// tracing::info!("Elapsed: {:?}", self.0.elapsed());
/// }
/// }
///
/// let serve = serve(|_ctx, i| async move {
/// Ok(i + 1)
/// }).before_and_after(PrintLatency(Instant::now()));
/// let response = serve.serve(context::current(), 1);
/// assert!(block_on(response).is_ok());
/// ```
fn before_and_after<Hook>(
self,
hook: Hook,
) -> HookThenServeThenHook<Self::Req, Self::Resp, Self, Hook>
where
Hook: BeforeRequest<Self::Req> + AfterRequest<Self::Resp>,
Self: Sized,
{
HookThenServeThenHook::new(self, hook)
}
}
impl<S: Serve> RequestHook for S {}