Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

higher-ranked lifetime error while spawning server in Tokio #209

Open
jgeluk opened this issue Jan 2, 2023 · 11 comments
Open

higher-ranked lifetime error while spawning server in Tokio #209

jgeluk opened this issue Jan 2, 2023 · 11 comments

Comments

@jgeluk
Copy link

jgeluk commented Jan 2, 2023

Describe the bug
When trying to not configure and start the Salvo server on the main thread but in a spawned thread the compiler fails with a higher-ranked lifetime error and messages like note: could not prove [async block@crate/server/src/server_builder.rs:115:22: 120:10]: Send.

To Reproduce
Steps to reproduce the behavior:

        let router = self.router()?;
        let rustls_config = rustls_config();
        let (tx, rx) = oneshot::channel();

        let runtime = tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap();

        let _guard = runtime.enter();

        let create_acceptor = async move {
            let listener = TcpListener::new(local_address.clone()).rustls(rustls_config.clone());

            let acceptor = QuinnListener::new(rustls_config, local_address.clone())
                .join(listener)
                .try_bind()
                .await?;

            Ok::<_, Error>(acceptor)
        };

        let acceptor = runtime.block_on(create_acceptor)?;

        let server = salvo::Server::new(acceptor);

        let serve1 = server.serve_with_graceful_shutdown(
            router,
            async {
                tracing::info!("Waiting for server to stop");
                rx.await.ok();
            },
            None,
        );

        let serve2 = async move {
            tracing::info!("server.await begin");
            // let x = force_send_sync::Send::new(serve);
            serve1.await;
            tracing::info!("server.await end");
        };

        runtime.spawn(serve2);  // <<< This is the line where the error occurs.

Additional context

  • rustc 1.68.0-nightly
@jgeluk
Copy link
Author

jgeluk commented Jan 2, 2023

It seems to be related to the .rustls() call, if that's all left out (and the QuinnListener), it compiles

@chrislearn
Copy link
Member

Can you give me the test project source code or repository url?

@jgeluk
Copy link
Author

jgeluk commented Jan 3, 2023

If you change examples/tls-rust/main.rs as follows and then do cargo run in that directory you'll see the same error:

use std::thread::spawn;
use salvo::conn::rustls::{Keycert, RustlsConfig};
use salvo::prelude::*;

#[handler]
async fn hello(res: &mut Response) {
    res.render(Text::Plain("Hello World"));
}

async fn run_server() {
    let router = Router::new().get(hello);
    let config = RustlsConfig::new(
        Keycert::new()
            .with_cert(include_bytes!("../certs/cert.pem").as_ref())
            .with_key(include_bytes!("../certs/key.pem").as_ref()),
    );
    let acceptor = TcpListener::new("127.0.0.1:7878").rustls(config).bind().await;
    Server::new(acceptor).serve(router).await;
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();

    let _ = spawn(run_server);

    tokio::task::yield_now().await;
    println!("main task done!");
}

Error:
Screenshot 2023-01-03 at 13 18 48

@jgeluk
Copy link
Author

jgeluk commented Jan 3, 2023

Or here's a version using tokio::task::spawn rather than std::thread::spawn:

use salvo::conn::rustls::{Keycert, RustlsConfig};
use salvo::prelude::*;

#[handler]
async fn hello(res: &mut Response) {
    res.render(Text::Plain("Hello World"));
}

async fn run_server() {
    let router = Router::new().get(hello);
    let config = RustlsConfig::new(
        Keycert::new()
            .with_cert(include_bytes!("../certs/cert.pem").as_ref())
            .with_key(include_bytes!("../certs/key.pem").as_ref()),
    );
    let acceptor = TcpListener::new("127.0.0.1:7878").rustls(config).bind().await;
    Server::new(acceptor).serve(router).await;
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();

    let _ = tokio::task::spawn(run_server());

    tokio::task::yield_now().await;
    println!("main task done!");
}

Same error.

I don't know whether it's a good idea to spawn the whole server as a tokio task or not. I'm trying to get it to run in the context of a Tauri plugin (https://tauri.app/v1/guides/features/plugin/) for an app that we're building where the salvo server (that usually runs as the backend server for the web-version of the app) is embedded in the tauri-app. Since Tauri wraps around a Tokio runtime itself and needs to start from a non-tokio main thread, we have to spawn the salvo server in a background thread, ideally managed by the same tokio runtime that tauri uses.

@jgeluk
Copy link
Author

jgeluk commented Jan 3, 2023

Point is that this example works when you replace run_server() with:

async fn run_server() {
    let router = Router::new().get(hello);
    // let config = RustlsConfig::new(
    //     Keycert::new()
    //         .with_cert(include_bytes!("../certs/cert.pem").as_ref())
    //         .with_key(include_bytes!("../certs/key.pem").as_ref()),
    // );
    // let acceptor = TcpListener::new("127.0.0.1:7878").rustls(config).bind().await;
    let acceptor = TcpListener::new("127.0.0.1:7878").bind().await;
    Server::new(acceptor).serve(router).await;
}

So it seems that RustlsConfig is not Send? I checked that, I tried to add more Send annotations to it, even implementing Send for RustlsConfig and Keycert etc, but couldn't get that to work...

@chrislearn
Copy link
Member

I have checked all futures in run_server is Send, hope the compiler will give more details about this error in future.

@chrislearn
Copy link
Member

rust-lang/rust#102211

@jgeluk
Copy link
Author

jgeluk commented Jan 4, 2023

Saw that article yes. The suggested solution is hard to do though, boxing the future created by .rustls(config).bind() does not work since it's not Unpin...

@chrislearn
Copy link
Member

RustlsAcceptor has a field config_stream, this issue may caused by it.
This may rust bug, I am not sure. this issue very similar to this: rust-lang/rust#102211 (comment)

@jgeluk
Copy link
Author

jgeluk commented Jan 5, 2023

It's not just RustlsConfig, NativeTlsConfig shows the same problem, if you replace examples/tls-native-tls/src/main.rs with this version it gives the higher-ranked lifetime error as well on line 22:

Screenshot 2023-01-05 at 11 36 41

use salvo::conn::native_tls::NativeTlsConfig;
use salvo::prelude::*;

#[handler]
async fn hello(res: &mut Response) {
    res.render(Text::Plain("Hello World"));
}

async fn run_server() {
    let router = Router::new().get(hello);
    let config = NativeTlsConfig::new()
        .with_pkcs12(include_bytes!("../certs/identity.p12").to_vec())
        .with_password("mypass");
    let acceptor = TcpListener::new("127.0.0.1:7878").native_tls(config).bind().await;
    Server::new(acceptor).serve(router).await;
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();

    let _ = tokio::task::spawn(run_server());

    tokio::task::yield_now().await;
    println!("main task done!");
}

Leaving out the call to native_tls() makes it compile happily...

@jgeluk
Copy link
Author

jgeluk commented Jan 5, 2023

So the question is, is this due to multiple causes? Like the one you mentioned regarding config_stream? Or is this due to one other cause, shared by both RustlsConfig and NativeTlsConfig?

I saw your latest changes (adding Send + 'static at various places), I tried the same thing but the above examples still give the same error, unfortunately. The problem with this very opaque error message is that it's a bit like stabbing in the dark, I update the compiler every day hoping that the error message will get more precise but no luck so far.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants