Skip to content

Deadlock when using WritableSession in middleware layer #47

Open
@mtorromeo

Description

@mtorromeo

Hi,
I am using axum-sessions inside a middleware layer that is checking if the user is logged in and short-circuits the router with a StatusCode::UNAUTHORIZED response if they are not.

If I then try to use a WritableSession in a route that is "behind" this middleware layer I get a deadlock that I am guessing is caused by the internal RWLock that is being acquired while the middleware layer is still holding a reference to the ReadableSession.

For the time being I am going to work-around this issue by bypassing the middleware when I need a WritableSession but is there a way to release the ReadableSession from the middleware after I am done using it so that I can avoid this issue?

Here's a minimal POC:

Cargo.toml

[package]
name = "axum-sessions-deadlock"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.6"
axum-sessions = "0.5"
hyper = "0.14"
rand = "0.8"
tokio = { version = "1", features = ["full"] }
toml = "0.7"
tower-http = "0.4"

main.rs

use axum::http::Request;
use axum::middleware::Next;
use axum::response::Response;
use axum::{routing::get, Router};
use axum_sessions::extractors::{ReadableSession, WritableSession};
use axum_sessions::{async_session::MemoryStore, SessionLayer};
use hyper::StatusCode;
use rand::RngCore;

#[tokio::main]
async fn main() {
    let store = MemoryStore::new();
    let mut secret = [0u8; 128];
    rand::thread_rng().fill_bytes(&mut secret);
    let session_layer = SessionLayer::new(store, &secret).with_secure(false);

    let app = Router::new()
        .route("/", get(deadlock))
        .layer(axum::middleware::from_fn(auth_middleware))
        .layer(session_layer);

    eprintln!("Listening on 127.0.0.1:3000");

    axum::Server::bind(&"127.0.0.1:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

pub async fn auth_middleware<T>(
    session: ReadableSession,
    request: Request<T>,
    next: Next<T>,
) -> Result<Response, StatusCode> {
    // e.g: check if user is logged in
    // let login: Option<String> = session.get("login");
    // match login {
    //     None => Err(StatusCode::UNAUTHORIZED),
    //     Some(_) => Ok(next.run(request).await),
    // }

    // assume logged-in
    Ok(next.run(request).await)
}

async fn deadlock(mut session: WritableSession) -> StatusCode {
    // e.g: session.destroy();
    StatusCode::NO_CONTENT
}

Thanks

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions