static_file_server_lib/startup/
server.rs1use crate::cli::CliArgs;
2use crate::terminal::colored_msg;
3use anyhow::anyhow;
4use axum::http::{header, HeaderValue};
5use axum::Router;
6use std::fs;
7use std::net::SocketAddr;
8use termcolor::Color;
9use tokio::net::TcpListener;
10use tower_http::services::ServeDir;
11use tower_http::set_header::{SetRequestHeaderLayer, SetResponseHeaderLayer};
12use tower_http::trace::TraceLayer;
13use tracing_subscriber::layer::SubscriberExt;
14use tracing_subscriber::util::SubscriberInitExt;
15
16pub struct Server {
17 port: u16,
18 listener: TcpListener,
19 addr: String,
20 cli_args: CliArgs,
21 routes: Router,
22}
23
24impl Server {
25 pub async fn new(cli_args: &CliArgs) -> anyhow::Result<Server> {
26 tracing_subscriber::registry()
27 .with(
28 tracing_subscriber::EnvFilter::try_from_default_env()
29 .unwrap_or_else(|_| "static_file_server=debug,tower_http=debug".into()),
30 )
31 .with(tracing_subscriber::fmt::layer())
32 .init();
33
34 let routes = Server::routes(cli_args)?;
35 let listener = Server::listen(cli_args).await?;
36 let port = listener.local_addr()?.port();
37
38 Ok(Server {
39 port,
40 addr: format!("http://localhost:{}", port),
41 cli_args: cli_args.clone(),
42 listener,
43 routes,
44 })
45 }
46
47 fn routes(cli_args: &CliArgs) -> anyhow::Result<Router> {
48 if fs::metadata(&cli_args.dir).is_err() {
49 colored_msg(&format!("Dir {} does not exist", cli_args.dir), Color::Red)?;
50 return Err(anyhow!(format!("Dir {} does not exist", cli_args.dir)));
51 } else {
52 colored_msg(&format!("Found dir {}", cli_args.dir), Color::Green)?;
53 }
54 let mut routes: Router;
55 routes = if cli_args.compression_dir {
56 Router::new().nest_service("/", ServeDir::new(&cli_args.dir).precompressed_gzip())
57 } else {
58 Router::new().nest_service("/", ServeDir::new(&cli_args.dir))
59 };
60 routes = if cli_args.compression_response {
61 routes
62 .layer(tower_http::compression::CompressionLayer::new())
63 .layer(SetRequestHeaderLayer::if_not_present(
64 header::ACCEPT_ENCODING,
65 HeaderValue::from_static("gzip"),
66 ))
67 } else {
68 routes
69 };
70 routes = if cli_args.cors {
71 routes
72 .layer(SetResponseHeaderLayer::if_not_present(
73 header::ACCESS_CONTROL_ALLOW_METHODS,
74 HeaderValue::from_static("*"),
75 ))
76 .layer(SetResponseHeaderLayer::if_not_present(
77 header::ACCESS_CONTROL_ALLOW_HEADERS,
78 HeaderValue::from_static("*"),
79 ))
80 .layer(SetResponseHeaderLayer::if_not_present(
81 header::ACCESS_CONTROL_ALLOW_ORIGIN,
82 HeaderValue::from_static("*"),
83 ))
84 } else {
85 routes
86 };
87 Ok(routes)
88 }
89
90 async fn listen(args: &CliArgs) -> anyhow::Result<TcpListener> {
91 let addr = SocketAddr::from(([127, 0, 0, 1], args.port));
92 let listener = TcpListener::bind(addr).await?;
93 Ok(listener)
94 }
95
96 pub async fn serve(self) -> anyhow::Result<()> {
97 colored_msg(&format!("Options:\n{:#?}", &self.cli_args,), Color::Green)?;
98 colored_msg(
99 &format!(
100 "Listening on = http://localhost:{} | {}",
101 &self.port, &self.addr
102 ),
103 Color::Green,
104 )?;
105 axum::serve(self.listener, self.routes.layer(TraceLayer::new_for_http()))
106 .await
107 .unwrap();
108 Ok(())
109 }
110}