ntex 4.0.0-beta.5

Framework for composable network services
Documentation
use std::{sync::mpsc, thread, time::Duration};

#[cfg(feature = "openssl")]
use tls_openssl::ssl::SslAcceptorBuilder;

#[cfg(feature = "rustls")]
mod rustls_utils;

use ntex::http::HttpServiceConfig;
use ntex::web::{self, App, HttpResponse, HttpServer, WebAppConfig};
use ntex::{SharedCfg, io::IoConfig, server::TestServer, time::Seconds};
#[cfg(unix)]
use ntex::{rt, time::sleep};
use ntex_tls::TlsConfig;

#[ntex::test]
async fn test_run() {
    let addr = TestServer::unused_addr();
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let sys = ntex::rt::System::new("test", ntex::rt::DefaultRuntime);

        sys.run(move || {
            let srv = HttpServer::new(async |_| {
                App::new().service(
                    web::resource("/").route(web::to(async || HttpResponse::Ok().body("test"))),
                )
            })
            .workers(1)
            .backlog(1)
            .maxconn(10)
            .maxconnrate(10)
            .server_hostname("localhost")
            .stop_runtime()
            .disable_signals()
            .bind(
                format!("{addr}"),
                ntex::SharedCfg::new("WEB")
                    .add(
                        HttpServiceConfig::new()
                            .set_keepalive(10)
                            .set_client_timeout(Seconds(5)),
                    )
                    .add(IoConfig::new().set_disconnect_timeout(Seconds(1)))
                    .add(TlsConfig::new().set_handshake_timeout(Seconds(1))),
            )
            .unwrap()
            .run();
            let _ = tx.send((srv, ntex::rt::System::current()));
            Ok(())
        })
    });
    let (srv, sys) = rx.recv().unwrap();

    use ntex::client;

    let client = client::Client::with_config(
        ntex::SharedCfg::new("DBG").add(IoConfig::new().set_connect_timeout(30)),
    );

    let host = format!("http://{addr}");
    let response = client.get(host.clone()).send().await.unwrap();
    assert!(response.status().is_success());

    // stop
    srv.stop(false).await;

    thread::sleep(Duration::from_millis(25));
    sys.stop();
}

#[cfg(feature = "openssl")]
fn ssl_acceptor() -> std::io::Result<SslAcceptorBuilder> {
    use tls_openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode};
    // load ssl keys
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
    builder.set_verify(SslVerifyMode::NONE);
    builder
        .set_private_key_file("./tests/key.pem", SslFiletype::PEM)
        .unwrap();
    builder
        .set_certificate_chain_file("./tests/cert.pem")
        .unwrap();
    Ok(builder)
}

#[cfg(feature = "openssl")]
async fn client() -> ntex::client::Client {
    use tls_openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
    let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
    builder.set_verify(SslVerifyMode::NONE);
    let _ = builder
        .set_alpn_protos(b"\x02h2\x08http/1.1")
        .map_err(|e| log::error!("Cannot set alpn protocol: {e:?}"));

    ntex::client::Client::builder()
        .openssl(builder.build())
        .build(
            SharedCfg::new("TEST")
                .add(IoConfig::new().set_connect_timeout(30))
                .add(ntex::client::ClientConfig::new().set_response_timeout(Seconds(30))),
        )
}

#[ntex::test]
#[cfg(feature = "openssl")]
async fn test_openssl() {
    use ntex::web::HttpRequest;

    let addr = TestServer::unused_addr();
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let sys = ntex::rt::System::new("test", ntex::rt::DefaultRuntime);
        let builder = ssl_acceptor().unwrap();

        sys.run(move || {
            let srv = HttpServer::new(async |_| {
                App::new().service(web::resource("/").route(web::to(
                    async move |req: HttpRequest| {
                        assert!(req.app_config().secure());
                        HttpResponse::Ok().body("test")
                    },
                )))
            })
            .workers(1)
            .shutdown_timeout(Seconds(1))
            .stop_runtime()
            .disable_signals()
            .bind_openssl(
                format!("{addr}"),
                builder,
                SharedCfg::new("WEB").add(WebAppConfig::new().set_secure()),
            )
            .unwrap()
            .run();
            let _ = tx.send((srv, ntex::rt::System::current()));
            Ok(())
        })
    });
    let (srv, sys) = rx.recv().unwrap();
    thread::sleep(Duration::from_millis(100));

    let client = client().await;
    let host = format!("https://{addr}");
    let response = client.get(host.clone()).send().await.unwrap();
    assert!(response.status().is_success());

    // stop
    srv.stop(false).await;

    thread::sleep(Duration::from_millis(25));
    sys.stop();
}

#[ntex::test]
#[cfg(all(unix, feature = "rustls", feature = "openssl"))]
async fn test_rustls() {
    use ntex::web::HttpRequest;

    let addr = TestServer::unused_addr();
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let sys = ntex::rt::System::new("test", ntex::rt::DefaultRuntime);
        let config = rustls_utils::tls_acceptor();

        sys.run(move || {
            let srv = HttpServer::new(async |_| {
                App::new().service(web::resource("/").route(web::to(async |req: HttpRequest| {
                    assert!(req.app_config().secure());
                    HttpResponse::Ok().body("test")
                })))
            })
            .workers(1)
            .shutdown_timeout(Seconds(1))
            .stop_runtime()
            .disable_signals()
            .bind_rustls(
                format!("{addr}"),
                &config,
                SharedCfg::new("WEB").add(WebAppConfig::new().set_secure()),
            )
            .unwrap()
            .run();
            let _ = tx.send((srv, ntex::rt::System::current()));
            Ok(())
        })
    });
    let (srv, sys) = rx.recv().unwrap();

    let client = client().await;
    let host = format!("https://localhost:{}", addr.port());
    let response = client.get(host).send().await.unwrap();
    assert!(response.status().is_success());

    // stop
    srv.stop(false).await;

    sleep(Duration::from_millis(25)).await;
    sys.stop();
}

#[ntex::test]
#[cfg(unix)]
async fn test_bind_uds() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let sys = ntex::rt::System::new("test", ntex::rt::DefaultRuntime);

        sys.run(move || {
            let srv = HttpServer::new(async |_| {
                App::new().service(
                    web::resource("/").route(web::to(async || HttpResponse::Ok().body("test"))),
                )
            })
            .workers(1)
            .shutdown_timeout(Seconds(1))
            .stop_runtime()
            .disable_signals()
            .bind_uds("/tmp/uds-test", SharedCfg::default())
            .unwrap()
            .run();
            let _ = tx.send((srv, ntex::rt::System::current()));
            Ok(())
        })
    });
    let (srv, sys) = rx.recv().unwrap();

    use ntex::client;

    let client = client::Client::builder()
        .connector(async |st: &SharedCfg, _| {
            Ok(rt::unix_connect("/tmp/uds-test", st.clone())
                .await
                .map_err(ntex::connect::ConnectError::from)?)
        })
        .build(SharedCfg::default());
    let response = client.get("http://localhost").send().await.unwrap();
    assert!(response.status().is_success());

    // stop
    srv.stop(false).await;

    sleep(Duration::from_millis(25)).await;
    sys.stop();
}

#[ntex::test]
#[cfg(unix)]
async fn test_listen_uds() {
    let (tx, rx) = mpsc::channel();

    thread::spawn(move || {
        let sys = ntex::rt::System::new("test", ntex::rt::DefaultRuntime);

        sys.run(move || {
            let _ = std::fs::remove_file("/tmp/uds-test2");
            let lst = std::os::unix::net::UnixListener::bind("/tmp/uds-test2").unwrap();

            let srv = HttpServer::new(async |_| {
                App::new().service(
                    web::resource("/").route(web::to(async || HttpResponse::Ok().body("test"))),
                )
            })
            .workers(1)
            .shutdown_timeout(Seconds(1))
            .stop_runtime()
            .disable_signals()
            .listen_uds(lst, SharedCfg::default())
            .unwrap()
            .run();
            let _ = tx.send((srv, ntex::rt::System::current()));
            Ok(())
        })
    });
    let (srv, sys) = rx.recv().unwrap();

    use ntex::client;

    let client = client::Client::builder()
        .connector(async |st: &SharedCfg, _| {
            Ok(rt::unix_connect("/tmp/uds-test2", st.clone())
                .await
                .map_err(ntex::connect::ConnectError::from)?)
        })
        .build(SharedCfg::default());
    let response = client.get("http://localhost").send().await.unwrap();
    assert!(response.status().is_success());

    // stop
    srv.stop(false).await;

    sleep(Duration::from_millis(25)).await;
    sys.stop();
}