6 releases (3 breaking)

0.12.0 Jul 8, 2024
0.11.1 Apr 5, 2024
0.11.0 Mar 15, 2024
0.10.0 Sep 22, 2023
0.1.0-alpha.0 Aug 29, 2022

#1373 in Network programming

Download history 291/week @ 2024-08-09 818/week @ 2024-08-16 668/week @ 2024-08-23 737/week @ 2024-08-30 835/week @ 2024-09-06 601/week @ 2024-09-13 627/week @ 2024-09-20 581/week @ 2024-09-27 640/week @ 2024-10-04 786/week @ 2024-10-11 657/week @ 2024-10-18 679/week @ 2024-10-25 524/week @ 2024-11-01 1100/week @ 2024-11-08 822/week @ 2024-11-15 524/week @ 2024-11-22

3,098 downloads per month

MIT license

25KB
435 lines

Tonic Async Interceptor

Crates.io Documentation Crates.io

This crate contains AsyncInterceptor, an async variant of Tonic's Interceptor. Other than accepting an async interceptor function, it works the same as Interceptor.

Async interceptor functions are useful for tasks like authentication, where you need to make asynchronous calls to another service or database within the interceptor.

Compatibility

The major/minor version corresponds with that of Tonic; so, if you are using Tonic v0.12.x, then you should likewise use Tonic Async Interceptor v0.12.x.

Usage

Using with Tonic built-in Server/Router

async fn my_async_interceptor(req: Request<()>) -> Result<Request<()>, Status> {
    // do things and stuff
    Ok(req)
}

async fn main() {
    use tonic::transport::server;
    use tonic_async_interceptor::async_interceptor;
    let router = server::Server::builder()
        .layer(async_interceptor(my_async_interceptor))
        .add_service(some_service)
        .add_service(another_service);
    // ...
}

Setting a custom extension

Here's an example of an async interceptor which authenticates a user and sets a custom extension for the underlying service to use.

// Your custom extension
struct UserContext {
    user_id: String,
}

// Async interceptor fn
async fn authenticate(req: Request<()>) -> Result<Request<()>, Status> {
    // Inspect the gRPC metadata.
    let auth_header_val = match req.metadata().get("x-my-auth-header") {
        Some(val) => val,
        None => return Err(Status::unauthorized("Request missing creds")),
    };
    // Call some async function (`verify_auth`).
    let maybe_user_id: Result<String> =
        verify_auth(auth_header_val).await;
    
    let user_id = match maybe_user_id {
        Ok(id) => id,
        Err(_) => return Err(Status::unauthorized("Invalid creds")),
    };
    
    // Insert an extension, which can be inspected by the service.
    req.extensions_mut().insert(UserContext { user_id });
    
    Ok(req)
}

Why is this a separate crate?

The code in this crate was originally intended to live in the official Tonic crate, alongside the non-async interceptor code. However, the maintainer decided not to merge it due to lack of time and misalignment with his future vision for Tonic.

Dependencies

~5–11MB
~112K SLoC