credence_lib/middleware/
render.rs1use super::{
2 super::{configuration::*, render::*},
3 defer::*,
4 socket::*,
5};
6
7use {
8 ::axum::{
9 extract::{Request, *},
10 http::{header::*, *},
11 middleware::*,
12 response::Response,
13 },
14 kutil::{http::*, std::immutable::*},
15 std::{cmp::*, result::Result},
16};
17
18#[derive(Clone, Debug)]
24pub struct RenderMiddleware {
25 pub configuration: CredenceConfiguration,
27
28 pub templates: Templates,
30}
31
32impl RenderMiddleware {
33 pub fn new(configuration: CredenceConfiguration) -> Self {
35 let templates = configuration.files.templates();
36 Self::new_with(configuration, templates)
37 }
38
39 pub fn new_with(configuration: CredenceConfiguration, templates: Templates) -> Self {
41 Self { configuration, templates }
42 }
43
44 pub async fn function(
46 State(state_self): State<Self>,
47 mut request: Request,
48 next: Next,
49 ) -> Result<Response, StatusCode> {
50 let uri_path: ByteString = match request.uri().decoded_path() {
51 Some(uri_path) => uri_path.as_ref().into(),
52 None => {
53 tracing::error!("cannot decode path: {}", request.uri());
55 return Err(StatusCode::INTERNAL_SERVER_ERROR);
56 }
57 };
58
59 if let Some(rendered_page_type) = state_self.configuration.render.is_rendered_page(&uri_path) {
60 let mut last_modified = None;
62 if let Some(if_modified_since) = request.headers().if_modified_since() {
63 let path = state_self.configuration.files.asset(&uri_path);
64 let mut modified = file_modified(&path).map_err_internal_server("file modified")?;
65
66 if let Some(coordinator_modified) = state_self
67 .configuration
68 .files
69 .coordinate
70 .coordinator_modified()
71 .map_err_internal_server("coordinator modified")?
72 {
73 modified = max(modified, coordinator_modified);
74 }
75
76 if modified_since(Some(modified), Some(if_modified_since)) {
77 last_modified = Some(modified);
78
79 let headers = request.headers_mut();
81 headers.remove(IF_MODIFIED_SINCE);
82 headers.remove(IF_NONE_MATCH);
83 }
84 }
85
86 let original_uri_path = DeferredResponse::get(&request).and_then(|deferred_response| {
87 if let DeferredResponse::RewriteFrom(original_uri_path) = deferred_response {
88 Some(original_uri_path.clone())
89 } else {
90 None
91 }
92 });
93
94 let is_json = is_json(&request);
95
96 let query = request.uri().decoded_query_map();
97
98 let socket = request.extensions().get::<Socket>().cloned();
99
100 let response = next.run(request).await;
101
102 if response.status() == StatusCode::OK {
103 tracing::debug!("rendered page: {}", uri_path);
104
105 let rendered_page = RenderedPage::new_from_response(
106 &uri_path,
107 rendered_page_type,
108 response,
109 &state_self.configuration.render,
110 )
111 .await?;
112
113 let mut context = rendered_page.context(
114 socket,
115 uri_path,
116 original_uri_path,
117 query,
118 last_modified,
119 is_json,
120 &state_self.templates,
121 &state_self.configuration,
122 );
123
124 context.prepare_default().await?;
125 context.prepare(CatalogPreparer).await?;
126
127 context.into_response().await
128 } else {
129 Ok(response)
130 }
131 } else {
132 Ok(next.run(request).await)
133 }
134 }
135}
136
137fn is_json(request: &Request) -> (bool, bool) {
138 if request
139 .headers()
140 .accept()
141 .best(RENDERED_PAGE_MEDIA_TYPES)
142 .map(|media_type_selector| media_type_selector == &JSON_MEDIA_TYPE)
143 .unwrap_or_default()
144 {
145 if let Some(query) = request.uri().decoded_query_map() {
146 return (true, query.get_single_as_ref("pretty") == Some("true"));
147 }
148
149 (true, false)
150 } else {
151 (false, false)
152 }
153}