1use std::net::{SocketAddr, ToSocketAddrs};
2use std::path::Path;
3use std::sync::Arc;
4
5use console::style;
6use hyper::body::Bytes;
7use hyper::{body, service::service_fn, Request, Response};
8use hyper_util::{
9 rt::{TokioExecutor, TokioIo},
10 server::conn::auto::Builder,
11};
12use rhai::Engine;
13use std::convert::Infallible;
14use tokio::sync::mpsc::Sender;
15use tokio::{net::TcpListener, sync::Mutex};
16
17use super::app_state::{AppState, Middleware};
18use super::args::EnvArgs;
19use super::config::{Config, ConfigUrlPaths, ConfigUrlPathsJsonpathPatterns};
20use super::constant::APP_NAME;
21use super::logger::init_logger;
22use super::server::handle;
23
24type BoxBody = http_body_util::combinators::BoxBody<Bytes, Infallible>;
25
26pub struct App {
28 addr: SocketAddr,
29 listener: TcpListener,
30 app_state: AppState,
31}
32
33impl App {
34 pub async fn new(
38 env_args: EnvArgs,
39 spawn_tx: Option<Sender<String>>,
40 includes_ansi_codes: bool,
41 ) -> Self {
42 let _ = init_logger(spawn_tx, includes_ansi_codes);
43
44 let mut config = Config::new(env_args.config_filepath.as_ref());
45
46 if let Some(port) = env_args.port {
47 config.port = port;
48 }
49
50 let addr = config
51 .listener_address()
52 .to_socket_addrs()
53 .expect("invalid listend address or port")
54 .next()
55 .expect("failed to resolve address");
56 let listener = TcpListener::bind(addr)
57 .await
58 .expect("tcp listener failed to bind address");
59
60 let middleware = match env_args.middleware_filepath {
61 Some(middleware_filepath) if Path::new(middleware_filepath.as_str()).exists() => {
62 let engine = Engine::new();
63 let ast = engine
65 .compile_file(middleware_filepath.clone().into())
66 .expect(
67 format!(
68 "failed to compile middleware file to get ast: {}",
69 middleware_filepath
70 )
71 .as_str(),
72 );
73
74 let middleware = Middleware {
75 engine: Arc::new(engine),
76 filepath: middleware_filepath.to_owned(),
77 ast,
78 };
79
80 log::info!("\nMiddleware is activated: {}", middleware_filepath);
81 Some(middleware)
82 }
83 _ => None,
84 };
85 let app_state = AppState { config, middleware };
86
87 App {
88 addr,
89 listener,
90 app_state,
91 }
92 }
93
94 pub async fn start(&self) {
96 log::info!(
97 "\nGreetings from {APP_NAME} !!\nListening on {} ...\n",
98 style(format!("http://{}", self.addr)).cyan()
99 );
100 let app_state = Arc::new(Mutex::new(self.app_state.clone()));
101 loop {
102 let (stream, _) = self
103 .listener
104 .accept()
105 .await
106 .expect("tcp listener failed to accept");
107 let io = TokioIo::new(stream);
108
109 let app_state = app_state.clone();
110 tokio::task::spawn(async move {
111 if let Err(err) = Builder::new(TokioExecutor::new())
113 .serve_connection(
115 io,
116 service_fn(move |req: Request<body::Incoming>| {
117 service(req, app_state.clone())
118 }),
119 )
120 .await
121 {
122 log::error!("error serving connection: {:?}", err);
123 }
124 });
125 }
126 }
127
128 pub fn config_url_paths(&self) -> Option<ConfigUrlPaths> {
130 self.app_state.config.paths.clone()
131 }
132
133 pub fn config_url_paths_patterns(&self) -> Option<ConfigUrlPathsJsonpathPatterns> {
135 self.app_state.config.paths_jsonpath_patterns.clone()
136 }
137}
138
139async fn service(
141 req: Request<body::Incoming>,
142 app_state: Arc<Mutex<AppState>>,
143) -> Result<Response<BoxBody>, hyper::http::Error> {
144 handle(req, Arc::clone(&app_state)).await
145}