Skip to content

Instantly share code, notes, and snippets.

@davidpdrsn
Created December 23, 2021 15:27
Show Gist options
  • Save davidpdrsn/2890612d4e2b9e419b3bf64fce081272 to your computer and use it in GitHub Desktop.
Save davidpdrsn/2890612d4e2b9e419b3bf64fce081272 to your computer and use it in GitHub Desktop.
use axum::{
extract::{Extension, Path},
routing::get,
AddExtensionLayer, Router,
};
use hyper::service::make_service_fn;
use std::{
convert::Infallible,
sync::{Arc, Mutex},
};
use tower::Layer;
#[tokio::main]
async fn main() {
let app = Router::new().route("/add-route/:path", get(add_route));
let shared_app = Arc::new(Mutex::new(app.clone()));
let make_service = make_service_fn(|_| {
let app = shared_app.clone().lock().unwrap().clone();
let app = AddExtensionLayer::new(shared_app.clone()).layer(app);
async move { Ok::<_, Infallible>(app) }
});
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(make_service)
.await
.unwrap();
}
async fn add_route(
Extension(shared_app): Extension<Arc<Mutex<Router>>>,
Path(path): Path<String>,
) -> String {
let shared_app = &mut *shared_app.lock().unwrap();
{
let path = path.clone();
*shared_app = shared_app.clone().route(
&format!("/{}", path),
get(|| async move { format!("Dynamic route `GET /{}` called", path.clone()) }),
);
}
format!("Dynamic route `GET /{}` added", path)
}
@cnwzhu
Copy link

cnwzhu commented Jun 29, 2023

Why does Mutex not compile after replacing it with Rwlock?

@davidpdrsn
Copy link
Author

I think because Router isn’t Sync thus Arc<RwLock> isn’t Send. This leads to a !Send future which is required.

@cnwzhu
Copy link

cnwzhu commented Jun 29, 2023

Yes,Router does not implement sync, so switching to RwLock will not compile

 fn assert(_: impl Send){}
 let app = Router::new().route("/", get(add_route));
 assert(app.clone());  // ok
 let tem = Arc::new(RwLock::new(app));
 assert(tem); // error: the trait `std::marker::Sync` is not implemented for `(dyn axum::boxed::ErasedIntoRoute<_, _, std::convert::Infallible> + 'static)`

@cnwzhu
Copy link

cnwzhu commented Jun 29, 2023

Finally, I implemented this with a shared pointer

#[tokio::main]
async fn main() {

    // run it
    let listener = TcpListener::bind("127.0.0.1:3000").unwrap();
    println!("listening on {}", listener.local_addr().unwrap());

    let router = Box::new(Router::new().route("/add_route", get(add_route)));
    let arc_router_ptr = Arc::new(AtomicPtr::new(Box::into_raw(router)));

    axum::Server::from_tcp(listener)
        .unwrap()
        .serve(make_service_fn(|_: &AddrStream| {
            let router = unsafe { arc_router_ptr.load(Ordering::SeqCst).as_ref() }.unwrap().clone();
            let app = AddExtensionLayer::new(arc_router_ptr.clone()).layer(router);
            async move { Ok::<_, Infallible>(app) }
        }))
        .await
        .unwrap();
}

async fn add_route(arc_router: Extension<Arc<AtomicPtr<Router>>>) {
    let old = unsafe { Box::from_raw(arc_router.load(Ordering::SeqCst)) };
    let new = old.merge(Router::new().route("/test", get(test)));
    arc_router.store(Box::into_raw(Box::new(new)), Ordering::SeqCst);
}

async fn test() -> String {
    "new Dynamic route".to_string()
}

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