1use apimock_config::Config;
17use apimock_routing::ParsedRequest;
18use console::style;
19use http_body_util::{BodyExt, Empty};
20use hyper::{
21 HeaderMap, Response, body,
22 header::{CONTENT_LENGTH, HeaderValue},
23 service::service_fn,
24};
25use hyper_util::{
26 rt::{TokioExecutor, TokioIo},
27 server::conn::auto::Builder,
28};
29use rustls::ServerConfig;
30use tokio::net::TcpListener;
31use tokio::sync::Mutex;
32use tokio_rustls::TlsAcceptor;
33
34use std::net::{SocketAddr, ToSocketAddrs};
35use std::sync::Arc;
36
37use crate::{
38 dyn_route::dyn_route_content,
39 error::{ServerError, ServerResult},
40 middleware::LoadedMiddlewares,
41 parsed_request::{capture_in_log, parsed_request_from},
42 respond_response::respond_response,
43 response::error_response::internal_server_error_response,
44 response_handler::default_response_headers,
45 tls::{load_certs, load_private_key},
46 types::BoxBody,
47};
48
49pub use crate::control::{ReloadHint, ServerControl, ServerHandle, ServerState};
50
51#[derive(Clone)]
53pub struct AppState {
54 pub config: Config,
55 pub middlewares: LoadedMiddlewares,
56}
57
58pub struct Server {
60 pub app_state: AppState,
61 pub http_addr: Option<SocketAddr>,
62 pub https_addr: Option<SocketAddr>,
63}
64
65impl Server {
66 pub async fn new(config: Config) -> ServerResult<Self> {
73 let http_addr = resolve_listener(config.listener_http_addr().as_deref())?;
74 let https_addr = resolve_listener(config.listener_https_addr().as_deref())?;
75
76 let relative_dir_path = config
78 .current_dir_to_parent_dir_relative_path()
79 .map_err(ServerError::Config)?;
80
81 let middlewares = LoadedMiddlewares::compile(
82 config.service.middlewares_file_paths.as_deref().unwrap_or(&[]),
83 relative_dir_path.as_str(),
84 )?;
85 if !middlewares.is_empty() {
86 log::info!("middleware is activated: {} file(s)", middlewares.len());
87 }
88
89 Ok(Server {
90 http_addr,
91 https_addr,
92 app_state: AppState {
93 config,
94 middlewares,
95 },
96 })
97 }
98
99 pub async fn start(&self) {
101 let http = self.http_start();
102 let https = self.https_start();
103 tokio::join!(http, https);
104 }
105
106 async fn http_start(&self) {
107 let Some(addr) = self.http_addr else {
108 return;
109 };
110
111 let listener = match TcpListener::bind(addr).await {
112 Ok(l) => l,
113 Err(err) => {
114 log::error!("failed to bind HTTP listener at {}: {}", addr, err);
115 return;
116 }
117 };
118
119 log::info!(
120 "Greetings from apimock-rs (API Mock) !!\nListening on {} ...\n",
121 style(format!("http://{}", addr)).cyan()
122 );
123
124 let app_state = Arc::new(Mutex::new(self.app_state.clone()));
125 loop {
126 let (stream, _) = match listener.accept().await {
127 Ok(pair) => pair,
128 Err(err) => {
129 log::error!("HTTP accept failed: {}", err);
130 continue;
131 }
132 };
133 let io = TokioIo::new(stream);
134
135 let app_state = app_state.clone();
136 tokio::task::spawn(async move {
137 if let Err(err) = Builder::new(TokioExecutor::new())
138 .serve_connection(
139 io,
140 service_fn(move |request: hyper::Request<body::Incoming>| {
141 service(request, app_state.clone())
142 }),
143 )
144 .await
145 {
146 log::error!("{} to build connection: {:?}", style("failed").red(), err);
147 }
148 });
149 }
150 }
151
152 async fn https_start(&self) {
153 let Some(addr) = self.https_addr else {
154 return;
155 };
156
157 let tls = match self
158 .app_state
159 .config
160 .listener
161 .as_ref()
162 .and_then(|l| l.tls.as_ref())
163 {
164 Some(t) => t.clone(),
165 None => {
166 log::error!("internal: HTTPS listener scheduled without TLS config");
167 return;
168 }
169 };
170
171 let certs = match load_certs(tls.cert.as_str()) {
172 Ok(c) => c,
173 Err(err) => {
174 log::error!("{}", err);
175 return;
176 }
177 };
178 let key = match load_private_key(tls.key.as_str()) {
179 Ok(k) => k,
180 Err(err) => {
181 log::error!("{}", err);
182 return;
183 }
184 };
185
186 let mut config = match ServerConfig::builder()
187 .with_no_client_auth()
188 .with_single_cert(certs, key)
189 {
190 Ok(c) => c,
191 Err(err) => {
192 log::error!("failed to build rustls ServerConfig: {}", err);
193 return;
194 }
195 };
196 config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
197 let acceptor = TlsAcceptor::from(Arc::new(config));
198
199 let listener = match TcpListener::bind(addr).await {
200 Ok(l) => l,
201 Err(err) => {
202 log::error!("failed to bind HTTPS listener at {}: {}", addr, err);
203 return;
204 }
205 };
206
207 log::info!(
208 "Greetings from apimock-rs (API Mock) !!\nListening on {} ...\n",
209 style(format!("https://{}", addr)).cyan()
210 );
211
212 let app_state = Arc::new(Mutex::new(self.app_state.clone()));
213 loop {
214 let (stream, _) = match listener.accept().await {
215 Ok(pair) => pair,
216 Err(err) => {
217 log::error!("HTTPS accept failed: {}", err);
218 continue;
219 }
220 };
221 let acceptor = acceptor.clone();
222 let app_state = app_state.clone();
223
224 tokio::spawn(async move {
225 let tls_stream = match acceptor.accept(stream).await {
226 Ok(s) => s,
227 Err(e) => {
228 log::error!("TLS handshake failed: {:?}", e);
229 return;
230 }
231 };
232 let io = TokioIo::new(tls_stream);
233 let app_state = app_state.clone();
234 tokio::task::spawn(async move {
235 if let Err(err) = Builder::new(TokioExecutor::new())
236 .serve_connection(
237 io,
238 service_fn(move |request: hyper::Request<body::Incoming>| {
239 service(request, app_state.clone())
240 }),
241 )
242 .await
243 {
244 log::error!("{} to build connection: {:?}", style("failed").red(), err);
245 }
246 });
247 });
248 }
249 }
250}
251
252fn resolve_listener(addr_str: Option<&str>) -> ServerResult<Option<SocketAddr>> {
254 let Some(addr_str) = addr_str else {
255 return Ok(None);
256 };
257
258 let mut addrs = addr_str
259 .to_socket_addrs()
260 .map_err(|e| ServerError::ListenerAddress {
261 addr: addr_str.to_owned(),
262 reason: e.to_string(),
263 })?;
264
265 addrs
266 .next()
267 .map(Some)
268 .ok_or_else(|| ServerError::ListenerAddress {
269 addr: addr_str.to_owned(),
270 reason: "address resolved to no socket addresses".to_owned(),
271 })
272}
273
274pub async fn service(
281 request: hyper::Request<body::Incoming>,
282 app_state: Arc<Mutex<AppState>>,
283) -> Result<hyper::Response<BoxBody>, hyper::http::Error> {
284 let request_headers = request.headers().clone();
285
286 if request.method() == hyper::Method::OPTIONS {
287 return handle_options(&request_headers);
288 }
289
290 let parsed_request = match parsed_request_from(request).await {
291 Ok(x) => x,
292 Err(err) => return internal_server_error_response(err.as_str(), &request_headers),
293 };
294
295 let shared_app_state = { app_state.lock().await.clone() };
296
297 let config = shared_app_state.config;
298 let middlewares = shared_app_state.middlewares;
299
300 capture_in_log(&parsed_request, config.log.clone().unwrap_or_default().verbose);
301
302 if let Some(response) = middleware_response(&middlewares, &parsed_request).await {
303 return response;
304 }
305
306 if let Some(response) = rule_set_response(&config, &parsed_request).await {
307 return response;
308 }
309
310 dyn_route_content(
311 parsed_request.url_path.as_str(),
312 config.service.fallback_respond_dir.as_str(),
313 &request_headers,
314 )
315 .await
316}
317
318async fn middleware_response(
320 middlewares: &LoadedMiddlewares,
321 parsed_request: &ParsedRequest,
322) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
323 for handler in middlewares.iter() {
324 match handler
325 .handle(
326 parsed_request.url_path.as_str(),
327 parsed_request.body_json.as_ref(),
328 &parsed_request.component_parts.headers,
329 )
330 .await
331 {
332 Some(x) => return Some(x),
333 None => continue,
334 }
335 }
336 None
337}
338
339async fn rule_set_response(
341 config: &Config,
342 parsed_request: &ParsedRequest,
343) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
344 for (rule_set_idx, rule_set) in config.service.rule_sets.iter().enumerate() {
345 if let Some(respond) =
346 rule_set.find_matched(parsed_request, config.service.strategy.as_ref(), rule_set_idx)
347 {
348 let dir_prefix = rule_set.dir_prefix();
349 return Some(respond_response(&respond, dir_prefix.as_str(), parsed_request).await);
350 }
351 }
352 None
353}
354
355fn handle_options(
357 request_headers: &HeaderMap,
358) -> Result<hyper::Response<BoxBody>, hyper::http::Error> {
359 let mut response = Response::new(Empty::new().boxed());
360 *response.status_mut() = hyper::StatusCode::NO_CONTENT;
361 response
362 .headers_mut()
363 .insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
364
365 for (header_key, header_value) in default_response_headers(request_headers).into_iter() {
366 if let Some(header_key) = header_key {
367 response.headers_mut().insert(header_key, header_value);
368 }
369 }
370
371 Ok(response)
372}