Skip to main content

prosa_hyper/
server.rs

1//! Module to handle HTTP server
2
3/// Adaptor for Hyper server processor
4pub mod adaptor;
5/// ProSA Hyper server processor
6pub mod proc;
7/// Hyper service definition
8pub(crate) mod service;
9
10#[cfg(test)]
11mod tests {
12    use bytes::Bytes;
13    use http_body_util::{Full, combinators::BoxBody};
14    use hyper::{Request, StatusCode};
15    use prosa::core::{
16        adaptor::Adaptor,
17        error::ProcError,
18        main::{MainProc, MainRunnable as _},
19        proc::{Proc, ProcConfig as _},
20    };
21    use prosa_utils::{
22        config::ssl::{SslConfig, Store},
23        msg::simple_string_tvf::SimpleStringTvf,
24    };
25    use reqwest::Certificate;
26    use std::{
27        env,
28        fs::{self, File},
29        io::Read as _,
30        time::Duration,
31    };
32    use tokio::time;
33    use url::Url;
34
35    use crate::{
36        HyperResp,
37        server::{adaptor::HyperServerAdaptor, proc::HyperServerProc},
38        tests::HttpTestSettings,
39    };
40
41    const WAIT_TIME: time::Duration = time::Duration::from_secs(5);
42
43    #[derive(Adaptor, Clone)]
44    struct ServerTestAdaptor {
45        // Nothing
46    }
47
48    impl<M> HyperServerAdaptor<M> for ServerTestAdaptor
49    where
50        M: 'static
51            + std::marker::Send
52            + std::marker::Sync
53            + std::marker::Sized
54            + std::clone::Clone
55            + std::fmt::Debug
56            + prosa_utils::msg::tvf::Tvf
57            + std::default::Default,
58    {
59        fn new(
60            _proc: &crate::server::proc::HyperServerProc<M>,
61        ) -> Result<Self, Box<dyn ProcError + Send + Sync>>
62        where
63            Self: Sized,
64        {
65            Ok(ServerTestAdaptor {})
66        }
67
68        async fn process_http_request(
69            &self,
70            req: Request<hyper::body::Incoming>,
71        ) -> HyperResp<Self, M> {
72            let resp_msg = if req.version() == hyper::Version::HTTP_2 {
73                "Hello, H2 world"
74            } else {
75                "Hello, world"
76            };
77            <ServerTestAdaptor as HyperServerAdaptor<M>>::response_builder(self, StatusCode::OK)
78                .body(BoxBody::new(Full::new(Bytes::from(resp_msg))))
79                .into()
80        }
81    }
82
83    async fn run_test(settings: HttpTestSettings, certificate: Option<Certificate>, http2: bool) {
84        let url = settings.server.listener.url.clone();
85
86        // Create bus and main processor
87        let (bus, main) = MainProc::<SimpleStringTvf>::create(&settings, Some(1));
88
89        // Launch the main task
90        let main_task = main.run();
91
92        // Launch an HTTP server processor
93        let http_server_proc = HyperServerProc::<SimpleStringTvf>::create(
94            1,
95            String::from("HTTP_SERVER_PROC"),
96            bus.clone(),
97            settings.server,
98        );
99        Proc::<ServerTestAdaptor>::run(http_server_proc);
100
101        // Wait for processor to start
102        std::thread::sleep(Duration::from_secs(1));
103
104        // Send request to the server with reqwest
105        let mut client_builder = reqwest::ClientBuilder::new()
106            .timeout(Duration::from_secs(WAIT_TIME.as_secs()))
107            .use_rustls_tls();
108        if let Some(cert) = certificate {
109            client_builder = client_builder.add_root_certificate(cert);
110        }
111        if http2 {
112            client_builder = client_builder.http2_prior_knowledge();
113        }
114        let client = client_builder.build().unwrap();
115        for _i in 0..20 {
116            let resp = client
117                .get(url.clone())
118                .send()
119                .await
120                .expect("Failed to send request");
121            assert_eq!(resp.status(), StatusCode::OK);
122            let server_header = resp.headers().get(hyper::header::SERVER).unwrap();
123            assert!(server_header.to_str().unwrap().starts_with(concat!(
124                env!("CARGO_PKG_NAME"),
125                "/",
126                env!("CARGO_PKG_VERSION")
127            )));
128        }
129
130        bus.stop("ProSA HTTP client server unit test end".into())
131            .await
132            .unwrap();
133
134        // Wait on main task to end
135        main_task.await;
136    }
137
138    #[tokio::test]
139    async fn http_client_server() {
140        let test_settings =
141            HttpTestSettings::new(Url::parse("http://localhost:48180").unwrap(), None, None);
142
143        // Run a ProSA to test
144        run_test(test_settings, None, false).await;
145    }
146
147    #[tokio::test]
148    async fn https_client_server() {
149        const PROSA_HTTPS_TEST_DIR_NAME: &str = "ProSA_server_HTTPS";
150        let prosa_temp_dir = env::temp_dir().join(PROSA_HTTPS_TEST_DIR_NAME);
151
152        let _ = fs::remove_dir_all(&prosa_temp_dir);
153        fs::create_dir_all(&prosa_temp_dir).unwrap();
154
155        let key_path = prosa_temp_dir.join("prosa_server_https.key");
156        let cert_path = prosa_temp_dir.join("prosa_server_https.pem");
157        let server_ssl_config = HttpTestSettings::create_server_cert(
158            key_path.as_os_str().to_str().unwrap().into(),
159            cert_path.as_os_str().to_str().unwrap().into(),
160        )
161        .unwrap();
162
163        let mut buf = Vec::new();
164        File::open(cert_path.as_os_str().to_str().unwrap())
165            .unwrap()
166            .read_to_end(&mut buf)
167            .unwrap();
168        let client_cert = reqwest::Certificate::from_pem(&buf).unwrap();
169
170        let client_ssl_store = Store::File {
171            path: format!("{}/", prosa_temp_dir.as_os_str().to_str().unwrap()),
172        };
173        let mut client_ssl_config = SslConfig::default();
174        client_ssl_config.set_store(client_ssl_store);
175
176        let test_settings = HttpTestSettings::new(
177            Url::parse("https://localhost:48543").unwrap(),
178            Some(server_ssl_config),
179            Some(client_ssl_config),
180        );
181
182        // Run a ProSA to test
183        run_test(test_settings, Some(client_cert), false).await;
184    }
185
186    #[tokio::test]
187    async fn h2_client_server() {
188        const PROSA_H2_TEST_DIR_NAME: &str = "ProSA_server_H2";
189        let prosa_temp_dir = env::temp_dir().join(PROSA_H2_TEST_DIR_NAME);
190
191        let _ = fs::remove_dir_all(&prosa_temp_dir);
192        fs::create_dir_all(&prosa_temp_dir).unwrap();
193
194        let key_path = prosa_temp_dir.join("prosa_server_h2.key");
195        let cert_path = prosa_temp_dir.join("prosa_server_h2.pem");
196        let mut server_ssl_config = HttpTestSettings::create_server_cert(
197            key_path.as_os_str().to_str().unwrap().into(),
198            cert_path.as_os_str().to_str().unwrap().into(),
199        )
200        .unwrap();
201        // Need to set the ALPN for server because of inline configuration @see TargetSetting::new
202        server_ssl_config.set_alpn(vec!["h2".into()]);
203
204        let mut buf = Vec::new();
205        File::open(cert_path.as_os_str().to_str().unwrap())
206            .unwrap()
207            .read_to_end(&mut buf)
208            .unwrap();
209        let client_cert = reqwest::Certificate::from_pem(&buf).unwrap();
210
211        let client_ssl_store = Store::File {
212            path: format!("{}/", prosa_temp_dir.as_os_str().to_str().unwrap()),
213        };
214        let mut client_ssl_config = SslConfig::default();
215        client_ssl_config.set_store(client_ssl_store);
216        client_ssl_config.set_alpn(vec!["h2".into()]);
217
218        let test_settings = HttpTestSettings::new(
219            Url::parse("https://localhost:49543").unwrap(),
220            Some(server_ssl_config),
221            Some(client_ssl_config),
222        );
223
224        // Run a ProSA to test
225        run_test(test_settings, Some(client_cert), true).await;
226    }
227}