1pub mod adaptor;
5pub mod proc;
7pub(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 }
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 let (bus, main) = MainProc::<SimpleStringTvf>::create(&settings, Some(1));
88
89 let main_task = main.run();
91
92 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 std::thread::sleep(Duration::from_secs(1));
103
104 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 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_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_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 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_test(test_settings, Some(client_cert), true).await;
226 }
227}