credence_lib/server/
routers.rs

1use super::super::{configuration::*, middleware::*};
2
3use {
4    ::axum::{
5        extract::{Request, *},
6        http::{header::*, *},
7        middleware::*,
8        response::*,
9        routing::*,
10    },
11    kutil::{
12        http::{
13            axum::*,
14            cache::{axum::*, *},
15        },
16        std::immutable::*,
17    },
18    tower_http::{limit::*, services::*, timeout::*, trace::*},
19};
20
21/// Create a Credence site router.
22pub fn new_site_router<CacheT>(shutdown: &Shutdown, cache: &CacheT, configuration: &CredenceConfiguration) -> Router
23where
24    CacheT: Cache<CommonCacheKey>,
25{
26    let admin_router = Router::default()
27        .route("/about", get(about_handler))
28        .route("/shutdown", post(shutdown_handler))
29        .with_state(shutdown.clone())
30        .route("/reset-cache", post(reset_cache_handler::<CacheT, _>))
31        .with_state(cache.clone())
32        .route("/status/{status_code}", get(status_code_handler));
33
34    let router = Router::default()
35        .fallback_service(ServeDir::new(&configuration.files.assets).append_index_html_on_directories(false))
36        .nest("/admin", admin_router)
37        .layer(from_fn_with_state(RenderMiddleware::new(configuration.clone()), RenderMiddleware::function))
38        .layer(configuration.caching_layer(cache.clone()))
39        .layer(from_fn_with_state(CatchMiddleware::new(configuration.files.status.clone()), CatchMiddleware::function));
40
41    // Request rewriting cannot happen in the handling router
42    // https://docs.rs/axum/latest/axum/middleware/index.html#rewriting-request-uri-in-middleware
43
44    let router = Router::default()
45        .merge(router)
46        .layer(map_request_with_state(FacadeMiddleware::new(configuration.clone()), FacadeMiddleware::function))
47        .layer(RequestBodyLimitLayer::new(configuration.requests.max_body_size.inner.into()))
48        .layer(TimeoutLayer::new(configuration.requests.max_duration.inner.into()))
49        .layer(TraceLayer::new_for_http());
50
51    router
52}
53
54/// Create a Credence redirecting router.
55pub fn new_redirecting_router(tls: bool, host: ByteString, tcp_port: u16) -> Router {
56    let scheme = if tls { "https://" } else { "http://" };
57
58    let tcp_port = match tcp_port {
59        80 | 443 => Default::default(),
60        _ => format!(":{}", tcp_port),
61    };
62
63    Router::default().fallback(async move |request: Request| {
64        let path_and_query = request
65            .uri()
66            .path_and_query()
67            .map(|path_and_query| path_and_query.to_string())
68            .unwrap_or_else(|| "/".into());
69
70        let uri = format!("{}{}{}{}", scheme, host, tcp_port, path_and_query);
71
72        (StatusCode::MOVED_PERMANENTLY, [(LOCATION, uri)]).into_response()
73    })
74}
75
76async fn status_code_handler(Path(status_code): Path<u16>) -> StatusCode {
77    tracing::debug!("status code: {}", status_code);
78    StatusCode::from_u16(status_code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
79}
80
81build_info::build_info!(fn build_info);
82
83async fn about_handler() -> impl IntoResponse {
84    let build_info = build_info();
85    let mut about = String::default();
86
87    about += &format!("credence-lib: {}\n", env!("CARGO_PKG_VERSION"));
88
89    if let Some(version_control) = &build_info.version_control
90        && let Some(git) = version_control.git()
91    {
92        about += "\n";
93        about += &format!("git-commit-id: {}\n", git.commit_id);
94        about += &format!("git-commit-timestamp: {}\n", git.commit_timestamp);
95        if let Some(branch) = &git.branch {
96            about += &format!("git-commit-branch: {}\n", branch);
97        }
98        if !git.tags.is_empty() {
99            about += &format!("git-commit-tags: {}\n", git.tags.join(","));
100        }
101        about += &format!("git-dirty: {}]\n", git.dirty);
102    }
103
104    about += "\n";
105    about += &format!("binary-cpu: {}\n", build_info.target.cpu.arch);
106    about += &format!("binary-cpu-bits: {}]\n", build_info.target.cpu.pointer_width);
107    about += &format!("binary-cpu-endianness: {}\n", build_info.target.cpu.endianness.to_string().to_lowercase());
108    if !build_info.target.cpu.features.is_empty() {
109        about += &format!("binary-cpu-features: {}\n", build_info.target.cpu.features.join(","));
110    }
111    about += &format!("binary-os: {}\n", build_info.target.os);
112    about += &format!("binary-architecture: {}\n", build_info.target.triple);
113
114    about += "\n";
115    about += &format!("compilation-timestamp: {}\n", build_info.timestamp);
116    about += &format!("compilation-profile: {}\n", build_info.profile);
117    about += &format!("compilation-optimization-level: {}\n", build_info.optimization_level);
118
119    about += "\n";
120    about += "compiler: rustc\n";
121    about += &format!("compiler-version: {}\n", build_info.compiler.version);
122    about += &format!("compiler-channel: {}\n", build_info.compiler.channel.to_string().to_lowercase());
123    if let Some(commit_id) = &build_info.compiler.commit_id {
124        about += &format!("compiler-git-commit-id: {}\n", commit_id);
125    }
126    if let Some(commit_date) = &build_info.compiler.commit_date {
127        about += &format!("compiler-git-commit-date: {}\n", commit_date);
128    }
129
130    ([("content-type", "text/plain")], about)
131}