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