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