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 tokio::net::TcpListener;
30use tokio::sync::Mutex;
31use tokio_rustls::TlsAcceptor;
32
33use std::net::{SocketAddr, ToSocketAddrs};
34use std::sync::Arc;
35
36use crate::{
37 dyn_route::dyn_route_content,
38 error::{ServerError, ServerResult},
39 middleware::LoadedMiddlewares,
40 parsed_request::{capture_in_log, parsed_request_from},
41 respond_response::respond_response,
42 response::error_response::internal_server_error_response,
43 response_handler::default_response_headers,
44 tls::{build_server_config_reloadable, load_certs, load_private_key},
45 types::BoxBody,
46};
47
48pub use crate::control::{ReloadHint, ServerControl, ServerHandle, ServerState};
49use crate::trace::{Outcome, RequestSummary, TraceEmitter};
50
51#[derive(Clone)]
53pub struct AppState {
54 pub config: Config,
55 pub middlewares: LoadedMiddlewares,
56 pub tracer: TraceEmitter,
58}
59
60pub struct Server {
62 pub app_state: AppState,
63 pub http_addr: Option<SocketAddr>,
64 pub https_addr: Option<SocketAddr>,
65}
66
67impl Server {
68 pub async fn new(config: Config) -> ServerResult<Self> {
75 let http_addr = resolve_listener(config.listener_http_addr().as_deref())?;
76 let https_addr = resolve_listener(config.listener_https_addr().as_deref())?;
77
78 let relative_dir_path = config
80 .current_dir_to_parent_dir_relative_path()
81 .map_err(ServerError::Config)?;
82
83 let middlewares = LoadedMiddlewares::compile(
84 config
85 .service
86 .middlewares_file_paths
87 .as_deref()
88 .unwrap_or(&[]),
89 relative_dir_path.as_str(),
90 )?;
91 if !middlewares.is_empty() {
92 log::info!("middleware is activated: {} file(s)", middlewares.len());
93 }
94
95 Ok(Server {
96 http_addr,
97 https_addr,
98 app_state: AppState {
99 config,
100 middlewares,
101 tracer: TraceEmitter::new(),
102 },
103 })
104 }
105
106 pub async fn start(&self) {
108 let http = self.http_start();
109 let https = self.https_start();
110 tokio::join!(http, https);
111 }
112
113 pub async fn bind_http(&self) -> ServerResult<Option<TcpListener>> {
128 let Some(addr) = self.http_addr else {
129 return Ok(None);
130 };
131
132 let listener =
133 TcpListener::bind(addr)
134 .await
135 .map_err(|err| ServerError::ListenerAddress {
136 addr: addr.to_string(),
137 reason: err.to_string(),
138 })?;
139
140 Ok(Some(listener))
141 }
142
143 pub async fn serve_http(&self, listener: TcpListener) {
145 if let Ok(addr) = listener.local_addr() {
146 log::info!(
147 "Greetings from apimock-rs (API Mock) !!\nListening on {} ...\n",
148 style(format!("http://{}", addr)).cyan()
149 );
150 }
151
152 let app_state = Arc::new(Mutex::new(self.app_state.clone()));
153 loop {
154 let (stream, _) = match listener.accept().await {
155 Ok(pair) => pair,
156 Err(err) => {
157 log::error!("HTTP accept failed: {}", err);
158 continue;
159 }
160 };
161 let io = TokioIo::new(stream);
162
163 let app_state = app_state.clone();
164 tokio::task::spawn(async move {
165 if let Err(err) = Builder::new(TokioExecutor::new())
166 .serve_connection(
167 io,
168 service_fn(move |request: hyper::Request<body::Incoming>| {
169 service(request, app_state.clone())
170 }),
171 )
172 .await
173 {
174 log::error!("{} to build connection: {:?}", style("failed").red(), err);
175 }
176 });
177 }
178 }
179
180 async fn http_start(&self) {
181 match self.bind_http().await {
182 Ok(Some(listener)) => self.serve_http(listener).await,
183 Ok(None) => (),
184 Err(err) => log::error!("{}", err),
185 }
186 }
187
188 pub async fn bind_https(&self) -> ServerResult<Option<(TcpListener, TlsAcceptor)>> {
196 let Some(addr) = self.https_addr else {
197 return Ok(None);
198 };
199
200 let tls = self
201 .app_state
202 .config
203 .listener
204 .as_ref()
205 .and_then(|l| l.tls.as_ref())
206 .cloned()
207 .ok_or_else(|| ServerError::ListenerAddress {
208 addr: addr.to_string(),
209 reason: "internal: HTTPS listener scheduled without TLS config".to_owned(),
210 })?;
211
212 let certs = load_certs(tls.cert.as_str())?;
213 let key = load_private_key(tls.key.as_str())?;
214
215 let (tls_config, resolver) = build_server_config_reloadable(certs, key).map_err(|err| {
218 ServerError::ListenerAddress {
219 addr: addr.to_string(),
220 reason: format!("failed to build TLS config: {}", err),
221 }
222 })?;
223 let acceptor = TlsAcceptor::from(Arc::new(tls_config));
224 drop(resolver); let listener =
227 TcpListener::bind(addr)
228 .await
229 .map_err(|err| ServerError::ListenerAddress {
230 addr: addr.to_string(),
231 reason: err.to_string(),
232 })?;
233
234 Ok(Some((listener, acceptor)))
235 }
236
237 pub async fn serve_https(&self, listener: TcpListener, acceptor: TlsAcceptor) {
239 if let Ok(addr) = listener.local_addr() {
240 log::info!(
241 "Greetings from apimock-rs (API Mock) !!\nListening on {} ...\n",
242 style(format!("https://{}", addr)).cyan()
243 );
244 }
245
246 let app_state = Arc::new(Mutex::new(self.app_state.clone()));
247 loop {
248 let (stream, _) = match listener.accept().await {
249 Ok(pair) => pair,
250 Err(err) => {
251 log::error!("HTTPS accept failed: {}", err);
252 continue;
253 }
254 };
255 let acceptor = acceptor.clone();
256 let app_state = app_state.clone();
257
258 tokio::spawn(async move {
259 let tls_stream = match acceptor.accept(stream).await {
260 Ok(s) => s,
261 Err(e) => {
262 log::error!("TLS handshake failed: {:?}", e);
263 return;
264 }
265 };
266 let io = TokioIo::new(tls_stream);
267 let app_state = app_state.clone();
268 tokio::task::spawn(async move {
269 if let Err(err) = Builder::new(TokioExecutor::new())
270 .serve_connection(
271 io,
272 service_fn(move |request: hyper::Request<body::Incoming>| {
273 service(request, app_state.clone())
274 }),
275 )
276 .await
277 {
278 log::error!("{} to build connection: {:?}", style("failed").red(), err);
279 }
280 });
281 });
282 }
283 }
284
285 async fn https_start(&self) {
286 match self.bind_https().await {
287 Ok(Some((listener, acceptor))) => self.serve_https(listener, acceptor).await,
288 Ok(None) => (),
289 Err(err) => log::error!("{}", err),
290 }
291 }
292}
293
294#[allow(clippy::result_large_err)]
299fn resolve_listener(addr_str: Option<&str>) -> ServerResult<Option<SocketAddr>> {
300 let Some(addr_str) = addr_str else {
301 return Ok(None);
302 };
303
304 let mut addrs = addr_str
305 .to_socket_addrs()
306 .map_err(|e| ServerError::ListenerAddress {
307 addr: addr_str.to_owned(),
308 reason: e.to_string(),
309 })?;
310
311 addrs
312 .next()
313 .map(Some)
314 .ok_or_else(|| ServerError::ListenerAddress {
315 addr: addr_str.to_owned(),
316 reason: "address resolved to no socket addresses".to_owned(),
317 })
318}
319
320pub async fn service(
327 request: hyper::Request<body::Incoming>,
328 app_state: Arc<Mutex<AppState>>,
329) -> Result<hyper::Response<BoxBody>, hyper::http::Error> {
330 let request_headers = request.headers().clone();
331
332 if request.method() == hyper::Method::OPTIONS {
333 return handle_options(&request_headers);
334 }
335
336 let parsed_request = match parsed_request_from(request).await {
337 Ok(x) => x,
338 Err(err) => return internal_server_error_response(err.as_str(), &request_headers),
339 };
340
341 let shared_app_state = { app_state.lock().await.clone() };
342
343 let config = shared_app_state.config;
344 let middlewares = shared_app_state.middlewares;
345 let tracer = shared_app_state.tracer;
346
347 let received_at_ms = std::time::SystemTime::now()
348 .duration_since(std::time::UNIX_EPOCH)
349 .unwrap_or_default()
350 .as_millis() as u64;
351 let start = std::time::Instant::now();
352
353 capture_in_log(
354 &parsed_request,
355 config.log.clone().unwrap_or_default().verbose,
356 );
357
358 if let Some(response) = middleware_response(&middlewares, &parsed_request).await {
359 return response;
360 }
361
362 if let Some(response) = rule_set_response(&config, &parsed_request).await {
363 if tracer.has_subscribers() {
365 let mut summary = RequestSummary {
366 method: parsed_request.component_parts.method.to_string(),
367 url_path: parsed_request.url_path.clone(),
368 headers: parsed_request
369 .component_parts
370 .headers
371 .iter()
372 .filter_map(|(k, v)| v.to_str().ok().map(|v| (k.to_string(), v.to_owned())))
373 .collect(),
374 body_json: None,
375 body_truncated: false,
376 };
377 tracer.enrich_with_body(&mut summary, parsed_request.body_json.as_ref());
378 tracer.emit(
379 received_at_ms,
380 start.elapsed().as_millis() as u32,
381 summary,
382 Outcome::Miss { status: 0 }, );
384 }
385 return response;
386 }
387
388 dyn_route_content(
389 parsed_request.url_path.as_str(),
390 config.service.fallback_respond_dir.as_str(),
391 &request_headers,
392 )
393 .await
394}
395
396async fn middleware_response(
398 middlewares: &LoadedMiddlewares,
399 parsed_request: &ParsedRequest,
400) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
401 for handler in middlewares.iter() {
402 match handler
403 .handle(
404 parsed_request.url_path.as_str(),
405 parsed_request.body_json.as_ref(),
406 &parsed_request.component_parts.headers,
407 )
408 .await
409 {
410 Some(x) => return Some(x),
411 None => continue,
412 }
413 }
414 None
415}
416
417async fn rule_set_response(
419 config: &Config,
420 parsed_request: &ParsedRequest,
421) -> Option<Result<hyper::Response<BoxBody>, hyper::http::Error>> {
422 for (rule_set_idx, rule_set) in config.service.rule_sets.iter().enumerate() {
423 if let Some(respond) = rule_set.find_matched(
424 parsed_request,
425 config.service.strategy.as_ref(),
426 rule_set_idx,
427 ) {
428 let dir_prefix = rule_set.dir_prefix();
429 let rule_set_default_delay_ms = rule_set
430 .default
431 .as_ref()
432 .and_then(|default| default.delay_response_milliseconds);
433 return Some(
434 respond_response(
435 &respond,
436 dir_prefix.as_str(),
437 parsed_request,
438 rule_set_default_delay_ms,
439 )
440 .await,
441 );
442 }
443 }
444 None
445}
446
447fn handle_options(
449 request_headers: &HeaderMap,
450) -> Result<hyper::Response<BoxBody>, hyper::http::Error> {
451 let mut response = Response::new(Empty::new().boxed());
452 *response.status_mut() = hyper::StatusCode::NO_CONTENT;
453 response
454 .headers_mut()
455 .insert(CONTENT_LENGTH, HeaderValue::from_static("0"));
456
457 for (header_key, header_value) in default_response_headers(request_headers).into_iter() {
458 if let Some(header_key) = header_key {
459 response.headers_mut().insert(header_key, header_value);
460 }
461 }
462
463 Ok(response)
464}